Skip to content

Commit fd77bf2

Browse files
authored
Merge pull request #24 from michellab/bugfix-calcset-calc-attrs
Ensure SimulationRunnerIterator saves states when tearing down
2 parents 2b1479d + 5e9bcfe commit fd77bf2

File tree

11 files changed

+85
-29
lines changed

11 files changed

+85
-29
lines changed

a3fe/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.3.0"
1+
__version__ = "0.3.1"

a3fe/read/_process_somd_files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def read_overlap_mat(outfile: str) -> _np.ndarray:
137137
overlap_mat = []
138138
in_overlap_mat = False
139139
for line in lines:
140-
if line.startswith("#Overlap matrix"):
140+
if "#Overlap matrix" in line:
141141
in_overlap_mat = True
142142
continue
143143
if line.startswith("#"):

a3fe/run/_simulation_runner.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,6 @@ def delta_g_er(self) -> _np.ndarray:
235235

236236
return self._delta_g_er
237237

238-
@property
239-
def is_complete(self) -> bool:
240-
f"""Whether the {self.__class__.__name__} has completed."""
241-
# Check if the overall_stats.dat file exists
242-
if _pathlib.Path(f"{self.output_dir}/overall_stats.dat").is_file():
243-
return True
244-
else:
245-
return False
246-
247238
def __str__(self) -> str:
248239
return self.__class__.__name__
249240

@@ -439,7 +430,6 @@ def analyse(
439430
def get_results_df(
440431
self,
441432
save_csv: bool = True,
442-
get_normalised_costs: bool = False,
443433
add_sub_sim_runners: bool = True,
444434
) -> _pd.DataFrame:
445435
"""

a3fe/run/_utils.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,19 @@ def __iter__(self):
153153
return self
154154

155155
def __next__(self) -> _T:
156-
if self.i >= len(self.base_dirs):
157-
# Tear down the current simulation runner
158-
if self.current_sim_runner is not None:
159-
self.current_sim_runner._close_logging_handlers()
160-
del self.current_sim_runner
161-
self.current_sim_runner = None
162-
raise StopIteration
156+
end_of_list = self.i >= len(self.base_dirs)
163157

164158
# Tear down the current simulation runner
165159
if self.current_sim_runner is not None:
160+
self.current_sim_runner._dump()
166161
self.current_sim_runner._close_logging_handlers()
167162
del self.current_sim_runner
163+
if end_of_list:
164+
self.current_sim_runner = None
165+
166+
if end_of_list:
167+
raise StopIteration
168+
168169
# Set up the next simulation runner
169170
self.current_sim_runner = self.subclass(
170171
**self.kwargs, base_dir=self.base_dirs[self.i]

a3fe/run/calc_set.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,36 @@ def analyse(
452452
offset=offset,
453453
stats=stats,
454454
)
455+
456+
# Avoid base class methods which aren't valid being called
457+
@property
458+
def delta_g(self):
459+
raise AttributeError("CalcSet objects do not have a delta_g attribute.")
460+
461+
@property
462+
def delta_g_err(self):
463+
raise AttributeError("CalcSet objects do not have a delta_g_err attribute.")
464+
465+
def get_results_df(self, save_csv: bool = True, add_sub_sim_runners: bool = True):
466+
# TODO: Implement this method
467+
raise NotImplementedError(
468+
"This method is not implemented for CalcSet objects. Use the analyse method to get free energy changes."
469+
)
470+
471+
def analyse_convergence(
472+
self,
473+
slurm: bool = False,
474+
run_nos: _Optional[_List[int]] = None,
475+
mode: str = "cumulative",
476+
fraction: float = 1,
477+
equilibrated: bool = True,
478+
):
479+
"""
480+
Not implemented for CalcSet objects as convergence analysis is expensive.
481+
Call the analyse_convergence method on individual calculations instead (or
482+
run analyse convergence on each calculation in the set, if you're determined).
483+
"""
484+
raise NotImplementedError(
485+
"This method is not implemented for CalcSet objects (due to high computational ",
486+
"expense. Call the analyse_convergence method on individual calculations instead.",
487+
)

a3fe/run/calculation.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import shutil as _shutil
99
from typing import List as _List
1010
from typing import Optional as _Optional
11+
from pathlib import Path as _Path
1112

1213
from ._simulation_runner import SimulationRunner as _SimulationRunner
1314
from .enums import LegType as _LegType
@@ -154,6 +155,15 @@ def prep_stage(self) -> _PreparationStage:
154155

155156
return self._prep_stage
156157

158+
@property
159+
def is_complete(self) -> bool:
160+
f"""Whether the {self.__class__.__name__} has completed."""
161+
# Check if the overall_stats.dat file exists
162+
if _Path(f"{self.output_dir}/overall_stats.dat").is_file():
163+
return True
164+
165+
return False
166+
157167
def setup(
158168
self,
159169
bound_leg_sysprep_config: _Optional[_SystemPreparationConfig] = None,

a3fe/run/lambda_window.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,3 @@ def analyse_convergence(self) -> None:
405405

406406
def setup(self) -> None:
407407
raise NotImplementedError("LamWindows are set up when they are created")
408-
409-
@property
410-
def is_complete(self) -> bool:
411-
raise NotImplementedError()

a3fe/run/simulation.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,3 @@ def analyse_convergence(self) -> None:
658658

659659
def setup(self) -> None:
660660
raise NotImplementedError("Simulations are set up when they are created")
661-
662-
@property
663-
def is_complete(self) -> bool:
664-
raise NotImplementedError()

a3fe/tests/test_calc_set.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,28 @@ def test_calc_set_analysis(calc_set):
5252
assert results_exp.loc["mdm2_pip2_short", "calc_er"] == pytest.approx(
5353
0.0935, abs=1e-2
5454
)
55+
56+
57+
def test_calc_set_sub_sim_attrs(calc_set):
58+
"""Test that the attributes of the sub-simulation runners are correctly saved."""
59+
60+
def check_attr(sim_runner, attr_name="test_attr", value=42, force=True):
61+
"""Recursively check that the attribute is set for all sub-simulation runners."""
62+
if force:
63+
assert hasattr(sim_runner, attr_name)
64+
assert getattr(sim_runner, attr_name) == value
65+
else:
66+
if hasattr(sim_runner, attr_name):
67+
assert getattr(sim_runner, attr_name) == value
68+
69+
for sub_sim_runner in sim_runner._sub_sim_runners:
70+
check_attr(sub_sim_runner)
71+
72+
# Try forcing an attribute to be set
73+
calc_set.recursively_set_attr("test_attr", force=True, value=42)
74+
check_attr(calc_set, attr_name="test_attr", value=42, force=True)
75+
76+
# Also check that we can set the equilibration time attribute
77+
calc_set.set_equilibration_time(0.77)
78+
check_attr(calc_set, attr_name="_equilibration_time", value=0.77, force=False)
79+
check_attr(calc_set, attr_name="_equilibrated", value=True, force=False)

docs/CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Change Log
33
===============
44

5+
0.3.1
6+
====================
7+
- Modified the SimulationRunnerIterator to ensure that the state of sub-simulation runners is saved before they are torn down. This means that setting the equilibration time at the CalcSet level will now work as expected (previously the state of the calculations was not saved, causing the analysis to fail).
8+
- Updated read_overlap_mat so that it works when "#Overlap" is not printed to a new line (but is included at the end of the previous line). This seems to happen when MBAR requires many iterations to converge.
9+
510
0.3.0
611
====================
712

0 commit comments

Comments
 (0)