Skip to content

Commit 7c9e83b

Browse files
authored
Merge pull request #94 from OpenBioSim/fix_93
Fix issue #93
2 parents 63398b4 + 5d12372 commit 7c9e83b

5 files changed

Lines changed: 87 additions & 36 deletions

File tree

src/somd2/_utils/_somd1.py

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def apply_pert(system, pert_file):
8484
return system
8585

8686

87-
def make_compatible(system):
87+
def make_compatible(system, fix_perturbable_zero_sigams=False):
8888
"""
8989
Makes a perturbation SOMD1 compatible.
9090
@@ -94,6 +94,9 @@ def make_compatible(system):
9494
system : sire.system.System, sire.legacy.System.System
9595
The system containing the molecules to be perturbed.
9696
97+
fix_perturbable_zero_sigams : bool
98+
Whether to prevent LJ sigma values being perturbed to zero.
99+
97100
Returns
98101
-------
99102
@@ -107,6 +110,9 @@ def make_compatible(system):
107110
"'system' must of type 'sire.system.System' or 'sire.legacy.System.System'"
108111
)
109112

113+
if not isinstance(fix_perturbable_zero_sigams, bool):
114+
raise TypeError("'fix_perturbable_zero_sigams' must be of type 'bool'.")
115+
110116
# Extract the legacy system.
111117
if isinstance(system, _LegacySystem):
112118
system = _System(system)
@@ -132,36 +138,36 @@ def make_compatible(system):
132138
##################################
133139
# First fix zero LJ sigmas values.
134140
##################################
135-
136-
# Tolerance for zero sigma values.
137-
null_lj_sigma = 1e-9
138-
139-
for atom in mol.atoms():
140-
# Get the end state LJ sigma values.
141-
lj0 = atom.property("LJ0")
142-
lj1 = atom.property("LJ1")
143-
144-
# Lambda = 0 state has a zero sigma value.
145-
if abs(lj0.sigma().value()) <= null_lj_sigma:
146-
# Use the sigma value from the lambda = 1 state.
147-
edit_mol = (
148-
edit_mol.atom(atom.index())
149-
.set_property(
150-
"LJ0", _SireMM.LJParameter(lj1.sigma(), lj0.epsilon())
141+
if fix_perturbable_zero_sigams:
142+
# Tolerance for zero sigma values.
143+
null_lj_sigma = 1e-9
144+
145+
for atom in mol.atoms():
146+
# Get the end state LJ sigma values.
147+
lj0 = atom.property("LJ0")
148+
lj1 = atom.property("LJ1")
149+
150+
# Lambda = 0 state has a zero sigma value.
151+
if abs(lj0.sigma().value()) <= null_lj_sigma:
152+
# Use the sigma value from the lambda = 1 state.
153+
edit_mol = (
154+
edit_mol.atom(atom.index())
155+
.set_property(
156+
"LJ0", _SireMM.LJParameter(lj1.sigma(), lj0.epsilon())
157+
)
158+
.molecule()
151159
)
152-
.molecule()
153-
)
154-
155-
# Lambda = 1 state has a zero sigma value.
156-
if abs(lj1.sigma().value()) <= null_lj_sigma:
157-
# Use the sigma value from the lambda = 0 state.
158-
edit_mol = (
159-
edit_mol.atom(atom.index())
160-
.set_property(
161-
"LJ1", _SireMM.LJParameter(lj0.sigma(), lj1.epsilon())
160+
161+
# Lambda = 1 state has a zero sigma value.
162+
if abs(lj1.sigma().value()) <= null_lj_sigma:
163+
# Use the sigma value from the lambda = 0 state.
164+
edit_mol = (
165+
edit_mol.atom(atom.index())
166+
.set_property(
167+
"LJ1", _SireMM.LJParameter(lj0.sigma(), lj1.epsilon())
168+
)
169+
.molecule()
162170
)
163-
.molecule()
164-
)
165171

166172
########################
167173
# Now process the bonds.

src/somd2/config/_config.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,13 @@ def __init__(
109109
include_constrained_energies=False,
110110
dynamic_constraints=True,
111111
ghost_modifications=True,
112+
fix_perturbable_zero_sigmas=False,
112113
charge_difference=None,
113114
coalchemical_restraint_dist=None,
114115
com_reset_frequency=10,
115116
minimise=True,
116117
minimisation_constraints=False,
118+
minimisation_errors=False,
117119
equilibration_time="0 ps",
118120
equilibration_timestep="2 fs",
119121
equilibration_constraints=True,
@@ -258,6 +260,9 @@ def __init__(
258260
sampling of non-physical conformations. We implement the recommended
259261
modifcations from https://pubs.acs.org/doi/10.1021/acs.jctc.0c01328
260262
263+
fix_perturbable_zero_sigmas: bool
264+
Whether to prevent LJ sigma values being perturbed to zero.
265+
261266
charge_difference: int
262267
The charge difference between the two end states. (Perturbed minus
263268
reference.) If None, then alchemical ions will automatically be
@@ -285,6 +290,9 @@ def __init__(
285290
constraints will be used. If True, then the use of constraints will be
286291
determined based on the value of 'equilibration_constraints'.
287292
293+
minimisation_errors: bool
294+
Whether to raise an exception if a minimisation fails to converge.
295+
288296
equilibration_time: str
289297
Time interval for equilibration. Only simulations starting from
290298
scratch will be equilibrated.
@@ -498,11 +506,13 @@ def __init__(
498506
self.include_constrained_energies = include_constrained_energies
499507
self.dynamic_constraints = dynamic_constraints
500508
self.ghost_modifications = ghost_modifications
509+
self.fix_perturbable_zero_sigmas = fix_perturbable_zero_sigmas
501510
self.charge_difference = charge_difference
502511
self.coalchemical_restraint_dist = coalchemical_restraint_dist
503512
self.com_reset_frequency = com_reset_frequency
504513
self.minimise = minimise
505514
self.minimisation_constraints = minimisation_constraints
515+
self.minimisation_errors = minimisation_errors
506516
self.equilibration_time = equilibration_time
507517
self.equilibration_timestep = equilibration_timestep
508518
self.equilibration_constraints = equilibration_constraints
@@ -1145,6 +1155,16 @@ def ghost_modifications(self, ghost_modifications):
11451155
raise ValueError("'ghost_modifications' must be of type 'bool'")
11461156
self._ghost_modifications = ghost_modifications
11471157

1158+
@property
1159+
def fix_perturbable_zero_sigmas(self):
1160+
return self._fix_perturbable_zero_sigmas
1161+
1162+
@fix_perturbable_zero_sigmas.setter
1163+
def fix_perturbable_zero_sigmas(self, fix_perturbable_zero_sigmas):
1164+
if not isinstance(fix_perturbable_zero_sigmas, bool):
1165+
raise ValueError("'fix_perturbable_zero_sigmas' must be of type 'bool'")
1166+
self._fix_perturbable_zero_sigmas = fix_perturbable_zero_sigmas
1167+
11481168
@property
11491169
def charge_difference(self):
11501170
return self._charge_difference
@@ -1222,6 +1242,16 @@ def minimisation_constraints(self, minimisation_constraints):
12221242
raise ValueError("'minimisation_constraints' must be of type 'bool'")
12231243
self._minimisation_constraints = minimisation_constraints
12241244

1245+
@property
1246+
def minimisation_errors(self):
1247+
return self._minimisation_errors
1248+
1249+
@minimisation_errors.setter
1250+
def minimisation_errors(self, minimisation_errors):
1251+
if not isinstance(minimisation_errors, bool):
1252+
raise ValueError("'minimisation_errors' must be of type 'bool'")
1253+
self._minimisation_errors = minimisation_errors
1254+
12251255
@property
12261256
def equilibration_time(self):
12271257
return self._equilibration_time

src/somd2/runner/_base.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ def __init__(self, system, config):
190190
self._config._extra_args["check_for_h_by_ambertype"] = False
191191

192192
# Make sure that perturbable LJ sigmas aren't scaled to zero.
193-
self._config._extra_args["fix_perturbable_zero_sigmas"] = True
193+
self._config._extra_args["fix_perturbable_zero_sigmas"] = (
194+
config.fix_perturbable_zero_sigmas
195+
)
194196

195197
# We're running in SOMD1 compatibility mode.
196198
if self._config.somd1_compatibility:
@@ -199,7 +201,10 @@ def __init__(self, system, config):
199201
# First, try to make the perturbation SOMD1 compatible.
200202

201203
_logger.info("Applying SOMD1 perturbation compatibility.")
202-
self._system = make_compatible(self._system)
204+
self._system = make_compatible(
205+
self._system,
206+
fix_perturbable_zero_sigmas=self.config.fix_perturbable_zero_sigmas,
207+
)
203208
self._system = _sr.morph.link_to_reference(self._system)
204209

205210
# Next, swap the water topology so that it is in AMBER format.

src/somd2/runner/_repex.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -824,10 +824,12 @@ def run(self):
824824
replica_list[i * num_workers : (i + 1) * num_workers],
825825
):
826826
if not success:
827-
_logger.error(
828-
f"Minimisation failed for {_lam_sym} = {self._lambda_values[index]:.5f}: {e}"
829-
)
830-
raise e
827+
msg = f"Minimisation failed for {_lam_sym} = {self._lambda_values[index]:.5f}: {e}"
828+
if self.config.minimisation_errors:
829+
_logger.error(msg)
830+
raise e
831+
else:
832+
_logger.warning(msg)
831833
except KeyboardInterrupt:
832834
_logger.error("Minimisation cancelled. Exiting.")
833835
_sys.exit(1)
@@ -1387,6 +1389,8 @@ def _equilibrate(self, index):
13871389
# Reset the timer.
13881390
if self._initial_time[index].value() != 0:
13891391
system.set_time(self._initial_time[index])
1392+
else:
1393+
system.set_time(_sr.u("0ps"))
13901394

13911395
# Delete the dynamics object.
13921396
self._dynamics_cache.delete(index)

src/somd2/runner/_runner.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,11 @@ def generate_lam_vals(lambda_base, increment=0.001):
460460
perturbable_constraint=perturbable_constraint,
461461
)
462462
except Exception as e:
463-
raise RuntimeError(f"Minimisation failed: {e}")
463+
msg = f"Minimisation failed for {_lam_sym} = {lambda_value:.5f}: {e}"
464+
if self.confing.minimisation_errors:
465+
raise RuntimeError(msg)
466+
else:
467+
_logger.warning(msg)
464468

465469
# Equilibration.
466470
is_equilibrated = False
@@ -523,6 +527,8 @@ def generate_lam_vals(lambda_base, increment=0.001):
523527
# Reset the timer.
524528
if self._initial_time[index].value() != 0:
525529
system.set_time(self._initial_time[index])
530+
else:
531+
system.set_time(_sr.u("0ps"))
526532

527533
except Exception as e:
528534
try:

0 commit comments

Comments
 (0)