HWRF  trunk@4391
wrfbase.py
1 """!low-level wrf implementation, underlying hwrf.wrf
2 
3 This module contains low-level classes that underlie the
4 implementation of the hwrf.wrf module. It also contains the WRFOutput
5 class, which is used to describe a WRF output file, and the
6 parse_wrf_outname, which turns WRF-style filename formats
7 (wrfout_d<domain>_<date>) into filenames."""
8 
9 import fractions,math,re,datetime, pdb, logging
10 
11 from hwrf.numerics import *
12 from hwrf.namelist import *
13 from hwrf.exceptions import *
14 
15 ## @var __all__
16 # the symbols exported by "from hwrf.wrfbase import *"
17 __all__=['parse_wrf_outname','WRFOutput','WRFDomainBase','WRFDomains']
18 
19 ########################################################################
20 
21 def parse_wrf_outname(outname,grid_id,date,nocolons):
22  """!generates wrf filenames from templates like construct_filename
23 
24  Takes a wrf outname, a grid_id and a date and produces the final
25  string outname. The mandatory boolean argument nocolons is set to
26  the namelist &time_control nocolons value. This mimics the
27  construct_filename family of functions in the WRF source code.
28  @param outname the filename format as passed to WRF
29  @param grid_id the integer grid ID
30  @param date the date (anything accepted by hwrf.numerics.to_datetime)
31  @param nocolons if True, colons in the date are converted to underscores
32  @return the string filename"""
33  assert(isinstance(outname,basestring))
34  out=outname
35  if re.search('(?i)<domain>',out):
36  out=re.sub('(?i)<domain>','%02d'%(int(grid_id),),out)
37  if re.search('(?i)<date>',out):
38  out=re.sub('(?i)<date>',to_datetime(date).\
39  strftime('%Y-%m-%d_%H:%M:%S'),out)
40  if nocolons:
41  out=out[0:2]+re.sub(':','_',out[2:])
42  return out
43 
44 ########################################################################
45 
46 class WRFOutput:
47  """!A class that provides information about WRF output and input files.
48 
49  This class is used throughout the HWRF scripts to identify WRF
50  input and output filenames. The underlying implementation knows
51  how to deal with the odd rounding issues involved in predicting
52  WRF filenames based on timesteps, including fractional timesteps.
53  This class also tracks the analysis time, forecast time, stream
54  and full path to the file."""
55  def __init__(self,anltime,stream,domain,path,validtime=None,
56  fcstdelta=None):
57  """!Creates a WRFOutput object that knows the path to its file
58  (self.path()), the output time as a datetime
59  (self.validtime()), the simulation start time as a datetime
60  (self.anltime()), the output forecast second as a timedelta
61  (self.fcstdelta()), the name of the WRF stream
62  (self.stream()), and the WRF domain object (self.domain()).
63  Do not modify the domain object or many things may break.
64 
65  You must specify exactly one of validtime or fcstdelta. If
66  you specify both, OverspecifiedOutputTime will be raised. If
67  you specify neither, NoOutputTime will be raised."""
68  assert(isinstance(stream,basestring))
69  assert(isinstance(path,basestring))
70  self._anltime=to_datetime(anltime)
71  if validtime is None and delta is None:
72  raise NoOutputTime('In WRFOutput.__init__, both validtime and '
73  'fcstdelta were None')
74  elif validtime is not None:
75  self._validtime=to_datetime(validtime)
76  self._fcstdelta=self._validtime-self._anltime
77  elif fcstdelta is not None:
78  self._fcstdelta=to_timedelta(fcstdelta)
79  self._validtime=self._anltime + self._fcstdelta
80  else:
82  'In WRFOutput.__init__, both validtime and fcstdelta were '
83  'specified. You must specify exactly one.')
84  self._path=path
85  self._stream=str(stream)
86  assert(domain is None or isinstance(domain,WRFDomainBase))
87  self._domain=domain
88  def __hash__(self):
89  """!provides an integer hash value so this object can be used
90  as a key in a dict"""
91  return hash(self._domain) ^ hash(self._stream) ^ hash(self._path) ^ \
92  hash(self._validtime) ^ hash(self._anltime)
93  def __eq__(self,other):
94  """!does this WRFOutput equal that one?
95 
96  Returns True if the other WRFOutput object is identical to
97  this one, and False if it is not. For anything other than a
98  WRFOutput, returns NotImplemented.
99  @param other another object"""
100  if not isinstance(other,WRFOutput):
101  return NotImplemented
102  if not self._domain==other._domain: return False
103  if not self._stream==other._stream: return False
104  if not self._path==other._path: return False
105  if not self._validtime==other._validtime: return False
106  if not self._anltime==other._anltime: return False
107  return True
108  def __str__(self):
109  """!a string representation of this output file"""
110  return '%s output stream=%s path=%s' % \
111  (repr(self.domain()),str(self.stream()),str(self.path()))
112  def __repr__(self):
113  """!a detailed string representation of this output file"""
114  return 'WRFOutput(%s,%s,%s,%s,validtime=%s)' % \
115  (repr(self.anltime()),repr(self.stream()),repr(self.domain()),
116  repr(self.path()),repr(self.validtime()))
117  def path(self):
118  """!Returns the full path to the output file."""
119  return self._path
120  def stream(self):
121  """!Returns the lower-case name of the WRF stream."""
122  return self._stream
123  def domain(self):
124  """!the hwrf.wrf.WRFDomain object
125 
126  Returns the domain object for this output file's WRF domain.
127  Do not modify the returned object or many things will break."""
128  return self._domain
129  def fcstdelta(self):
130  """!the difference between the analysis and forecast time.
131 
132  Returns the time difference between the valid (output) time
133  and the analysis (simulation start) time."""
134  return self._fcstdelta
135  def validtime(self):
136  """!Returns the output time as a datetime.datetime."""
137  return self._validtime
138  def anltime(self):
139  """!Returns the analysis time as a datetime.datetime."""
140  return self._anltime
141 
142 ########################################################################
143 
144 class WRFDomainBase(object):
145  """!superclass of WRFDomain
146 
147  This is the superclass of WRFDomain and it should not be
148  instantiated directly. It exists to eliminate a cyclic dependency
149  in the module imports. """
150  def __init__(self,conf,section,name=None,copy=None):
151  """!Creates a new WRFDomainBase.
152  conf - an HWRFConfig with configuration information
153  section - the section to read for information about this domain
154  name - the name of the domain. Default: section name.
155  copy - do not specify this. It is used by self.copy()
156  to copy a WRFDomainBase."""
157  self.nestlevel=None
158  self.parent=None
159  self.nocolons=True
160  self._start=None
161  self._end=None
162  self._dt=None
163  self._output={}
164  if copy is not None:
165  self.nl=copy.nl.copy()
166  self.name=str(copy.name)
167  ( self._start,self._end,self._dt,self.nestlevel,self.parent,
168  self.nocolons) = \
169  (copy._start,copy._end,copy._dt,copy.nestlevel,copy.parent,
170  copy.nocolons)
171  else:
172  self.nl=Conf2Namelist(conf,section)
173  self.name=str(section)
174 
175  ##@var name
176  # the name of this domain
177 
178  ##@var nl
179  # the hwrf.namelist.Conf2Namelist with namelist information for
180  # this domain
181 
182  ## @var nestlevel
183  # the wrf nesting level
184 
185  ## @var parent
186  # the parent domain
187 
188  ## @var nocolons
189  # should colons be eliminated from filenames?
190 
191  ## @var _start
192  # the start time for this domain
193 
194  ## @var _end
195  # the end time for this domain
196 
197  ## @var _dt
198  # this domain's timestep
199 
200  ## @var _output
201  # internal data structure used by subclasses to track output streams
202 
203  def __hash__(self):
204  """!an integer representation for hashing"""
205  return hash(self.name)
206  def __repr__(self):
207  """!a detailed string representation"""
208  return '<WRFDomain name=%s>'%(str(self.name),)
209  def __cmp__(self,other):
210  """!integer comparison for sorting, like the cmp function.
211  Sorts by domain's string name.
212  @param other the domain to compare against"""
213  return cmp(self.name,other.name)
214  def __str__(self):
215  """!synonym for __repr__()"""
216  return repr(self)
217  def get_anl_time(self):
218  """!returns the analysis time
219 
220  Returns the parent _start time if there is a parent, otherwise
221  returns this domain's analysis time."""
222  if self.parent is not None:
223  return self.parent._start
224  else:
225  return self._start
226  def get_grid_id(self):
227  """!Raises NotImplementedError. The WRFDomain overrides this
228  to return the grid id."""
229  raise NotImplementedError(
230  'WRFDomainBase does not implement get_grid_id')
231  def remove_forbidden(self):
232  """!removes forbidden namelist entries
233 
234  Removes all namelist entries that the conf files are forbidden
235  from specifying. Generally this is anything related to domain
236  start, end, size, I/O or timesteps."""
237  self.nl=self.nl.copy(var_subset=self._nl_subsetter)
238  def _nl_subsetter(self,s,v):
239  """!returns True
240 
241  This returns True. It is intended to be overridden in
242  subclasses to subset the namelist values provided by the
243  configuration files. It should return True to keep the
244  variable, or False to reject it.
245  @param s the namelist name
246  @param v the variable name"""
247  return True
248  def copy(self):
249  """!Returns a copy of this object. The copy has its own data
250  structures, so modifying the copy will not modify the
251  original."""
252  return WRFDomainBase(None,None,copy=self)
253  def _validate_timespan(self,start,end,timestep):
254  """!checks if a timespan is valid
255 
256  Analyzes a potential WRF simulation start, end and timestep
257  to make sure it matches assumptions made within WRF, and
258  within the hwrf package.
259  1. There must be no timezone (UTC only)
260  2. The start time must not contain microseconds.
261  3. The end time must be at or after the start time
262  @param start the start time of the simulation
263  @param end the end time of the simulation
264  @param timestep the simulation timestep or output frequency
265  @returns a tuple (ts,te,dt) where:
266  - ts = the start time as a datetime.datetime
267  - te = the end time as a datetime.datetime
268  - dt = the timestep as a fractions.Fraction"""
269  ts=to_datetime(start)
270  te=to_datetime(end)
271  dt=to_fraction(timestep)
272  if te.tzinfo is not None or ts.tzinfo is not None:
273  raise TimezoneProvided(
274  'WRF start and end times must not contain timezones.')
275  if te.microsecond!=0 or ts.microsecond!=0:
276  raise PrecisionTooHigh(
277  'WRF start and end times must lie exactly on a second.')
278  if(te<ts):
279  raise EndBeforeStart(
280  'Invalid domain start/end times: end is before start.',ts,te)
281  return (ts,te,dt)
282  def set_timing(self,start,end,timestep):
283  """!sets the start and end times of this domain, and the domain
284  timestep
285 
286  Validates the provided inputs through _validate_timespan().
287  If accepted, sets the start and end times, and the timestep.
288  @param start the start time of the simulation
289  @param end the end time of the simulation
290  @param timestep the simulation timestep or output frequency"""
291  (ts,te,dt)=self._validate_timespan(start,end,timestep)
292  self._start=ts
293  self._end=te
294  self._dt=dt
295  def init_domain(self,grid_id):
296  """!initializes grid ID and non-nesting-related variables
297 
298  Initializes this domain's variables that are unrelated to
299  nesting.
300  @param grid_id the grid id in WRF (1=moad, 2=...)"""
301  def init_as_moad(self,simstart,simend,simdt,eta_levels):
302  """!initializes this domain as the outermost domain
303 
304  Initializes this domain's variables so it knows to act as
305  the Mother Of All Domains (MOAD) - the outermost domain.
306  The grid ID is set to 1 and the parent to None.
307  @param simstart simulation start time
308  @param simend simulation end time
309  @param simdt simulation outer domain timestep
310  @param eta_levels NMM eta vertical levels as set in the
311  eta_levels namelist value."""
312  (self._simstart,self._simend,self._simdt)=(simstart,simend,simdt)
313  self.set_timing(simstart,simend,simdt)
314  self.init_domain(1)
315  self.parent=None
316  def init_as_nest(self,parent,grid_id,start,end):
317  """!initializes this domain as a nest
318 
319  Initializes this domain's variables to make it a nest within
320  the given parent WRFDomain
321  @param parent the parent of this nest
322  @param grid_id the grid ID
323  @param start the nest's start time
324  @param end the nest's end time"""
325  self.init_domain(grid_id)
326  self.parent=parent
327  (self._simstart,self._simend,self._simdt) = \
328  (parent._simstart,parent._simend,parent._simdt)
329  if not is_at_timestep(parent._start,start,parent._dt):
331  'Start time %s for domain %d is not at parent %d timestep.'
332  %(parent._start,grid_id,parent.grid_id))
333  self.set_timing(parent._simstart,parent._simend,parent._simdt/3)
334  def make_namelist(self):
335  """!creates the wrf namelist as a string, and returns it."""
336  return self.nl.make_namelist()
337 
338 ########################################################################
339 
340 class WRFDomains(object):
341  """!abstract base class of WRFSimulation
342 
343  This is the abstract base class of WRFSimulation. You should not
344  instantiate it directly. Its purpose is to combine information
345  about multiple WRFDomainBase objects (or WRFDomain objects) to
346  make a single namelist. It also has the ability to make aggregate
347  changes to those objects, or obtain aggregate information about
348  them. This class exists only to avoid cyclic dependencies."""
349  def copy(self):
350  """! duplicates this object
351 
352  Returns a deep copy of this object, providing new data
353  structures so modifying the copy will not modify the original.
354  The underlying WRFDomain objects and their data structures are
355  also duplicated."""
356  return WRFDomains(None,None,None,None,None,None,dup=self)
357  ## @var _nextid
358  # the next grid_id to use
359 
360  ## @var _domains_done
361  # if True, we can add more domains
362 
363  ## @var nl
364  # the hwrf.namelist.Conf2Namelist with domain-independent namelist
365  # information like eta_levels and debug=2. The per-domain
366  # information is in each WRFDomain contained within this WRFSimulation
367 
368  ## @var _grid
369  # mapping from domain name to domain
370 
371  ## @var _to_id
372  # mapping from domain name to grid_id
373 
374  ## @var _to_name
375  # mapping from grid_id to domain name
376 
377  ## @var _parent
378  # mapping from domain name to its parent domain's name
379 
380  ## @var _simstart
381  # the simulation start time
382 
383  ## @var _simend
384  # the simulation end time
385 
386  ## @var _timestep
387  # the outermost domain timestep
388 
389  ## @var _io_form
390  # the default io_form
391 
392  def __init__(self,conf,section,moad,simstart,simend,timestep=None,dup=None):
393  """!WRFDomains constructor
394 
395  Creates a new WRFDomains object.
396  @param conf the HWRFConfig to provide configuration information
397  @param section the section to use in that config object
398  @param moad the Mother of All Domains, as a WRFDomain
399  @param simstart,simend - simulation start and end times
400  @param timestep the simulation timestep
401  @param dup do not use. This is used by the self.copy() do do a deep
402  copy of a WRFDomains."""
403  if dup is not None:
404  # copy constructor
405  self.nl=dup.nl.copy()
406  ( self._simstart,self._simend,self._timestep,self._io_form ) = \
407  ( dup._simstart, dup._simend, dup._timestep, dup._io_form )
408  self._nextid=dup._nextid
409  self._domains_done=dup._domains_done
410  self._grid=dict()
411  self._to_id=dict()
412  self._to_name=dict()
413  self._parent=dict()
414  for (name,domain) in dup._grid.iteritems():
415  gid=dup._to_id[name]
416  self._grid[name]=domain.copy()
417  self._to_id[name]=gid
418  self._to_name[gid]=name
419  for (a,b) in dup._parent.iteritems(): self._parent[a]=b
420  return
421  self.nl=Conf2Namelist(conf,str(section))
422  eta_levels=self.nl.nl_get('domains','eta_levels')
423  self._simstart=to_datetime(simstart)
424  self._simend=to_datetime(simend)
425  if timestep is None:
426  timestep=self.nl.trait_get('dt')
427  self._timestep=to_fraction(timestep)
428  iof=self.nl.trait_get('io_form','missing')
429  if iof=='missing':
430  self._io_form=2 # default IO form
431  else:
432  self._io_form=int(iof)
433 
434  nc=self.get_nocolons()
435  mymoad=moad.copy()
436  mymoad.remove_forbidden()
437  mymoad.init_as_moad(self._simstart,self._simend,self._timestep,
438  eta_levels)
439  mymoad.nocolons=nc
440 
441  self._grid={mymoad.name:mymoad}
442  self._to_id={mymoad.name:1}
443  self._to_name={1:mymoad.name}
444  self._parent={} # mapping of child name to parent name
445  self._nextid=2 # next unused grid ID
446 
447  self._domains_done=False # True if we can no longer add domains
448  def fill_auto_starts(self,ivalue,jvalue=None,fillthis='**AUTO**',
449  centerthis='**CENTERED**'):
450  """!sets i_parent_start and j_parent_start if needed
451 
452  For domains whose i_parent_start or j_parent_start are set
453  automatically (start=centered or start=auto), this fills those
454  domains starts with the provided values. Domains that should
455  be centered in their parent (start=centerthis) are centered
456  relative to their parent if they have one.
457 
458  @param ivalue the i value to set if an external fortran
459  program has to be run to determine the i_parent_start and
460  j_parent_start.
461  @param jvalue the j value to set. Defaults to the ivalue
462  @param fillthis do not change - the string to locate for start=auto
463  @param centerthis do not change - the string to locate for start=centered
464  @returns self"""
465  if jvalue is None: jvalue=ivalue
466 
467  # Make sure that the start J is odd if it is set. Mimics the
468  # ksh calculation.
469  jvalue=(int(jvalue)+1)/2*2-1
470  def intify(bob):
471  if isinstance(bob,int): return bob
472  return int(bob,10)
473 
474  for domain in self:
475  got=domain.nl.nl_get('domains','i_parent_start')
476  if isinstance(got,basestring):
477  if got==fillthis:
478  domain.nl.nl_set('domains','i_parent_start',ivalue)
479  elif got==centerthis:
480  if domain.parent is None:
481  raise InvalidDomainStart(
482  '%s: invalid value for start. Only nests can '
483  'have start=centered'%(domain.name,))
484  dxp=intify(domain.parent.nl.nl_get('domains','e_we'))
485  dxd=intify(domain.nl.nl_get('domains','e_we'))
486  rat=intify(domain.nl.nl_get('domains','parent_grid_ratio',3))
487  icen=(dxp-dxd//rat)//2
488  domain.nl.nl_set('domains','i_parent_start',icen)
489  else:
490  raise InvalidDomainStart(
491  '%s: invalid value for start. Must be %s, %s or an '
492  'integer'%(got,fillthis,centerthis))
493  got=domain.nl.nl_get('domains','j_parent_start')
494  if isinstance(got,basestring):
495  if got==fillthis:
496  domain.nl.nl_set('domains','j_parent_start',jvalue)
497  elif got==centerthis:
498  if domain.parent is None:
499  raise InvalidDomainStart(
500  '%s: invalid value for start. Only nests can '
501  'have start=centered'%(domain.name,))
502  dyp=intify(domain.parent.nl.nl_get('domains','e_sn'))
503  dyd=intify(domain.nl.nl_get('domains','e_sn'))
504  rat=intify(domain.nl.nl_get('domains','parent_grid_ratio',3))
505  jcen=(dyp-dyd//rat)//2
506  jcen=(int(jcen)+1)//2*2-1
507  domain.nl.nl_set('domains','j_parent_start',jcen)
508  else:
509  raise InvalidDomainStart(
510  '%s: invalid value for start. Must be %s, %s or an '
511  'integer'%(got,fillthis,centerthis))
512  return self
513  def fill_auto_starts_multistorm(self,ivalues,jvalues=None):
514  """For domains whose i_parent_start or j_parent_start are set
515  automatically, this fills those domains starts with the
516  provided values, for the fakestorm of a multistorm run.
517  ivalue = a list of i start values for N storms
518  jvalue = a list of j start values for N storms"""
519 
520  numberof_outernests = int((self.maxdom()-1)/2)
521  numberof_istartvalues = len(ivalues)
522  assert(numberof_outernests==numberof_istartvalues)
523  ij_index=0
524  for domain in self:
525 
526  if jvalues[ij_index] is None: jvalues[ij_index]=ivalues[ij_index]
527 
528  # Make sure that the start J is odd if it is set. Mimics the
529  # ksh calculation.
530  jvalues[ij_index]=(int(jvalues[ij_index])+1)/2*2-1
531 
532  # TODO: Analyze and Harden. <jtf>
533  # Basic assumption is that all the I's and J's for the N storms
534  # are passed in and the sequences ths iterates through the domains,
535  # is in the same order. Once a "set" for J is made by hitting an
536  # "**AUTO**", than increment the ij index and assUme that the
537  # ij_index is referencing the number to the domain intended.
538 
539  got=domain.nl.nl_get('domains','i_parent_start')
540  if not isinstance(got,int):
541  domain.nl.nl_set('domains','i_parent_start',ivalues[ij_index])
542  got=domain.nl.nl_get('domains','j_parent_start')
543  if not isinstance(got,int):
544  domain.nl.nl_set('domains','j_parent_start',jvalues[ij_index])
545  ij_index+=1
546  if numberof_istartvalues==ij_index:
547  break
548 
549  return self
550 
551  def simstart(self):
552  """!Returns the simulation start time as a datetime.datetime."""
553  return self._simstart
554  def simend(self):
555  """!Returns the simulation end time as a datetime.datetime."""
556  return self._simend
557  def timestep(self):
558  """!Returns the simulation timestep as a datetime.time_delta."""
559  return self._timestep
560 
561  @property
562  def nocolons(self):
563  """!Should colons be omitted from filenames?
564 
565  Should colons be omitted from filenames? This is determined
566  from the configuration files, if it is specified there, or
567  from the I/O forms selected (some don't support colons). The
568  return value may be cached. To ensure the value is recomputed
569  instead, you can call get_nocolons."""
570  if '_nocolons_cache' not in self.__dict__:
571  return self.get_nocolons()
572  return self._nocolons_cache
573 
574  def get_nocolons(self):
575  """!Force a recheck of whether colons be omitted from filenames.
576 
577  Should colons be omitted from filenames? This is determined
578  from the configuration files, if it is specified there, or
579  from the I/O forms selected (some don't support colons). This
580  method guesses the nocolons setting from the WRF/WPS/Real
581  namelist. The result of this calculation is never cached: all
582  namelist options are re-scanned and the flag is recomputed
583  each time. To get the cached value, use the nocolons property
584  instead."""
585  nc=self.nl.trait_get('nocolons',True)
586  if not nc:
587  for var,value in self.nl.nl_each('time_control'):
588  if var.find('io_form')>=0:
589  iof=int(value)%100
590  if iof==1 or iof==11:
591  nc=True
592  break
593  if not nc:
594  for var,value in self.nl.trait_each():
595  if var.find('io_form')>=0:
596  iof=int(value)%100
597  if iof==1 or iof==11:
598  nc=True
599  break
600  self.nl.nl_set('time_control','nocolons',nc)
601  self.__dict__['_nocolons_cache']=nc
602  return nc
603  def get_io_form(self):
604  """!Gets the default io_form."""
605  return self._io_form
606  def set_io_form(self,i):
607  """!Sets the default io_form."""
608  self._io_form=i
609 
610  ## @var io_form
611  # the default io_form if none is specified
612 
613  io_form=property(get_io_form,set_io_form,None,
614  "The default I/O form for this WRF")
615 
616  def io_form_for(self,stream):
617  """!Returns the io_form for the specified stream.
618 
619  @return the io_form for the given stream. If none is
620  specified, the default io_form is returned.
621  @param stream the stream, a lower-case string"""
622  if stream=='history': stream='output'
623  iof=self.nl.trait_get('io_form_%s'%(stream),int(self._io_form))
624  return iof
625  def get_moad(self):
626  """!returns the MOAD as a WRFDomain."""
627  return self._grid[self._to_name[1]]
628  def get_last(self):
629  """!returns the last domain added to this WRF."""
630  return self._grid[self._to_name[self._nextid-1]]
631  def add_output(self,stream='history',domain=None,io_form=None,
632  start=None,end=None,step=None,frames_per_outfile=None,
633  outname=None):
634  """!request output from one or more streams
635 
636  Requests that one or all WRF domains output the specified
637  stream. The stream should be "history" or "auxhistN" for an
638  integer N>0. Optional "domain" specifies the domain that
639  should output this stream, otherwise all domains will output
640  it.
641 
642  @param stream the stream: "history" or "auxhistN" for an integer N>0
643  @param domain the domain (optional)
644  @param io_form WRF IO form. Simply calls self.set_io_form(stream,io_form)
645  @param start output start time (anything accepted by to_datetime)
646  Default: simulation start time.
647  @param end output end time (anything accepted by to_datetime.
648  Default: simulation end time.
649  @param step output interval, sent into to_out_interval().
650  Default: trait stream+"_interval" or 6hrs
651  @param frames_per_outfile how many output times per output file
652  @param outname output name or array of output names (one per
653  domain). Can only specify for all domains, not for only
654  one. Default: leave unspecified, and let WRF use its
655  defaults.
656 
657  NOTE: You cannot add any more domains after calling this
658  routine."""
659  self._domains_done=True
660  if io_form is None:
661  io_form=self.io_form_for(stream)
662  else:
663  self.set_io_form(stream,io_form)
664  if stream=='inputout':
665  if outname is None: outname='wrfinputout_d<domain>'
666  self.nl.nl_set('time_control','write_input',True)
667  if outname is not None: self.set_outname(stream,outname)
668  if start is not None: start=to_datetime_rel(start,self._simstart)
669  nc=self.get_nocolons()
670  for mydom in self:
671  mydom.nocolons=nc
672  if domain is not None and domain!=mydom and \
673  not mydom.has_output(stream):
674  mydom.no_output(stream)
675  else:
676  mydom.add_output(
677  stream,start,end,step,outname,frames_per_outfile,
678  io_form,simstart=self._simstart)
679  if stream=='restart':
681 
682  return self
684  """!Ensure that only one restart frequency is set
685 
686  Unlike most output frequencies, the restart frequency has to
687  be the same for all domains, and is specified as a scalar
688  value, not an array. This function removes per-domain restart
689  frequencies, moving the restart frequency up to the
690  domain-independent namelist values. The frequency used is the
691  one for the last domain listed by self.__iter__."""
692  for domain in self:
693  for n in [ 'restart_begin', 'restart_begin_s', 'restart_begin_m',
694  'restart_begin_h', 'restart_begin_d',
695  'restart_interval', 'restart_interval_s',
696  'restart_interval_m', 'restart_interval_h',
697  'restart_interval_d' ]:
698  if domain.nl.nl_have('time_control',n):
699  v=domain.nl.nl_get('time_control',n)
700  domain.nl.nl_del('time_control',n)
701  self.nl.nl_set('time_control',n,v)
702  def set_outname(self,stream,outname):
703  """!Set the wrf output filename for a stream
704 
705  Sets the WRF output filename format for the specified
706  stream to the given name. The name should contain @<domain@>
707  and @<date@> if appropriate.
708  @param stream the stream to change
709  @param outname the new output filename format"""
710  if stream=='inputout': stream='input'
711  self.nl.nl_set('time_control','%s_outname'
712  %(str(stream),),str(outname))
713  def set_io_form(self,stream,io_form):
714  """!Sets the io_form for the specified stream.
715 
716  Set the io_form for the specified stream. The "history" and
717  "output" streams are synonyms.
718  @param stream the stream
719  @param io_form the io_form"""
720  if stream=='history': stream='output'
721  self.nl.nl_set('time_control','io_form_%s'%(stream),int(io_form))
722  self.nl.trait_set('io_form_%s'%(stream),int(io_form))
723  def get_io_suffix(self,stream='history'):
724  """!Return the suggested output suffix for filenames
725 
726  Gets the suggested output suffix for the specified stream.
727  Returns "int" for 1 and "nc" for 2 or 11.
728  @param stream the stream. Default: "history" """
729  iof=self.io_form_for(stream)%100
730  if iof == 1:
731  ios = "int"
732  elif iof == 2 or iof==11:
733  ios = "nc"
734  else:
735  raise NameError("Unsupported IO form %d" %self_.io_form)
736  return ios
737  def get(self,what):
738  """!return the specified domain
739 
740  Returns a domain with the given name or grid_id. This method
741  may raise IndexError or KeyError if the domain does not exist.
742  If passed a WRFDomainBase, then this simulation's copy of the
743  domain with the same name is returned.
744 
745  @returns the specified domain.
746  @param what If this is a string or WRFDomain, then the domain
747  with that name is returned. If "what" is an integer, the
748  domain with that ID is returned. If it is a WRFDomainBase,
749  then the name stored in that object is used instead."""
750  if isinstance(what,WRFDomainBase): return self._grid[str(what.name)]
751  if isinstance(what,basestring): return self._grid[str(what)]
752  if isinstance(what,int): return self._grid[self._to_name[what]]
753  raise KeyError('In WRF.get, the key must be a basestring, '
754  'WRFDomain or an int (or subclass thereof). You '
755  'provided %s.'%(repr(what),))
756  def maxdom(self):
757  """!returns the highest domain number, which is also the number
758  of domains."""
759  return self._nextid-1
760  def can_add(self):
761  """!Can we still add domains to this simulation?
762 
763  Returns true if this WRF can accept new domains. Certain
764  operations, such as requesting output files, end the ability
765  to add domains."""
766  return not self._domans_done
767  def add(self,child,parent=None):
768  """!Add a WRFDomain to this simulation.
769 
770  Adds the child WRFDomain to this WRF, with the specified
771  parent. If the parent is not specified, the last added domain
772  is used as the parent. If specified, the parent may be
773  anything accepted by self.get. The return value is the new
774  WRFDomain.
775 
776  @param child the domain to add
777 
778  @param parent the WRFDomain of the parent
779 
780  @return a new WRFDomain object with the same name as child """
781 
782  if self._domains_done:
783  raise DomainsDone(
784  'You cannot add any domains after setting up I/O for a '
785  'WRF object.')
786 
787  # Get the settings for the new domain, plus some related inputs:
788  newid=self._nextid
789  newname=str(child.name)
790  newparent=self.get_last() if parent is None else self.get(parent)
791  parentname=str(newparent.name)
792  moad=self.get_moad()
793  if newname in self._grid:
794  raise DomainExists('%s: domain already exists'%(newname,),newname)
795 
796  # Initialize the new domain:
797  mine=child.copy()
798  mine.nl.trait_set('id',newid)
799  mine.nocolons=self.get_nocolons()
800  mine.remove_forbidden()
801  mine.init_as_nest(newparent,newid,self._simstart,self._simend)
802 
803  # Add the new domain. These assignments must not fail or the
804  # object will be corrupted, but it should not be possible to
805  # get a failure here:
806  self._grid[newname]=mine
807  self._to_id[newname]=newid
808  self._to_name[newid]=newname
809  self._parent[newname]=parentname
810  self._nextid=newid+1
811 
812  self.nl.nl_set('domains','max_dom',int(self.maxdom()))
813 
814  return self
815  # Special methods to allow wrf[domain], len(wrf), domain in wrf,
816  # and for domain in wrf
817  def __getitem__(self,what):
818  """!Same as self.get(what)
819 
820  Calls self.get(what)
821  @param what the grid_id, domain name or WRFDomain to search for.
822  @return the WRFDomain"""
823  return self.get(what)
824  def __len__(self):
825  """!Same as self.maxdom()."""
826  return self.maxdom()
827  def __contains__(self,what):
828  """!does this simulation contain this domain?
829 
830  Returns True if self.get(what) succeeds, and False if it
831  raises KeyError. Any other exceptions are passed to the
832  caller.
833  @param what the grid_id or name of the WRFDomain to search
834  for, or another WRFDomain whose name we should search for."""
835  try:
836  self.get(what)
837  return True
838  except KeyError: pass
839  return False
840  def __iter__(self):
841  """!Iterates over all WRFDomain objects in this WRFDomains."""
842  for grid_id in xrange(1,self._nextid):
843  yield self._grid[self._to_name[grid_id]]
def stream(self)
Returns the lower-case name of the WRF stream.
Definition: wrfbase.py:120
def get_moad(self)
returns the MOAD as a WRFDomain.
Definition: wrfbase.py:625
_parent
mapping from domain name to its parent domain's name
Definition: wrfbase.py:413
def init_as_moad(self, simstart, simend, simdt, eta_levels)
initializes this domain as the outermost domain
Definition: wrfbase.py:301
Generates a Fortran namelist entirely from config files.
Definition: namelist.py:411
def maxdom(self)
returns the highest domain number, which is also the number of domains.
Definition: wrfbase.py:756
_nextid
the next grid_id to use
Definition: wrfbase.py:408
_start
the start time for this domain
Definition: wrfbase.py:160
def remove_forbidden(self)
removes forbidden namelist entries
Definition: wrfbase.py:231
def add_output
request output from one or more streams
Definition: wrfbase.py:633
nl
the hwrf.namelist.Conf2Namelist with namelist information for this domain
Definition: wrfbase.py:165
_output
internal data structure used by subclasses to track output streams
Definition: wrfbase.py:163
def __hash__(self)
provides an integer hash value so this object can be used as a key in a dict
Definition: wrfbase.py:88
def __iter__(self)
Iterates over all WRFDomain objects in this WRFDomains.
Definition: wrfbase.py:840
def get_last(self)
returns the last domain added to this WRF.
Definition: wrfbase.py:628
def set_timing(self, start, end, timestep)
sets the start and end times of this domain, and the domain timestep
Definition: wrfbase.py:282
def __repr__(self)
a detailed string representation of this output file
Definition: wrfbase.py:112
def __repr__(self)
a detailed string representation
Definition: wrfbase.py:206
_timestep
the outermost domain timestep
Definition: wrfbase.py:427
def copy(self)
Returns a copy of this object.
Definition: wrfbase.py:248
def make_restart_time_scalar(self)
Ensure that only one restart frequency is set.
Definition: wrfbase.py:683
_domains_done
if True, we can add more domains
Definition: wrfbase.py:409
def __str__(self)
a string representation of this output file
Definition: wrfbase.py:108
def get_io_form(self)
Gets the default io_form.
Definition: wrfbase.py:603
nocolons
should colons be eliminated from filenames?
Definition: wrfbase.py:159
Raised when it is no longer possible to add domains, but the caller tried to add one.
Definition: exceptions.py:304
def init_domain(self, grid_id)
initializes grid ID and non-nesting-related variables
Definition: wrfbase.py:295
def simstart(self)
Returns the simulation start time as a datetime.datetime.
Definition: wrfbase.py:551
def init_as_nest(self, parent, grid_id, start, end)
initializes this domain as a nest
Definition: wrfbase.py:316
nl
the hwrf.namelist.Conf2Namelist with domain-independent namelist information like eta_levels and debu...
Definition: wrfbase.py:405
def __hash__(self)
an integer representation for hashing
Definition: wrfbase.py:203
Time manipulation and other numerical routines.
Definition: numerics.py:1
def add
Add a WRFDomain to this simulation.
Definition: wrfbase.py:767
Raised when adding a domain, if a domain by the same name already exists.
Definition: exceptions.py:323
_grid
mapping from domain name to domain
Definition: wrfbase.py:410
def can_add(self)
Can we still add domains to this simulation?
Definition: wrfbase.py:760
def make_namelist(self)
creates the wrf namelist as a string, and returns it.
Definition: wrfbase.py:334
_dt
this domain's timestep
Definition: wrfbase.py:162
def nocolons(self)
Should colons be omitted from filenames?
Definition: wrfbase.py:562
This module provides two different ways to generate Fortran namelist files from HWRFConfig sections: ...
Definition: namelist.py:1
def __init__
Creates a new WRFDomainBase.
Definition: wrfbase.py:150
Raised when the end of a timespan is before the beginning.
Definition: exceptions.py:225
Raised when a time was requested with higher precision than available.
Definition: exceptions.py:194
_to_id
mapping from domain name to grid_id
Definition: wrfbase.py:411
superclass of WRFDomain
Definition: wrfbase.py:144
A class that provides information about WRF output and input files.
Definition: wrfbase.py:46
_io_form
the default io_form
Definition: wrfbase.py:430
def __str__(self)
synonym for repr()
Definition: wrfbase.py:214
abstract base class of WRFSimulation
Definition: wrfbase.py:340
def __init__
WRFDomains constructor.
Definition: wrfbase.py:392
Raised when a time was required, but none was provided.
Definition: exceptions.py:189
def __cmp__(self, other)
integer comparison for sorting, like the cmp function.
Definition: wrfbase.py:209
def get_io_suffix
Return the suggested output suffix for filenames.
Definition: wrfbase.py:723
def simend(self)
Returns the simulation end time as a datetime.datetime.
Definition: wrfbase.py:554
_simstart
the simulation start time
Definition: wrfbase.py:423
def fill_auto_starts
sets i_parent_start and j_parent_start if needed
Definition: wrfbase.py:449
def __eq__(self, other)
does this WRFOutput equal that one?
Definition: wrfbase.py:93
def set_io_form(self, i)
Sets the default io_form.
Definition: wrfbase.py:606
Raised when a timespan's beginning is not at a timestep.
Definition: exceptions.py:234
def path(self)
Returns the full path to the output file.
Definition: wrfbase.py:117
Raised when a timezone is provided.
Definition: exceptions.py:191
def _nl_subsetter(self, s, v)
returns True
Definition: wrfbase.py:238
def parse_wrf_outname(outname, grid_id, date, nocolons)
generates wrf filenames from templates like construct_filename
Definition: wrfbase.py:21
def __init__
Creates a WRFOutput object that knows the path to its file (self.path()), the output time as a dateti...
Definition: wrfbase.py:56
_to_name
mapping from grid_id to domain name
Definition: wrfbase.py:412
def get_nocolons(self)
Force a recheck of whether colons be omitted from filenames.
Definition: wrfbase.py:574
def _validate_timespan(self, start, end, timestep)
checks if a timespan is valid
Definition: wrfbase.py:253
parent
the parent domain
Definition: wrfbase.py:158
name
the name of this domain
Definition: wrfbase.py:166
Exceptions raised by the hwrf package.
Definition: exceptions.py:1
def domain(self)
the hwrf.wrf.WRFDomain object
Definition: wrfbase.py:123
def copy(self)
duplicates this object
Definition: wrfbase.py:349
def get(self, what)
return the specified domain
Definition: wrfbase.py:737
nestlevel
the wrf nesting level
Definition: wrfbase.py:157
_end
the end time for this domain
Definition: wrfbase.py:161
def validtime(self)
Returns the output time as a datetime.datetime.
Definition: wrfbase.py:135
def anltime(self)
Returns the analysis time as a datetime.datetime.
Definition: wrfbase.py:138
_simend
the simulation end time
Definition: wrfbase.py:424
Raised when an output time is specified in two redundant ways.
Definition: exceptions.py:184
def get_grid_id(self)
Raises NotImplementedError.
Definition: wrfbase.py:226
def timestep(self)
Returns the simulation timestep as a datetime.time_delta.
Definition: wrfbase.py:557
def get_anl_time(self)
returns the analysis time
Definition: wrfbase.py:217
def __getitem__(self, what)
Same as self.get(what)
Definition: wrfbase.py:817
def __len__(self)
Same as self.maxdom().
Definition: wrfbase.py:824
def fcstdelta(self)
the difference between the analysis and forecast time.
Definition: wrfbase.py:129
def io_form_for(self, stream)
Returns the io_form for the specified stream.
Definition: wrfbase.py:616
def set_outname(self, stream, outname)
Set the wrf output filename for a stream.
Definition: wrfbase.py:702
Raised when the hwrf.wrf.WRFDomain start type is unrecognized.
Definition: exceptions.py:318
def __contains__(self, what)
does this simulation contain this domain?
Definition: wrfbase.py:827