Skip to content

Commit

Permalink
Merge pull request #913 from oliverdutton/master
Browse files Browse the repository at this point in the history
Fix: OPS hangs when external engine errors/exits unexpectedly
  • Loading branch information
dwhswenson committed Dec 23, 2020
2 parents 9671b53 + c2d47f0 commit c6095fd
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 18 deletions.
18 changes: 10 additions & 8 deletions openpathsampling/engines/dynamics_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class DynamicsEngine(StorableNamedObject):
some trajectory you might not restart completely. Possibilities are
1. `full` will restart completely and use the initial frames (default)
2. `50%` will cut the existing in half but keeping at least the initial
2. `keep_half` will cut the existing in half but keeping at least the initial
3. `remove_interval` will remove as many frames as the `interval`
4. a callable will be used as a function to generate the new from the
old trajectories, e.g. `lambda t: t[:10]` would restart with the
Expand Down Expand Up @@ -509,15 +509,17 @@ def iter_generate(self, initial, running=None, direction=+1,
int(len(trajectory) * 0.9),
max(
len(initial),
len(trajectory) / 2))]
int(len(trajectory) / 2)))]
elif hasattr(self.on_retry, '__call__'):
trajectory = self.on_retry(trajectory)

if direction > 0:
self.current_snapshot = trajectory[-1]
elif direction < 0:
# backward simulation needs reversed snapshots
self.current_snapshot = trajectory[0].reversed

""" Case of run dying before first output"""
if len(trajectory) >= 1:
if direction > 0:
self.current_snapshot = trajectory[-1]
elif direction < 0:
# backward simulation needs reversed snapshots
self.current_snapshot = trajectory[0].reversed

logger.info("Starting trajectory")
self.start()
Expand Down
15 changes: 9 additions & 6 deletions openpathsampling/engines/external_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,11 @@ def generate_next_frame(self):
#print self.frame_num, next_frame # DEBUG LOGGER
now = time.time()
if next_frame == "partial":
if not self.proc.is_running():
if self.proc.poll() is not None:
raise RuntimeError("External engine died unexpectedly")
time.sleep(0.001) # wait a millisec and rerun
elif next_frame is None:
if not self.proc.is_running():
if self.proc.poll() is not None:
raise RuntimeError("External engine died unexpectedly")
logger.debug("Sleeping for {:.2f}ms".format(self.sleep_ms))
time.sleep(self.sleep_ms/1000.0)
Expand Down Expand Up @@ -265,10 +265,13 @@ def stop(self, trajectory):
proc = self.who_to_kill()
logger.info("About to send signal %s to %s", str(self.killsig),
str(proc))
proc.send_signal(self.killsig)
logger.debug("Signal has been sent")
proc.wait() # wait for the zombie to die
logger.debug("Zombie should be dead")
try:
proc.send_signal(self.killsig)
logger.debug("Signal has been sent")
proc.wait() # wait for the zombie to die
logger.debug("Zombie should be dead")
except psutil.NoSuchProcess:
logger.debug("Tried to kill process, but it was already dead")
self.cleanup()

# FROM HERE ARE THE FUNCTIONS TO OVERRIDE IN SUBCLASSES:
Expand Down
11 changes: 8 additions & 3 deletions openpathsampling/engines/gromacs/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ class GromacsEngine(ExternalEngine):
-e self.edr_file -g self.log_file``, where the ``topol.top``
is generated by :meth:`.prepare`, and the other filenames are
set by :meth:`.set_filenames`. Default is the empty string.
* ``snapshot_timestep``: time between frames analysed by ops.
You keep track of the unit, I'd advise ps so the output
rates will be in ps. Example. 2 fs timestep in the mdp with
nstxout of 30 would give snapshot_timestep of 60 fs = 0.06 ps
base_dir : string
root directory where all files will be found (defaults to pwd)
Expand All @@ -127,7 +131,8 @@ class GromacsEngine(ExternalEngine):
**{
'gmx_executable': "gmx ",
'grompp_args': "",
'mdrun_args': ""
'mdrun_args': "",
'snapshot_timestep':1.0
}
)
GROMPP_CMD = ("{e.options[gmx_executable]}grompp -c {e.gro} "
Expand Down Expand Up @@ -278,9 +283,9 @@ def write_frame_to_file(self, filename, snapshot, mode='w'):
xyz = np.asarray([snapshot.xyz], dtype=np.float32)
time = np.asarray([0.0], dtype=np.float32)
step = np.asarray([0], dtype=np.int32)
box = np.asarray([snapshot.box_vectors], dtype=np.float32)
box = np.asarray([np.asarray(snapshot.box_vectors)], dtype=np.float32)
lambd = np.asarray([0.0], dtype=np.float32)
vel = np.asarray([snapshot.velocities], dtype=np.float32)
vel = np.asarray([np.asarray(snapshot.velocities)], dtype=np.float32)

try:
trr = TRRTrajectoryFile(filename, mode)
Expand Down
2 changes: 1 addition & 1 deletion openpathsampling/engines/toy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .integrators import (LangevinBAOABIntegrator, LeapfrogVerletIntegrator)
from .pes import Gaussian, HarmonicOscillator, LinearSlope, OuterWalls, \
PES_Add, PES_Combination, PES_Sub, PES
PES_Add, PES_Combination, PES_Sub, PES, BolhuisExample

from .engine import ToyEngine as Engine
from .engine import ToyEngine
Expand Down
51 changes: 51 additions & 0 deletions openpathsampling/engines/toy/pes.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,54 @@ def dVdx(self, sys):
"""
# this is independent of the position
return self._local_dVdx

class BolhuisExample(PES):
r"""Creates an x**4 - 2 x**2 1 dimensional PES
Parameters
----------
None
"""
def __init__(self):
super(BolhuisExample, self).__init__()
self._local_dVdx = np.zeros(1)

def __repr__(self): # pragma: no cover
return "The x**4 - 2 x**2 example in Bolhuis papers potential"

def V(self, sys):
"""Potential energy
Parameters
----------
sys : :class:`.ToyEngine`
engine contains its state, including velocities and masses
Returns
-------
float
the potential energy
"""
dx = sys.positions
myV = 0.0
for i in range(len(dx)):
myV += dx[i]**4 - 2 * dx[i]**2
return myV

def dVdx(self, sys):
"""Derivative of potential energy (-force)
Parameters
----------
sys : :class:`.ToyEngine`
engine contains its state, including velocities and masses
Returns
-------
np.array
the derivatives of the potential at this point
"""
dx = sys.positions
for i in range(len(dx)):
self._local_dVdx[i] =4*dx[i]**3 - 4*dx[i]
return self._local_dVdx

0 comments on commit c6095fd

Please sign in to comment.