1
2
3
4
5
6 """
7 :mod:`gromacs.run` -- 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.run')
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
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
201 """Manage running :program:`mdrun_d`.
202 """
203 mdrun = "mdrun_d"
204
206 """Manage running :program:`mdrun` as an OpenMP_ multiprocessor job.
207
208 .. _OpenMP: http://openmp.org/wp/
209 """
210 mdrun = "mdrun_openmp"
211 mpiexec = "mpiexec"
212
214 """Manage running :program:`mdrun` as an OpenMP_ multiprocessor job (64-bit executable).
215
216 .. _OpenMP: http://openmp.org/wp/
217 """
218 mdrun = "mdrun_openmp64"
219 mpiexec = "mpiexec"
220
222 """Manage running :program:`mdrun` as mpich2_ multiprocessor job with the SMPD mechanism.
223
224 .. _mpich2: http://www.mcs.anl.gov/research/projects/mpich2/
225 """
226 mdrun = "mdrun_mpich2"
227 mpiexec = "mpiexec"
228
230 """Launch local smpd."""
231 cmd = ['smpd', '-s']
232 logger.info("Starting smpd: "+" ".join(cmd))
233 rc = subprocess.call(cmd)
234 return rc
235
236 - def posthook(self, **kwargs):
237 """Shut down smpd"""
238 cmd = ['smpd', '-shutdown']
239 logger.info("Shutting down smpd: "+" ".join(cmd))
240 rc = subprocess.call(cmd)
241 return rc
242
243
244
246 """Check if ``mdrun`` finished successfully.
247
248 Analyses the output from ``mdrun`` in *logfile*. Right now we are
249 simply looking for the line "Finished mdrun on node" in the last 1kb of
250 the file. (The file must be seeakable.)
251
252 :Arguments:
253 logfile : filename
254 Logfile produced by ``mdrun``.
255 :Returns: boolean (``True`` if all ok, ``False`` otherwise)
256 """
257 status = False
258 log = open(logfile)
259 try:
260 log.seek(-1024L, 2)
261 for line in log:
262 if line.startswith("Finished mdrun on node"):
263 status = True
264 break
265 finally:
266 log.close()
267
268 return status
269