ehratm APIs
geogrid.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # *******************************************************
4 # * International Data Centre *
5 # * Comprehensive Nuclear Test Ban Treaty Organization *
6 # * Vienna *
7 # * Austria *
8 # *
9 # * Don Morton (DM) *
10 # * Boreal Scientific Computing *
11 # * Fairbanks, Alaska USA *
12 # *******************************************************
13 
14 
15 
16 #-----------------------------------------------------------------------------
17 
18 #import argparse
19 import datetime
20 import logging
21 import os
22 import uuid
23 
24 import f90nml
25 
26 import ehratm.defaults
27 import ehratm.mylogger
28 
29 import nwpservice.wps.namelistwps
30 import nwpservice.wps.geogrid
31 
33 
35 
36 class GeogridWorkflow(object):
37 
38 
39  '''Class for managing preparation and execution of geogrid components
40  '''
41 
42 
43  def __init__(self,
44  wpswrf_distro_path=None,
45  projection_dict=None,
46  nest_dict=None,
47  geog_data_path=None,
48  working_rootdir=None,
49  num_mpi_tasks=None,
50  log_level=None):
51 
52  '''Initialises the class
53 
54  Brings in the projection, nest and WPS geog_data path information.
55 
56  Assembles into a single dict ready for running
57 
58  Note that at some later time I should probably want to add in some
59  verifications of projection, nest, etc., for its suitability with
60  geogrid
61 
62  Parameters
63  ----------
64  wpswrf_distro_path : str
65  (Optional) Full path to the WPS/WRF distribution to be used.
66  This is assumed to have been installed in a way that is compatible
67  with the nwpservice module. If arg is not present, uses a default
68  value
69  projection_dict : dict
70  Collection of projection parameters for the domain
71  nest_dict : dict
72  Collection of WPS-style nesting parameters
73  geog_data_path : str
74  (Optional) Path to the geog files used by WPS. This might be a
75  relative path, and will ultimately be inserted into namelist.wps
76  as written. If arg is not present, uses a default value
77  working_rootdir : str
78  (Optional) Full path to a directory (assumed to have already
79  been created), to be used as scratch space for setting up and
80  running this geogrid instance. If arg is not present, uses a
81  default value
82  num_mpi_tasks : int
83  (Optional) Number of MPI tasks to use for running this instance
84  of geogrid. If arg is not present, non-MPI execution is assumed.
85  log_level : int
86  Python logging level (e.g. logging.INFO)
87 
88 
89  Example
90  -------
91  These are examples of the projection and nest dictionaries (one- and
92  two-nest)
93 
94  ::
95 
96  projection_dict = {
97  'map_proj' : 'lambert',
98  'ref_lat' : 50.0,
99  'ref_lon' : 5.0,
100  'truelat1' : 60.0,
101  'truelat2' : 30.0,
102  'stand_lon' : 5.0
103  }
104 
105  onenest_dict = {
106  'parent_id' : [1],
107  'parent_grid_ratio' : [1],
108  'i_parent_start' : [1],
109  'j_parent_start' : [1],
110  'e_we' : [140],
111  'e_sn' : [125],
112  'geog_data_res' : ['10m'],
113  'dx' : 30000,
114  'dy' : 30000,
115  }
116 
117  twonest_dict = {
118  'parent_id' : [1, 1],
119  'parent_grid_ratio' : [1, 3],
120  'i_parent_start' : [1, 38],
121  'j_parent_start' : [1, 77],
122  'e_we' : [140, 232],
123  'e_sn' : [125, 214],
124  'geog_data_res' : ['10m', '30s'],
125  'dx' : 30000,
126  'dy' : 30000,
127  }
128 
129 
130  '''
131 
132 
133  if log_level:
134  self._logging_level = log_level
135  else:
136  self._logging_level = DEFAULTS.log_level()
137  LOGGER.setLevel(self._logging_level)
138 
139  # NOW I can use the logger
140  LOGGER.debug('started')
141 
142  # Do some checks (need to expand on these over the years)
143  # Essentially, would be good to control bail here if there's a problem
144  # as opposed to further down
145 
146  if not wpswrf_distro_path:
147  wpswrf_distro_path = DEFAULTS.wpswrf_distro_path()
148  self._wpswrf_distro_path = wpswrf_distro_path
149  LOGGER.debug('wpswrf_distro_path: %s' % self._wpswrf_distro_path)
150  if not os.path.isdir(self._wpswrf_distro_path):
151  raise FileNotFoundError('wpswrf_distro_path not found: %s' %
152  self._wpswrf_distro_path)
153 
154  if not geog_data_path:
155  geog_data_path = DEFAULTS.wps_geog_data_dir()
156  self._wps_geog_data_dir = geog_data_path
157 
158 
161 
162  #if not os.path.isdir(self._wps_geog_data_dir):
163  # raise FileNotFoundError('wps_geog_data_dir not found: %s' %
164  # self._wps_geog_data_dir)
165  LOGGER.debug('wps_geog_data_dir: %s' % self._wps_geog_data_dir)
166 
167  if not working_rootdir:
168  working_rootdir = DEFAULTS.working_scratch_rootdir()
169  self._working_rootdir = working_rootdir
170  if not os.path.isdir(self._working_rootdir):
171  raise FileNotFoundError('working_rootdir not found: %s' %
172  self._working_rootdir)
173  LOGGER.debug('working_rootdir: %s' % self._working_rootdir)
174 
175 
176  # Use specified number of MPI tasks, or default to 0, which will imply
177  # non-MPI execution
178  if num_mpi_tasks:
179  # Ensure valid number, otherwise raise exception. If the value
180  # looks good, check that the default mpirun executable is
181  # accessible
182  if 1 <= num_mpi_tasks <= DEFAULTS.max_mpi_tasks():
183  # Go ahead and check the mpirun path
184  mpirunpath = DEFAULTS.mpirun_path()
185  if os.path.isfile(mpirunpath) and \
186  os.access(mpirunpath, os.X_OK):
187  self._num_mpi_tasks = num_mpi_tasks
188  else:
189  raise FileNotFoundError('mpirun not executable: %s' %
190  mpirunpath)
191  else:
192  raise ValueError('Bad num_mpi_tasks value: %d' % num_mpi_tasks)
193 
194  else:
195  # This default value of 0 will denote non-MPI execution when
196  # invoking the geogrid service
197  self._num_mpi_tasks = 0
198  LOGGER.debug('num_mpi_tasks: %d' % self._num_mpi_tasks)
199 
200  self._geogrid_def_dict = { 'share' : {}, 'geogrid' : {} }
201 
202 
203 
204  # Create the share section, finding number of domains by looking at
205  # nest_dict
206  try:
207  max_dom = len(nest_dict['i_parent_start'])
208  except:
209  raise ValueError('Problem getting max_dom value')
210  # Make sure max_dom is a reasonable value
211  if not 0 < max_dom <= DEFAULTS.max_dom_supported():
212  raise ValueError('Bad max_dom value: %d' % max_dom)
213  self._geogrid_def_dict['share']['max_dom'] = max_dom
214 
215  # Add in the projection, nest and geog data path
216  geogrid_sect = nest_dict
217  geogrid_sect['projection'] = projection_dict
218  geogrid_sect['geog_data_path'] = geog_data_path
219 
220 
221  # Put it all together
222  self._geogrid_def_dict['geogrid'] = geogrid_sect
223 
224  LOGGER.debug('self._geogrid_def_dict: %s' % self._geogrid_def_dict)
225 
226 
227 
228 
229  def run_geogrid(self, geogrid_output_stagedir=None):
230 
231  '''Set up and run, via nwpservice module, instance of geogrid
232 
233  Parameters
234  ----------
235  geogrid_output_stagedir : str
236  (Optional) Full path to dir where geogrid files will be staged.
237  If not specified, no staging will be done. If specified, we
238  assume the dir already exists.
239 
240  Returns
241  -------
242  return_dict : dict
243  Dictionary with manifest of the geogrid files and the location
244  (if applicable) of staged files
245  '''
246 
247 
248  LOGGER.debug('Start _run_geogrid()')
249 
250 
251  # Create name for a tempdir for the run
252  #wpswrf_rundir = os.path.join(self._working_rootdir,
253  # 'geogrid_rundir_' + str(uuid.uuid4()))
254  #wpswrf_rundir = os.path.join(self._working_rootdir, 'geogridrun')
255  #LOGGER.debug('wpswrf_rundir: %s' % wpswrf_rundir)
256 
257 
258  # Create name for namelist.wps
259  #namelist_wps_path = os.path.join(self._working_rootdir,
260  # str(uuid.uuid4()) + '_namelist.wps')
261  # This needs to be a unique name, on the off chance (which might
262  # occur in testing) that the working_rootdir has been used before
263  # and already has this file, which would cause a collision
264  #unique_nml_name = 'geogrid_namelist.wps-' + str(uuid.uuid4())[0:8]
265  namelist_wps_path = os.path.join(self._working_rootdir,
266  'geogrid_namelist.wps')
267  LOGGER.debug('namelist_wps_path: %s' % namelist_wps_path)
268 
269 
270  # Create namelist.wps
271  myobj = nwpservice.wps.namelistwps.NamelistWpsWriter(
272  destpath=namelist_wps_path,
273  section_data_dict=self._geogrid_def_dict
274  )
275  LOGGER.debug('Writing namelist: %s' % namelist_wps_path)
276  myobj.write()
277 
278  # Init geogrid component, run it, stage the output
279  domainpath = os.path.join(self._working_rootdir,
280  str(uuid.uuid4()) + '_geogridrun')
281  domainpath = os.path.join(self._working_rootdir, 'geogrid_rundir')
282 
283  nwpgeogrid_args = {
284  'wpswrf_distro_path' : self._wpswrf_distro_path,
285  'wpswrf_rundir' : domainpath,
286  'namelist_wps' : namelist_wps_path,
287  'output_dir' : geogrid_output_stagedir,
288  'log_level' : self._logging_level
289  }
290 
291  # Add MPI args if appropriate
292  if self._num_mpi_tasks > 0:
293  nwpgeogrid_args['numpes'] = self._num_mpi_tasks
294  nwpgeogrid_args['mpirun_path'] = DEFAULTS.mpirun_path()
295 
296  geogrid_obj = nwpservice.wps.geogrid.Geogrid(**nwpgeogrid_args)
297  """
298  geogrid_obj = nwpservice.wps.geogrid.Geogrid(
299  wpswrf_distro_path=self._wpswrf_distro_path,
300  wpswrf_rundir=domainpath,
301  namelist_wps=namelist_wps_path,
302  output_dir=geogrid_output_stagedir,
303  log_level=DEFAULT_LOG_LEVEL
304  )
305  """
306 
307  geogrid_obj.setup()
308  output_manifest = geogrid_obj.run()
309  LOGGER.debug('output_manifest: %s' % output_manifest)
310 
311  # If this wasn't successful (even if an output staging dir was
312  # never specified), it should return False
313  stage_success = geogrid_obj.stage_output(auxfiles=False)
314  if stage_success:
315  staging_dir = geogrid_output_stagedir
316  else:
317  staging_dir = None
318 
319  return_dict = {
320  'geogrid_output_manifest' : output_manifest,
321  'staging_dir' : staging_dir
322  }
323 
324  LOGGER.debug('return_dict: %s' % return_dict)
325  return return_dict
ehratm.defaults
Definition: defaults.py:1
ehratm.wps.geogrid.GeogridWorkflow._wpswrf_distro_path
_wpswrf_distro_path
Definition: geogrid.py:141
ehratm.wps.geogrid.GeogridWorkflow._wps_geog_data_dir
_wps_geog_data_dir
Definition: geogrid.py:149
ehratm.wps.geogrid.GeogridWorkflow._logging_level
_logging_level
Definition: geogrid.py:127
ehratm.defaults.Defaults
Definition: defaults.py:9
ehratm.wps.geogrid.GeogridWorkflow.run_geogrid
def run_geogrid(self, geogrid_output_stagedir=None)
Definition: geogrid.py:229
ehratm.wps.geogrid.GeogridWorkflow.__init__
def __init__(self, wpswrf_distro_path=None, projection_dict=None, nest_dict=None, geog_data_path=None, working_rootdir=None, num_mpi_tasks=None, log_level=None)
Definition: geogrid.py:43
ehratm.wps.geogrid.GeogridWorkflow._geogrid_def_dict
_geogrid_def_dict
Definition: geogrid.py:193
ehratm.mylogger.getlogger
def getlogger()
Definition: mylogger.py:11
ehratm.wps.geogrid.GeogridWorkflow._num_mpi_tasks
_num_mpi_tasks
Definition: geogrid.py:180
ehratm.mylogger
Definition: mylogger.py:1
ehratm.wps.geogrid.GeogridWorkflow._working_rootdir
_working_rootdir
Because geog_data_path is a literal string, and may reflect a relative path, checking its presence at...
Definition: geogrid.py:162
ehratm.wps.geogrid.GeogridWorkflow
Definition: geogrid.py:36