Skip to content

Commit 917b5aa

Browse files
author
Chad Cumba
committed
Merge branch 'master' of https://github.com/ChadCumba/nipype
2 parents fd8d976 + 68a2e63 commit 917b5aa

File tree

4 files changed

+151
-48
lines changed

4 files changed

+151
-48
lines changed

doc/users/plugins.rst

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,20 +174,59 @@ Workflow execution with HTCondor DAGMan is done by calling::
174174
workflow.run(plugin='CondorDAGMan')
175175

176176
Job execution behavior can be tweaked with the following optional plug-in
177-
arguments::
178-
179-
template : submit spec template to use for job submission. The template
180-
all generated submit specs are appended to this template. This
181-
can be a str or a filename.
182-
submit_specs : additional submit specs that are appended to the generated
183-
submit specs to allow for overriding or extending the defaults.
184-
This can be a str or a filename.
185-
dagman_args : arguments to be prepended to the job execution script in the
186-
dagman call
177+
arguments. The value of most arguments can be a literal string or a filename,
178+
where in the latter case the content of the file will be used as the argument
179+
value::
180+
181+
submit_template : submit spec template for individual jobs in a DAG (see
182+
CondorDAGManPlugin.default_submit_template for the default.
183+
initial_specs : additional submit specs that are prepended to any job's
184+
submit file
185+
override_specs : additional submit specs that are appended to any job's
186+
submit file
187+
wrapper_cmd : path to an exectuable that will be started instead of a node
188+
script. This is useful for wrapper script that execute certain
189+
functionality prior or after a node runs. If this option is
190+
given the wrapper command is called with the respective Python
191+
exectuable and the path to the node script as final arguments
192+
wrapper_args : optional additional arguments to a wrapper command
193+
dagman_args : arguments to be prepended to the job execution script in the
194+
dagman call
195+
block : if True the plugin call will block until Condor has finished
196+
prcoessing the entire workflow (default: False)
187197

188198
Please see the `HTCondor documentation`_ for details on possible configuration
189199
options and command line arguments.
190200

201+
Using the ``wrapper_cmd`` argument it is possible to combine Nipype workflow
202+
execution with checkpoint/migration functionality offered by, for example,
203+
DMTCP_. This is especially useful in the case of workflows with long running
204+
nodes, such as Freesurfer's recon-all pipeline, where Condor's job
205+
prioritization algorithm could lead to jobs being evicted from compute
206+
nodes in order to maximize overall troughput. With checkpoint/migration enabled
207+
such a job would be checkpointed prior eviction and resume work from the
208+
checkpointed state after being rescheduled -- instead of restarting from
209+
scratch.
210+
211+
On a Debian system, executing a workflow with support for checkpoint/migration
212+
for all nodes could look like this::
213+
214+
# define common parameters
215+
dmtcp_hdr = """
216+
should_transfer_files = YES
217+
when_to_transfer_output = ON_EXIT_OR_EVICT
218+
kill_sig = 2
219+
environment = DMTCP_TMPDIR=./;JALIB_STDERR_PATH=/dev/null;DMTCP_PREFIX_ID=$(CLUSTER)_$(PROCESS)
220+
"""
221+
shim_args = "--log %(basename)s.shimlog --stdout %(basename)s.shimout --stderr %(basename)s.shimerr"
222+
# run workflow
223+
workflow.run(
224+
plugin='CondorDAGMan',
225+
plugin_args=dict(initial_specs=dmtcp_hdr,
226+
wrapper_cmd='/usr/lib/condor/shim_dmtcp',
227+
wrapper_args=shim_args)
228+
)
229+
191230
``qsub`` emulation
192231
~~~~~~~~~~~~~~~~~~
193232

@@ -230,3 +269,4 @@ Optional arguments::
230269
.. _HTCondor: http://www.cs.wisc.edu/htcondor/
231270
.. _DAGMan: http://research.cs.wisc.edu/htcondor/dagman/dagman.html
232271
.. _HTCondor documentation: http://research.cs.wisc.edu/htcondor/manual
272+
.. _DMTCP: http://dmtcp.sourceforge.net

nipype/interfaces/ants/registration.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,9 @@ def _formatMetric(self, index):
437437
if isinstance(name_input, list):
438438
items = stage_inputs.items()
439439
indexes = range(0, len(name_input))
440-
specs = [{k: v[i] for k, v in items} for i in indexes]
440+
# dict-comprehension only works with python 2.7 and up
441+
#specs = [{k: v[i] for k, v in items} for i in indexes]
442+
specs = [dict([(k, v[i]) for k,v in items]) for i in indexes]
441443
else:
442444
specs = [stage_inputs]
443445

nipype/interfaces/dipy/tensors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
iflogger = logging.getLogger('interface')
2020

2121
try:
22-
package_check('dipy', version='0.7.0')
22+
package_check('dipy', version='0.6.0')
2323
import dipy.reconst.dti as dti
2424
from dipy.core.gradients import GradientTable
2525
except Exception, e:

nipype/pipeline/plugins/dagman.py

Lines changed: 97 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import os
55
import sys
66
import uuid
7+
import time
8+
from warnings import warn
79

810
from .base import (GraphPluginBase, logger)
911

@@ -14,19 +16,46 @@ class CondorDAGManPlugin(GraphPluginBase):
1416
"""Execute using Condor DAGMan
1517
1618
The plugin_args input to run can be used to control the DAGMan execution.
19+
The value of most arguments can be a literal string or a filename, where in
20+
the latter case the content of the file will be used as the argument value.
21+
1722
Currently supported options are:
1823
19-
- template : submit spec template for individual jobs in a DAG. All
20-
generated submit spec components (e.g. executable name and
21-
arguments) are appended to this template. This can be a str or
22-
a filename. In the latter case the file content is used as a
23-
template.
24-
- submit_specs : additional submit specs that are appended to the generated
25-
submit specs to allow for overriding or extending the defaults.
26-
This can be a str or a filename.
24+
- submit_template : submit spec template for individual jobs in a DAG (see
25+
CondorDAGManPlugin.default_submit_template for the default.
26+
- initial_specs : additional submit specs that are prepended to any job's
27+
submit file
28+
- override_specs : additional submit specs that are appended to any job's
29+
submit file
30+
- wrapper_cmd : path to an exectuable that will be started instead of a node
31+
script. This is useful for wrapper script that execute certain
32+
functionality prior or after a node runs. If this option is
33+
given the wrapper command is called with the respective Python
34+
exectuable and the path to the node script as final arguments
35+
- wrapper_args : optional additional arguments to a wrapper command
2736
- dagman_args : arguments to be prepended to the job execution script in the
2837
dagman call
38+
- block : if True the plugin call will block until Condor has finished
39+
prcoessing the entire workflow (default: False)
2940
"""
41+
42+
default_submit_template = """
43+
universe = vanilla
44+
notification = Never
45+
executable = %(executable)s
46+
arguments = %(nodescript)s
47+
output = %(basename)s.out
48+
error = %(basename)s.err
49+
log = %(basename)s.log
50+
getenv = True
51+
"""
52+
def _get_str_or_file(self, arg):
53+
if os.path.isfile(arg):
54+
content = open(arg).read()
55+
else:
56+
content = arg
57+
return content
58+
3059
# XXX feature wishlist
3160
# - infer data file dependencies from jobs
3261
# - infer CPU requirements from jobs
@@ -35,21 +64,34 @@ class CondorDAGManPlugin(GraphPluginBase):
3564
# actually have to run. would be good to be able to decide whether they
3665
# actually have to be scheduled (i.e. output already exist).
3766
def __init__(self, **kwargs):
38-
self._template = "universe = vanilla\nnotification = Never"
39-
self._submit_specs = ""
40-
self._dagman_args = ""
41-
if 'plugin_args' in kwargs and not kwargs['plugin_args'] is None:
67+
for var, id_, val in \
68+
(('_template', 'submit_template', self.default_submit_template),
69+
('_initial_specs', 'template', ''),
70+
('_initial_specs', 'initial_specs', ''),
71+
('_override_specs', 'submit_specs', ''),
72+
('_override_specs', 'override_specs', ''),
73+
('_wrapper_cmd', 'wrapper_cmd', None),
74+
('_wrapper_args', 'wrapper_args', ''),
75+
('_block', 'block', False),
76+
('_dagman_args', 'dagman_args', '')):
77+
if 'plugin_args' in kwargs \
78+
and not kwargs['plugin_args'] is None \
79+
and id_ in kwargs['plugin_args']:
80+
if id_ == 'wrapper_cmd':
81+
val = os.path.abspath(kwargs['plugin_args'][id_])
82+
elif id_ == 'block':
83+
val = kwargs['plugin_args'][id_]
84+
else:
85+
val = self._get_str_or_file(kwargs['plugin_args'][id_])
86+
setattr(self, var, val)
87+
# TODO remove after some time
88+
if 'plugin_args' in kwargs \
89+
and not kwargs['plugin_args'] is None:
4290
plugin_args = kwargs['plugin_args']
4391
if 'template' in plugin_args:
44-
self._template = plugin_args['template']
45-
if os.path.isfile(self._template):
46-
self._template = open(self._template).read()
92+
warn("the 'template' argument is deprecated, use 'initial_specs' instead")
4793
if 'submit_specs' in plugin_args:
48-
self._submit_specs = plugin_args['submit_specs']
49-
if os.path.isfile(self._submit_specs):
50-
self._submit_specs = open(self._submit_specs).read()
51-
if 'dagman_args' in plugin_args:
52-
self._dagman_args = plugin_args['dagman_args']
94+
warn("the 'submit_specs' argument is deprecated, use 'override_specs' instead")
5395
super(CondorDAGManPlugin, self).__init__(**kwargs)
5496

5597
def _submit_graph(self, pyfiles, dependencies, nodes):
@@ -62,26 +104,35 @@ def _submit_graph(self, pyfiles, dependencies, nodes):
62104
# as jobs in the DAG
63105
for idx, pyscript in enumerate(pyfiles):
64106
node = nodes[idx]
65-
template, submit_specs = self._get_args(
66-
node, ["template", "submit_specs"])
67107
# XXX redundant with previous value? or could it change between
68108
# scripts?
109+
template, initial_specs, override_specs, wrapper_cmd, wrapper_args = \
110+
self._get_args(node,
111+
["template", "initial_specs",
112+
"override_specs", "wrapper_cmd",
113+
"wrapper_args"])
114+
# add required slots to the template
115+
template = '%s\n%s\n%s\nqueue\n' % (
116+
'%(initial_specs)s',
117+
template,
118+
'%(override_specs)s')
69119
batch_dir, name = os.path.split(pyscript)
70120
name = '.'.join(name.split('.')[:-1])
71-
submitspec = '\n'.join(
72-
(template,
73-
'executable = %s' % sys.executable,
74-
'arguments = %s' % pyscript,
75-
'output = %s' % os.path.join(batch_dir,
76-
'%s.out' % name),
77-
'error = %s' % os.path.join(batch_dir,
78-
'%s.err' % name),
79-
'log = %s' % os.path.join(batch_dir,
80-
'%s.log' % name),
81-
'getenv = True',
82-
submit_specs,
83-
'queue'
84-
))
121+
specs = dict(
122+
# TODO make parameter for this,
123+
initial_specs=initial_specs,
124+
executable=sys.executable,
125+
nodescript=pyscript,
126+
basename=os.path.join(batch_dir, name),
127+
override_specs=override_specs
128+
)
129+
if not wrapper_cmd is None:
130+
specs['executable'] = wrapper_cmd
131+
specs['nodescript'] = \
132+
'%s %s %s' % (wrapper_args % specs, # give access to variables
133+
sys.executable,
134+
pyscript)
135+
submitspec = template % specs
85136
# write submit spec for this job
86137
submitfile = os.path.join(batch_dir,
87138
'%s.submit' % name)
@@ -105,3 +156,13 @@ def _submit_graph(self, pyfiles, dependencies, nodes):
105156
self._dagman_args)
106157
cmd.run()
107158
logger.info('submitted all jobs to Condor DAGMan')
159+
if self._block:
160+
# wait for DAGMan to settle down, no time wasted it is already running
161+
time.sleep(10)
162+
if not os.path.exists('%s.condor.sub' % dagfilename):
163+
raise EnvironmentError("DAGMan did not create its submit file, please check the logs")
164+
# wait for completion
165+
logger.info('waiting for DAGMan to finish')
166+
lockfilename = '%s.lock' % dagfilename
167+
while os.path.exists(lockfilename):
168+
time.sleep(5)

0 commit comments

Comments
 (0)