Skip to content

Commit 8b1e152

Browse files
committed
Add option to save OpenMM system to XML file.
1 parent b9ad5f0 commit 8b1e152

4 files changed

Lines changed: 47 additions & 0 deletions

File tree

src/somd2/config/_config.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ def __init__(
165165
auto_fix_minimise=True,
166166
save_crash_report=False,
167167
save_energy_components=True,
168+
save_xml=False,
168169
page_size=None,
169170
timeout="300 s",
170171
):
@@ -507,6 +508,11 @@ def __init__(
507508
save_energy_components: bool
508509
Whether to save the energy contribution for each force when checkpointing.
509510
511+
save_xml: bool
512+
Whether to write an XML file for the OpenMM system to the output
513+
directory on startup. This can be useful for debugging or for
514+
use with other tools that can read OpenMM XML files.
515+
510516
page_size: int
511517
The page size for trajectory handling in megabytes. If None, then Sire
512518
will automatically set the page size.
@@ -606,6 +612,7 @@ def __init__(
606612
self.auto_fix_minimise = auto_fix_minimise
607613
self.save_crash_report = save_crash_report
608614
self.save_energy_components = save_energy_components
615+
self.save_xml = save_xml
609616
self.timeout = timeout
610617
self.num_energy_neighbours = num_energy_neighbours
611618
self.null_energy = null_energy
@@ -2418,6 +2425,16 @@ def save_energy_components(self, save_energy_components):
24182425
raise ValueError("'save_energy_components' must be of type 'bool'")
24192426
self._save_energy_components = save_energy_components
24202427

2428+
@property
2429+
def save_xml(self):
2430+
return self._save_xml
2431+
2432+
@save_xml.setter
2433+
def save_xml(self, save_xml):
2434+
if not isinstance(save_xml, bool):
2435+
raise ValueError("'save_xml' must be of type 'bool'")
2436+
self._save_xml = save_xml
2437+
24212438
@property
24222439
def page_size(self):
24232440
return self._page_size

src/somd2/runner/_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,7 @@ def increment_filename(base_filename, suffix):
11981198
)
11991199
filenames["gcmc_ghosts"] = str(output_directory / f"gcmc_ghosts_{lam}.txt")
12001200
filenames["sampler_stats"] = str(output_directory / f"sampler_stats_{lam}.pkl")
1201+
filenames["xml"] = str(output_directory / f"system_{lam}.xml")
12011202
if restart:
12021203
filenames["config"] = str(
12031204
output_directory / increment_filename("config", "yaml")

src/somd2/runner/_repex.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def __init__(
5353
gcmc_kwargs=None,
5454
output_directory=None,
5555
perturbed_system=None,
56+
xml_filenames=None,
5657
):
5758
"""
5859
Constructor.
@@ -84,6 +85,10 @@ def __init__(
8485
perturbed_system: :class: `System <sire.system.System>`
8586
The perturbed end-state system used to seed starting coordinates for
8687
lambda > 0.5 replicas. If None, the perturbed state is not used.
88+
89+
xml_filenames: list of str
90+
A list of file paths for the OpenMM XML output, one per replica.
91+
If None, XML files are not written.
8792
"""
8893

8994
# Warn if the number of replicas is not a multiple of the number of GPUs.
@@ -117,6 +122,7 @@ def __init__(
117122
gcmc_kwargs=gcmc_kwargs,
118123
output_directory=output_directory,
119124
perturbed_system=perturbed_system,
125+
xml_filenames=xml_filenames,
120126
)
121127

122128
def __setstate__(self, state):
@@ -168,6 +174,7 @@ def _create_dynamics(
168174
gcmc_kwargs=None,
169175
output_directory=None,
170176
perturbed_system=None,
177+
xml_filenames=None,
171178
):
172179
"""
173180
Create the dynamics objects.
@@ -199,6 +206,10 @@ def _create_dynamics(
199206
perturbed_system: :class: `System <sire.system.System>`
200207
The perturbed end-state system used to seed starting coordinates for
201208
lambda > 0.5 replicas. If None, the perturbed state is not used.
209+
210+
xml_filenames: list of str
211+
A list of file paths for the OpenMM XML output, one per replica.
212+
If None, XML files are not written.
202213
"""
203214

204215
from math import floor
@@ -315,6 +326,13 @@ def _create_dynamics(
315326
# Append the dynamics object.
316327
self._dynamics.append(dynamics)
317328

329+
# Write the OpenMM XML file to the output directory.
330+
if xml_filenames is not None:
331+
_logger.info(
332+
f"Writing OpenMM XML for lambda {lam:.5f} on device {device}"
333+
)
334+
dynamics.to_xml(xml_filenames[i])
335+
318336
# Track memory footprint for this device.
319337
info = device_mem[device]
320338
info["count"] += 1
@@ -740,6 +758,11 @@ def __init__(self, system, config):
740758

741759
# Create the dynamics cache.
742760
if not self._is_restart:
761+
xml_filenames = (
762+
[self._filenames[i]["xml"] for i in range(len(self._lambda_values))]
763+
if self._config.save_xml
764+
else None
765+
)
743766
self._dynamics_cache = DynamicsCache(
744767
self._system,
745768
self._lambda_values,
@@ -749,6 +772,7 @@ def __init__(self, system, config):
749772
gcmc_kwargs=self._gcmc_kwargs,
750773
perturbed_system=self._perturbed_system,
751774
output_directory=self._config.output_directory,
775+
xml_filenames=xml_filenames,
752776
)
753777
else:
754778
_logger.debug("Restarting from file")

src/somd2/runner/_runner.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,11 @@ def generate_lam_vals(lambda_base, increment=0.001):
641641
# Create the dynamics object.
642642
dynamics = system.dynamics(**dynamics_kwargs)
643643

644+
# Write the OpenMM XML file to the output directory.
645+
if self._config.save_xml and not is_restart:
646+
_logger.info(f"Writing OpenMM XML for {_lam_sym} = {lambda_value:.5f}")
647+
dynamics.to_xml(self._filenames[index]["xml"])
648+
644649
# Reset the GCMC sampler. This resets the sampling statistics and clears
645650
# the associated OpenMM forces.
646651
if gcmc_sampler is not None:

0 commit comments

Comments
 (0)