HWRF  trunk@4391
hwrf_alerts.py
1 ##@namespace hwrf_alerts
2 #This module contains the track delivery function and all dbn
3 #alerts in the HWRF system. It is intended to sit on top of the
4 #ush/hwrf_expt.py as the NCO alert and NHC data delivery module.
5 #Generally the functions in this module are run by the scripts/ex*.py
6 #programs just before handing control over to the hwrf_expt package.
7 
8 import os, socket, re
9 import produtil.fileop
10 from produtil.dbnalert import DBNAlert
11 from produtil.run import run, exe
12 from produtil.log import jlogger
13 from hwrf.numerics import to_datetime_rel, to_datetime
14 
15 import hwrf_expt
16 
17 class TrackDeliveryFailed(Exception):
18  """!Raised when the send_nhc_track() is unable to deliver the
19  track file."""
20 
21 ########################################################################
22 ### TRACK FILE DELIVERY ################################################
23 ########################################################################
24 def send_nhc_track(*args,**kwargs):
25  """!Sends the track file to special NHC disk locations on the
26  WCOSS supercomputer.
27 
28  @param args,kwargs Ignored.
29  @note This does not sort the track file, and does not grep out the
30  correct storm, since that is already done before this function
31  starts."""
32  from hwrf_expt import tracker, conf
33  logger=tracker.log('to_nhc')
34  ENV=os.environ
35 
36  if ENV.get('SENDCOM','NO').upper() != 'YES':
37  logger.warning('Not delivering track file because SENDCOM is not YES')
38  return
39 
40  ATCFdir=tracker.getdir('ATCFdir','missing')
41  if ATCFdir=='missing':
42  if 'ATCFdir' in ENV:
43  ATCFdir=ENV['ATCFdir']
44  else:
45  msg='''ABORTING: Cannot deliver track file: $ATCFdir is unset, and is not in hwrf.conf. Don't know where to send track file.'''
46  logger.critical(msg)
47  raise TrackDeliveryFailed(msg)
48 
49  i_am_nco=ENV.get('PARAFLAG','NO').upper() == 'NO'
50  if not i_am_nco and ATCFdir[0:4]=='/com':
51  logger.warning('Not delivering track file because you are not NCO (PARAFLAG is not NO) and your destination directory is inside /com or /com2')
52  return
53 
54  # src = the track file (name is automatically generated by tracker.py)
55  src=tracker.product('cleanatcfunix').location
56 
57  # tgt = something like /com/nhc/prod/atcf/bb062013/ncep_abb062013.dat
58  tgt=os.path.join(ATCFdir,
59  '{hwrfbasin2}{stnum:02d}{when.year}/ncep_a{hwrfbasin2}{stnum:02d}{when.year}.dat' \
60  .format(**tracker.storminfo.__dict__).lower())
61  logger.info('%s: will read this track file'%(src,))
62  logger.info('%s: will append track here'%(tgt,))
63 
64  with open(src,'rt') as f:
65  outfull=''
66  out112=''
67  for line in f:
68  line=line.rstrip()
69  outfull+=line+'\n'
70  rline=line[0:112]+'\n'
71  out112+=rline
72 
73  if 'gltrkdir' in ENV or ENV.get('PARAFLAG','NO') == 'NO':
74  if 'gltrkdir' in ENV:
75  gltrkdir=ENV['gltrkdir']
76  else:
77  gltrkdir='/com/hur/'+ENV.get('envir','prod')+'/global'
78  yy=tracker.storminfo.when.year%100
79  glatuxarch=os.path.join(gltrkdir,'tracks.atcfunix.%02d'%(yy,))
80  logger.info('%s: will also append track here'%(glatuxarch,))
81  produtil.fileop.makedirs(gltrkdir,logger=logger)
82  # Append the track in a single operation:
83  with open(glatuxarch,'at') as o:
84  o.write(out112)
85 
86  # Make the /com/nhc/prod/atcf/bb062013 part:
87  tgtdir=os.path.dirname(tgt)
88  produtil.fileop.makedirs(tgtdir,logger=logger)
89 
90  # Append the track to the target file in a single operation:
91  with open(tgt,'at') as f:
92  f.write(outfull)
93  logger.info('%s: track delivered from %s'%(tgt,src))
94 
95 ########################################################################
96 ### DBN ALERTS #########################################################
97 ########################################################################
98 
100  """!Adds dbn alerts for the WRF program's wrfdiag files, which are
101  used by the downstream wave model.
102 
103  Ensures the wrfdiag files have the necessary dbn_alerts to trigger
104  delivery to the NCEP FTP server and ecFlow events (if required).
105  Those files will be delivered by the NHCProducts object at the end
106  of the JHWRF_PRODUCTS job, after the hwrf_nhc_products program
107  completes. We run the hwrf_nhc_products program before delivering
108  the wrfdiag files to ensure that the files are plausable before
109  delivering them to the wave model."""
110  from hwrf_expt import nhcp, conf
111  # Loop over all wrfdiag files:
112  nalerts=0
113  logger=conf.log('add_wave_alerts')
114  for prod in nhcp.wrfdiag_products():
115  # Add a dbn_alert to send as "HWRF_WAVE_INPUT"
116  logger.info('%s@%s: added alert for this product'%(repr(prod),repr(prod.did)))
117  nalerts+=1
118  prod.add_callback(DBNAlert(['MODEL','HWRF_NETCDF','{job}',
119  '{location}']))
120  logger.info('Added %d wave alerts'%nalerts)
121 
122 ########################################################################
124  """!Adds dbn alerts for GRIB products by adding DBNAlert objects
125  to the hwrf_expt.gribber.
126 
127  Adds calls to the dbnet alerts so that GRIB files will be
128  delivered to the NCEP FTP server."""
129 
130  from hwrf_expt import cycle, gribber, conf
131 
132  # Arrays of datetime objects that refer to six-hourly and
133  # three-hourly output times:
134  flen=conf.getint('config','forecast_length',126)
135  iflen=flen/6
136  nsix=flen/6+1
137  nthree=flen/3+1
138  atime=to_datetime(cycle)
139  six_hourly=[ to_datetime_rel(3600*6*x,atime) for x in xrange(nsix) ]
140  three_hourly=[ to_datetime_rel(3600*3*x,atime) for x in xrange(nthree) ]
141 
142  # Alert all three-hourly files for non-satellite post:
143  nonsat_mapping=dict(hwrf2prs_p='HWRF_GRIB2P',
144  hwrf2prs_i='HWRF_GRIB2I',
145  hwrf2prs_g='HWRF_GRIB2G',
146  hwrf2prs_c='HWRF_GRIB2C',
147  hwrf2prs_n='HWRF_GRIB2N',
148  hwrf2prs_m='HWRF_GRIB2M')
149  for (prodname,alertname) in nonsat_mapping.iteritems():
150  for time in three_hourly:
151  for p in gribber.products(name=prodname,time=time):
152  p.add_callback(DBNAlert(['MODEL',alertname,'{job}','{location}']))
153  break
154 
155  # Alert all three-hourly GRIB2 files for non-satellite post:
156  sat_mapping=dict(hwrf2sat_p='HWRF_SAT_GRIB2P',
157  hwrf2sat_i='HWRF_SAT_GRIB2I',
158  hwrf2sat_g='HWRF_SAT_GRIB2G',
159  hwrf2sat_c='HWRF_SAT_GRIB2C',
160  hwrf2sat_n='HWRF_SAT_GRIB2N',
161  hwrf2sat_m='HWRF_SAT_GRIB2M')
162 
163  # Alert all six-hourly GRIB2 files for satellite post:
164  for (prodname,alertname) in sat_mapping.iteritems():
165  for time in three_hourly:
166  for p in gribber.products(name=prodname,time=time):
167  p.add_callback(DBNAlert(['MODEL',alertname,'{job}','{location}']))
168  break
169 
170 ########################################################################
171 def email_afos_to_sdm(afos,*args,**kwargs):
172  """!Emails the AFOS file to the NOAA Senior Duty Meterologist (SDM)
173 
174  This function is called from scripts.exhwrf_output to email the
175  AFOS file (simplified track file) to the Senior Duty Meteorologist
176  (NOAA SDM). It is controlled by a number of environment
177  variables, requested by NCEP Central Operations:
178  * EMAIL_SDM=YES/NO --- turn on or off the email
179  * HWRF_TRACK_EMAIL_LIST=(list of emails) --- list of email addresses
180  to receive the AFOS file.
181  * HWRF_EMAIL_SSH_MACHINE --- no longer used. This was the machine
182  to ssh into, to send the mail. The mail is now sent directly from
183  the machine running the exhwrf_output script.
184 
185  The following configuration settings have the same meaning as the
186  environment variables:
187  * [config] email_sdm = $EMAIL_SDM
188  * [config] track_email_list = $HWRF_TRACK_EMAIL_LIST
189  * [config] email_ssh_machine = $HWRF_EMAIL_SSH_MACHINE
190 
191  In all cases, the environment variables override the config file
192  settings (again, by request of NCO). """
193  from hwrf_expt import nhcp, conf
194  if not afos.available:
195  jlogger.error('Not emailing AFOS file: Product is not available.')
196  return
197  logger=conf.log('email_sdm')
198 
199  email_sdm_str=os.environ.get('EMAIL_SDM','YES').upper()
200  email_sdm=email_sdm_str=='YES'
201  if not email_sdm:
202  jlogger.error('EMAIL_SDM=%s in environment - disable email to SDM'
203  %(email_sdm_str,))
204  return
205  email_sdm_flag=conf.getbool('config','email_sdm',False)
206  if not email_sdm_flag:
207  jlogger.error('email_sdm=no in config files - disable email to SDM')
208  return
209 
210  afosfile=afos.location
211  if not afosfile or not produtil.fileop.isnonempty(afosfile):
212  jlogger.error('Not emailing AFOS file: is empty or nonexistent: '
213  +afosfile)
214  return
215 
216  subject=conf.strinterp('config',
217  "{cyc}Z HWRF Output for {vit[basinname]} "
218  "Tropical System {vit[stormname]} ({vit[stormid3]})")
219  cmd=exe('mail')['-v','-s',subject] < afosfile
220 
221  # Set the "From:" line if requested:
222  email_from=os.environ.get('HWRF_EMAIL_FROM',
223  'ncep.list.spa-helpdesk@noaa.gov')
224  if email_from:
225  cmd=cmd['-r',email_from]
226  logger.info('Email "From:" address is: %s'%(repr(email_from),))
227  else:
228  logger.warning('Not changing email "From:" line. Will use system '
229  'default. This may break email if the system default'
230  'has a domain that is not world-visible.')
231 
232  # Add the destination addresses:
233  track_email_list=os.environ.get('HWRF_TRACK_EMAIL_LIST','') \
234  or conf.getstr('config','track_email_list', '') \
235  or 'SDM@noaa.gov'
236  addresses=re.split('[, \t]+',track_email_list)
237  cmd=cmd[addresses] # add the addresses to the argument list
238 
239  # Send the email:
240  jlogger.info('Emailing AFOS to [%s] with command: %s'%
241  (', '.join(addresses),repr(cmd)))
242  status=run(cmd,logger=logger)
243  if status==0:
244  jlogger.info('Rejoice: have emailed AFOS to SDM.')
245  else:
246  jlogger.error(
247  'Trouble emailing AFOS to SDM: mail returned status %d.'
248  %int(status))
249 
250 ########################################################################
252  """!Adds dbn alerts for the hwrf_nhc_products program's output by
253  adding DBNAlert objects to the hwrf_expt.nhcp object's
254  Products."""
255  from hwrf_expt import nhcp
256  afos=nhcp.product('afos')
257  afos.add_callback(DBNAlert(['MODEL','HWRF_AFOS','{job}','{location}']))
258  # afos.add_callback(email_afos_to_sdm,[afos]) moved to output job
259  nhcp.product('htcf').add_callback(DBNAlert(['MODEL','HWRF_ASCII','{job}','{location}']))
260  nhcp.product('rainfall').add_callback(DBNAlert(['MODEL','HWRF_ASCII','{job}','{location}']))
261  nhcp.product('wind10m').add_callback(DBNAlert(['MODEL','HWRF_ASCII','{job}','{location}']))
262  nhcp.product('wind10hrly').add_callback(DBNAlert(['MODEL','HWRF_ASCII','{job}','{location}']))
263  nhcp.product('stats').add_callback(DBNAlert(['MODEL','HWRF_STATS','{job}','{location}']))
264 
265 ########################################################################
267  """!Adds dbn alerts for the tracker and requests delivery of the
268  tracker to NHC deck locations."""
269  from hwrf_expt import tracker
270  hr3=tracker.product('atcf3hourly')
271  hr6=tracker.product('atcfshort6hr')
272  tr=tracker.product('cleanatcfunix')
273  logger=hwrf_expt.conf.log('tracker_alerts')
274  logger.warning('cleaned 3hourly track is %s'%(hr3.did,))
275  logger.warning('cleaned, 112-char-line, 6hourly subset track is %s'%(hr6.did,))
276  logger.warning('cleaned, complete track is %s'%(tr.did,))
277  hr3.add_callback(DBNAlert(['MODEL','HWRF_ASCII','{job}','{location}']))
278  hr6.add_callback(DBNAlert(['MODEL','HWRF_ASCII','{job}','{location}']))
279  tr.add_callback(DBNAlert(['MODEL','HWRF_ASCII','{job}','{location}']))
280  tr.add_callback(send_nhc_track)
281  assert(hr3.has_callbacks())
282  assert(hr6.has_callbacks())
283  assert(tr.has_callbacks())
This module provides a set of utility functions to do filesystem operations.
Definition: fileop.py:1
A shell-like syntax for running serial, MPI and OpenMP programs.
Definition: run.py:1
def email_afos_to_sdm(afos, args, kwargs)
Emails the AFOS file to the NOAA Senior Duty Meterologist (SDM)
Definition: hwrf_alerts.py:171
def isnonempty(filename)
Returns True if the filename refers to an existent file that is non-empty, and False otherwise...
Definition: fileop.py:333
def makedirs
Make a directory tree, working around filesystem bugs.
Definition: fileop.py:224
Time manipulation and other numerical routines.
Definition: numerics.py:1
def add_wave_alerts()
DBN ALERTS #########################################################.
Definition: hwrf_alerts.py:99
def add_regrib_alerts()
Adds dbn alerts for GRIB products by adding DBNAlert objects to the hwrf_expt.gribber.
Definition: hwrf_alerts.py:123
This module runs the NCO dbn_alert program, or logs dbn_alert messages if run with dbn alerts disable...
Definition: dbnalert.py:1
def add_tracker_alerts()
Adds dbn alerts for the tracker and requests delivery of the tracker to NHC deck locations.
Definition: hwrf_alerts.py:266
This class represents a call to dbn_alert, as a callable Python object.
Definition: dbnalert.py:47
def add_nhc_alerts()
Adds dbn alerts for the hwrf_nhc_products program's output by adding DBNAlert objects to the hwrf_exp...
Definition: hwrf_alerts.py:251
Configures logging.
Definition: log.py:1
def send_nhc_track(args, kwargs)
Sends the track file to special NHC disk locations on the WCOSS supercomputer.
Definition: hwrf_alerts.py:24
Raised when the send_nhc_track() is unable to deliver the track file.
Definition: hwrf_alerts.py:17