Skip to content

Commit 357beaf

Browse files
authored
Merge pull request #366 from OpenBioSim/fix_amber_fep_mappings
Fix AMBER FEP mappings
2 parents 9e1d70f + 78444b6 commit 357beaf

File tree

12 files changed

+429
-122
lines changed

12 files changed

+429
-122
lines changed

python/BioSimSpace/Align/_align.py

+276-23
Large diffs are not rendered by default.

python/BioSimSpace/FreeEnergy/_relative.py

+27-33
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def __init__(
163163
164164
engine : str
165165
The molecular dynamics engine used to run the simulation. Available
166-
options are "GROMACS", or "SOMD". If this argument is omitted then
166+
options are "AMBER", "GROMACS", or "SOMD". If this argument is omitted then
167167
BioSimSpace will choose an appropriate engine for you.
168168
169169
setup_only : bool
@@ -1158,8 +1158,8 @@ def _preprocess_data(data, estimator, **kwargs):
11581158
# Assign defaults in case not passed via kwargs.
11591159
auto_eq = False
11601160
stat_ineff = False
1161-
truncate = False
1162-
truncate_keep = "start"
1161+
truncate_upper = 100
1162+
truncate_lower = 0
11631163

11641164
# Parse kwargs.
11651165
for key, value in kwargs.items():
@@ -1168,35 +1168,31 @@ def _preprocess_data(data, estimator, **kwargs):
11681168
auto_eq = value
11691169
if key == "STATISTICALINEFFICIENCY":
11701170
stat_ineff = value
1171-
if key == "TRUNCATEPERCENTAGE":
1172-
truncate = value
1173-
if key == "TRUNCATEKEEP":
1174-
truncate_keep = value
1171+
if key == "TRUNCATEUPPER":
1172+
truncate_upper = value
1173+
if key == "TRUNCATELOWER":
1174+
truncate_lower = value
11751175

1176-
# First truncate data.
1176+
# Copy the data.
11771177
raw_data = data
1178-
if truncate:
1179-
# Get the upper and lower bounds for truncate.
1180-
data_len = len(data[0])
1181-
data_step = round((data[0].index[-1][0] - data[0].index[-2][0]), 1)
1182-
data_kept = data_len * (truncate / 100)
1183-
data_time = data_kept * data_step
1184-
if truncate_keep == "start":
1185-
truncate_lower = 0
1186-
truncate_upper = data_time - data_step
1187-
if truncate_keep == "end":
1188-
truncate_lower = (data_len * data_step) - data_time
1189-
truncate_upper = (data_len * data_step) - data_step
11901178

1191-
try:
1192-
data = [
1193-
_slicing(i, lower=truncate_lower, upper=truncate_upper)
1194-
for i in raw_data
1195-
]
1196-
except:
1197-
_warnings.warn("Could not truncate data.")
1198-
data = raw_data
1199-
else:
1179+
# Data length.
1180+
data_len = len(data[0])
1181+
1182+
# Step size.
1183+
data_step = round((data[0].index[-1][0] - data[0].index[-2][0]), 1)
1184+
1185+
# Get the upper and lower bounds for truncate.
1186+
truncate_lower = (data_len * (truncate_lower / 100)) * data_step
1187+
truncate_upper = (data_len * (truncate_upper / 100)) * data_step
1188+
1189+
try:
1190+
data = [
1191+
_slicing(i, lower=truncate_lower, upper=truncate_upper)
1192+
for i in raw_data
1193+
]
1194+
except:
1195+
_warnings.warn("Could not truncate data.")
12001196
data = raw_data
12011197

12021198
# The decorrelate function calls either autoequilibration or statistical_inefficiency
@@ -1230,10 +1226,7 @@ def _preprocess_data(data, estimator, **kwargs):
12301226
)
12311227
sampled_data = data
12321228

1233-
# Concatanate in alchemlyb format.
1234-
processed_data = _alchemlyb.concat(sampled_data)
1235-
1236-
return processed_data
1229+
return sampled_data
12371230

