1
1
"""Run calculations defined by a config."""
2
2
3
+ import functools
4
+ import multiprocessing
3
5
import pathlib
4
6
import tempfile
5
7
import typing
@@ -172,13 +174,26 @@ def _equilibrate(
172
174
return simulation .context .getState (getPositions = True )
173
175
174
176
175
- def _run_phase_hremd (
177
+ def _run_eq_phase (
176
178
protocol : absolv .config .EquilibriumProtocol ,
177
- temperature : openmm .unit .Quantity ,
178
179
prepared_system : PreparedSystem ,
180
+ output_dir : pathlib .Path | None ,
181
+ temperature : openmm .unit .Quantity ,
179
182
platform : femto .md .constants .OpenMMPlatform ,
180
- output_dir : pathlib .Path | None = None ,
181
183
) -> tuple [dict [str , float ], dict [str , numpy .ndarray ]]:
184
+ """Run HREMD for one of the solvent phases.
185
+
186
+ Args:
187
+ protocol: The protocol to run.
188
+ prepared_system: The prepared system to run.
189
+ output_dir: The (optional) directory to save HREMD samples to.
190
+ temperature: The temperature to run at.
191
+ platform: The OpenMM platform to run using.
192
+
193
+ Returns:
194
+ The free energy estimates and the overlap matrices. See
195
+ ``femto.fe.ddg.estimate_ddg`` for more details.
196
+ """
182
197
platform = (
183
198
femto .md .constants .OpenMMPlatform .REFERENCE
184
199
if prepared_system .topology .box_vectors is None
@@ -239,6 +254,7 @@ def run_eq(
239
254
prepared_system_b : PreparedSystem ,
240
255
platform : femto .md .constants .OpenMMPlatform = femto .md .constants .OpenMMPlatform .CUDA ,
241
256
output_dir : pathlib .Path | None = None ,
257
+ parallel : bool = False ,
242
258
) -> absolv .config .Result :
243
259
"""Perform a simulation at each lambda window and for each solvent.
244
260
@@ -248,27 +264,34 @@ def run_eq(
248
264
prepared_system_b: The prepared system b. See ``setup`` for more details.
249
265
platform: The OpenMM platform to run using.
250
266
output_dir: The (optional) directory to save HREMD samples to.
267
+ parallel: Whether to run calculations for solvent A and solvent B in
268
+ parallel. This is mostly useful when running HFE calculations where
269
+ the vacuum phase will typically run on the CPU while the solvent phase
270
+ will run on the GPU.
251
271
"""
252
272
253
- results_a , overlap_a = _run_phase_hremd (
254
- config .alchemical_protocol_a ,
255
- config .temperature ,
256
- prepared_system_a ,
257
- platform ,
258
- None if output_dir is None else output_dir / "solvent-a" ,
273
+ output_dir_a = None if output_dir is None else output_dir / "solvent-a"
274
+ output_dir_b = None if output_dir is None else output_dir / "solvent-b"
275
+
276
+ args = [
277
+ (config .alchemical_protocol_a , prepared_system_a , output_dir_a ),
278
+ (config .alchemical_protocol_b , prepared_system_b , output_dir_b ),
279
+ ]
280
+ run_fn = functools .partial (
281
+ _run_eq_phase , temperature = config .temperature , platform = platform
259
282
)
260
283
261
- dg_a , dg_a_std = results_a ["ddG_kcal_mol" ], results_a ["ddG_error_kcal_mol" ]
262
- # overlap_a = overlap_a["overlap_0"]
284
+ if parallel :
285
+ with multiprocessing .Pool (2 ) as pool :
286
+ results = list (pool .starmap (run_fn , args ))
287
+ else :
288
+ results = [run_fn (* args [0 ]), run_fn (* args [1 ])]
263
289
264
- results_b , overlap_b = _run_phase_hremd (
265
- config .alchemical_protocol_b ,
266
- config .temperature ,
267
- prepared_system_b ,
268
- platform ,
269
- None if output_dir is None else output_dir / "solvent-b" ,
270
- )
290
+ results_a , overlap_a = results [0 ]
291
+ results_b , overlap_b = results [1 ]
271
292
293
+ dg_a , dg_a_std = results_a ["ddG_kcal_mol" ], results_a ["ddG_error_kcal_mol" ]
294
+ # overlap_a = overlap_a["overlap_0"]
272
295
dg_b , dg_b_std = results_b ["ddG_kcal_mol" ], results_b ["ddG_error_kcal_mol" ]
273
296
# overlap_b = overlap_b["overlap_0"]
274
297
0 commit comments