HWRF  trunk@4391
run_hwrf.py
1 #! /usr/bin/env python
2 
3 ##@namespace run_hwrf
4 # @brief A wrapper around the Rocoto workflow system that knows how to run HWRF in Rocoto
5 #
6 # @anchor run_hwrf_main
7 # This is a Python program, run_hwrf.py, that users run to launch and maintain an
8 # HWRF workflow for one storm, ensemble or basin-scale forecast.
9 #
10 # @code{.sh}
11 # run_hwrf.py [options] [ensids and cycles] 95E case_root [conf]
12 # @endcode
13 #
14 # Arguments:
15 # * 95E --- stormid
16 # * case_root --- HISTORY for retrospective runs or FORECAST for real-time
17 #
18 # Options:
19 # * -f --- Force a run even fiif the *.xml and *.db file already exist
20 # * -w workflow.xml --- specify the Rocoto XML file
21 # * -d workflow.db --- specify the Rocoto database file
22 # * -s site-file.ent --- Specify the site file in the sites/ subdirectory
23 #
24 # Ensids and Cycles:
25 # * 03 --- an ensemble member to run, if the GEFS ensemble is used
26 # * 03-10 --- several ensemble members to run, if the GEFS ensemble is used
27 # * 2014081412 --- one cycle to run
28 # * 2014081400-2014081618 --- a range of cycles to run
29 # * 2014 --- run all cycles for this storm in this year
30 # * H214:2014 --- run whatever cycles H214 ran for this storm
31 # * -t --- include cycles even if they are not in the tcvitals
32 # * -n --- disable renumbering of invests into non-invests
33 # * -W N --- discard invests weaker than N m/s before renumbering
34 #
35 # Conf opitons:
36 # * ../parm/hwrf_3km.conf --- read this configuration file
37 # * config.run_ocean=yes --- specify the value of one configuration option
38 # * prelaunch.basin_overrides=no --- Disasble per-basin configuration overrides
39 # * prelaunch.rsmc_overrides=no --- Disable per-center (RSMC) configuration overrides
40 
41 ##@cond RUN_HWRF_PY
42 
43 import os, sys, re, logging, collections, StringIO, getopt, itertools
44 from os.path import realpath, normpath, dirname
45 
46 def ask(question):
47  sys.stdout.write(question)
48  itry=0
49  itrytoohard=100
50  go=True
51  while go:
52  itry+=1
53  x=sys.stdin.readline()
54  if x.lower()=='y\n':
55  return True
56  elif x.lower()=='n\n':
57  return False
58  elif itry>=itrytoohard:
59  sys.stderr.write('Giving up after %d failed responses.'%itry)
60  sys.exit(2)
61  else:
62  sys.stdout.write('Please answer y or n.')
63 
64 def usage(message=None,logger=None):
65  """!Dumps a usage message and exits with status 2.
66  @param message An extra message to send to stderr after the usage message
67  @param logger Ignored."""
68  print>>sys.stderr, '''
69 Usage: run_hwrf.py [options] [ensids and cycles] 95E case_root [conf]
70 
71 Mandatory arguments:
72  95E -- the storm to run
73  case_root -- FORECAST = real-time mode, HISTORY = retrospective mod
74 
75 Workflow options:
76  -f -- Tells the run_hwrf.py that you already ran it once for this
77  storm, cycle list and experiment. It will use any existing
78  *.xml or *.db file without asking for permission. Critical
79  in crontabs.
80  -w workflow-file.xml -- use this as the output XML file to send
81  into rocotorun (rocotorun's -w option)
82  -d workflow-db.db -- use this as the SQLite3 database file for
83  Rocoto (rocotorun's -d option)
84 
85 Specifying a site:
86  -s site-file -- path to a custom-made site file, rather than using
87  one automatically chosen from sites/*.ent. Do not include any
88  shell or XML metacharacters in the name.
89 
90 PATHS:
91  This script should be run from the rocoto/ subdirectory of the HWRF
92  installation location so it can guess the ush/ and parm/ locations
93  (../ush and ../parm). You can override those guesses by providing
94  the paths in the $USHhwrf and $PARMhwrf environment variables.
95 
96 ENSEMBLE OPTIONS:
97  [ensids] -- one or more ensemble id specification:
98  03 - run member 03
99  07-13 - run members 07, 08, 09, 10, 11, 12 and 13
100  00-20 - run members 00, 01, 02, 03, ..., 20
101  You can give multiple specifications, so this:
102  03-05 06
103  Is the same as giving all four members explicitly:
104  03 04 05 06
105 
106 MULTISTORM OPTIONS:
107  -m SIDS -- One or more storm ID's, as a comma separated list.
108  For example: -m 04E,05E,00L
109  -M BASINS -- One or more basin ID's, as a comma separated list.
110  For example: -m L,E,C
111 
112 SPECIFYING CYCLES:
113 
114  -c N -- number of hours between cycles. This ONLY affects cycle
115  specifications after the -c option.
116 
117  [cycles] -- one or more cycle specifications:
118  2014091312-2014091712 - run this range of cycles
119  2014091312 - run this cycle
120  2014 - all cycles from 0Z January 1, 2014 to
121  the end of that year.
122  2014091312-2014091712 2014091800 - run cycles from 2014091312
123  through 2014091712 AND run 2014091800
124 
125  H214:2012 - run whatever cycles H214 ran for this 2012 storm
126 
127  -t -- include cycles even if they are not in the tcvitals. This
128  option is turned on automatically when H214 cycle lists are
129  requested.
130 
131  -n -- disable renumbering of invests to non-invests. This is done
132  automatically when an invest is requested.
133 
134  -W N -- discard invests weaker than N meters per second before
135  renumbering. Default: -W 14 if a non-invest storm is
136  requested, and -W 0 (don't discard) if an invest is requested.
137 
138 Configuration ([conf]):
139 section.option=value -- override conf options on the command line
140 /path/to/file.conf -- additional conf files to parse'''
141  if message is not None:
142  print>>sys.stderr,str(message).rstrip()+'\n'
143  sys.exit(2)
144 
145 ########################################################################
146 # Try to guess $USHhwrf and $PARMhwrf. The $HOMEhwrf or current
147 # working directory are used if $USHhwrf and $PARMhwrf are not set in
148 # the environment. We also add the $USHhwrf to the Python library
149 # path.
150 
151 ##@var USHhwrf
152 # The ush/ subdirectory of the HWRF installation directory
153 USHhwrf=None
154 
155 ##@var HOMEhwrf
156 # The HWRF installation directory
157 HOMEhwrf=None
158 
159 ##@var PARMhwrf
160 # The parameter directory
161 PARMhwrf=None
162 
163 if os.environ.get('USHhwrf',''): USHhwrf=os.environ['USHhwrf']
164 if os.environ.get('PARMhwrf',''): PARMhwrf=os.environ['PARMhwrf']
165 if os.environ.get('HOMEhwrf',''): HOMEhwrf=os.environ['HOMEhwrf']
166 
167 if HOMEhwrf is None and (USHhwrf is None or PARMhwrf is None):
168  HOMEhwrf=dirname(os.getcwd())
169  USHguess=os.path.join(HOMEhwrf,'ush')
170  PARMguess=os.path.join(HOMEhwrf,'parm')
171  if os.path.isdir(USHguess) and os.path.isdir(PARMguess):
172  if USHhwrf is None: USHhwrf=USHguess
173  if PARMhwrf is None: PARMhwrf=PARMguess
174 
175 if HOMEhwrf is not None:
176  if USHhwrf is None: USHhwrf=os.path.join(HOMEhwrf,'ush')
177  if PARMhwrf is None: PARMhwrf=os.path.join(HOMEhwrf,'parm')
178 
179 if USHhwrf is None:
180  print>>sys.stderr, "Cannot guess $USHhwrf. Please set $HOMEhwrf or " \
181  "$USHhwrf in environment."
182  sys.exit(2)
183 
184 if PARMhwrf is None:
185  print>>sys.stderr, "Cannot guess $PARMhwrf. Please set $HOMEhwrf or " \
186  "$PARMhwrf in environment."
187  sys.exit(2)
188 
189 if HOMEhwrf is None:
190  print>>sys.stderr, "Cannot guess $HOMEhwrf. Please set $HOMEhwrf " \
191  "in the environment."
192  sys.exit(2)
193 
194 sys.path.append(USHhwrf)
195 
196 ########################################################################
197 # Load and set up the produtil package.
198 
202 import hwrf_expt
203 
204 from hwrf.numerics import to_datetime, to_timedelta
205 from hwrf.rocoto import entity_quote
206 
207 from produtil.fileop import remove_file, isnonempty
208 from produtil.run import run, exe, runstr
209 from produtil.prog import shbackslash
210 
212 produtil.setup.setup(send_dbn=False)
213 
214 ########################################################################
215 # Global variables and constants
216 
217 logger=logging.getLogger('run_hwrf')
218 
219 epsilon = to_timedelta(5) # five seconds
220 six_hours = to_timedelta(6*3600)
221 cycling_interval = six_hours
222 cycleset = set()
223 enset = set()
224 mslist = list()
225 mblist = list()
226 benchmarkset = None
227 parse_tcvitals = True
228 renumber = True
229 force = False
230 site_file = ''
231 outxml = ''
232 outdb = ''
233 dateargs = list()
234 iarg = 0
235 firstarg = 0
236 weak_invest = None
237 
238 def okpath(path):
239  return produtil.fileop.norm_expand_path(path,fullnorm=True)
240 
241 ########################################################################
242 # Parse the options and arguments.
243 
244 short_opts = "c:d:fm:M:n:s:tWw:"
245 long_opts = ["cycling=",
246  "database=",
247  "force",
248  "multistorms=",
249  "multibasins=",
250  "renumber=",
251  "site=",
252  "tcvitals",
253  "weak",
254  "workflow="
255  ]
256 try:
257  opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
258 except getopt.GetoptError as err:
259  print str(err)
260  usage('SCRIPT IS ABORTING DUE TO UNRECOGNIZED ARGUMENT')
261 
262 for k, v in opts:
263  if k in ('-c', '--cycling'):
264  cycling_interval = to_datetime(int(v)*3600)
265  elif k in ('-d', '--database'):
266  outdb = v
267  elif k in ('-f', '--force'):
268  force = True
269  elif k in ('-m', '--multistorms'):
270  mslist.extend(v.split(","))
271  elif k in ('-M', '--multibasins'):
272  mblist.extend(v.split(","))
273  elif k in ('-n', '--renumber'):
274  renumber = False
275  elif k in ('-s', '--site'):
276  site_file = str(v)
277  elif k in ('-t', '--tcvitals'):
278  parse_tcvitals = False
279  elif k in ('-W', '--weak'):
280  weak_invest = int(v)
281  elif k in ('-w', '--workflow'):
282  outxml = v
283  else:
284  assert False, "UNHANDLED OPTION"
285 
286 
287 # Make sure the workflow isn't the database
288 if outxml[-3:]=='.db':
289  usage('When using the -d option, the Rocoto XML filename must '
290  'not end with ".db".')
291 # Make sure the database isn't the workflow
292 if outdb[-4:]=='.xml':
293  usage('When using the -d option, the database filename must '
294  'not end with ".xml".')
295 
296 for arg in args:
297  if re.match('\A\d\d\Z',arg):
298  logger.info('ensemble id')
299  # Single ensemble ID
300  enset.add('%02d'%int(arg,10))
301  elif re.match('\A\d\d-\d\d\Z',arg):
302  logger.info('list of ensemble ids')
303  # List of ensemble IDs
304  en1=int(arg[0:2],10)
305  en2=int(arg[3:],10)
306  enset.update([ "%02d"%(x+en1) for x in range(en2-en1+1) ])
307  elif re.match('\A\d{10}\Z',arg):
308  logger.info('single date/time')
309  # Single date/time
310  cycleset.add(arg)
311  dateargs.append(arg)
312  elif re.match('\A\d{4}\Z',arg):
313  logger.info('year')
314  # Year
315  start=to_datetime(arg+'01010000')
316  end=to_datetime(arg+'12312359')
317  now=start
318  while now<end+epsilon:
319  cycleset.add(now.strftime('%Y%m%d%H'))
320  now+=cycling_interval
321  dateargs.append(arg)
322  elif re.match('\A\d{10}-\d{10}\Z',arg):
323  logger.info('range of cycles')
324  # Range of date/times
325  start=to_datetime(arg[0:10])
326  end=to_datetime(arg[11:])
327  now=start
328  while now<end+epsilon:
329  cycleset.add(now.strftime('%Y%m%d%H'))
330  now+=cycling_interval
331  dateargs.append(start)
332  elif re.match('\A\d\d[A-Z]\Z',arg.upper()):
333  logger.info('storm id')
334  # Storm ID. This ends our argument parsing. We pass the
335  # remaining arguments on to parse_launch_args.
336  firstarg=iarg
337  if renumber:
338  if arg[0]=='9':
339  logger.info('Disabling renumbering for invest storm '+arg)
340  renumber=False
341  elif arg[0]=='8':
342  logger.info('Disabling renumbering for test storm '+arg)
343  renumber=False
344  break
345  elif re.match('\AH214:\d\d\d\d\Z',arg.upper()):
346  # H214 cycle requested
347  logger.info('H214 - use the H214 benchmark cycles')
348  parse_tcvitals=False
349  benchmarkset=arg.upper()
350 # else:
351 # usage('SCRIPT IS ABORTING DUE TO UNRECOGNIZED ARGUMENT "%s"'%(arg,))
352  iarg+=1
353 
354 if benchmarkset and cycleset:
355  usage('SCRIPT IS ABORTING: YOU CANNOT SPECIFY CYCLES AND '
356  'USE A BENCHMARK SET')
357 
358 if enset==set(['99']):
359  enset=set()
360 
361 # Multistorm basin specifications
362 if mblist:
363  s_opts = list()
364  s_opts.append(dateargs[0])
365  s_opts += mblist
366  pir_vits = runstr(exe(shbackslash(USHhwrf) + '/hwrf_multistorm_sort.py')
367  [s_opts], logger=logger)
368  mslist.extend([line.split()[1] for line in pir_vits.splitlines()])
369 
370 # Export the multistorm SIDS
371 if mslist:
372  ms_seen = {}
373  ms_unique = []
374  ms_basins = []
375  mb_seen = {}
376  mb_unique = []
377  for item in mslist:
378  if item in ms_seen: continue
379  ms_seen[item] = 1
380  ms_unique.append(item)
381  ms_basins.append(item[-1:])
382  mslist = ms_unique
383  os.environ['MULTISTORM_SIDS'] = ' '.join(mslist)
384 
385  for item in itertools.chain(ms_basins, mblist):
386  basin = item[-1:]
387  if basin in mb_seen: continue
388  mb_seen[basin] = 1
389  mb_unique.append(basin)
390  mblist = mb_unique
391  logger.info('BASINS: ' +repr(mblist))
392  os.environ['BASINS'] = ' '.join(mblist)
393 
394 # Now parse the rest of the arguments the same way as exhwrf_launch:
395 print 'firstarg',firstarg
396 print 'argsfirstarg..',args[firstarg:]
397 def fullify(s):
398  m=re.match('''(?x)(?P<section>[a-zA-Z][a-zA-Z0-9_]*)\.(?P<option>[^=]+)=(?P<value>.*)$''',s)
399  if not m:
400  return os.path.abspath(s)
401  else:
402  return s
403 
404 # Turn any conf files specified in arguments into fully-qualified
405 # paths. This is needed because run_hwrf will generally be started
406 # from a different directory than the exhwrf_launch.py.
407 if firstarg+2<len(args):
408  confargs=args[(firstarg+2):]
409  more_launch_vars=' '.join(
410  entity_quote(shbackslash(fullify(str(x))))
411  for x in confargs)
412 else:
413  confargs=list()
414  more_launch_vars=''
415 logger.info('MORE_LAUNCH_VARS='+repr(more_launch_vars))
416 
417 # Tell the hwrf.launcher to parse the remaining arguments so we can
418 # make the conf information:
419 
420 # Generate the conf file and run the hwrf.launcher's sanity checks
421 # that do not require a cycle:
422 # mslist does not contain the fakestorm id
423 logger.info('MSLIST: ' +repr(mslist))
424 if mslist:
425  (case_root,parm,infiles,stids,stid,pstid,moreopts)=hwrf.launcher.multistorm_parse_args(
426  mslist,args[firstarg:],logger,usage,PARMhwrf=PARMhwrf)
427  fakestid = stid
428  logger=logging.getLogger('run_hwrf_'+str(fakestid))
429  # stids list includes all storm ids AND the fake storm id.
430  fakestorm_conf = hwrf.launcher.launch(infiles,None,fakestid,moreopts[stids.index(pstid)],case_root,
431  init_dirs=False,prelaunch=hwrf_expt.prelaunch,
432  fakestorm=True)
433  global_storm_num = 2
434  for i,storm in reversed(list(enumerate(stids))):
435  if storm != fakestid:
436  if(weak_invest is None):
437  if(str(storm)[0]=='9'):
438  logger.info('Invest requested, and no -w given. Not discarding '
439  'weak Invests.')
440  weak_invest=0
441  else:
442  logger.info('Non-Invest requested, and no -w given. Will start '
443  'cycling off of last Invest <14m/s.')
444  weak_invest=14
445  # Note: this weak_invest default must match the value in
446  # ush/hwrf/relocate.py's Stage1.run function.
447 
448  conf=hwrf.launcher.launch(infiles,None,storm,moreopts[i],case_root,
449  init_dirs=False,prelaunch=hwrf_expt.prelaunch,
450  fakestorm_conf=fakestorm_conf, storm_num=global_storm_num)
451  global_storm_num += 1
452 else:
453  (case_root,parm,infiles,stid,moreopt)=hwrf.launcher.parse_launch_args(
454  args[firstarg:],logger,usage,PARMhwrf=PARMhwrf)
455 
456  logger=logging.getLogger('run_hwrf_'+str(stid))
457 
458  if(weak_invest is None):
459  if(str(stid)[0]=='9'):
460  logger.info('Invest requested, and no -w given. Not discarding '
461  'weak Invests.')
462  weak_invest=0
463  else:
464  logger.info('Non-Invest requested, and no -w given. Will start '
465  'cycling off of last Invest <14m/s.')
466  weak_invest=14
467  # Note: this weak_invest default must match the value in
468  # ush/hwrf/relocate.py's Stage1.run function.
469 
470  conf=hwrf.launcher.launch(infiles,None,stid,moreopt,case_root,
471  init_dirs=False,prelaunch=hwrf_expt.prelaunch)
472 
473 logger.info('Run sanity checks.')
474 try:
475  conf.timeless_sanity_check(enset,logger)
476 except Exception as e:
478  sys.exit(1)
479 logger.info("I think I'm sane.")
480 
481 # Try to connect to the jlogfile:
482 loghere=conf.getloc('jlogfile','')
483 if not loghere:
484  try:
485  loghere=os.path.join(
486  conf.getloc('CDSCRUB'),conf.getstr('config','SUBEXPT'),
487  'log','jlogfile')
488  except KeyError as ke:
489  loghere=None
490 if loghere:
491  print 'Sending jlogfile messages to %s'%(loghere,)
493 
494 ########################################################################
495 # Parse the tcvitals
496 
497 def check_test_vitals(vl):
498  """!This is a replacement for hwrf.storminfo.name_number_okay for
499  use with TEST storms and internal stormids. It allows through
500  only the storm numbers matching stormnum, regardless of the
501  storm name (usually TEST and UNKNOWN would be dropped)."""
502  logger.info('Keeping only storm number %d in vitals'%(stid,))
503  for vital in vl:
504  if vital.stnum.upper()==stid.upper():
505  yield vital
506 
507 if parse_tcvitals:
508  logger.info('Getting list of tcvitals files.')
509  syndatdir=conf.getdir('syndat')
510  vitpattern=conf.getstr('config','vitpattern','syndat_tcvitals.%Y')
511  fileset=set()
512  for cycle in cycleset:
513  when=to_datetime(cycle)
514  vitfile=os.path.join(syndatdir,when.strftime(vitpattern))
515  fileset.add(vitfile)
516  revit=hwrf.revital.Revital(logger=logger)
517  logger.info('List of files to scan: '+(','.join(fileset)))
518  revit.readfiles(fileset,raise_all=False)
519  if renumber:
520  logger.info('Renumber invest cycles.')
521  if weak_invest:
522  revit.renumber(threshold=int(weak_invest))
523  else:
524  revit.renumber()
525  elif stid[0]=='8':
526  logger.info('Fake stormid requested. Running limited clean-up.')
527  revit.clean_up_vitals(name_number_checker=check_test_vitals)
528  else:
529  logger.info('Not renumbering invest cycles. Will just clean.')
530  revit.clean_up_vitals()
531 
532  tcvset = set()
533  if mslist:
534  for ms_id in mslist:
535  tcvset.update([ vit.when.strftime('%Y%m%d%H') for vit in revit.each(ms_id) ])
536  else:
537  tcvset.update([ vit.when.strftime('%Y%m%d%H') for vit in revit.each(stid) ])
538  notok = cycleset - tcvset
539  okset = cycleset - notok
540  cycleset=okset
541 
542  listed=list(notok)
543  listed.sort()
544  logger.debug('NOTOK = '+( ','.join(listed) ))
545 
546  listed=list(cycleset)
547  listed.sort()
548 
549  if not listed:
550  produtil.log.jlogger.info(
551  '%s %s: no cycles to run. Exiting.'
552  %(str(stid),' '.join(dateargs)))
553  sys.exit(0)
554  else:
555  logger.info('I will ONLY run these cycles, since they have vitals:'
556  +(','.join(listed)))
557 
558 
559 ########################################################################
560 # Create the list of variables to send to the ATParser
561 
562 VARS=dict(os.environ)
563 if cycleset:
564  VARS['CYCLE_LIST']=hwrf.rocoto.cycles_as_entity(cycleset)
565  for line in VARS['CYCLE_LIST'].splitlines():
566  logger.info('Rocoto cycles: %s'%(line.rstrip(),))
567  cyclelist=list(cycleset)
568  cyclelist.sort()
569  firstcycle=to_datetime(cyclelist[0])
570  cycledesc=firstcycle.strftime('%Y%m%d%H')
571 else:
572  assert(isinstance(benchmarkset,basestring))
573  year=int(benchmarkset[5:])
574  number=sid[0:2]
575  basin1=sid[2].upper()
576  (ibasin2,basin2,basin1,longinfo) = \
578  cycledesc='&%s%s%s;'%(basin2,number,year)
579  VARS['CYCLE_LIST']=cycledesc
580 
581 fgatstr=conf.getint('fgat','FGATSTR',-3)
582 fgatend=conf.getint('fgat','FGATEND',3)
583 fgatinv=conf.getint('fgat','FGATINV',3)
584 
585 if fgatend > fgatstr:
586  fhrlist=' '.join([ '%d'%(i+6) for i in xrange(fgatstr,fgatend+1,fgatinv) ])
587  initmodel=' '.join([ 'GDAS1' for i in xrange(fgatstr,fgatend+1,fgatinv) ])
588  initparts=' '.join([ '3DVAR' for i in xrange(fgatstr,fgatend+1,fgatinv) ])
589  VARS.update(INIT_FHR=fhrlist)
590  VARS.update(INIT_MODEL=initmodel)
591  VARS.update(INIT_PARTS=initparts)
592 
593 if enset:
594  enlist=list(enset)
595  enlist.sort()
596  VARS.update(ENSIDS=' '.join(enlist),
597  ENSEMBLE='YES')
598 else:
599  VARS.update(ENSIDS='99',ENSEMBLE='NO')
600 
601 if conf.getbool('config','run_ensemble_da'):
602  VARS.update(DA_ENSEMBLE='YES',ENSDA_ENS_MEMBER='99')
603  esize=conf.getint('hwrf_da_ens','ensda_size',40)
604  assert(esize>=1)
605  ensdalist=' '.join([ '%03d'%(i+1) for i in xrange(esize) ])
606  VARS.update(ENSDA_IDS=ensdalist)
607 else:
608  VARS.update(DA_ENSEMBLE='NO',ENSDA_ENS_MEMBER='99',ENSDA_IDS='001')
609 
610 if mslist:
611  VARS.update(MULTISTORM='YES',
612  BASINS=' '.join(mblist),
613  PRIORITY_SIDS=' '.join(mslist),
614  FAKE_SID=fakestid.upper())
615 else:
616  VARS.update(MULTISTORM='NO')
617 try:
618  stormlabel=conf.get('config','stormlabel','storm1')
619 except KeyError:
620  stormlabel='storm1'
621 
622 def yesno(b):
623  return 'YES' if(b) else 'NO'
624 
625 VARS.update(SID=stid.upper(), stormlabel=str(stormlabel),
626  WHERE_AM_I=conf.get('holdvars','WHERE_AM_I'),
627  WHICH_JET=conf.get('holdvars','WHICH_JET','none'),
628  MORE_LAUNCH_VARS=more_launch_vars,
629  CASE_ROOT=case_root,
630  SITE_FILE=site_file,
631  FETCH_INPUT=yesno(conf.get('config','input_catalog')=='hwrfdata'),
632  ARCHIVE_WRFOUT=yesno(conf.getraw('archive','wrfout','')),
633  ARCHIVE_HYCOMAB=yesno(conf.getraw('archive','hycomab',''))
634  )
635 
636 for (key,val) in conf.items('rocotostr'):
637  VARS[key]=str(val)
638 for (key,val) in conf.items('rocotobool'):
639  VARS[key]=yesno(conf.getbool('rocotobool',key))
640 
641 
642 bad=False
643 for k,v in VARS.iteritems():
644  if not isinstance(v,basestring):
645  logger.error('%s: value is not a string. '
646  'It is type %s with value %s'%(
647  str(k),type(v).__name__,repr(v)))
648  bad=True
649 if bad: sys.exit(1)
650 
651 ########################################################################
652 # Order the ATParser to create the XML file.
653 
654 rocotoxml=StringIO.StringIO()
655 parser=produtil.atparse.ATParser(rocotoxml,varhash=VARS,logger=logger)
656 if mslist:
657  parser.parse_file('hwrf_multistorm_workflow.xml.in')
658 else:
659  parser.parse_file('hwrf_workflow.xml.in')
660 
661 
662 outbase='hwrf-%s-%s-%s'%(
663  conf.get('config','SUBEXPT'),
664  stid.upper(),
665  cycledesc)
666 
667 if not outxml: outxml=okpath(outbase+'.xml')
668 if not outdb: outdb=okpath(outbase+'.db')
669 havexml=isnonempty(outxml)
670 
671 if havexml:
672  if not force and \
673  not ask('ALERT! %s: XML file exists. Overwrite (y/n)?'%(outxml,)):
674  logger.error('%s: file exists, user does not want to overwrite.'
675  %(outxml,))
676  sys.exit(1)
677  else:
678  logger.warning('%s: overwriting pre-existing XML file.'%(outxml,))
679 
680 havedb=isnonempty(outdb)
681 deletedb=False
682 if havedb:
683  if force or ask(
684  'ALERT! %s: database for old configuration exists. Use it (y/n)?'
685  %(outdb,)):
686  logger.warning('%s: not deleting database for old configuration.'
687  %(outdb,))
688  elif ask('%s: Delete database for old configuration (y/n)?'%(outdb,)):
689  logger.warning('%s: deleting database for old configuration.'
690  %(outdb,))
691  remove_file(outdb)
692  else:
693  logger.error('%s: database exists, user does not want to delete '
694  'or use it. Aborting.')
695  sys.exit(2)
696 
697 with open(outxml,'wt') as outf:
698  outf.write(rocotoxml.getvalue())
699 
700 ########################################################################
701 # Run rocotorun
702 
703 clustername=produtil.cluster.name()
704 
705 if clustername in ('tide','gyre'):
706  WHERE_AM_I='wcoss2'
707 elif clustername in ('luna','surge'):
708  WHERE_AM_I='wcosscray'
709 else:
710  WHERE_AM_I=clustername
711 
712 cmd = exe('sh') [
713  '--login', '-c', '. %s/hwrf_pre_job.sh.inc ; which ruby ; which rocotorun ; rocotorun --verbose=5 -d %s -w %s'
714  %( shbackslash(USHhwrf), shbackslash(outdb),
715  shbackslash(outxml) ) ] .env(QUIET_PRE_JOB='YES',
716  HOMEhwrf=HOMEhwrf,
717  WHERE_AM_I=WHERE_AM_I) \
718  < '/dev/null'
719 result=run(cmd,logger=logger)
720 if result:
721  sys.exit(result)
722  produtil.jlogger.critical('rocotorun failed')
723 
724 produtil.log.postmsg('Successfully ran rocotorun for %s.'%(outbase,))
725 bakdb=outdb+'.bak'
726 logger.info('Making a backup copy of .db file here: %s'%(bakdb,))
727 produtil.fileop.deliver_file(outdb,bakdb)
728 logger.info('Success. Rejoice: hurrah!')
729 ##@endcond
This module provides a set of utility functions to do filesystem operations.
Definition: fileop.py:1
def deliver_file
This moves or copies the file "infile" to "outfile" in a unit operation; outfile will never be seen i...
Definition: fileop.py:359
def expand_basin
Converts basin identifiers.
Definition: storminfo.py:1515
def set_jlogfile(filename)
Tells the jlogger to log to the specified file instead of the current jlogfile.
Definition: log.py:56
Contains setup(), which initializes the produtil package.
Definition: setup.py:1
def cycles_as_entity(cycleset)
Returns a set of Rocoto XML tags to add to an XML file.
Definition: rocoto.py:162
Implements the produtil.run: provides the object tree for representing shell commands.
Definition: prog.py:1
A shell-like syntax for running serial, MPI and OpenMP programs.
Definition: run.py:1
def postmsg(message)
Sends the message to the jlogfile logging stream at level INFO.
Definition: log.py:46
def setup(ignore_hup=False, dbnalert_logger=None, jobname=None, cluster=None, send_dbn=None, thread_logger=False, thread_stack=2 **24, kwargs)
Initializes the produtil package.
Definition: setup.py:15
def parse_launch_args
Parsed arguments to scripts that launch the HWRF system.
Definition: launcher.py:170
def launch
Initializes the directory structure for a new HWRF workflow.
Definition: launcher.py:404
Time manipulation and other numerical routines.
Definition: numerics.py:1
def set_jobname(jobname)
Sets the value that jobname() should return.
Definition: batchsystem.py:50
Provides information about the cluster on which this job is running.
Definition: cluster.py:1
def norm_expand_path
Normalizes path and expand home directories.
Definition: fileop.py:910
def sanity_check_failed(logger, ex)
Logs information about a failure of a sanity check routine.
Definition: rocoto.py:150
Creates the initial HWRF directory structure, loads information into each job.
Definition: launcher.py:1
Takes input files or other data, and replaces certain strings with variables or functions.
Definition: atparse.py:98
Provides information about the batch system.
Definition: batchsystem.py:1
This module contains utilities for plugging HWRF into the Rocoto workflow manager.
Definition: rocoto.py:1
def name()
Synonym for here.name.
Definition: cluster.py:109
def multistorm_parse_args
Definition: launcher.py:34
ATParser is a text parser that replaces strings with variables and function output.
Definition: atparse.py:1
This class reads one or more tcvitals files and rewrites them as requested.
Definition: revital.py:38