12381231
@staticmethod
12391232
def _analyse_internal(files, temperatures, lambdas, engine, estimator, **kwargs):
@@ -1319,6 +1312,7 @@ def _analyse_internal(files, temperatures, lambdas, engine, estimator, **kwargs)
13191312
# Preprocess the data.
13201313
try:
13211314
processed_data = Relative._preprocess_data(data, estimator, **kwargs)
1315+
processed_data = _alchemlyb.concat(processed_data)
13221316
except:
13231317
_warnings.warn("Could not preprocess the data!")
13241318
processed_data = _alchemlyb.concat(data)

python/BioSimSpace/Process/_amber.py

+8
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ def _setup(self, **kwargs):
277277
"perturbable molecule!"
278278
)
279279

280+
# Make sure the protocol is valid.
281+
if self._protocol.getPerturbationType() != "full":
282+
raise NotImplementedError(
283+
"AMBER currently only supports the 'full' perturbation "
284+
"type. Please use engine='SOMD' when running multistep "
285+
"perturbation types."
286+
)
287+
280288
# If this is vacuum simulation with pmemd.cuda then
281289
# we need to add a simulation box.
282290
if self._is_vacuum and self._is_pmemd_cuda:

python/BioSimSpace/Process/_openmm.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1488,13 +1488,12 @@ def getFrame(self, index):
14881488

