##@namespace hwrf_expt # Generates the object structure of the HWRF system for use by the scripts. # # @anchor hwrf_expt_overview # # This module is the part of the \ref experiment_layer that creates an # object structure from classes in the hwrf and pom packages. It # defines what the HWRF experiment will run, and how the various parts # of the HWRF system connect to one another. As this module has # gotten more complex, some of its implementation has been moved to # the hwrf.hwrfsystem module, which contains helper functions for # defining parts of the workflow. # # @sa hwrf.launcher The launcher module creates the initial directory # structure, and runs sanity checks on the hwrf_expt # # @sa scripts The scripts load this hwrf_expt module and call the # run() (or similar) function for one or more objects within. # # @sa hwrf The hwrf package contains classes that know how to run each # part of the HWRF system. # # @sa pom The pom package defines the initialization of the MPIPOMTC # ocean model. # # @sa hwrf_alerts The hwrf_alerts module adds product alerts needed to # ensure data is delivered to forecasters and the public in the # NCEP production system. ##@var __all__ # Ensures that accidental "from hwrf_expt import *" does nothing. __all__=[] import os,os.path,sys,re import produtil.datastore, produtil.run import hwrf.config, hwrf.wrf, hwrf.post, hwrf.numerics, hwrf.launcher import hwrf.regrib, hwrf.gribtask, hwrf.tracker, hwrf.storminfo import hwrf.wps, hwrf.nhc_products, hwrf.copywrf, hwrf.fcsttask, hwrf.ensda import hwrf.relocate, hwrf.init, hwrf.prep, hwrf.gsi, hwrf.mpipomtc import hwrf.bufrprep, hwrf.gsipost, hwrf.prelaunch, hwrf.multistorm import hwrf.enkf import hwrf.coupling import hwrf.hwrfsystem, hwrf.finalmergetask, hwrf.hycom, hwrf.ww3 from produtil.run import exe,alias from produtil.fileop import isnonempty from hwrf.wrf import WRFDomain,WRFSimulation,ExternalWRFTask from hwrf.post import PostManyWRF from hwrf.regrib import RegribMany,igrb1,clatlon,GRIB2,SATGRIB2 def prelaunch(conf,logger,cycle): """!This function makes per-cycle modifications to the configuration file storm1.conf. This is called in scripts.exhwrf_launch and run_hwrf.py by hwrf.launcher.launch() on the configuration object (hwrf.launcher.HWRFLauncher, a subclass of hwrf.config.HWRFConfig), before the per-cycle storm1.conf configuration file is written. Any changes made to the conf object will be stored in storm1.conf file and used in later jobs. This allows modifications to the configuration on a per-cycle basis. Note that cycle=None and conf.cycle is unavailable when run_hwrf.py calls prelaunch. @param conf the hwrf.launcher.HWRFLauncher to modify @param logger a logging.Logger for log messages @param cycle the cycle to run, or None if this is being run from run_hwrf.py or the ush.psychoanalyst""" # Prelaunch that happens even when running run_hwrf.py: hwrf.prelaunch.prelaunch_ensid(conf,logger) hwrf.prelaunch.prelaunch_basin(conf,logger,cycle) if cycle is None: return # Prelaunch that only happens in exhwrf_launch, not run_hwrf.py: hwrf.prelaunch.prelaunch_ungrib(conf,logger,cycle) hwrf.prelaunch.prelaunch_rsmc(conf,logger,cycle) def sanity_check(logger): """!Runs a sanity check on this module's contents. This should be called after init_module. This sanity check routine is called automatically by hwrf.launcher.HWRFLauncher.sanity_check_expt() as part of the standard sanity checks in the scripts.exhwrf_launch job. It checks to see if all expected module-scope variables are present and initialized correctly. @param logger a logging.Logger for log messages""" logger.info('The runwrf object = %s'%(repr(runwrf),)) logger.info('The gribber object = %s'%(repr(gribber),)) logger.info('The datastore (ds) object = %s'%(repr(ds),)) logger.info('The wrf object = %s'%(repr(wrf),)) # Make sure the mandatory configuration variables are specified: gsi_flag=conf.getbool('config','run_gsi') ocean_flag=conf.getbool('config','run_ocean') ocean=conf.getstr('config','ocean_model') wave_flag=conf.getbool('config','run_wave') wave=conf.getstr('config','wave_model') reloc_flag=conf.getbool('config','run_relocation') gfsinit_type=conf.getint('config','gfsinit_type',1) gfsbdy_type=conf.getint('config','gfsbdy_type',1) fallbacks_flag=conf.getbool('config','allow_fallbacks') extra_trackers=conf.getbool('config','extra_trackers',False) wrf_output_step=conf.getint('forecast_products','wrf_output_step',10800) # set data format flags if gfsinit_type == 1 and gfsbdy_type == 1 : spectral_flag=False spectral_bdy=False highres_grib=False elif gfsinit_type == 2 and gfsbdy_type == 1 : spectral_flag=True spectral_bdy=False highres_grib=False elif gfsinit_type == 3 and gfsbdy_type == 1 : spectral_flag=True spectral_bdy=False highres_grib=False elif gfsinit_type == 2 and gfsbdy_type == 2 : spectral_flag=True spectral_bdy=True highres_grib=False elif gfsinit_type == 3 and gfsbdy_type == 3 : spectral_flag=True spectral_bdy=True highres_grib=False elif gfsinit_type == 4 and gfsbdy_type == 1 : spectral_flag=False spectral_bdy=False highres_grib=True elif gfsinit_type == 5 and gfsbdy_type == 1 : spectral_flag=True spectral_bdy=False highres_grib=False else: raise hwrf.exceptions.HWRFConfigUnsupported( 'The specifed configuration is not supported') # Check if the ocean model is a valid one and its init object exists: if ocean_flag: if ocean=='HYCOM': logger.info('The hycominit object = %s'%(repr(hycominit),)) logger.info('The hycompost object = %s'%(repr(hycompost),)) elif ocean=='POM': logger.info('The pominit object = %s'%(repr(pominit),)) else: raise hwrf.exceptions.HWRFConfigUnsupported( '[config] ocean_model=%s but should be POM or HYCOM'%( ocean)) if wrf_output_step<1: raise hwrf.exceptions.PrecisionTooHigh( 'The wrf_output_step must be at least 1 second') elif wrf_output_step!=10800 and 0 != (wrf_output_step%3600): raise hwrf.exceptions.TimestepModularityError( 'One hour (3600 seconds) must be divisable by the WRF ' 'output timestep. You specified %d.'%(wrf_output_step,)) # Check if the wave model is a valid one and its init object exists: if wave_flag: if wave=='WW3': logger.info('The ww3init object = %s'%(repr(ww3init),)) logger.info('The ww3post object = %s'%(repr(ww3post),)) else: raise hwrf.exceptions.HWRFConfigUnsupported( '[config] wave_model=%s but should be WW3'%( wave)) # Make sure the configuration makes sense and is supported: if gsi_flag and not reloc_flag: logger.error("Cannot use GSI without relocation.") raise hwrf.exceptions.HWRFConfigUnsupported( "Cannot use GSI without relocation.") def inputiter(): """!Iterates over all inputs required by this configuration. Calls the inputiter() function on all tasks in the module scope that are expected to have input data. The result can be passed into the "data" argument of hwrf.input.InputSource.get. Iterates over dicts that contain the following: * dataset --- string name of the dataset (gfs, gdas1, gefs, enkf, etc.) * item --- string name of the object (ie.: gfs_sf, gfs_sfcanl, bufr) * atime --- self.conf.cycle * ftime --- only present when relevant: the forecast time, in a format accepted by to_datetime_rel * enkfmem --- only present when relevant: the ENKF member ID * obstype --- only present when relevant: the bufr data type. * optional --- True if the absence of this data is not considered a failure.""" gsi_flag=conf.getbool('config','run_gsi') run_ensemble_da=conf.getbool('config','run_ensemble_da',False) gfsinit_type=conf.getint('config','gfsinit_type',1) gfsbdy_type=conf.getint('config','gfsbdy_type',1) ocean_flag=conf.getbool('config','run_ocean') ocean=conf.getstr('config','ocean_model','POM') wave_flag=conf.getbool('config','run_wave') wave=conf.getstr('config','wave_model','WW3') conditional_gsid03=conf.getbool('config','conditional_gsid03',False) tdrflagfile=conf.strinterp('dir','{com}/{stormlabel}.tdr') realtime=conf.getbool('config','realtime') # set data format flags if gfsinit_type == 1 and gfsbdy_type == 1 : spectral_flag=False spectral_bdy=False highres_grib=False elif gfsinit_type == 2 and gfsbdy_type == 1 : spectral_flag=True spectral_bdy=False highres_grib=False elif gfsinit_type == 3 and gfsbdy_type == 1 : spectral_flag=True spectral_bdy=False highres_grib=False elif gfsinit_type == 2 and gfsbdy_type == 2 : spectral_flag=True spectral_bdy=True highres_grib=False elif gfsinit_type == 3 and gfsbdy_type == 3 : spectral_flag=True spectral_bdy=True highres_grib=False elif gfsinit_type == 4 and gfsbdy_type == 1 : spectral_flag=False spectral_bdy=False highres_grib=True elif gfsinit_type == 5 and gfsbdy_type == 1 : spectral_flag=True spectral_bdy=False highres_grib=False else: raise hwrf.exceptions.HWRFConfigUnsupported( 'The specifed configuration is not supported') if not conditional_gsid03 or (conditional_gsid03 and isnonempty(tdrflagfile)): gsid03_flag=True else: gsid03_flag=False if ocean_flag: if ocean=='HYCOM': for d in hycominit.inputiter(): yield d elif ocean=='POM': for d in pominit.inputiter(): yield d if wave_flag: if wave=='WW3': for d in ww3init.inputiter(): yield d for d in gfs_init.inputiter(): yield d if gsi_flag: for d in gsi_d02.inputiter(): yield d if gsid03_flag: for d in gsi_d03.inputiter(): yield d for d in fgat_init.inputiter(): yield d if run_ensemble_da: for d in ensda.inputiter(): #yield d #Temporarily disable the 3-hourly input data (for LBC) the input #job tries to download for the EnKF ensemble member forecast #if d['item']=='enkf_sfg' and d['ftime']-d['atime']!=hwrf.numerics.to_timedelta(3*3600): # yield d if d['item']=='enkf_sfg' and d['ftime']-d['atime']==hwrf.numerics.to_timedelta(3*3600): pass else: yield d if not realtime or not os.path.isdir('/dcom/prod'): for d in ensda_pre.inputiter(): yield d ##@var ww3init # An hwrf.ww3.WW3Init that initializes wavewatch3, or None if wave # coupling is disabled ww3init=None ##@var hycominit # An hwrf.hycom.HYCOMInit that initializes hycom, or None if hycom # coupling is not in use. hycominit=None ##@var ww3post # An hwrf.ww3.WW3Post that post-processes wavewatch3 output, or None # if wave coupling is disabled. ww3post=None ##@var hycompost # An hwrf.hycom.HYCOMPost that post-processes hycom output, or None if # hycom coupling is not in use hycompost=None ##@var conf # An hwrf.launcher.HWRFLauncher for configuration information. conf=None ##@var ds # A produtil.datstore.Datastore for product and task information storage. ds=None ##@var moad # The hwrf.wrf.WRFDomain for the Mother Of All Domains (MOAD) moad=None ##@var storm1outer # The hwrf.wrf.WRFDomain for the intermediate resolution domain storm1outer=None ##@var storm1inner # The hwrf.wrf.WRFDomain for the innermost resolution domain storm1inner=None ##@var wrf # The hwrf.wrf.WRFSimulation describing the simulation to run wrf=None ##@var runwrf # The hwrf.fcsttask.WRFAtmos or hwrf.mpipomtc.WRFCoupledPOM that # runs the full-length forecast. runwrf=None ##@var nonsatpost # The hwrf.post.PostManyWRF for generating non-satellite, native E grid GRIB1 files # from the runwrf object. nonsatpost=None ##@var satpost # The hwrf.post.PostManyWRF for generating synthetic satellite, native # E grid GRIB1 files from the runwrf object. satpost=None ##@var gfs_init # The hwrf.init.HWRFInit that runs initialization tasks such as WPS # and real_nmm on the parent global model analysis. gfs_init=None ##@var gribber # The hwrf.gribtask.GRIBTask that takes satpost and nonsatpost output, # regrids it and converts to GRIB2 format. gribber=None ##@var tracker # The hwrf.tracker.TrackerTask that runs the tracker on output from the gribber. tracker=None ##@var pominit # The initialization for the MPIPOMTC ocean model that is coupled to # WRF in the runwrf object. This is an hwrf.mpipomtc.POMInit object. pominit=None ##@var nhcp # The hwrf.nhc_products.NHCProducts object that generates custom # products for the National Hurricane Center. nhcp=None ##@var wrfcopier # An object that copies WRF inputs and outputs to the COM directory. # This is an hwrf.copywrf.WRFCopyTask object. wrfcopier=None ##@var WORKhwrf # The scrub directory for this job WORKhwrf=None ##@var HOMEhwrf # The HWRF installation location HOMEhwrf=None ##@var fgat_init # The FGAT initialization, which interpolates parent global model # forecasts to the HWRF grid and runs other initialization jobs to # prepare inputs to the GSI. This is an hwrf.init.FGATInit object. fgat_init=None ##@var non_ocean_basins # A list of one-letter basin IDs that are not supported by the # selected ocean model. This is used by the scripts.exhwrf_check_init # to determine if the ocean initialization incorrectly decided not to # run the ocean. non_ocean_basins=None ##@var bufrprep # An hwrf.bufrprep.Bufrprep object that turns data tanks into bufr # files for input to GSI. bufrprep=None ##@var gsi_d02 # The intermediate resolution GSI, an hwrf.gsi.FGATGSI, which knows # how to run the GSI data assimilation system on output from the # fgat_init, bufrprep and other objects. gsi_d02=None ##@var gsi_d03 # The innermost domain resolution GSI, an hwrf.gsi.FGATGSI, which # knows how to run the GSI data assimilation system on output from the # fgat_init, bufrprep and other objects. gsi_d03=None ##@var gsid03_flag # A boolean value, True if gsi_d03 should be run and False otherwise gsid03_flag=None ##@var gdas_merge # An hwrf.relocate.Merge object that merges output from GSI and the # fgat_init's hwrf.relocate.Stage3 to create the final input fields # to the runwrf gdas_merge=None ##@var cycle # A synonym of conf.cycle: the cycle being run cycle=None ##@var ensda # An hwrf.ensda.DAEnsemble filled with hwrf.ensda.FromGFSENKF objects # that run six hour forecasts based on the GFS ENKF ensda=None ##@var prior_ensda # An hwrf.ensda.DAEnsemble filled with hwrf.ensda.FromPriorCycle objects # that provide access to the prior cycle's ENSDA six hour forecasts. prior_ensda=None ##@var entest # An hwrf.ensda.FromGFSENKF for testing #@bug the hwrf_expt.entest is no longer needed and should be removed entest=None ##@var da_ensemble_size # Number of members in the ensda da_ensemble_size=None ##@var trackerd01 # If enabled, an hwrf.tracker.TrackerTask that only uses the moad data # to generate the track. trackerd01=None ##@var trackerd02 # If enabled, an hwrf.tracker.TrackerTask that only uses the moad and # intermediate resolution domains' data to generate the track. trackerd02=None ##@var gsipost # An hwrf.gsipost.GSIPost that post-processes inputs and outputs to the # gsi_d02 and gsi_d03 to create native E grid GRIB files input to # the gsigribber gsipost=None ##@var gsigribber # An hwrf.gribtask.GRIBTask that takes the gsipost output and turns it # into lat-lon GRIB2 files suitable for analysis of the effect of GSI. gsigribber=None ##@var ensda_pre # Output from hwrf.ensda.ensda_pre_object_for(), the object that determines # whether the data assimiltation ensemble should be run. ensda_pre=None ##@var ensda_pre_gfsinputcheck # Output from hwrf.ensda.GFSEnKFInputCheck(), the object that determines # whether GFS EnKF analysis data are available, which is used to determine whether # the data assimiltation ensemble should be run. ensda_pre_gfsinputcheck=None ##@var ensda_relocate_pre # Output from hwrf.ensda.EnsRelocateCheck.run(), the object that determines # whether relcation for ensemble should be run ensda_relocate_pre=None ##@var gsi_meanhx # hwrf.gsi.GSIMeanHx that runs GSI for data selection based on ensemble mean gsi_meanhx=None ##@var enkf_prep # hwrf.gsi.GSIHx that runs GSI to get ensemble innovation for EnKF enkf_prep=None ##@var enkf_d02 # The intermediate resolution EnKF, an hwrf.enkf.ENKF, which knows # how to run the EnKF data assimilation system on output from the # ensda, gsi_meanhx, enkf_prep and other objects. enkf_d02=None def argv_preload(conf,logger): """!Utility function to pass to init_module to override a few configuration options from the sys.argv This function should be passed into init_module's preload argument. It will scan sys.argv for arguments 1 onward, parsing them as configuration options (ie.: config.run_ocean=yes). Those options will be overridden in the conf object returned. @param conf the input and return HWRFConfig object @param logger a logging.Logger for logging """ for arg in sys.argv[1:]: logger.info(arg) m=re.match('''(?x) (?P
[a-zA-Z][a-zA-Z0-9_]*) \.(?P