HWRF  trunk@4391
mpiprog.py
1 """!Object structure for describing MPI programs.
2 
3 Do not load this module directly. It is meant to be loaded only by
4 the produtil.run module.
5 
6 This module handles execution of MPI programs, and execution of
7 groups of non-MPI programs through an MPI interface (which requires
8 all sorts of tricks). This module is also the interface to the
9 various produtil.mpi_impl.* modules that generate the shell command to
10 run MPI programs. This module is built on top of the produtil.prog
11 module and uses it to run the MPI-launching program for your local
12 cluster (mpiexec, mpirun, poe, etc.)
13 
14 In addition, this module contains code to simplify adding new MPI
15 implementations to the produtil.mpi_impl subpackage. High-level code,
16 such as the HWRF scripts, use the produtil.run module to generate
17 object trees of MPIRanksBase objects. The produtil.mpi_impl
18 subpackages then implement an mpirunner function that turns those into
19 a produtil.prog.Runner to be directly executed. The MPIRanksBase
20 object, and its subclasses, implement a few utilites to automate that
21 for you:
22 
23 * to_arglist --- converts the MPI ranks to an mpi launcher command
24  as a produtil.prog.Runner, or to an array of strings
25  for a command file.
26 
27 * nranks --- calculates the number of requested MPI ranks
28 
29 * expand_iter --- iterates over groups of identical MPI ranks
30 
31 * check_serial --- tells whether this program is running MPI programs,
32  or running serial programs as if they were MPI
33  (or both, which most MPI implementations don't support)
34 
35 For MPI implementations that require a command file, see the
36 produtil.mpi_impl.mpi_impl_base CMDFGen class to have the
37 produtil.prog module automatically write the command file before
38 executing the program. The produtil.mpi_impl.mpirun_lsf shows an
39 example of how to use it.
40 
41 See the produtil.run module for full documentation."""
42 
43 ##@var __all__
44 # Ensure nothing is loaded by "from produtil.mpiprog import *"
45 __all__=[]
46 
47 import produtil.prog
48 from produtil.prog import ProgSyntaxError
49 
51  """!Base class of syntax errors in MPI program specifications"""
53  """!Raised when something that cannot be expressed as a pure MPI
54  rank is given as a pure MPI rank."""
56  """!Raised when an MPI program was expected but something else was
57  given."""
59  """!Raised when a serial program was expected, but something else
60  was given."""
62  """!Raised when the validation scripts were expecting string
63  arguments or string executable names, but something else was
64  found."""
65 
66 ########################################################################
67 
68 class MPIRanksBase(object):
69  """!This is the abstract superclass of all classes that represent
70  one or more MPI ranks, including MPI ranks that are actually
71  serial programs. Subclasses of MPIRanksBase allow an MPI program
72  to be represented as a tree of MPIRanksBase objects, in such a way
73  that they can be easily converted to a produtil.prog.Runner object
74  for execution. The actual conversion to a Runner is done in the
75  produtil.mpi_impl package (see produtil/mpi_impl/__init__.py)"""
76 
77  def to_arglist(self,to_shell=False,expand=False,shell_validate=None,
78  pre=[],before=[],between=[],after=[],post=[],extra={}):
79  """!This is the underlying implementation of most of the
80  mpi_impl modules, and hence make_runner as well. It converts
81  this group of MPI ranks into a set of arguments suitable for
82  sending to a Runner object or for writing to a command file.
83  This is done by iterating over either all ranks (if
84  expand=True) or groups of repeated ranks (if expand=False),
85  converting their arguments to a list. It prepends an
86  executable, and can insert other arguments in specified
87  locations (given in the pre, before, between, after, and post
88  arguments). It can also use the to_shell argument to convert
89  programs to POSIX sh commands, and it performs simple string
90  interpolation via the "extra" hash.
91 
92  If to_shell=False then the executable and arguments are
93  inserted directly to the output list. Otherwise (when
94  to_shell=True) the to_shell subroutine is called on the
95  MPIRank object to produce a single argument that contains a
96  shell command. That single argument is then used in place of
97  the executable and arguments. Note that may raise
98  NotValidPosixSh (or a subclass thereof) if the command cannot
99  be expressed as a shell command. In addition, if
100  shell_validate is not None, then it is called on each
101  post-conversion shell argument, and the return value is used
102  instead.
103 
104  You can specify additional argument lists to be inserted in
105  certain locations. Each argument in those lists will be
106  processed through the % operator, specifying "extra" as the
107  keyword list with two new keywords added: nworld is the number
108  of ranks in the MPI program, and "n" is the number in the
109  current group of repeated ranks if expand=False (n=1 if
110  expand=True). Those argument lists are: pre, before, between,
111  after and post.
112 
113  @param to_shell If True, convert executable and arguments to
114  a POSIX sh command instead of inserting them directly.
115  @param expand If True, groups of repeated ranks are expanded.
116  @param shell_validate A function to convert each argument to
117  some "shell-acceptable" version.
118  @param pre Inserted before everything else. This is where you
119  would put the "mpiexec" and any global settings.
120  @param before Inserted before each rank (if expand=True) or group
121  (if expand=False)
122  @param between Inserted between each rank (if expand=True) or group
123  (if expand=False)
124  @param after Inserted after each rank (if expand=True) or group (if
125  expand=False)
126  @param post Appended at the end of the list of arguments.
127  @param extra used for string expansion"""
128  kw=dict(extra)
129  kw['nworld']=self.nranks()
130  for x in pre: yield x%kw
131  first=True
132  for rank,count in self.expand_iter(bool(expand)):
133  assert(isinstance(rank,MPIRanksBase))
134  assert(isinstance(count,int))
135  if not count>0:
136  continue
137  if first:
138  first=False
139  else:
140  for x in between: yield x%kw
141  kw['n']=count
142  for x in before: yield x%kw
143  if to_shell:
144  if shell_validate is not None:
145  yield shell_validate(rank.to_shell())
146  else:
147  yield rank.to_shell()
148  else:
149  for arg in rank.args(): yield arg
150  for x in after: yield x%kw
151  for x in post: yield x%kw
153  """!Returns a copy of this object where all child
154  produtil.prog.Runner objects have been replaced with
155  produtil.prog.ImmutableRunner objects."""
156  def get_logger(self):
157  """!Returns a logger.Logger object for this MPIRanksBase or one
158  from its child MPIRanksBase objects (if it has any). If no
159  logger is found, None is returned."""
160  return None
161  def check_serial(self):
162  """!Returns a tuple (s,p) where s=True if there are serial
163  ranks in this part of the MPI program, and p=True if there are
164  parallel ranks. Note that it is possible that both could be
165  True, which is an error. It is also possible that neither are
166  True if there are zero ranks."""
167  return (False,False)
168  def nranks(self):
169  """!Returns the number of ranks in this part of the MPI
170  program."""
171  return 0
172  def ranks(self):
173  """!Iterates over all MPIRank objects in this part of the MPI
174  program."""
175  pass
176  def ngroups(self):
177  """!Returns the number of groups of repeated MPI ranks in the
178  MPI program."""
179  return 0
180  def groups(self,threads=False):
181  """!Iterates over all groups of repeating MPI ranks in the MPI
182  program returning tuples (r,c) containing a rank r and the
183  count (number) of that rank c.
184  @param threads If True, then a three-element tuple is
185  iterated, (r,c,t) where the third element is the number of
186  threads."""
187  pass
188 
189  def getthreads(self):
190  """!Returns the number of threads requested by this MPI rank,
191  or by each MPI rank in this group of MPI ranks. If different
192  ranks have different numbers of threads, returns the maximum
193  requested. Returns None if no threads are requested."""
194  n=None
195  for r,c,t in self.groups(threads=True):
196  if t is not None and n is None:
197  n=t
198  elif t is not None and n is not None and t>n:
199  n=t
200  return n
201  def setthreads(self,nthreads):
202  """!Sets the number of threads requested by each MPI rank
203  within this group of MPI ranks."""
204  for r,c in self.groups():
205  r.threads=nthreads
206  return nthreads
207  def delthreads(self):
208  """!Removes the request for threads."""
209  for r,c in self.groups():
210  del r.threads
211  threads=property(getthreads,setthreads,delthreads,"""The number of threads per rank.""")
212 
213  def __eq__(self,other):
214  """!Returns True if this part of
215  the MPI program is the same as another part.
216  @param other the other object"""
217  return NotImplemented
218  def __mul__(self,factor):
219  """!Returns a new set of MPI ranks that consist of this group
220  of ranks repeated "factor" times.
221  @param factor how many times to duplicate"""
222  return NotImplemented
223  def __rmul__(self,other):
224  """!Returns a new set of MPI ranks that consist of this group
225  of ranks repeated "factor" times.
226  @param other how many times to duplicate"""
227  return NotImplemented
228  def __add__(self,other):
229  """!Returns a new set of MPI ranks that consist of this set of
230  ranks with the "other" set appended.
231  @param other the data to append"""
232  return NotImplemented
233  def __radd__(self,other):
234  """!Returns a new set of MPI ranks that consist of the "other"
235  set of ranks with this set appended.
236  @param other the data to prepend"""
237  return NotImplemented
238  def isplainexe(self):
239  """!Determines if this set of MPI ranks can be represented by a
240  single serial executable with a single set of arguments run
241  without MPI. Returns false by default: this function can only
242  return true for MPISerial."""
243  return False
244  def to_shell(self):
245  """!Returns a POSIX sh command that will execute the serial
246  program, if possible, or raise a subclass of NotValidPosixSh
247  otherwise. Works only on single MPI ranks that are actually
248  MPI wrappers around a serial program (ie.: from mpiserial)."""
249  raise NotSerialProg('This is an MPI program, so it cannot be represented as a non-MPI POSIX sh command.')
250  def expand_iter(self,expand,threads=False):
251  """!This is a wrapper around ranks() and groups() which will
252  call self.groups() if expand=False. If expand=True, this will
253  call ranks() returning a tuple (rank,1) for each rank.
254  @param expand If True, expand groups of identical ranks into one
255  rank of each member
256  @param threads If True, then a third element will be in each
257  tuple: the number of requested threads per MPI rank."""
258  if expand:
259  if threads:
260  for rank in self.ranks():
261  yield (rank,1,self.threads)
262  else:
263  for rank in self.ranks():
264  yield (rank,1)
265  elif threads:
266  for rank,count,threads in self.groups(threads=True):
267  yield (rank,count,threads)
268  else:
269  for rank,count in self.groups(threads=False):
270  yield (rank,count)
271  def __repr__(self):
272  """!Returns a string representation of this object intended for debugging."""
273  raise NotImplementedError('This class did not implement __repr__.')
274 
275 ########################################################################
276 
278  """!Represents one MPI program duplicated across many ranks."""
279  def __init__(self,mpirank,count):
280  """!MPIRanksSPMD constructor
281  @param mpirank the program to run
282  @param count how many times to run it"""
283  if not isinstance(mpirank,MPIRank):
284  raise MPIProgSyntaxError('Input to MPIRanksSPMD must be an MPIRank.')
285  self._mpirank=mpirank
286  self._count=int(count)
288  """!Returns a new MPIRanksSPMD with an immutable version of
289  self._mpirank."""
290  return MPIRanksSPMD(self._mpirank.make_runners_immutable(),self._count)
291  def __repr__(self):
292  """!Returns "X*N" where X is the MPI program and N is the
293  number of ranks."""
294  return '%s*%d'%(repr(self._mpirank),int(self._count))
295  def ngroups(self):
296  """!Returns 1 or 0: 1 if there are ranks and 0 if there are none."""
297  if self._count>0:
298  return 1
299  else:
300  return 0
301  def groups(self,threads=False):
302  """!Yields a tuple (X,N) where X is the mpi program and N is
303  the number of ranks."""
304  if threads:
305  yield self._mpirank,self._count,self._mpirank.threads
306  else:
307  yield self._mpirank,self._count
308  def copy(self):
309  """!Returns a deep copy of self."""
310  return MPIRanksSPMD(self._mpirank.copy(),self._count)
311  def ranks(self):
312  """!Iterates over MPI ranks within self."""
313  if self._count>0:
314  for i in xrange(self._count):
315  yield self._mpirank
316  def nranks(self):
317  """!Returns the number of ranks this program requests."""
318  if self._count>0:
319  return self._count
320  else:
321  return 0
322  def __mul__(self,factor):
323  """!Multiply the number of requested ranks by some factor."""
324  if not isinstance(factor,int):
325  return NotImplemented
326  return MPIRanksSPMD(self._mpirank.copy(),count*factor)
327  def __rmul__(self,factor):
328  """!Multiply the number of requested ranks by some factor."""
329  if not isinstance(factor,int):
330  return NotImplemented
331  return MPIRanksSPMD(self._mpirank.copy(),count*factor)
332  def __add__(self,other):
333  """!Add some new ranks to self. If they are not identical to
334  the MPI program presently requested, this returns a new
335  MPIRanksMPMD."""
336  copy=True
337  ocount=other.nranks()
338  for mpirank,count in other.groups():
339  if not mpirank==self._mpirank:
340  copy=False
341  break
342  if copy:
343  return MPIRanksSPMD(self._mpirank.copy(),self.nranks()+ocount)
344  else:
345  return MPIRanksMPMD([self.copy(),other.copy()])
346  def check_serial(self):
347  """!Checks to see if this program contains serial (non-MPI) or
348  MPI components.
349  @returns a tuple (serial,parallel) where serial is True if
350  there are serial components, and parallel is True if there are
351  parallel components. If there are no components, returns
352  (False,False)"""
353  if self._count>0:
354  return self._mpirank.check_serial()
355  else:
356  return (False,False)
357  def get_logger(self):
358  """!Returns my MPI program's logger."""
359  return self._mpirank.get_logger()
360 
361 ########################################################################
362 
364  """!Represents a group of MPI programs, each of which have some
365  number of ranks assigned."""
366  def __init__(self,args):
367  """!MPIRanksMPMD constructor
368  @param args an array of MPIRanksBase to execute."""
369  self._el=list(args)
370  self._ngcache=None
371  self._nrcache=None
372  self._threads=None
374  """!Tells each containing element to make its
375  produtil.prog.Runners into produtil.prog.ImmutableRunners so
376  that changes to them will not change the original."""
377  return MPIRanksMPMD([el.make_runners_immutable() for el in self._el])
378  def __repr__(self):
379  """!Returns a pythonic description of this object."""
380  reprs=[]
381  for el in self._el:
382  if el.nranks()>0:
383  reprs.append(repr(el))
384  return ' + '.join(reprs)
385  def ngroups(self):
386  """!How many groups of identical repeated ranks are in this
387  MPMD program?"""
388  if self._ngcache is None:
389  ng=0
390  for g in self._el:
391  ng+=g.ngroups()
392  self._ngcache=ng
393  return self._ngcache
394  def nranks(self):
395  """!How many ranks does this program request?"""
396  if self._nrcache is None:
397  nr=0
398  for g in self._el:
399  nr+=g.nranks()
400  self._nrcache=nr
401  return self._nrcache
402  def groups(self,threads=False):
403  """!Iterates over tuples (rank,count) of groups of identical
404  ranks."""
405  if threads:
406  for groups in self._el:
407  for rank,count,threads in groups.groups(threads=True):
408  yield rank,count,threads
409  else:
410  for groups in self._el:
411  for rank,count in groups.groups():
412  yield rank,count
413  def ranks(self):
414  """!Iterates over groups of repeated ranks returning the number
415  of ranks each requests."""
416  for ranks in self._el:
417  for rank in ranks.ranks():
418  yield rank
419  def __add__(self,other):
420  """!Adds more ranks to this program.
421  @param other an MPIRanksMPMD or MPIRanksSPMD to add"""
422  if isinstance(other,MPIRanksMPMD):
423  return MPIRanksMPMD(self._el+other._el)
424  elif isinstance(other,MPIRank) or isinstance(other,MPIRanksBase):
425  return MPIRanksMPMD(self._el+[other])
426  return NotImplemented
427  def __radd__(self,other):
428  """!Prepends more ranks to this program.
429  @param other an MPIRanksMPMD or MPIRanksSPMD to prepend"""
430  if isinstance(other,MPIRanksMPMD):
431  return MPIRanksMPMD(other._el+self._el)
432  elif isinstance(other,MPIRank) or isinstance(other,MPIRanksBase):
433  return MPIRanksMPMD([other]+self._el)
434  return NotImplemented
435  def __mul__(self,factor):
436  """!Duplicates this MPMD program "factor" times.
437  @param factor how many times to duplicate this program."""
438  if isinstance(factor,int):
439  return MPIRanksMPMD(self._el*factor)
440  return NotImplemented
441  def __rmul__(self,factor):
442  """!Duplicates this MPMD program "factor" times.
443  @param factor how many times to duplicate this program."""
444  if isinstance(factor,int):
445  return MPIRanksMPMD(factor*self._el)
446  def check_serial(self):
447  """!Checks to see if this program contains serial (non-MPI) or
448  MPI components.
449  @returns a tuple (serial,parallel) where serial is True if
450  there are serial components, and parallel is True if there are
451  parallel components."""
452  serial=False
453  parallel=False
454  for el in self._el:
455  (s,p)=el.check_serial()
456  serial = (serial or s)
457  parallel = (parallel or p)
458  return (serial,parallel)
459  def get_logger(self):
460  """!Returns a logging.Logger for the first rank that has one."""
461  for el in self._el:
462  logger=el.get_logger()
463  if logger is not None:
464  return logger
465  return None
466 
467 ########################################################################
468 
470  """!Represents a single MPI rank."""
471  def __init__(self,arg,logger=None):
472  """!MPIRank constructor.
473  @param arg What program to run. Can be a produtil.prog.Runner,
474  or some way of creating one, such as a program name or list
475  of program+arguments.
476  @param logger a logging.Logger for log messages or None to
477  have no logger."""
478  self._logger=logger
479  self._threads=None
480  if isinstance(arg,MPIRank):
481  if self._logger is None:
482  self._logger=arg._logger
483  self._args=list(arg._args)
484  elif isinstance(arg,produtil.prog.Runner):
485  if arg.isplainexe():
486  self._args=[x for x in arg.args()]
487  else:
488  raise ComplexProgInput(
489  'Tried to convert a Runner to an MPIRank directly, when '
490  'the Runner had more than an executable and arguments. '
491  'Use mpiserial instead.')
492  elif isinstance(arg,basestring):
493  self._args=[arg]
494  elif isinstance(arg,list) or isinstance(arg,tuple):
495  self._args=[x for x in arg]
496  else:
497  raise MPIProgSyntaxError(
498  'Input to MPIRank.__init__ must be a string, a list of '
499  'strings, or a Runner that contains only the executable '
500  'and its arguments.')
501  self.validate()
502  def getthreads(self):
503  """!Returns the number of threads requested by this MPI rank,
504  or by each MPI rank in this group of MPI ranks."""
505  return self._threads
506  def setthreads(self,nthreads):
507  """!Sets the number of threads requested by each MPI rank
508  within this group of MPI ranks."""
509  self._threads=int(nthreads)
510  return self._threads
511  def delthreads(self):
512  """!Removes the request for threads."""
513  self._threads=None
514  def to_shell(self):
515  """!Return a POSIX sh representation of this MPI rank, if
516  possible."""
517  return ' '.join([produtil.prog.shbackslash(x) for x in self._args])
518  def __getitem__(self,args):
519  """!Adds arguments to this MPI rank's program."""
520  c=self.copy()
521  if isinstance(args,basestring):
522  c._args.append(args)
523  else:
524  c._args.extend(args)
525  return c
526  def __repr__(self):
527  """!Returns a Pythonic representation of this object for
528  debugging."""
529  s='mpi(%s)'%(repr(self._args[0]))
530  if len(self._args)>1:
531  s=s+'['+','.join([repr(x) for x in self._args[1:]])+']'
532  return s
533  def get_logger(self):
534  """!Returns a logging.Logger for this object, or None."""
535  return logger
536  def validate(self,more=None):
537  """!Checks to see if this MPIRank is valid, or has errors.
538  @param more Arguments to the executable to validate.
539  @returns None if there are no errors, or raises a descriptive
540  exception."""
541  for x in self.args():
542  if not isinstance(x,basestring):
543  raise InputsNotStrings(
544  'Executable and arguments must be strings.')
545  if more is not None and len(more)>0:
546  for x in more:
547  if not isinstance(x,basestring):
548  raise InputsNotStrings(
549  'Executable and arguments must be strings.')
550  def args(self):
551  """!Iterates over the executable arguments."""
552  for arg in self._args: yield arg
553  def copy(self):
554  """!Return a copy of self. This is a deep copy except for the
555  logger which whose reference is copied."""
556  return MPIRank(self)
557  def nranks(self):
558  """!Returns 1: the number of MPI ranks."""
559  return 1
560  def ngroups(self):
561  """!Returns 1: the number of groups of identical ranks."""
562  return 1
563  def ranks(self):
564  """!Yields self once: all MPI ranks."""
565  yield self
566  def groups(self,threads=False):
567  """!Yields (self,1): all groups of identical ranks and the
568  number per group."""
569  if threads:
570  yield (self,1,self._threads)
571  else:
572  yield (self,1)
573  def __add__(self,other):
574  """!Creates an MPIRanksSPMD or MPIRanksMPMD with this MPIRank
575  and the other ranks.
576  @param other The other ranks."""
577  if not isinstance(other,MPIRank):
578  return NotImplemented
579  elif other==self:
580  return MPIRanksSPMD(self.copy(),2)
581  else:
582  return MPIRanksMPMD([self,other])
583  def __mul__(self,factor):
584  """!Creates an MPIRanksSPMD with this MPIRank duplicated factor times.
585  @param factor the number of times to duplicate"""
586  if isinstance(factor,int):
587  return MPIRanksSPMD(self,factor)
588  return NotImplemented
589  def __rmul__(self,factor):
590  """!Creates an MPIRanksSPMD with this MPIRank duplicated factor times.
591  @param factor the number of times to duplicate"""
592  if isinstance(factor,int):
593  return MPIRanksSPMD(self,factor)
594  return NotImplemented
595  def __eq__(self,other):
596  """!Returns True if this MPIRank is equal to the other object."""
597  return self._args==other._args
598  def check_serial(self):
599  """!Returns (False,True): this is a pure parallel program."""
600  return (False,True)
601 
602 ########################################################################
603 
605  """!Represents a single rank of an MPI program that is actually
606  running a serial program. This is supported directly by some
607  MPI implementations while others require kludges to work properly."""
608  def __init__(self,runner,logger=None):
609  """!MPISerial constructor."""
610  self._runner=runner
611  self._logger=logger
613  """!Creates a version of self with a produtil.prog.ImmutableRunner child."""
614  if not isinstance(self._runner,produtil.prog.ImmutableRunner):
616  else:
617  return self
618  def copy(self):
619  """!Duplicates self."""
620  return MPISerial(self._runner,self._logger)
621  def __repr__(self):
622  """!Returns a pythonic string representation of self for debugging."""
623  return 'mpiserial(%s)'%(repr(self._runner),)
624  def args(self):
625  """!Iterates over command arguments of the child serial program."""
626  for arg in self._runner.args():
627  yield arg
628  def get_logger(self):
629  """!Returns my logging.Logger that I use for log messages."""
630  if self._logger is not None:
631  return self._logger
632  return self._runner.get_logger()
633  def validate(self):
634  """!Does nothing."""
635  def __eq__(self,other):
636  """!Returns True if other is an MPISerial with the same Runner,
637  False otherwise.
638  @param other the other object to compare against."""
639  return isinstance(other,MPISerial) and other._runner==self._runner
640  def check_serial(self):
641  """!Returns (True,False) because this is a serial program
642  (True,) and not a parallel program (,False)."""
643  return (True,False)
644  def isplainexe(self):
645  """!Returns True if the child serial program is a plain
646  executable, False otherwise. See
647  produtil.prog.Runner.isplainexe() for details."""
648  return self._runner.isplainexe()
649  def to_shell(self):
650  """!Returns a POSIX sh version of the child serial program."""
651  return self._runner.to_shell()
def delthreads(self)
Removes the request for threads.
Definition: mpiprog.py:511
def __add__(self, other)
Adds more ranks to this program.
Definition: mpiprog.py:419
def __mul__(self, factor)
Returns a new set of MPI ranks that consist of this group of ranks repeated "factor" times...
Definition: mpiprog.py:218
def to_shell(self)
Returns a POSIX sh version of the child serial program.
Definition: mpiprog.py:649
def make_runners_immutable(self)
Returns a new MPIRanksSPMD with an immutable version of self._mpirank.
Definition: mpiprog.py:287
def to_shell(self)
Returns a POSIX sh command that will execute the serial program, if possible, or raise a subclass of ...
Definition: mpiprog.py:244
def isplainexe(self)
Returns True if the child serial program is a plain executable, False otherwise.
Definition: mpiprog.py:644
def groups
Yields a tuple (X,N) where X is the mpi program and N is the number of ranks.
Definition: mpiprog.py:301
def __eq__(self, other)
Returns True if this part of the MPI program is the same as another part.
Definition: mpiprog.py:213
def ngroups(self)
Returns the number of groups of repeated MPI ranks in the MPI program.
Definition: mpiprog.py:176
def make_runners_immutable(self)
Returns a copy of this object where all child produtil.prog.Runner objects have been replaced with pr...
Definition: mpiprog.py:152
def delthreads(self)
Removes the request for threads.
Definition: mpiprog.py:207
def args(self)
Iterates over the executable arguments.
Definition: mpiprog.py:550
def __repr__(self)
Returns a Pythonic representation of this object for debugging.
Definition: mpiprog.py:526
Represents a group of MPI programs, each of which have some number of ranks assigned.
Definition: mpiprog.py:363
def validate
Checks to see if this MPIRank is valid, or has errors.
Definition: mpiprog.py:536
def get_logger(self)
Returns my logging.Logger that I use for log messages.
Definition: mpiprog.py:628
def __rmul__(self, factor)
Duplicates this MPMD program "factor" times.
Definition: mpiprog.py:441
def to_arglist
This is the underlying implementation of most of the mpi_impl modules, and hence make_runner as well...
Definition: mpiprog.py:78
def ranks(self)
Iterates over all MPIRank objects in this part of the MPI program.
Definition: mpiprog.py:172
def groups
Iterates over tuples (rank,count) of groups of identical ranks.
Definition: mpiprog.py:402
def __repr__(self)
Returns a string representation of this object intended for debugging.
Definition: mpiprog.py:271
def __repr__(self)
Returns "X*N" where X is the MPI program and N is the number of ranks.
Definition: mpiprog.py:291
def __init__
MPIRank constructor.
Definition: mpiprog.py:471
def setthreads(self, nthreads)
Sets the number of threads requested by each MPI rank within this group of MPI ranks.
Definition: mpiprog.py:201
def nranks(self)
Returns the number of ranks in this part of the MPI program.
Definition: mpiprog.py:168
def __getitem__(self, args)
Adds arguments to this MPI rank's program.
Definition: mpiprog.py:518
def __add__(self, other)
Add some new ranks to self.
Definition: mpiprog.py:332
def setthreads(self, nthreads)
Sets the number of threads requested by each MPI rank within this group of MPI ranks.
Definition: mpiprog.py:506
def ranks(self)
Iterates over MPI ranks within self.
Definition: mpiprog.py:311
def make_runners_immutable(self)
Creates a version of self with a produtil.prog.ImmutableRunner child.
Definition: mpiprog.py:612
Implements the produtil.run: provides the object tree for representing shell commands.
Definition: prog.py:1
def check_serial(self)
Checks to see if this program contains serial (non-MPI) or MPI components.
Definition: mpiprog.py:446
def copy(self)
Return a copy of self.
Definition: mpiprog.py:553
def get_logger(self)
Returns a logging.Logger for the first rank that has one.
Definition: mpiprog.py:459
def __add__(self, other)
Returns a new set of MPI ranks that consist of this set of ranks with the "other" set appended...
Definition: mpiprog.py:228
Represents a single MPI rank.
Definition: mpiprog.py:469
def check_serial(self)
Returns (True,False) because this is a serial program (True,) and not a parallel program (...
Definition: mpiprog.py:640
def copy(self)
Duplicates self.
Definition: mpiprog.py:618
def validate(self)
Does nothing.
Definition: mpiprog.py:633
def isplainexe(self)
Determines if this set of MPI ranks can be represented by a single serial executable with a single se...
Definition: mpiprog.py:238
def __repr__(self)
Returns a pythonic description of this object.
Definition: mpiprog.py:378
def __mul__(self, factor)
Duplicates this MPMD program "factor" times.
Definition: mpiprog.py:435
def get_logger(self)
Returns a logger.Logger object for this MPIRanksBase or one from its child MPIRanksBase objects (if i...
Definition: mpiprog.py:156
def args(self)
Iterates over command arguments of the child serial program.
Definition: mpiprog.py:624
Base class of exceptions raised when a Runner is given arguments that make no sense.
Definition: prog.py:46
def nranks(self)
Returns the number of ranks this program requests.
Definition: mpiprog.py:316
def __eq__(self, other)
Returns True if this MPIRank is equal to the other object.
Definition: mpiprog.py:595
def __rmul__(self, factor)
Creates an MPIRanksSPMD with this MPIRank duplicated factor times.
Definition: mpiprog.py:589
def __rmul__(self, other)
Returns a new set of MPI ranks that consist of this group of ranks repeated "factor" times...
Definition: mpiprog.py:223
def __radd__(self, other)
Returns a new set of MPI ranks that consist of the "other" set of ranks with this set appended...
Definition: mpiprog.py:233
def expand_iter
This is a wrapper around ranks() and groups() which will call self.groups() if expand=False.
Definition: mpiprog.py:250
def get_logger(self)
Returns my MPI program's logger.
Definition: mpiprog.py:357
Raised when a serial program was expected, but something else was given.
Definition: mpiprog.py:58
def __mul__(self, factor)
Creates an MPIRanksSPMD with this MPIRank duplicated factor times.
Definition: mpiprog.py:583
def __init__
MPISerial constructor.
Definition: mpiprog.py:608
Raised when an MPI program was expected but something else was given.
Definition: mpiprog.py:55
def __mul__(self, factor)
Multiply the number of requested ranks by some factor.
Definition: mpiprog.py:322
Represents one MPI program duplicated across many ranks.
Definition: mpiprog.py:277
def getthreads(self)
Returns the number of threads requested by this MPI rank, or by each MPI rank in this group of MPI ra...
Definition: mpiprog.py:189
Raised when the validation scripts were expecting string arguments or string executable names...
Definition: mpiprog.py:61
def __radd__(self, other)
Prepends more ranks to this program.
Definition: mpiprog.py:427
def to_shell(self)
Return a POSIX sh representation of this MPI rank, if possible.
Definition: mpiprog.py:514
def get_logger(self)
Returns a logging.Logger for this object, or None.
Definition: mpiprog.py:533
def ranks(self)
Yields self once: all MPI ranks.
Definition: mpiprog.py:563
Represents a single rank of an MPI program that is actually running a serial program.
Definition: mpiprog.py:604
def check_serial(self)
Returns (False,True): this is a pure parallel program.
Definition: mpiprog.py:598
def __init__(self, mpirank, count)
MPIRanksSPMD constructor.
Definition: mpiprog.py:279
def __rmul__(self, factor)
Multiply the number of requested ranks by some factor.
Definition: mpiprog.py:327
def shbackslash(s)
Given a Python str, returns a backslashed POSIX sh string, or raises NotValidPosixShString if that ca...
Definition: prog.py:105
This is the abstract superclass of all classes that represent one or more MPI ranks, including MPI ranks that are actually serial programs.
Definition: mpiprog.py:68
def getthreads(self)
Returns the number of threads requested by this MPI rank, or by each MPI rank in this group of MPI ra...
Definition: mpiprog.py:502
def check_serial(self)
Checks to see if this program contains serial (non-MPI) or MPI components.
Definition: mpiprog.py:346
def ranks(self)
Iterates over groups of repeated ranks returning the number of ranks each requests.
Definition: mpiprog.py:413
def __add__(self, other)
Creates an MPIRanksSPMD or MPIRanksMPMD with this MPIRank and the other ranks.
Definition: mpiprog.py:573
def copy(self)
Returns a deep copy of self.
Definition: mpiprog.py:308
def make_runners_immutable(self)
Tells each containing element to make its produtil.prog.Runners into produtil.prog.ImmutableRunners so that changes to them will not change the original.
Definition: mpiprog.py:373
Base class of syntax errors in MPI program specifications.
Definition: mpiprog.py:50
def ngroups(self)
Returns 1 or 0: 1 if there are ranks and 0 if there are none.
Definition: mpiprog.py:295
def ngroups(self)
Returns 1: the number of groups of identical ranks.
Definition: mpiprog.py:560
Represents a single stage of a pipeline to execute.
Definition: prog.py:299
def nranks(self)
How many ranks does this program request?
Definition: mpiprog.py:394
def __eq__(self, other)
Returns True if other is an MPISerial with the same Runner, False otherwise.
Definition: mpiprog.py:635
def ngroups(self)
How many groups of identical repeated ranks are in this MPMD program?
Definition: mpiprog.py:385
An copy-on-write version of Runner.
Definition: prog.py:884
def __repr__(self)
Returns a pythonic string representation of self for debugging.
Definition: mpiprog.py:621
def check_serial(self)
Returns a tuple (s,p) where s=True if there are serial ranks in this part of the MPI program...
Definition: mpiprog.py:161
def groups
Yields (self,1): all groups of identical ranks and the number per group.
Definition: mpiprog.py:566
def groups
Iterates over all groups of repeating MPI ranks in the MPI program returning tuples (r...
Definition: mpiprog.py:180
def __init__(self, args)
MPIRanksMPMD constructor.
Definition: mpiprog.py:366
def nranks(self)
Returns 1: the number of MPI ranks.
Definition: mpiprog.py:557
Raised when something that cannot be expressed as a pure MPI rank is given as a pure MPI rank...
Definition: mpiprog.py:52