HWRF  trunk@4391
mpi_impl_base.py
1 ##@namespace produtil.mpi_impl.mpi_impl_base
2 # Utilities like CMDFGen to simplify adding new MPI implementations to the
3 # produtil.run suite of modules.
4 #
5 # This module contains classes and functions to assist developers in
6 # extending the functionality of the produtil.mpi_impl package. The
7 # main highlight is the CMDFGen, which generates command files. Some
8 # MPI implementations, and the mpiserial program, want to read a file
9 # with one line per MPI rank telling what program to run on each rank.
10 # For example, LSF+IBMPE and LoadLeveler+IBMPE work this way if one
11 # wants to run different programs on different ranks.
12 
13 import tempfile,stat,os
14 
15 class MPIConfigError(Exception):
16  """!Base class of MPI configuration exceptions."""
18  """!Unused: raised when the wrong MPI implementation is accessed. """
20  """!Raised when the mpiserial program is required, but is missing."""
22  """!Raised when the allranks=True keyword is sent to mpirun or mpirunner,
23 but the MPI program specification has more than one rank."""
25  """!Thrown to indicate serial and parallel processes are being mixed in a single mpi_comm_world."""
27  """!Thrown to MPI is not supported."""
29  """!Raised when OpenMP is not supported by the present implementation."""
30 class CMDFGen(object):
31  """!Generates files with one line per MPI rank, telling what
32  program to run on each rank.
33 
34  This class is used to generate command files for mpiserial, poe or
35  mpirun.lsf. Command files are files with one MPI rank per line
36  containing a shell command to run for that rank. Generally the
37  input (lines) is generated by the to_arglist function in a
38  subclass of produtil.mpiprog.MPIRanksBase. See the
39  produtil.mpi_impl.mpirun_lsf for an example of how to use this."""
40  def __init__(self,base,lines,cmd_envar='SCR_CMDFILE',
41  model_envar=None,filename_arg=False,**kwargs):
42  """!CMDFGen constructor
43 
44  @param base type of command file being generated. See below.
45  @param lines the command file contents as a list of strings, one per line
46  @param cmd_envar environment variable to set to the command file path
47  @param model_envar environment variable to set to "MPMD"
48  @param kwargs Sets the command file name. See below.
49  @param filename_arg If True, the name of the command file is appended to the program argument list.
50 
51  The command file is generated from
52  tempfile.NamedTemporaryFile, passing several arguments from
53  kwargs, if provided, or suitable defaults otherwise. There
54  are several arguments used. In all cases, replace "base" with
55  the contents of the @c base argument:
56 
57  * base_suffix --- temporary file suffix (default: "base.")
58  * base_prefix --- temporary file prefix (default: ".cmdf")
59  * base_tempdir --- directory in which to create the file
60 
61  @bug The base_suffix keyword is used for both the suffix and prefix"""
62  assert(base is not None)
63  assert(isinstance(lines,list))
64  assert(len(lines)>0)
65  assert(isinstance(lines[0],basestring))
66  assert(len(lines[0])>0)
67  self.filename=kwargs.get(str(base),None)
68  self.tmpprefix=kwargs.get('%s_suffix'%(base,),'%s.'%(base,))
69  self.tmpsuffix=kwargs.get('%s_suffix'%(base,),'.cmdf')
70  self.tmpdir=kwargs.get('%s_tmpdir'%(base,),'.')
71  self.cmd_envar=cmd_envar
72  self.model_envar=model_envar
73  self.filename_arg=filename_arg
74  out='\n'.join(lines)
75  if len(out)>0:
76  out+='\n'
77  self.cmdf_contents=out
78  return
79  ##@var filename
80  # command file's filename
81 
82  ##@var tmpprefix
83  # temporary file prefix
84 
85  ##@var tmpsuffix
86  # temporary file suffix
87 
88  ##@var tmpdir
89  # temporary file directory
90 
91  ##@var cmd_envar
92  # Environment variable to set telling the path to the
93  # command file
94 
95  ##@var model_envar
96  # Environment variable to set to "MPMD"
97 
98  ##@var cmdf_contents
99  # String containing the command file contents.
100 
101  def _add_more_vars(self,envars,logger):
102  """!Adds additional environment variables to the envars dict,
103  needed to configure the MPI implementation correctly. This is
104  used to set MP_PGMMODEL="MPMD" if the constructor receives
105  model_envar="MP_PGMMODEL".
106 
107  @param envars[out] the dict to modify
108  @param logger a logging.Logger for log messages"""
109  if self.model_envar is not None:
110  if logger is not None:
111  logger.info('Set %s="MPMD"'%(self.model_envar,))
112  envars[self.model_envar]='MPMD'
113  def __call__(self,runner,logger=None):
114  """!Adds the environment variables to @c runner and creates the command file.
115 
116  @param[out] runner A produtil.prog.Runner to modify
117  @param logger a logging.Logger for log messages"""
118  if self.filename is not None:
119  with open(self.filename,'wt') as f:
120  f.write(self.cmdf_contents)
121  if logger is not None:
122  logger.info('Write command file to %s'%(repr(filename),))
123  kw={self.cmd_envar: self.filename}
124  self._add_more_vars(kw,logger)
125  if logger is not None:
126  for k,v in kw.iteritems():
127  logger.info('Set %s=%s'%(k,repr(v)))
128  if self.filename_arg:
129  runner=runner[self.filename]
130  return runner.env(**kw)
131  else:
132  with tempfile.NamedTemporaryFile(mode='wt',suffix=self.tmpsuffix,
133  prefix=self.tmpprefix,dir=self.tmpdir,delete=False) as t:
134  if logger is not None:
135  logger.info('Write command file to %s'%(repr(t.name),))
136  t.write(self.cmdf_contents)
137  # Make the file read-only and readable for everyone:
138  os.fchmod(t.fileno(),stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH)
139  kw={self.cmd_envar: t.name}
140  self._add_more_vars(kw,logger)
141  if logger is not None:
142  for k,v in kw.iteritems():
143  logger.info('Set %s=%s'%(k,repr(v)))
144  runner.env(**kw)
145  if self.filename_arg:
146  runner=runner[t.name]
147  return runner
Generates files with one line per MPI rank, telling what program to run on each rank.
Base class of MPI configuration exceptions.
def __call__
Adds the environment variables to runner and creates the command file.
def _add_more_vars(self, envars, logger)
Adds additional environment variables to the envars dict, needed to configure the MPI implementation ...
def __init__(self, base, lines, cmd_envar='SCR_CMDFILE', model_envar=None, filename_arg=False, kwargs)
CMDFGen constructor.
model_envar
Environment variable to set to "MPMD".
cmd_envar
Environment variable to set telling the path to the command file.
Raised when the mpiserial program is required, but is missing.
Raised when the allranks=True keyword is sent to mpirun or mpirunner, but the MPI program specificati...
Unused: raised when the wrong MPI implementation is accessed.
cmdf_contents
String containing the command file contents.
Thrown to indicate serial and parallel processes are being mixed in a single mpi_comm_world.
Thrown to MPI is not supported.
Raised when OpenMP is not supported by the present implementation.