1
2
3
4
5
6 """
7 :mod:`gromacs.simulation` -- Running simulations
8 ================================================
9
10 Helper functions and classes around :class:`gromacs.tools.Mdrun`.
11
12 .. autoclass:: MDrunner
13 :members:
14 .. autoclass:: MDrunnerOpenMP
15 .. autoclass:: MDrunnerOpenMP64
16 .. autoclass:: MDrunnerMpich2Smpd
17
18 .. autofunction:: check_mdrun_success
19
20 """
21 from __future__ import with_statement
22 __docformat__ = "restructuredtext en"
23
24 import subprocess
25 import os.path
26
27
28 import logging
29 logger = logging.getLogger('gromacs.simulation')
30
31
32
33 import core
34 import utilities
35
37 """A class to manage running :program:`mdrun` in various ways.
38
39 In order to do complicated multiprocessor runs with mpiexec or
40 similar you need to derive from this class and override
41
42 - :attr:`MDrunner.mdrun` with the path to the ``mdrun`` executable
43 - :attr:`MDrunner.mpiexec` with the path to the MPI launcher
44 - :meth:`MDrunner.mpicommand` with a function that returns the mpi command as a list
45
46 In addition there are two methods named :meth:`prehook` and
47 :meth:`posthook` that are called right before and after the
48 process is started. If they are overriden appropriately then they
49 can be used to set up a mpi environment.
50 """
51
52
53 mdrun = "mdrun"
54
55 mpiexec = None
56
57 - def __init__(self, dirname=os.path.curdir, **kwargs):
58 """Set up a simple run with ``mdrun``.
59
60 :Keywords:
61 *dirname*
62 Change to this directory before launching the job. Input
63 files must be supplied relative to this directory.
64 *keywords*
65 All other keword arguments are used to construct the
66 :class:`~gromacs.tools.mdrun` commandline. Note that only
67 keyword arguments are allowed.
68
69 """
70
71 self.dirname = dirname
72
73
74 cls = type('MDRUN', (core.GromacsCommand,),
75 {'command_name': self.mdrun,
76 '__doc__': "MDRUN command %r" % self.mdrun})
77
78 kwargs['failure'] = 'raise'
79 self.MDRUN = cls(**kwargs)
80
81
82 logname = kwargs.get('g', None)
83 if logname in (True, None):
84 logname = 'md'
85 deffnm = kwargs.get('deffnm', None)
86 if not deffnm is None:
87 logname = deffnm
88 self.logname = os.path.realpath(
89 os.path.join(self.dirname, self.filename(logname, ext='log')))
90
92 """Returns simple command line to invoke mdrun.
93
94 If :attr:`mpiexec` is set then :meth:`mpicommand` provides the mpi
95 launcher command that prefixes the actual ``mdrun`` invocation:
96
97 :attr:`mpiexec` [*mpiargs*] :attr:`mdrun` [*mdrun-args*]
98
99 The *mdrun-args* are set on initializing the class. Override
100 :meth:`mpicommand` to fit your system if the simple default
101 OpenMP launcher is not appropriate.
102 """
103 cmd = self.MDRUN.commandline()
104 if self.mpiexec:
105 cmd = self.mpicommand(**mpiargs) + cmd
106 return cmd
107
109 """Return a list of the mpi command portion of the commandline.
110
111 Only allows primitive mpi at the moment:
112 *mpiexec* -n *ncores* *mdrun* *mdrun-args*
113
114 (This is a primitive example for OpenMP. Override it for more
115 complicated cases.)
116 """
117 if self.mpiexec is None:
118 raise NotImplementedError("Override mpiexec to enable the simple OpenMP launcher")
119
120 ncores = kwargs.pop('ncores', 8)
121 return [self.mpiexec, '-n', str(ncores)]
122
124 """Called directly before launching the process."""
125 return
126
127 - def posthook(self, **kwargs):
128 """Called directly after the process terminated (also if it failed)."""
129 return
130
131 - def run(self, pre=None, post=None, **mpiargs):
132 """Execute the mdrun command (possibly as a MPI command) and run the simulation.
133
134 :Keywords:
135 *pre*
136 a dictionary containing keyword arguments for the :meth:`prehook`
137 *post*
138 a dictionary containing keyword arguments for the :meth:`posthook`
139 *mpiargs*
140 keyword arguments that are processed by :meth:`mpicommand`
141 """
142
143 if pre is None:
144 pre = {}
145 if post is None:
146 post = {}
147
148 cmd = self.commandline(**mpiargs)
149
150 with utilities.in_dir(self.dirname, create=False):
151 try:
152 self.prehook(**pre)
153 logger.info(" ".join(cmd))
154 rc = subprocess.call(cmd)
155 except:
156 logger.exception("Failed MD run for unknown reasons.")
157 raise
158 finally:
159 self.posthook(**post)
160 if rc == 0:
161 logger.info("MDrun completed ok, returncode = %d" % rc)
162 else:
163 logger.critical("Failure in MDrun, returncode = %d" % rc)
164 return rc
165
167 """Run :program:`mdrun` and check if run completed when it finishes.
168
169 This works by looking at the mdrun log file for 'Finished
170 mdrun on node'. It is useful to implement robust simulation
171 techniques.
172
173 :Arguments:
174 *kwargs* are keyword arguments that are passed on to
175 :meth:`run` (typically used for mpi things)
176
177 :Returns:
178 - ``True`` if run completed successfully
179 - ``False`` otherwise
180 """
181 rc = None
182 try:
183 rc = self.run(**kwargs)
184 except:
185 logger.exception("run_check: caught exception")
186 status = self.check_success()
187 if status:
188 logger.info("run_check: Hooray! mdrun finished successfully")
189 else:
190 logger.error("run_check: mdrun failed to complete run")
191 return status
192
194 """Check if :program:`mdrun` finished successfully.
195
196 (See :func:`check_mdrun_success` for details)
197 """
198 return check_mdrun_success(self.logname)
199
200
202 """Manage running :program:`mdrun` as an OpenMP_ multiprocessor job.
203
204 .. _OpenMP: http://openmp.org/wp/
205 """
206 mdrun = "mdrun_openmp"
207 mpiexec = "mpiexec"
208
210 """Manage running :program:`mdrun` as an OpenMP_ multiprocessor job (64-bit executable).
211
212 .. _OpenMP: http://openmp.org/wp/
213 """
214 mdrun = "mdrun_openmp64"
215 mpiexec = "mpiexec"
216
218 """Manage running :program:`mdrun` as mpich2_ multiprocessor job with the SMPD mechanism.
219
220 .. _mpich2: http://www.mcs.anl.gov/research/projects/mpich2/
221 """
222 mdrun = "mdrun_mpich2"
223 mpiexec = "mpiexec"
224
226 """Launch local smpd."""
227 cmd = ['smpd', '-s']
228 logger.info("Starting smpd: "+" ".join(cmd))
229 rc = subprocess.call(cmd)
230 return rc
231
232 - def posthook(self, **kwargs):
233 """Shut down smpd"""
234 cmd = ['smpd', '-shutdown']
235 logger.info("Shutting down smpd: "+" ".join(cmd))
236 rc = subprocess.call(cmd)
237 return rc
238
239
240
242 """Check if ``mdrun`` finished successfully.
243
244 Analyses the output from ``mdrun`` in *logfile*. Right now we are
245 simply looking for the line "Finished mdrun on node" in the last 1kb of
246 the file. (The file must be seeakable.)
247
248 :Arguments:
249 logfile : filename
250 Logfile produced by ``mdrun``.
251 :Returns: boolean (``True`` if all ok, ``False`` otherwise)
252 """
253 status = False
254 log = open(logfile)
255 try:
256 log.seek(-1024L, 2)
257 for line in log:
258 if line.startswith("Finished mdrun on node"):
259 status = True
260 break
261 finally:
262 log.close()
263
264 return status
265