14891489
if not type(index) is int:
14901490
raise TypeError("'index' must be of type 'int'")
1491-
max_index = (
1492-
int(
1493-
(self._protocol.getRunTime() / self._protocol.getTimeStep())
1494-
/ self._protocol.getRestartInterval()
1495-
)
1496-
- 1
1491+
1492+
max_index = int(
1493+
(self._protocol.getRunTime() / self._protocol.getTimeStep())
1494+
/ self._protocol.getRestartInterval()
14971495
)
1496+
14981497
if index < 0 or index > max_index:
14991498
raise ValueError(f"'index' must be in range [0, {max_index}].")
15001499

python/BioSimSpace/Protocol/_equilibration.py

+40-4
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,15 @@ class Equilibration(_Protocol, _PositionRestraintMixin):
4242
def __init__(
4343
self,
4444
timestep=_Types.Time(2, "femtosecond"),
45-
runtime=_Types.Time(0.2, "nanoseconds"),
45+
runtime=_Types.Time(1, "nanoseconds"),
4646
temperature_start=_Types.Temperature(300, "kelvin"),
4747
temperature_end=_Types.Temperature(300, "kelvin"),
4848
temperature=None,
4949
pressure=None,
5050
thermostat_time_constant=_Types.Time(1, "picosecond"),
51-
report_interval=100,
52-
restart_interval=500,
51+
report_interval=200,
52+
restart_interval=1000,
53+
restart=False,
5354
restraint=None,
5455
force_constant=10 * _Units.Energy.kcal_per_mol / _Units.Area.angstrom2,
5556
):
@@ -89,6 +90,9 @@ def __init__(
8990
The frequency at which restart configurations and trajectory
9091
frames are saved. (In integration steps.)
9192
93+
restart : bool
94+
Whether this is a continuation of a previous simulation.
95+
9296
restraint : str, [int]
9397
The type of restraint to perform. This should be one of the
9498
following options:
@@ -155,6 +159,9 @@ def __init__(
155159
# Set the restart interval.
156160
self.setRestartInterval(restart_interval)
157161

162+
# Set the restart flag.
163+
self.setRestart(restart)
164+
158165
# Set the posistion restraint.
159166
_PositionRestraintMixin.__init__(self, restraint, force_constant)
160167

@@ -169,7 +176,7 @@ def _get_parm(self):
169176
f"thermostat_time_constant={self._thermostat_time_constant}, "
170177
f"report_interval={self._report_interval}, "
171178
f"restart_interval={self._restart_interval}, "
172-
+ _PositionRestraintMixin._get_parm(self)
179+
f"restart={self._restart}, " + _PositionRestraintMixin._get_parm(self)
173180
)
174181

175182
def __str__(self):
@@ -204,6 +211,7 @@ def __eq__(self, other):
204211
and self._thermostat_time_constant == other._thermostat_time_constant
205212
and self._report_interval == other._report_interval
206213
and self._restart_interval == other._restart_interval
214+
and self._restart == other._restart
207215
and _PositionRestraintMixin.__eq__(self, other)
208216
)
209217

@@ -484,6 +492,34 @@ def setRestartInterval(self, restart_interval):
484492

485493
self._restart_interval = restart_interval
486494

495+
def isRestart(self):
496+
"""
497+
Return whether this restart simulation.
498+
499+
Returns
500+
-------
501+
502+
is_restart : bool
503+
Whether this is a restart simulation.
504+
"""
505+
return self._restart
506+
507+
def setRestart(self, restart):
508+
"""
509+
Set the restart flag.
510+
511+
Parameters
512+
----------
513+
514+
restart : bool
515+
Whether this is a restart simulation.
516+
"""
517+
if isinstance(restart, bool):
518+
self._restart = restart
519+
else:
520+
_warnings.warn("Non-boolean restart flag. Defaulting to False!")
521+
self._restart = False
522+
487523
def isConstantTemp(self):
488524
"""
489525
Return whether the protocol has a constant temperature.

python/BioSimSpace/Protocol/_free_energy_equilibration.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,15 @@ def __init__(
4444
max_lam=1.0,
4545
num_lam=11,
4646
timestep=_Types.Time(2, "femtosecond"),
47-
runtime=_Types.Time(0.2, "nanoseconds"),
47+
runtime=_Types.Time(1, "nanoseconds"),
4848
temperature_start=_Types.Temperature(300, "kelvin"),
4949
temperature_end=_Types.Temperature(300, "kelvin"),
5050
temperature=None,
5151
pressure=None,
5252
thermostat_time_constant=_Types.Time(1, "picosecond"),
5353
report_interval=200,
5454
restart_interval=1000,
55+
restart=False,
5556
restraint=None,
5657
force_constant=10 * _Units.Energy.kcal_per_mol / _Units.Area.angstrom2,
5758
perturbation_type="full",
@@ -106,6 +107,9 @@ def __init__(
106107
The frequency at which restart configurations and trajectory
107108
frames are saved. (In integration steps.)
108109
110+
restart : bool
111+
Whether this is a continuation of a previous simulation.
112+
109113
restraint : str, [int]
110114
The type of restraint to perform. This should be one of the
111115
following options:
@@ -217,4 +221,5 @@ def _to_regular_protocol(self):
217221
restart_interval=self.getRestartInterval(),
218222
restraint=self.getRestraint(),
219223
force_constant=self.getForceConstant(),
224+
restart=self.isRestart(),
220225
)

python/BioSimSpace/Protocol/_production.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def __init__(
4646
temperature=_Types.Temperature(300, "kelvin"),
4747
pressure=_Types.Pressure(1, "atmosphere"),
4848
thermostat_time_constant=_Types.Time(1, "picosecond"),
49-
report_interval=100,
50-
restart_interval=100,
49+
report_interval=200,
50+
restart_interval=1000,
5151
restart=False,
5252
restraint=None,
5353
force_constant=10 * _Units.Energy.kcal_per_mol / _Units.Area.angstrom2,

python/BioSimSpace/_Config/_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,6 @@ def steps(self):
213213
steps = self._protocol.getSteps()
214214
else:
215215
steps = _math.ceil(
216-
self._protocol.getRunTime() / self._protocol.getTimeStep()
216+
round(self._protocol.getRunTime() / self._protocol.getTimeStep(), 6)
217217
)
218218
return steps

python/BioSimSpace/_Config/_gromacs.py

+31-18
Original file line numberDiff line numberDiff line change
@@ -100,30 +100,21 @@ def createConfig(self, version=None, extra_options={}, extra_lines=[]):
100100
if not all(isinstance(line, str) for line in extra_lines):
101101
raise TypeError("Lines in 'extra_lines' must be of type 'str'.")
102102

103-
# Make sure the report interval is a multiple of nstcalcenergy.
104-
if isinstance(self._protocol, _FreeEnergyMixin):
105-
nstcalcenergy = 250
106-
else:
107-
nstcalcenergy = 100
108-
report_interval = self.reportInterval()
109-
if report_interval % nstcalcenergy != 0:
110-
report_interval = nstcalcenergy * _math.ceil(
111-
report_interval / nstcalcenergy
112-
)
113-
114103
# Define some miscellaneous defaults.
115104
protocol_dict = {
116105
# Interval between writing to the log file.
117-
"nstlog": report_interval,
106+
"nstlog": self.reportInterval(),
118107
# Interval between writing to the energy file.
119-
"nstenergy": report_interval,
108+
"nstenergy": self.reportInterval(),
120109
# Interval between writing to the trajectory file.
121110
"nstxout-compressed": self.restartInterval(),
122111
}
123112

124113
# Minimisation.
125114
if isinstance(self._protocol, _Protocol.Minimisation):
126115
protocol_dict["integrator"] = "steep"
116+
# Maximum step size in nanometers.
117+
protocol_dict["emstep"] = "0.001"
127118
else:
128119
# Timestep in picoseconds
129120
timestep = self._protocol.getTimeStep().picoseconds().value()
@@ -212,7 +203,7 @@ def createConfig(self, version=None, extra_options={}, extra_lines=[]):
212203
# Temperature control.
213204
if not isinstance(self._protocol, _Protocol.Minimisation):
214205
if isinstance(self._protocol, _FreeEnergyMixin):
215-
# Langevin dynamics.
206+
# Leap-frog stochastic dynamics integrator.
216207
protocol_dict["integrator"] = "sd"
217208
else:
218209
# Leap-frog molecular dynamics.
@@ -260,6 +251,24 @@ def createConfig(self, version=None, extra_options={}, extra_lines=[]):
260251
"%.2f" % self._protocol.getTemperature().kelvin().value()
261252
)
262253

254+
# Set as a continuation run.
255+
if self.isRestart():
256+
protocol_dict["continuation"] = "yes"
257+
protocol_dict["gen-vel"] = "no"
258+
# Generate velocities.
259+
else:
260+
protocol_dict["continuation"] = "no"
261+
protocol_dict["gen-vel"] = "yes"
262+
protocol_dict["gen-seed"] = "-1"
263+
if isinstance(self._protocol, _Protocol.Equilibration):
264+
protocol_dict["gen-temp"] = (
265+
"%.2f" % self._protocol.getStartTemperature().kelvin().value()
266+
)
267+
else:
268+
protocol_dict["gen-temp"] = (
269+
"%.2f" % self._protocol.getTemperature().kelvin().value()
270+
)
271+
263272
# Free energies.
264273
if isinstance(self._protocol, _FreeEnergyMixin):
265274
# Extract the lambda array.
@@ -280,10 +289,14 @@ def createConfig(self, version=None, extra_options={}, extra_lines=[]):
280289
protocol_dict["couple-lambda1"] = "vdw-q"
281290
# Write all lambda values.
282291
protocol_dict["calc-lambda-neighbors"] = -1
283-
# Calculate energies every 250 steps.
284-
protocol_dict["nstcalcenergy"] = 250
285-
# Write gradients every 250 steps.
286-
protocol_dict["nstdhdl"] = 250
292+
# Calculate energies at the report interval.
293+
protocol_dict["nstcalcenergy"] = self.reportInterval()
294+
# Write gradients at the report interval.
295+
protocol_dict["nstdhdl"] = self.reportInterval()
296+
# Soft-core parameters.
297+
protocol_dict["sc-alpha"] = "0.30"
298+
protocol_dict["sc-sigma"] = "0.25"
299+
protocol_dict["sc-coul"] = "yes"
287300

288301
# Put everything together in a line-by-line format.
289302
total_dict = {**protocol_dict, **extra_options}

0 commit comments

Comments
 (0)