Package qtcm :: Module qtcm
[hide private]
[frames] | no frames]

Source Code for Module qtcm.qtcm

   1  #!/usr/bin/python -tt 
   2  #======================================================================= 
   3  #                        General Documentation 
   4   
   5  """Definition of the Qtcm class. 
   6  """ 
   7   
   8  #----------------------------------------------------------------------- 
   9  #                       Additional Documentation 
  10  # 
  11  # RCS Revision Code: 
  12  #   $Id: qtcm.py 57 2008-09-12 17:27:04Z jlin $ 
  13  # 
  14  # Modification History: 
  15  # - 29 May 2008:  Original by Johnny Lin, Physics Department, North 
  16  #   Park University.  Passed passably reasonable tests. 
  17  # 
  18  # Notes: 
  19  # - Written for Python 2.4. 
  20  # - Module docstrings can be tested using the doctest module.  To 
  21  #   test, execute "python qtcm.py". 
  22  # - See import statements throughout for non-"built-in" packages and 
  23  #   modules required. 
  24  # 
  25  # Copyright (c) 2008 by Johnny Lin.  For licensing, distribution  
  26  # conditions, contact information, and additional documentation see 
  27  # the URL http://www.johnny-lin.com/py_pkgs/qtcm/doc/. 
  28  #======================================================================= 
  29   
  30   
  31   
  32   
  33  #---------------- Module General Import and Declarations --------------- 
  34   
  35  #- Import os and sys.  Also, if you're importing this module in 
  36  #  testing mode, or you're running pydoc on this module via the  
  37  #  command line, import user-specific settings to make sure any  
  38  #  non-standard libraries are found: 
  39   
  40  import os, sys 
  41  if (__name__ == "__main__") or \ 
  42     ("pydoc" in os.path.basename(sys.argv[0])): 
  43      import user 
  44   
  45   
  46  #- Import package version and set module version to package version: 
  47   
  48  import package_version as _package_version 
  49  __version__ = _package_version.version 
  50  __author__  = _package_version.author 
  51  __date__    = _package_version.date 
  52  __credits__ = _package_version.credits 
  53   
  54   
  55  #- Import numpy/Numeric/numarray as appropriate: 
  56   
  57  import num_settings as num 
  58  from num_settings import N 
  59   
  60   
  61  #- Other imports that then are used as module variables: 
  62   
  63  import copy 
  64  import defaults 
  65  from field import Field 
  66  from plot import plot_ncdf_output 
  67  import Scientific.IO.NetCDF as S 
  68  import shutil 
  69  import tempfile 
  70   
  71   
  72   
  73   
  74  #--------------------------- Module Variables -------------------------- 
  75   
  76  #- Field object instance used usually in type tests: 
  77   
  78  _test_field = Field('dt') 
  79   
  80   
  81  #- Dictionary of all prognostic variables and right-hand sides that 
  82  #  could be initialized and their default initial values.  List of 
  83  #  those variable keys, plus dateofmodel and title, which are all the 
  84  #  variables usually written out into a restart file: 
  85   
  86  _init_prog_dict = defaults.init_prognostic_dict 
  87  _init_vars_keys = _init_prog_dict.keys() + ['dateofmodel', 'title'] 
  88   
  89   
  90   
  91   
  92  #----------------------- Custom Exception Classes ---------------------- 
  93  # 
  94   
95 -class FieldNotReadableFromCompiledModel(UnboundLocalError):
96 "Field variable unable to be read from QTCM compiled model."
97 98 99 100 101 #----------------------------- Class: Qtcm ---------------------------- 102 # 103 # Note: With exception of the __init__ object, all the public methods 104 # come first followed by the private methods. Within each category, 105 # the methods are listed in alphabetical order. 106
107 -class Qtcm(object):
108 """Qtcm model object. 109 110 Public Instance Attributes: 111 * compiled_form: What form the compiled Fortran version of the 112 QTCM model has. This attribute is a string and can have the 113 following values: 114 115 - 'full': The compiled portion of the model encompasses the 116 entire QTCM model. Thus, the only compiled QTCM model 117 modules or subroutines that Python should interact with is 118 __qtcm.driver (which executes the entire model) and 119 __qtcm.setbypy (which enables communication between the 120 compiled model and the Python-level of model fields. 121 122 - 'parts': The compiled portion of the model encompasses 123 parts of the model as separate units all the way down to 124 an atmosphere timestep. Thus, compiled QTCM model 125 module/subroutines include those that are executed within 126 an atmosphere timestep. 127 128 This attribute must be set on instantiation via a keyword 129 input parameter. 130 131 * coupling_day: Current value of atmosphere-ocean coupling 132 day. When compiled_form of type 'parts' is run, is started 133 at 1 and then is updated every coupling day. coupling_day 134 not changed from instantiation value for compiled_form 'full'. 135 Is a Field object with integer scalar value, but is set to 136 None on instantiation (unless its value is overridden through 137 keyword input parameters). Is set to a Field object to enable 138 run lists that specify it to be able to be updated. 139 140 * init_with_instance_state: This keyword only has an effect if 141 compiled_form = 'parts'. Boolean scalar. 142 143 If True, uses, in general, the values of the instance attributes 144 (for prognostic variables and right-hand sides, and start 145 date) are right before the run_session method call as the 146 initial values for the run_session. If False, run_session 147 initialization, in general, follows the rules set by all the 148 standard QTCM model input parameters (e.g., the sign of day0, 149 month0, and year0, the value of the mrestart flag, etc.). 150 See the docstring for the varinit method for details, as well 151 as the manual. Default is True, unless overridden, on 152 instantiation. 153 154 * QTCM fields: This is not the name of an attribute, but a 155 category of attributes. These attributes are QTCM fields 156 whose names correspond to those given in the _qtcm_fields_ids 157 attribute, and whose values are Field objects. Initially set 158 on instantiation. 159 160 Override of default values on instantiation is accomplished 161 by setting the value to a keyword input parameter with the 162 Field object id as the keyword key. The __init__ method takes 163 all keyword input and sets it to instance attributes with the 164 key as the attribute name and attribute values being Field 165 objects. Note that the instantiation keyword parameter does 166 not have to itself be a Field object; if the keyword is set 167 to a plain value, the __init__method will form a Field object 168 around that value and set the Field object to the instance 169 attribute. 170 171 * runlists: Dictionary of lists of methods, routines, and other 172 run lists that can be executed by the run_list method. Altering 173 lists in this dictionary enables one to simply change order 174 or execution or insert/delete methods. Runlist's keys are 175 strings, and are names that describe that the category of 176 routines does. Run list names should not be preceeded by two 177 underscores (though runlist elements may be very private 178 variables), nor should runlist names be the same as any 179 instance attribute. See list of run lists below for details 180 about the lists. 181 182 * sodir: Name of temporary directory containing all the copies 183 of shared object files for this instance of Qtcm. This 184 temporary directory is located in the default temporary 185 directory, whose location is platform dependent, using the 186 rules of the module tempfile. The name of the temporary 187 directory is generated randomly, and should not conflict with 188 any existing temporary directory. String. Set on model 189 instantiation. 190 191 192 Public Instance Methods: 193 * get_qtcm_item: Get field from the compiled QTCM model (same 194 as get_qtcm1_item). 195 196 * get_qtcm1_item: Get field from the compiled QTCM model. 197 198 * make_snapshot: Make copy of current state of run session 199 variables that would be used for a restart. 200 201 * more_first_method_at_atm_oc_step: More of first method 202 executed at the atmosphere-coupling timestep. This gives an 203 easy way to change the model at the atmosphere-coupling 204 timestep, after the update of the interval attribute: Just 205 overload this method. 206 207 * plotm: Plot mean output for model field id. Matplotlib is 208 used. 209 210 * qtcm: Run the qtcm atmosphere over a coupling interval step. 211 212 * run_list: Run runlist of run lists and/or instance methods. 213 214 * run_session: Run model session. 215 216 * set_qtcm_item: Set Python-accessible compiled QTCM model 217 fields (same as set_qtcm1_item). 218 219 * set_qtcm1_item: Set Python-accessible compiled QTCM model 220 fields. 221 222 * sync_set_py_values_to_snapshot: Set Python attributes to 223 snapshot values from a dictionary created by make_snapshot. 224 225 * sync_set_qtcm_items_to_all_py_values: Synchronize so that 226 any Python attribute that corresponds to a compiled model 227 Python-changable variable is set, on both the Python attribute 228 side and the compiled model side, to the value of the Python 229 attribute. 230 231 * sync_set_all_py_values_to_qtcm_items: Set all Python-level 232 attributes to QTCM compiled model values. Synchoronize so 233 that any Python attribute that corresponds to a compiled model 234 Python-changable variable is set to the value of the QTCM 235 compiled variable. 236 237 * varinit: Initialize model variables in a run session. 238 239 240 List of Run Lists: 241 * 'atm_oc_step': Methods to run at an atmosphere-ocean coupling 242 time step. As default, set to methods that set the calendar, 243 run the ocean (make SST for atmo), run the atmosphere, and 244 output all output variables. Set on model instantiation. 245 See method __run_parts for details. 246 247 * 'init_model': Methods to initialize model variables. Not 248 all model variables are initalized in these methods. Note 249 that many of these methods use the values of QTCM parameters 250 as defined as instance attributes (and the defaults of which 251 are set by the _set_all_qtcm_scalar_fields_to_defaults method). 252 As default, set to initialize the atmosphere, ocean, and 253 output files. Set on model instantiation. See method 254 __run_parts for details. 255 256 * 'qtcminit': Duplicates functionality of the Fortran subroutine 257 qtcminit, for compiled_form 'parts'. 258 259 260 Private Instance Attributes (partial list): 261 * _monlen: Integer array of the number of days in each month. 262 Assumes a 365 day year. 263 264 * __qtcm: The compiled Fortran model for this instance. Set on 265 instantiation. 266 267 * _qtcm_fields_ids: Field ids for all default QTCM fields. 268 NB: The model assumes this is a list of all the default fields. 269 Set on instantiation. 270 271 * _runlists_long_names: Descriptions of the standard run lists. 272 273 274 Private Instance Methods (partial list): 275 * _bartropic_mode_at_atm_step: Calculate the atmosphere 276 barotropic mode at atmosphere timestep. 277 278 * _first_method_at_atm_oc_step: First method executed at the 279 atmosphere-coupling timestep. 280 281 * __run_parts: Run parts model starting at the atmosphere-ocean 282 coupling level. This method duplicates the functionality of 283 the driver subroutine in the original compiled QTCM model. 284 285 * _set_qtcm_array_item_in_model: Set Python-accessible QTCM 286 array settings in compiled model. 287 288 * _set_qtcm_scalar_item_in_model: Set Python scalar variable 289 in the compiled QTCM model. 290 291 292 Description of Compiled QTCM Modules Available to Class: 293 * _qtcm_full_365.so: QTCM shared object file for the full 294 model, compiled assuming a year is 365 days. This is essentially 295 the default version of QTCM, with netCDF output used. 296 297 * _qtcm_parts_365.so: QTCM shared object file for the model 298 separated so units within the atmosphere timestep are separate 299 compiled units. 300 """ 301 302 303 304 305 #----------------- Class Qtcm Private Method: __init__ ---------------- 306
307 - def __init__(self, **kwds):
308 """Initialize Qtcm instance. 309 310 Items in kwds are attributes of the model instance to be 311 set. There are two types of attributes: (1) Field objects, 312 and (2) Non-Field objects. In both cases you can pass in 313 the name and value in the input kwds dictionary. For Field 314 object attributes, the kwds key is the id, and the kwds 315 value can be either the value of the Field object or a Field 316 object itself. For Non-Field objects, the kwds key:value 317 pair directly provides the instance attribute name and 318 value. A more detailed description of this input and other 319 input is provided in the class documentation. 320 321 Examples: 322 model = Qtcm(dt=1200., compiled_form='parts') 323 model = Qtcm(dt=Field('dt'), compiled_form='full') 324 model = Qtcm(dt=Field('dt', 1200), compiled_form='full') 325 inputs = {} 326 inputs['dt'] = 1200. 327 inputs['title'] ='QTCM spinup part 1 test (aquaplanet)' 328 inputs['compiled_form'] = 'parts' 329 model = Qtcm(**inputs) 330 331 NB: Not all compiled QTCM model variables are accessible 332 at the Python level, and vice versa. Only the variables 333 in defaults (whose names are specified by the _qtcm_fields_ids 334 attribute) can be passed between the compiled QTCM model 335 and the Python model instance. 336 337 Additionally, there is only a full-as-possible syncronization 338 between compiled QTCM model fields and Python fields at the 339 beginning and end of the execution of a Python method. If 340 the compiled QTCM model changes a variable as the model is 341 running, the Python field that variable corresponds to will 342 in general not also be changed, since you need to execute 343 get_qtcm_item at the Python level to retrieve it. This 344 only occurs with Python methods (e.g., run_session, varinit). 345 """ 346 #- Set instance private attributes: 347 348 self._qtcm_fields_ids = defaults.qtcm_fields_ids 349 350 351 #- Set compiled_form keyword, which is required input: 352 353 if kwds.has_key('compiled_form'): 354 setattr( self, 'compiled_form', kwds['compiled_form'] ) 355 else: 356 raise ValueError, 'must pass in complied_form on instantiation' 357 358 359 #- Set compiled model as private variable and set default 360 # values for QTCM parameters: 361 362 self._set_compiled_qtcm_attr() 363 self._set_all_qtcm_scalar_fields_to_defaults() 364 365 366 #- For QTCM variables entered in as keyword input parameters, 367 # overwrite the values with the ones passed in on instantiation. 368 # For all other keyword input parameters, set as attributes. 369 # (Recall the set_qtcm_item method also makes sure the Qtcm 370 # instance attribute matches the compiled model value and 371 # that set_qtcm_item takes as input Field variables or Field 372 # variable values: 373 374 for ikey in kwds.keys(): 375 if ikey in self._qtcm_fields_ids: 376 self.set_qtcm_item(ikey, kwds[ikey]) 377 else: 378 setattr(self, ikey, kwds[ikey]) 379 380 381 #- Set other attributes: 382 # * coupling_day: If not yet set via kwds, initialized to 383 # None. 384 # * init_with_instance_state: If not yet set via kwds, set 385 # to default value of True. 386 387 if not hasattr(self, 'coupling_day'): 388 self.coupling_day = Field( 'coupling_day', None, units='dy' \ 389 , long_name='current coupling day') 390 else: 391 self.coupling_day = Field( 'coupling_day', self.coupling_day \ 392 , units='dy' \ 393 , long_name='current coupling day') 394 395 if not hasattr(self, 'init_with_instance_state'): 396 self.init_with_instance_state = True 397 398 if not hasattr(self, '_monlen'): 399 self._monlen = N.array([31,28,31,30,31,30,31,31,30,31,30,31]) 400 401 402 #- Set run lists: 403 404 self.runlists = {} 405 406 #+ Runlist for initializng the entire model (atmos. and ocean): 407 self.runlists['init_model'] = \ 408 [ 'qtcminit', 409 '__qtcm.wrapcall.woceaninit', 410 '__qtcm.wrapcall.woutpinit' ] 411 412 #+ Runlist for initializing the atmos. part of the model: 413 self.runlists['qtcminit'] = \ 414 [ '__qtcm.wrapcall.wparinit', 415 '__qtcm.wrapcall.wbndinit', 416 'varinit', 417 {'__qtcm.wrapcall.wtimemanager':[1,]}, 418 'atm_physics1' ] 419 420 #+ Runlist for calculating atmospheric physics at one instant: 421 self.runlists['atm_physics1'] = \ 422 [ '__qtcm.wrapcall.wmconvct', 423 '__qtcm.wrapcall.wcloud', 424 '__qtcm.wrapcall.wradsw', 425 '__qtcm.wrapcall.wradlw', 426 '__qtcm.wrapcall.wsflux' ] 427 428 #+ Runlist for coupling atmos./ocean at a coupling timestep: 429 self.runlists['atm_oc_step'] = \ 430 [ '_first_method_at_atm_oc_step', 431 {'__qtcm.wrapcall.wtimemanager':[self.coupling_day,]}, 432 {'__qtcm.wrapcall.wocean': [self.interval, self.coupling_day]}, 433 'qtcm', 434 '__qtcm.wrapcall.woutpall' ] 435 436 #+ Runlist for calculating atmos. at one atmos. timestep: 437 self.runlists['atm_step'] = \ 438 [ 'atm_physics1', #* physics assoc. w/ temp. mode T1 439 '__qtcm.wrapcall.wsland1', #* land module 440 '__qtcm.wrapcall.wadvctuv', #* advection of mom'm u1,v1,u0,v0 441 '__qtcm.wrapcall.wadvcttq', #* advection of T1,q1 442 '__qtcm.wrapcall.wdffus', #* diffusion of u1,v1,u0,v0,T1,q1 443 '__qtcm.wrapcall.wbarcl', #* baroclinic mode 444 '_bartropic_mode_at_atm_step', #* barotropic mode 445 '__qtcm.wrapcall.wvarmean' ] #* time mean (sum up variables, 446 # output as spec'd. in output.F) 447 448 #+ Runlist for calculate the atmos. barotropic mode at the 449 # barotropic timestep: 450 self.runlists['atm_bartr_mode'] = \ 451 [ '__qtcm.wrapcall.wsavebartr', #+ save u0,v0 for surf. geopot. 452 '__qtcm.wrapcall.wbartr', #+ barotropic mode 453 '__qtcm.wrapcall.wgradphis'] #+ surf. geopot. gradient 454 455 456 #- Set run lists descriptions: 457 458 self._runlists_long_names = {} 459 self._runlists_long_names['init_model'] = \ 460 'initialize the entire model, i.e., the atmosphere and ocean ' + \ 461 'components and output' 462 463 self._runlists_long_names['qtcminit'] = \ 464 'initialize the atmosphere portion of the entire model' 465 466 self._runlists_long_names['atm_physics1'] = \ 467 'calculate atmospheric physics at one instant' 468 469 self._runlists_long_names['atm_oc_step'] = \ 470 'calculate the atmosphere and ocean models at a coupling timestep' 471 472 self._runlists_long_names['atm_step'] = \ 473 'calculate the entire atmosphere at one atmosphere timestep' 474 475 self._runlists_long_names['atm_bartr_mode'] = \ 476 'calculate the atmospheric barotropic mode at the barotropic ' + \ 477 'timestep' 478 479 480 #- Check run list keys for descriptions and the actual lists 481 # match, at least on instantiation: 482 483 if len(self.runlists) != len(self._runlists_long_names): 484 raise ValueError, 'number of run lists incorrect' 485 for ikey in self.runlists.keys(): 486 if not self._runlists_long_names.has_key(ikey): 487 raise ValueError, 'description and runlist key mismatch'
488 489 490 491 492 #----------------- Class Qtcm Private Method: __del__ ----------------- 493
494 - def __del__(self):
495 """Method to execute before garbage collection. 496 497 Delete the sodir temporary directory. 498 """ 499 shutil.rmtree(self.sodir)
500 501 502 503 504 #-------------- Class Qtcm Public Method: get_qtcm_item --------------- 505
506 - def get_qtcm_item(self, key):
507 """Get field from the compiled QTCM model. 508 509 Gets the value of the variable key from the compiled QTCM 510 model. (Note that not all compiled QTCM model variables 511 are accessible to Python.) Method does nothing else (such 512 as saving that value as an object attribute). 513 514 For scalar variables, in order to know which Fortran routine 515 to use to access the item, this method needs to know the 516 type of the variable named key. For arrays, it needs to 517 know the rank of the array. It assumes that type is the 518 same as the corresponding Field object default value, and 519 rank is the same as the corresponding Field object default 520 value. 521 522 If the compiled QTCM model variable is unreadable, the 523 custom exception FieldNotReadableFromCompiledModel is thrown. 524 525 Positional Input Parameter: 526 * key: Name of QTCM variable. String. Scalar. 527 528 Output: 529 * Returns the value of the variable named key. Scalar 530 string or numeric value, or numeric array. The value 531 returned is a copy of the values, not a reference to the 532 array in memory. 533 """ 534 #- Get information about the field defined by key, using 535 # the default values: 536 537 tmpfield = Field(key) 538 value_type = tmpfield.typecode() 539 value_rank = tmpfield.rank() 540 541 542 #- Execute compiled model subroutine to extract the variable 543 # from the compiled model. Which compiled model subroutine 544 # is used for the extraction depends on value_type and 545 # value_rank. For arrays, the __qtcm.setbypy module 546 # attribute corresponding to an array (e.g., real_rank1_array) 547 # is set to None, thus deallocating the attribute in 548 # __qtcm.setbypy, at the end of extracting the value from the 549 # compiled model. Note that if the variable referenced by key 550 # is not readable in the compiled module (which is given via a 551 # module logical variable is_readable), a custom exception is 552 # raised. There is a single return line for this method. The 553 # return is set to the local variable tmp, which is what the 554 # value of the variable obtained from the compiled model is 555 # set to: 556 557 if value_type in N.typecodes['Float']: 558 if value_rank == 0: 559 tmp = copy.copy(self.__qtcm.setbypy.getitem_real(key)) 560 if not self.__qtcm.setbypy.is_readable: 561 raise FieldNotReadableFromCompiledModel, \ 562 'Compiled model variable not readable' 563 else: 564 self.__qtcm.setbypy.getitem_real_array(key) 565 if not self.__qtcm.setbypy.is_readable: 566 raise FieldNotReadableFromCompiledModel, \ 567 'Compiled model variable not readable' 568 else: 569 if value_rank == 1: 570 tmp = copy.copy(self.__qtcm.setbypy.real_rank1_array) 571 self.__qtcm.setbypy.real_rank1_array = None 572 elif value_rank == 2: 573 tmp = copy.copy(self.__qtcm.setbypy.real_rank2_array) 574 self.__qtcm.setbypy.real_rank2_array = None 575 elif value_rank == 3: 576 tmp = copy.copy(self.__qtcm.setbypy.real_rank3_array) 577 self.__qtcm.setbypy.real_rank3_array = None 578 else: 579 raise ValueError, 'bad rank value' 580 581 elif value_type in N.typecodes['Integer']: 582 if value_rank == 0: 583 tmp = copy.copy(self.__qtcm.setbypy.getitem_int(key)) 584 if not self.__qtcm.setbypy.is_readable: 585 raise FieldNotReadableFromCompiledModel, \ 586 'Compiled model variable not readable' 587 else: 588 raise ValueError, 'variable option does not exist' 589 590 elif value_type in num.typecodes['Character']: 591 if value_rank == 0: 592 tmp = self.__qtcm.setbypy.getitem_str(key) 593 if not self.__qtcm.setbypy.is_readable: 594 raise FieldNotReadableFromCompiledModel, \ 595 'Compiled model variable not readable' 596 tmp = tmp.strip() 597 else: 598 raise ValueError, 'variable option does not exist' 599 600 else: 601 raise TypeError, 'Value type does not exist' 602 603 604 #- Return value obtained from compiled model (should already 605 # be a copy): 606 607 return tmp
608 609 610 611 612 #-------------- Class Qtcm Public Method: get_qtcm1_item -------------- 613
614 - def get_qtcm1_item(self, *args, **kwds):
615 """Get field from the compiled QTCM model. 616 617 Identical to get_qtcm_item. See get_qtcm_item description. 618 """ 619 return self.get_qtcm_item(*args, **kwds)
620 621 622 623 624 #--------------- Class Qtcm Public Method: make_snapshot -------------- 625
626 - def make_snapshot(self):
627 """Make copy of current state of run session variables. 628 629 The snapshot is a copy of the variables of the current state 630 of the run session that would be used for a restart. The 631 variables are saved in a dictionary, and the dictionary is 632 saved in the attribute snapshot. As most of the variables 633 are Field objects, most of the values in the dictionary are 634 Field objects. Any variables that don't exist are just 635 left out of the dictionary. 636 """ 637 self.snapshot = {} 638 for ikey in _init_vars_keys: 639 if hasattr(self, ikey): 640 self.snapshot[ikey] = copy.deepcopy(getattr(self, ikey))
641 642 643 644 645 #------------------ Class Qtcm Private Method: _plot ------------------ 646
647 - def _plot(self, id, datafn, **kwds):
648 """Plot model field id from the data in netCDF file datafn. 649 650 See docstrings for submodule plot in package qtcm, module 651 function plot_ncdf_output. 652 """ 653 plot_ncdf_output(id, datafn, **kwds)
654 655 656 657 658 #------------------ Class Qtcm Public Method: plotm ------------------- 659
660 - def plotm(self, id, **kwds):
661 """Plot mean output for model field id. 662 663 Plotting is done using Matplotlib, and can accomodate either 664 line plots or contour plots. This method is designed for 665 a quick look at the output, and thus only a few plotting 666 parameters can be adjusted. 667 668 Positional Input Parameter: 669 * id: Name of the id of the field to plot. String. 670 671 Input Keyword Parameters: 672 * lat: Latitude range to plot. List of the min and max 673 value, or a scalar, in degrees. If the keyword is not 674 passed in, the entire range is plotted. 675 676 * lon: Longitude range to plot. List of the min and max 677 value, or a scalar, in degrees. If the keyword is not 678 passed in, the entire range is plotted. 679 680 * time: Time range to plot. List of the min and max value, 681 or a scalar, in days. If the keyword is not passed in, 682 the entire range is plotted. 683 684 * fn: Filename to write figure out to. String. If set to 685 None, plot is output to screen. 686 687 * levels: If a contour plot, the min, max, and interval 688 of the contour levels to be plotted. If set to None, 689 contour levels are calculated automatically. 690 691 * filled: If is True, and the plot is a contour plot, a 692 filled contour plot is drawn along with a horizontal 693 colorbar. If not True, a line contour plot is drawn. If 694 plot is a line plot, this keyword is ignored. 695 696 * title: Title of plot. String. 697 698 * xlabel: Label for the x-axis. 699 700 * ylabel: Label for the y-axis. 701 702 * nlatlon: For lat vs. lon contour plots, this sets the 703 approximate number of meridions and parallels to annotate. 704 Ignored for all other types of plots. Default is 8. 705 706 * tmppreview: If True, and your platform is 'darwin', 707 instead of screen display the method will write the plot 708 to a temp file and display that file using Preview. You 709 must delete the temp file yourself (it's located probably 710 in /tmp, and is created using the tempfile module default 711 settings). The variable is boolean. 712 713 The default of all input keywords is None, unless otherwise 714 noted. Depending on which series of keywords is chosen, 715 the range is chosen accordingly. Title, x-axis and y-axis 716 labels are automatically added, unless overridden by the 717 title, xlabel, and ylabel keywords. 718 719 For a lat vs. lon plot, the contour plot is superimposed 720 onto a cylindrical projection map of the Earth with continents 721 drawn and labeled meridians and parallels. The title also 722 includes the model time, and x- and y-axis labels are not 723 drawn. 724 725 Examples for model an instance of Qtcm: 726 * model.plotm('Qc', lat=1.875): A time vs. longitude contour 727 plot is made for the full range of time and longitude, 728 at the latitude 1.875 deg N, for mean precipitation. The 729 period over which the mean is taken is self.ntout. 730 731 * model.plotm('Evap', lat=1.875, lon=[100,200]): A time 732 vs. longitude contour plot of evaporation is made for the 733 longitude points between 100 and 200 degrees east, at the 734 latitude 1.875 deg N. The period over which the mean is 735 taken is self.ntout. 736 737 * model.plotm('cl1', lat=1.875, lon=[100,200], time=20): 738 A deep cloud amount vs. longitude line plot is made for 739 the longitude points between 100 and 200 degrees east, 740 at the latitude 1.875 deg N, at day 20 of the model run. 741 The period over which the mean is taken is self.ntout. 742 """ 743 datafn = os.path.join( self.outdir.value, 744 "qm_" + self.runname.value + ".nc" ) 745 self._plot(id, datafn, **kwds)
746 747 748 749 750 #------------------- Class Qtcm Public Method: qtcm ------------------- 751
752 - def qtcm(self):
753 """Run the qtcm atmosphere over a coupling interval step. 754 755 This method duplicates the functionality of the qtcm 756 subroutine in the original compiled QTCM model. It assumes 757 all fields that can be synchronized between the Python and 758 compiled QTCM model levels have been so before the calling 759 of this method. The coupling interval step is given by the 760 interval attribute. It is meant to be called once in a run 761 session, and assumes that the compiled_form is 'parts'. 762 """ 763 #- Get time changng boundary data, SST, albedo etc. Check that 764 # the number of atmos. time steps within the atmos.-ocean 765 # coupling interval is a divisor of 1 day (nastep is the 766 # number of atmospheric time steps within one air-sea coupling 767 # interval and set the value of nastep in the compiled QTCM 768 # model: 769 770 self.run_list(['__qtcm.wrapcall.wgetbnd',]) 771 nastep = self.interval.value * (86400./self.dt.value) 772 if N.mod(nastep, self.mt0.value) != 0: 773 raise ValueError, 'Fatal error: mt0*dt not a divisor of 1 day' 774 self.set_qtcm_item('nastep', int(nastep)) 775 776 777 #- The land-atmosphere coupling loop: 778 779 for self.it.value in xrange(1, self.nastep.value+1): 780 self.run_list(['atm_step',])
781 782 783 784 785 #----------------- Class Qtcm Public Method: run_list ----------------- 786
787 - def run_list(self, runlist):
788 """Run runlist of run lists and/or instance methods. 789 790 Run through list of elements that specify other run lists 791 or instance method names to execute through in runlist 792 order. Methods with private attribute names are automatically 793 mangled as needed to become executable by the method. Note 794 that if an item in runlist is an instance method, it should 795 be the entire name (after "self.") of the callable method, 796 separated by periods as appropriate. 797 798 Input Via Arguments: 799 800 * runlist: List whose elements are 1-element dictionaries 801 or strings. The list can contain a mix of 1-element 802 dictionaries and strings, or just one of those types: 803 804 + If 1-element dictionaries: The key of the dictionary 805 is a string, and is the name of the method to execute. 806 The value of the entry is a list, which gives the 807 positional input parameters, or is None, if there are 808 no input parameters. 809 810 + If strings: Each string is the name of a run list or 811 the name of the method to execute. All methods are 812 assumed to not require any positional input parameters. 813 814 The methods in runlist are called in the order given in 815 runlist. For each element, we first check if the key name 816 corresponds to the key of an entry in self.runlists. If 817 so, run_list is executed using that run list (recursive 818 call). If the key name is not a run list, we check if it 819 is a method of the instance, and if so the method is called. 820 Any other value throws an exception. 821 822 If input parameters for a method are of class Field, we 823 first try to pass the parameters into the method as is 824 (i.e., as Field object(s)). If that fails, we pass its 825 parameters in as the value of the Field object. 826 827 Examples: 828 a = Qtcm(...) 829 a.run_list( ['qtcminit' \ 830 , {'sync_set_all_py_values_to_qtcm_items':None} \ 831 , '__qtcm.driver' \ 832 , {'set_qtcm_item': ['outdir', '/home/jlin']}] ) 833 a.run_list( [{'sync_set_all_py_values_to_qtcm_items':None} \ 834 , {'__qtcm.driver':None} \ 835 , {'set_qtcm_item': ['outdir', '/home/jlin']} ) 836 a.run_list(['sync_set_all_py_values_to_qtcm_items',]) 837 """ 838 #- Comparison cases for string and dict types: 839 840 str_type = type('a') 841 dict_type = type({'a':None}) 842 843 844 #- Check runlist is a list. This prevents undetected errors 845 # if another iterable type (e.g., a string) is passed in as 846 # runlist: 847 848 if type(runlist) != type([]): 849 raise TypeError, 'runlist must be a list' 850 851 852 #- Loop through all methods in runlist: 853 854 for imethod in runlist: 855 856 857 #+ Case for runlist item being a string: Access name of 858 # run list or method, and mangle private name as needed 859 # if is a method. If item is itself the name for a 860 # run list, recursively call run_list. If is a method, 861 # recursively find the callable method, and call the 862 # method: 863 864 if type(imethod) == str_type: 865 imethodname = imethod 866 if self.runlists.has_key(imethodname): 867 if hasattr(self, imethodname): 868 raise AttributeError, \ 869 'run list cannot be same name as instance attrib.' 870 self.run_list(self.runlists[imethodname]) 871 else: 872 if imethodname[0:2] == '__': 873 imethodname = '_' + self.__class__.__name__ \ 874 + imethodname 875 imethodname_seplist = imethodname.split('.') 876 f = self 877 for subname in imethodname_seplist: 878 f = getattr(f, subname) 879 f() 880 881 882 #+ Case for runlist item being a 1-entry dictionary: 883 884 elif type(imethod) == dict_type: 885 886 #* Access name of method, and mangle private name if needed: 887 888 imethodname = imethod.keys()[0] 889 if imethodname[0:2] == '__': 890 imethodname = '_' + self.__class__.__name__ + imethodname 891 imethodname_seplist = imethodname.split('.') 892 893 894 #* Recursively find the callable method and extract the 895 # list of input parameters for the method: 896 897 f = self 898 for subname in imethodname_seplist: 899 f = getattr(f, subname) 900 inputparams = imethod.values()[0] 901 902 903 #* Call the method of the runlist item: For the case of 904 # no input parameters, call without any parameters. If 905 # the input parameters cause a TypeError while being 906 # passed into the method, assume it's because one 907 # or more of the parameters is a Field object. Replace 908 # the Field input parameters with their values, and 909 # retry calling the method: 910 911 if inputparams == None: 912 f() 913 else: 914 try: 915 f(*inputparams) 916 except TypeError: 917 inputparams_vals = [] 918 for iparam in inputparams: 919 if type(iparam) == type(_test_field): 920 inputparams_vals.append(iparam.value) 921 try: 922 f(*inputparams_vals) 923 except: 924 print "Unexpected error (A):", sys.exc_info()[0] 925 raise 926 except: 927 print "Unexpected error (B):", sys.exc_info()[0] 928 raise 929 930 931 #+ Case for runlist item of bad input type: 932 933 else: 934 raise TypeError, 'bad run_list element input type'
935 936 937 938 939 #---------------- Class Qtcm Public Method: run_session --------------- 940
941 - def run_session(self, **kwds):
942 """Run model session. 943 944 Run the QTCM model in a model session. A model session is 945 defined as a "complete" model run, at the end of which 946 restart files are written and the Python Qtcm instance is 947 synchronized to the Fortran model. 948 949 The following tasks are done by this method, in this order: 950 * The attribute snapshot, if present, is deleted. 951 * The compiled model is synchronized with the Python model 952 instance so that any Python attribute that corresponds to a 953 compiled model Python-changable variable is set to the value 954 of the Python attribute. If the compiled model variable is 955 not accessible, nothing is done, and the Python attribute is 956 left unchanged. 957 * The model is run until lastday. Restart and output files 958 are written accordingly. 959 * The Python-changable model attributes are set to the compiled 960 model's values. If the compiled model variable is not 961 accessible, an exception is raised. 962 * A snapshot of the Python attributes that store the variables 963 that would be used in a restart run is taken and stored as 964 the attribute snapshot. 965 966 Input Via Keywords Arguments: 967 968 * cont: If set to False, the run session is not a continuation 969 of the previous run, but a new run session. If set to 970 True, the run session is a continuation of the previous 971 run, and whatever the field values are in the Python 972 instance are used in the model (if init_with_instance_state 973 is True). If set to an integer greater than zero, the run 974 session is a continuation just like cont=True, but the 975 value set to cont is used for lastday and replaces 976 lastday.value. This keyword has no effect if 977 compiled_form='full'. Default is False. (Note whatever 978 cont is set to in this method call is stored as attribute 979 _cont, in case you really need to access it elsewhere.) 980 """ 981 #- Set default values for arguments and keywords: 982 983 if kwds.has_key('cont'): 984 cont = kwds['cont'] 985 else: 986 cont = False 987 self._cont = cont 988 989 if type(cont) == type(1): 990 self.lastday.value = cont 991 elif type(cont) == type(False): 992 pass 993 else: 994 raise TypeError, 'cont keyword must be integer or boolean' 995 996 997 # @@@this option doesn't work right. only python stdout is 998 # written while compiled code generate stdout seems to go to 999 # /dev/null. and the file isn't closed at the end of this 1000 # method executing. stdout doesn't come back to screen. 1001 # 1002 # * stdout: String. If set to: 1003 # 'screen': Writes to screen only. 1004 # 'logfile': Writes to logfile only; file opened in mode "a+". 1005 # * logfile: String. Name of logfile. Set to os.devnull to send it 1006 # to /dev/null (or equivalent in your OS). 1007 # if kwds.has_key('stdout'): 1008 # stdout = kwds['stdout'] 1009 # else: 1010 # stdout = 'screen' 1011 # 1012 # if kwds.has_key('logfile'): 1013 # logfile = kwds['logfile'] 1014 # else: 1015 # logfile = 'stdout.log' 1016 # 1017 # 1018 # #- Open stdout redirect, if needed: 1019 # 1020 # if stdout=='screen': 1021 # pass 1022 # elif stdout=='logfile': 1023 # all_stdout_log_file = open(logfile, 'a+') 1024 # os.dup2(all_stdout_log_file.fileno(), 1) 1025 1026 1027 #- Delete snapshot attribute, if present. Set the values 1028 # of Python-setable variables in the compiled code to the 1029 # value at the Python-level: 1030 1031 if hasattr(self, 'snapshot'): del self.snapshot 1032 self.sync_set_qtcm_items_to_all_py_values() 1033 1034 1035 #- Run model: 1036 1037 if self.compiled_form == 'full': 1038 self.run_list(['__qtcm.driver',]) 1039 elif self.compiled_form == 'parts': 1040 self.run_list(['__run_parts',]) 1041 else: 1042 raise ValueError, 'Compiled form not recognized' 1043 1044 1045 #- Set the values of Python-setable variables at the Python-level 1046 # to the values in the compiled code. Take snapshot of model 1047 # variables that would be used in another session: 1048 1049 self.sync_set_all_py_values_to_qtcm_items() 1050 self.make_snapshot()
1051 1052 1053 #@@@this doesn't seem to work right, so comment out. 1054 # #- Close stdout redirect, if needed: 1055 # 1056 # if stdout=='screen': 1057 # pass 1058 # elif stdout=='logfile': 1059 # all_stdout_log_file.close() 1060 1061 1062 1063 1064 #-------------- Class Qtcm Public Method: set_qtcm_item --------------- 1065
1066 - def set_qtcm_item(self, *args):
1067 """Set Python-accessible compiled QTCM model fields. 1068 1069 Sets the value in the compiled QTCM model as well as at the 1070 Python level. If no value given, the default value is used. 1071 When the compiled model variable is set, a copy of the 1072 Python value input via the parameter list is passed to the 1073 Fortran model, not a reference. 1074 1075 If this method is called with a single positional input 1076 argument, and that argument is a string, the compiled QTCM 1077 model variable is set to the default value of the Python 1078 counterpart of that same name. If the single positional 1079 argument is a Field variable, the compiled QTCM model 1080 variable is set to that Field variable (the id and value 1081 attributes of the Field variable thus corresponding to the 1082 name and value of the compiled QTCM variable, respectively). 1083 1084 Positional Input Parameters (for 1 argument): 1085 * Name of QTCM variable (scalar string) or a Field variable. 1086 1087 Positional Input Parameters (for 2 arguments): 1088 * key: Name of QTCM variable. String. Scalar. 1089 * value_in: Value of variable or a Field object. Scalar 1090 or numeric array. If you want to set an array to a single 1091 quantity, value can be a scalar. Type of value must be 1092 the same as the type of Field(key), which is the same as 1093 in the compiled QTCM model; the routine doesn't check for 1094 this, however, as the compiled will return a fatal error 1095 if this mismatch happens. 1096 1097 Some compiled QTCM model variables are not ready to be set. 1098 An example is a compiled QTCM model pointer variable prior 1099 to the pointer being associated with a target (this would 1100 result in a bus error). In such cases, this method will 1101 throw a FieldNotReadableFromCompiledModel exception, nothing 1102 will be set in the compiled QTCM model, and the Python 1103 counterpart variable (if it previously existed) would be 1104 left unchanged. Otherwise, both the compiled QTCM model 1105 variable and its Python attribute counterpart are set by 1106 the method to the same value, overwriting any previous 1107 values held by either. 1108 """ 1109 #- Set key and value based on key and value_in. After this 1110 # section, key will be a string and value_in either a scalar 1111 # or a numeric array: 1112 1113 if len(args) == 1: 1114 if type(args[0]) == type(_test_field): 1115 key = args[0].id 1116 value_in = args[0].value 1117 else: 1118 key = args[0] 1119 value_in = Field(key).value #- This sets value to the default 1120 elif len(args) == 2: 1121 key = args[0] 1122 value_in = args[1] 1123 if type(value_in) == type(_test_field): #- value_in a Field obj. 1124 if key != value_in.id: 1125 raise ValueError, 'inconsistent input args.' 1126 value_in = args[1].value 1127 else: 1128 raise ValueError, 'set_qtcm_item uses only 1 or 2 args' 1129 1130 1131 #- Make value a copy of the input value, not a reference 1132 # assignment. Find type and rank of value. Use value 1133 # instead of value_in for rest of method: 1134 1135 value = copy.copy(value_in) 1136 value_rank = N.rank(value) 1137 if value_rank == 0: 1138 value_dtype = type(value) 1139 else: 1140 value_dtype = num.typecode(value) 1141 1142 # (Here I provide code to check type of value is good, if you 1143 # want to do this at the Python level. I commented it out, 1144 # however, for speed gains, since the compiled QTCM model 1145 # already checks this, returning a fatal error if there is a 1146 # mismatch): 1147 # 1148 # field_default_dtype = Field(key).typecode() 1149 # if ( (value_dtype in N.typecodes['Integer']) and \ 1150 # (field_default_dtype not in N.typecodes['Integer']) ) or \ 1151 # ( (value_dtype in N.typecodes['Float']) and \ 1152 # (field_default_dtype not in N.typecodes['Float']) ) or \ 1153 # ( (value_dtype in num.typecodes['Character']) and \ 1154 # (field_default_dtype not in num.typecodes['Character']) ): 1155 # raise TypeError, 'Type of value does not match default' 1156 1157 1158 #- Set compiled QTCM variable using methods chosen based 1159 # upon upon field rank (we use field rank to enable you to 1160 # set all values in an array to the same value). If 1161 # FieldNotReadableFromCompiledModel is returned, nothing is 1162 # set in the compiled QTCM model and the exception is sent 1163 # to continue upwards. Set Python attribute accordingly: 1164 1165 field_rank = Field(key).rank() 1166 1167 1168 #+ For rank of the field 0 (i.e. a scalar). The first line 1169 # after the "try" is to just see if the variable can be set: 1170 1171 if field_rank == 0: 1172 try: 1173 tmp = self.get_qtcm_item(key) 1174 self._set_qtcm_scalar_item_in_model(key, value) 1175 setattr( self, key, Field(key, value) ) 1176 except FieldNotReadableFromCompiledModel: 1177 raise FieldNotReadableFromCompiledModel, \ 1178 'Compiled model variable not writeable' 1179 except: 1180 print "Unexpected error:", sys.exc_info()[0] 1181 raise 1182 1183 1184 #+ For rank of the field non-zero (i.e., an array). Note that 1185 # in this section, we ensure that the Field value is also an 1186 # array as in the compiled model: 1187 1188 else: 1189 try: 1190 ashape = self._set_qtcm_array_item_in_model(key, value) 1191 if value_rank == 0: 1192 valarr = N.empty(ashape, dtype=value_dtype) 1193 valarr.fill(value) 1194 else: 1195 valarr = value 1196 setattr( self, key, Field(key, valarr) ) 1197 except FieldNotReadableFromCompiledModel: 1198 raise FieldNotReadableFromCompiledModel, \ 1199 'Compiled model variable not writeable' 1200 except: 1201 print "Unexpected error:", sys.exc_info()[0] 1202 raise
1203 1204 1205 1206 1207 #-------------- Class Qtcm Public Method: set_qtcm1_item -------------- 1208
1209 - def set_qtcm1_item(self, *args, **kwds):
1210 """Set Python-accessible compiled QTCM model fields. 1211 1212 Identical to set_qtcm_item. See set_qtcm_item description. 1213 """ 1214 return self.set_qtcm_item(*args, **kwds)
1215 1216 1217 1218 1219 #------ Class Qtcm Public Method: sync_set_py_values_to_snapshot ------ 1220
1221 - def sync_set_py_values_to_snapshot(self, snapshot=None):
1222 """Set Python attributes to snapshot values. 1223 1224 The snapshot is copies of the variables of the current state 1225 of the run session that would be used for a restart. This 1226 method sets the Python attributes corresponding to the 1227 snapshot variables to the snapshot values. It does not set 1228 anything on the compiled QTCM model side. 1229 1230 Keyword Input Parameter: 1231 * snapshot: The snapshot (a dictionary, following the rules 1232 of method make_snapshot) that is the source of the value 1233 for the syncronization. If set to None, the instance 1234 attribute snapshot is used. Default is None. 1235 """ 1236 if snapshot == None: 1237 for ikey in self.snapshot.keys(): 1238 setattr(self, ikey, self.snapshot[ikey]) 1239 else: 1240 for ikey in snapshot.keys(): 1241 setattr(self, ikey, snapshot[ikey])
1242 1243 1244 1245 1246 #--- Class Qtcm Public Method: sync_set_qtcm_items_to_all_py_values --- 1247
1249 """Set QTCM items in compiled model to all Python-level values. 1250 1251 Synchronize so that any Python attribute that corresponds 1252 to a compiled model Python-changable variable is set, on 1253 both the Python attribute side and the compiled model side, 1254 to the value of the Python attribute. Note this method 1255 only sets attributes that are already defined in the object; 1256 it does not create new attributes. If a compiled QTCM model 1257 variable is not ready to be set (e.g., a pointer variable 1258 is not yet associated, that variable is not set in the 1259 compiled QTCM model, and its Python counterpart is left 1260 unchanged. 1261 """ 1262 for ikey in self._qtcm_fields_ids: 1263 if hasattr(self, ikey): 1264 try: 1265 self.set_qtcm_item(getattr(self, ikey)) 1266 except FieldNotReadableFromCompiledModel: 1267 pass 1268 except: 1269 print "Unexpected error:", sys.exc_info()[0] 1270 raise
1271 1272 1273 1274 1275 #--- Class Qtcm Public Method: sync_set_all_py_values_to_qtcm_items --- 1276
1278 """Set all Python-level attributes to QTCM compiled model values. 1279 1280 Synchoronize so that any Python attribute that corresponds 1281 to a compiled model Python-changable variable is set to the 1282 value of the QTCM compiled variable. Note this method goes 1283 through all items listed in self._qtcm_fields_ids and sets 1284 those values as object attributes. 1285 1286 If a compiled QTCM model variable is not ready to be read 1287 (e.g., a pointer variable is not yet associated, a 1288 FieldNotReadableFromCompiledModel exception is raised, 1289 because for the situations where this method is called 1290 (usually after the model has run for some time), that 1291 situation should not occur. 1292 """ 1293 for ikey in self._qtcm_fields_ids: 1294 try: 1295 setattr( self, ikey, Field(ikey, self.get_qtcm_item(ikey)) ) 1296 except FieldNotReadableFromCompiledModel: 1297 raise FieldNotReadableFromCompiledModel, \ 1298 'Compiled model variable not readable' 1299 except: 1300 print "Unexpected error:", sys.exc_info()[0] 1301 raise
1302 1303 1304 1305 1306 #------ Part of Run Private Method: _first_method_at_atm_oc_step ------ 1307
1309 """First method executed at the atmosphere-coupling timestep. 1310 1311 This is a private method. If you wish to add more computations 1312 at the beginning of the atmosphere-ocean coupling timestep, 1313 overload more_first_method_at_atm_oc_step. 1314 """ 1315 self.interval.value = self.get_qtcm_item('interval') 1316 self.more_first_method_at_atm_oc_step()
1317 1318 1319 1320 1321 #----- Part of Run Public Method: more_first_method_at_atm_oc_step ---- 1322
1324 """More of first method executed at the atmo.-coupling timestep. 1325 1326 This gives an easy way to change the model at the 1327 atmosphere-coupling timestep, after the update of the interval 1328 attribute: Just overload this method. 1329 """ 1330 pass
1331 1332 1333 1334 1335 #------- Part of Run Private Method: _bartropic_mode_at_atm_step ------ 1336
1338 """Calculate the atmos. barotropic mode at atmos. timestep. 1339 1340 The calculation is made at self.it timestep for the atmosphere, 1341 which is the time of day in terms of atmospheric timesteps. 1342 It assumes that the compiled_form is 'parts'. 1343 """ 1344 if N.mod(self.it.value, self.mt0.value) == 0: 1345 self.run_list(['atm_bartr_mode',])
1346 1347 1348 1349 1350 #----------------- Part of Run Public Method: varinit ----------------- 1351
1352 - def varinit(self):
1353 """Initialize model variables in a run session. 1354 1355 Method duplicates the functionality of the Fortran QTCM1 1356 subroutine varinit, but with changes that will enable us 1357 to handle restarts at the Python-level in a dynamic way. 1358 This method only works with compiled_form set to 'parts'. 1359 1360 If init_with_instance_state is False, this method just 1361 executes the compiled Fortran QTCM model varinit subroutine, 1362 and thus run_session initialization follows the rules set 1363 by all the standard QTCM model input parameters (e.g., the 1364 sign of day0, month0, and year0, the value of the mrestart 1365 flag, etc.). If init_with_instance_state is True, variable 1366 initialization uses the algorithm described below. 1367 1368 First, all prognostic pointer variables are associated. 1369 Next, initialization of prognostic variables and right-hand 1370 sides to default values (which for most of the variables 1371 is 0) occur for the cases where the corresponding Python 1372 attribute is not defined. If the corresponding Python 1373 attribute is defined, the compiled QTCM model variable is 1374 set to the value that the attribute already has (the mrestart 1375 flag, given as the instance attribute mrestart, is ignored). 1376 The exception is day0, month0, and year0, which are overwritten 1377 with values derived from dateofmodel to set the run to start 1378 the day after dateofmodel. If dateofmodel is less than or 1379 equal to 0, day0, month0, and year0 are set to their 1380 respective instance values, if valid (for invalid values, 1381 day0, month0, and year0 are all set to 1). 1382 1383 Note for init_with_instance_state True or False, at the end 1384 of this method, day0, month0, and year0, may all be changed, 1385 and dateofmodel may be inconsistent with will be updated 1386 to match the values of day0, month0, and year0. 1387 """ 1388 #- Dictionary of all prognostic variables and right-hand 1389 # sides that could be initialized and their default initial 1390 # values (note that these default values should be the same 1391 # as given in the first part of the Fortran QTCM1 subroutine 1392 # varinit, prior to the mrestart test, but there is no check 1393 # in this method for this: 1394 1395 init_dict = copy.copy(_init_prog_dict) 1396 1397 1398 #- If do not initialize QTCM variables with the instance 1399 # state: Run __qtcm.wrapcall.wvarinit, set all Python values 1400 # of these initialized variables to the compiled QTCM model 1401 # value, recalculate dateofmodel and put updated value into 1402 # both the Python and compiled model sides: 1403 1404 if not self.init_with_instance_state: 1405 self.run_list(['__qtcm.wrapcall.wvarinit',]) 1406 1407 update_list = init_dict.keys() + ['day0', 'month0', 'year0'] 1408 for ikey in update_list: 1409 setattr(self, ikey, Field(ikey, self.get_qtcm_item(ikey)) ) 1410 1411 self.dateofmodel.value = self.year0.value*10000 \ 1412 + self.month0.value*100 \ 1413 + self.day0.value 1414 self.set_qtcm_item(self.dateofmodel) 1415 1416 1417 #- If do initialize QTCM variables with the instance state: 1418 1419 else: 1420 #+ Associate pointer variables: 1421 1422 if not self._cont: 1423 self.run_list(['__qtcm.varptrinit',]) 1424 1425 1426 #+ Calculate WD at 70% saturation and make that initial 1427 # value as part of init_dict. STYPE equals 0 for ocean, 1428 # 1 for forest, 2 for grass, and 3 for desert. WD0 is 1429 # indexed in Fortran from 0 to NSTYPE-1, which matches 1430 # Python indexing so there doesn't need to be any index 1431 # shifting below: 1432 1433 tmpstype = self.get_qtcm_item('STYPE') 1434 tmpwd0 = self.get_qtcm_item('WD0') 1435 tmpwd = N.choose(tmpstype.astype(int), tmpwd0) * 0.7 1436 init_dict['WD'] = tmpwd 1437 1438 1439 #+ Initialize all prognostic variables and right hand 1440 # sides if the attributes do not currently exist: 1441 1442 for ikey in init_dict.keys(): 1443 if not hasattr(self, ikey): 1444 self.set_qtcm_item(ikey, init_dict[ikey]) 1445 else: 1446 self.set_qtcm_item(getattr(self, ikey)) 1447 1448 1449 #+ Calculate the day0, month0, year0 values for the day 1450 # after dateofmodel. Checks that the day after 1451 # dateofmodel is correct, i.e., if the next day is in 1452 # the next month, the month, day, and year are changed 1453 # as needed: 1454 1455 yearr = self.dateofmodel.value / 10000 1456 monthr = N.mod(self.dateofmodel.value, 10000) / 100 1457 dayr = N.mod(self.dateofmodel.value, 100) + 1 1458 1459 if self._monlen[monthr-1] < dayr: 1460 dayr = 1 1461 monthr = monthr + 1 1462 if monthr > 12: 1463 monthr = 1 1464 yearr = yearr + 1 1465 1466 1467 #+ Set day0, month0, year0. If dateofmodel is > zero, 1468 # change day0, etc. so the model will run the day after 1469 # dateofmodel. If dateofmodel is <= 0, keep day0, etc. 1470 # unchanged if day0, etc. are valid (for invalid cases, 1471 # set day0, etc. all to 1). 1472 1473 if self.dateofmodel.value > 0: 1474 self.year0.value = yearr 1475 self.month0.value = monthr 1476 self.day0.value = dayr 1477 else: 1478 if (self.day0.value < 1) or (self.day0.value > 31): 1479 self.day0.value = 1 1480 if (self.month0.value < 1) or (self.month0.value > 12): 1481 self.month0.value = 1 1482 if self.year0.value < 1: 1483 self.year0.value = 1 1484 1485 #+ Update dateofmodel with the changed year0, month0, 1486 # day0 values: 1487 1488 self.dateofmodel.value = self.year0.value*10000 \ 1489 + self.month0.value*100 \ 1490 + self.day0.value 1491 1492 1493 #+ Update the QTCM side with the changed Python date 1494 # attributes: 1495 1496 update_list = ['day0', 'month0', 'year0', 'dateofmodel'] 1497 for ikey in update_list: 1498 self.set_qtcm_item(getattr(self, ikey))
1499 1500 1501 1502 1503 #--------------- Class Qtcm Private Method: __run_parts --------------- 1504
1505 - def __run_parts(self):
1506 """Run parts model starting at the atmos.-oc. coupling level. 1507 1508 This method duplicates the functionality of the driver 1509 subroutine in the original compiled QTCM model. It assumes 1510 all fields that can be synchronized between the Python and 1511 compiled QTCM model levels have been so before the calling 1512 of this method. It is meant to be called once in a run 1513 session, and assumes that the compiled_form is 'parts'. 1514 """ 1515 #- Initialize variables. If the run session is not a 1516 # continuation of the previous run, initialize the model 1517 # using the init_model run list. Otherwise, do not do 1518 # that initialization but just varinit which mainly sets 1519 # the date-related fields for continuation run sessions: 1520 1521 interval = self.interval.value 1522 lastday = self.lastday.value 1523 if not self._cont: 1524 self.run_list(self.runlists['init_model']) 1525 else: 1526 self.run_list(['varinit',]) 1527 1528 1529 #- Main loop of ocean-atmosphere coupling. Note that with 1530 # the current way of handling netCDF output of the dayofmodel 1531 # in the compiled QTCM model, startday has to be 1 or the 1532 # timemanager function will feed the wrong value to the output 1533 # routine, resulting in totally messed up output: 1534 1535 startday = 1 1536 endday = lastday+interval 1537 1538 for self.coupling_day.value in xrange(startday, endday, interval): 1539 self.run_list(self.runlists['atm_oc_step']) 1540 print 'Driver: Running for %i days at model date %i ' \ 1541 % (self.coupling_day.value, self.get_qtcm_item('dateofmodel')) 1542 1543 1544 #- Write restart file: 1545 1546 self.run_list(['__qtcm.wrapcall.woutrestart',]) 1547 1548 1549 #- Because startday has to be at 1, as noted in the comment 1550 # earlier where startday is set, if this is a continuation 1551 # run, we need to rewrite the values of the time output file 1552 # variable in both the instantaneous and mean output files 1553 # to make it correct. Note that the compiled QTCM model 1554 # output algorithm will output a first time value of 0; this 1555 # method checks that that is the case. This section also 1556 # deals with the peculiar case where oldvalue[0] will not be 1557 # a scalar, and when oldvalue is a short integer and Python 1558 # will not automatically make a long to short int conversion: 1559 1560 if self._cont: 1561 testoutdir = self.get_qtcm_item('outdir') 1562 if self.outdir.value != testoutdir: 1563 raise ValueError, 'bad outdir value for contiguous run' 1564 1565 path = self.outdir.value 1566 suffix = self.runname.value 1567 fn_inst = os.path.join( path, "qi_" + suffix + ".nc" ) 1568 fn_mean = os.path.join( path, "qm_" + suffix + ".nc" ) 1569 1570 for ifn in [fn_inst, fn_mean]: 1571 fileobj = S.NetCDFFile(ifn, mode='r+') 1572 oldvalue = fileobj.variables['time'].getValue() 1573 varshape = N.shape(oldvalue) 1574 if len(varshape) != 1: raise ValueError, 'bad time shape' 1575 if oldvalue[0] != 0: 1576 if N.squeeze(oldvalue[0]) != 0: 1577 raise ValueError, 'bad time first value' 1578 newvalue = N.arange(varshape[0], dtype=num.typecode(oldvalue)) 1579 fileobj.variables['time'].assignValue(newvalue) 1580 fileobj.close()
1581 1582 1583 1584 1585 #- Class Qtcm Private Method: _set_all_qtcm_scalar_fields_to_defaults - 1586
1588 """Set all scalar QTCM fields to their default values. 1589 1590 Sets the values in the compiled QTCM model as well as at 1591 the Python level to their default values. Scalar values 1592 are defined as those with rank 0. 1593 """ 1594 for ikey in self._qtcm_fields_ids: 1595 if N.rank(Field(ikey).value) == 0: 1596 self.set_qtcm_item(ikey)
1597 1598 1599 1600 1601 #--------- Class Qtcm Private Method: _set_compiled_qtcm_attr --------- 1602
1603 - def _set_compiled_qtcm_attr(self):
1604 """Set compiled QTCM attribute in Qtcm instance. 1605 1606 This method makes a copy of the needed compiled shared 1607 object file in a unique hidden subdirectory of the current 1608 working directory, imports that .so file, and sets that 1609 imported shared object library to the compiled QTCM attribute 1610 self.__qtcm. 1611 """ 1612 #- Create temporary shared object directory for instance, 1613 # self.sodir. Extract the random part of the sodir 1614 # directory name, put a '_' in front of it, and set to local 1615 # variable. Note how the sodir prefix is hard-wired in: 1616 1617 self.sodir = tempfile.mkdtemp(prefix='qtcm_sodir_') 1618 1619 1620 #- Selected name of the shared object library of interest 1621 # based on the compiled_form attribute: 1622 1623 if self.compiled_form == 'full': 1624 soname = '_qtcm_full_365' 1625 elif self.compiled_form == 'parts': 1626 soname = '_qtcm_parts_365' 1627 else: 1628 raise ValueError, 'Compiled form not recognized' 1629 1630 1631 #- Assuming that the directory path of the original shared 1632 # object is the same as that for the package_version module, 1633 # the original shared object is copied to the temporary 1634 # directory, and that copy (which will now be unique for 1635 # this instance, if the temporary directory is put at the 1636 # beginning of sys.path) is loaded and set to __qtcm. The 1637 # shared object name is removed from sys.modules, to prevent 1638 # future instances of this class setting __qtcm to the 1639 # modules in other instance sodirs. To make sure nothing 1640 # bad happens, at the end I check that the path for the 1641 # __qtcm attribute is sodir: 1642 1643 origsofile = \ 1644 os.path.join( os.path.split(_package_version.__file__)[0] \ 1645 , soname + '.so' ) 1646 shutil.copy2(origsofile, self.sodir) 1647 sys.path.insert(0, self.sodir) 1648 self.__qtcm = __import__(soname) 1649 sys.path.remove(self.sodir) 1650 del sys.modules[soname] 1651 1652 if os.path.split(self.__qtcm.__file__)[0] != self.sodir: 1653 raise ValueError, 'Incorrect import of .so library'
1654 1655 1656 1657 1658 #------ Class Qtcm Private Method: _set_qtcm_array_item_in_model ------ 1659
1660 - def _set_qtcm_array_item_in_model(self, key, value):
1661 """Set Python-accessible QTCM array settings in compiled model. 1662 1663 Sets the value of arrays in the compiled QTCM model only. 1664 Custom exception FieldNotReadableFromCompiledModel is raised 1665 if the compiled QTCM model variable is not readable/writable. 1666 1667 Positional Input Parameters: 1668 * key: Name of QTCM variable. String. Scalar. 1669 1670 * value: Value of variable. Must be numeric scalar or 1671 real numeric array. If a scalar, all values in the array 1672 named key are set to that scalar value. If value cannot 1673 be mapped onto the compiled QTCM model array successfully, 1674 you'll receive a bus error or other unexpected error. 1675 1676 Output: 1677 * Sets value into the compiled QTCM model array variable, 1678 and also returns the shape of the array that was set. 1679 """ 1680 #- Set type and rank information: 1681 1682 default_type = Field(key).typecode() 1683 value_type = num.typecode(value) 1684 field_rank = Field(key).rank() 1685 1686 1687 #- Test the value_type and default type are the same for 1688 # floating point arrays: 1689 1690 if (value_type in N.typecodes['Float']) and \ 1691 (default_type not in N.typecodes['Float']): 1692 raise TypeError, 'value type different from default for key' 1693 1694 1695 #- Use the Fortran routine getitem_real_array to create the 1696 # memory needed and to see if the compiled QTCM model variable 1697 # is readable. Raise an exception if it is not readable. 1698 # Set the Fortran module variable real_rank?_array to value, 1699 # write that to the compiled QTCM model variable, and deallo- 1700 # cate module variable real_rank?_array: 1701 1702 if value_type in N.typecodes['Float']: 1703 if field_rank == 1: 1704 self.__qtcm.setbypy.getitem_real_array(key) 1705 if not self.__qtcm.setbypy.is_readable: 1706 self.__qtcm.setbypy.real_rank1_array = None 1707 raise FieldNotReadableFromCompiledModel, \ 1708 'Compiled model variable not readable' 1709 else: 1710 if N.rank(value) == 0: 1711 ashape = N.shape(self.__qtcm.setbypy.real_rank1_array) 1712 tmpa = N.zeros(ashape, float) 1713 tmpa.fill(value) 1714 self.__qtcm.setbypy.real_rank1_array = tmpa 1715 else: 1716 ashape = N.shape(value) 1717 self.__qtcm.setbypy.real_rank1_array = value 1718 self.__qtcm.setbypy.setitem_real_array(key) 1719 self.__qtcm.setbypy.real_rank1_array = None 1720 1721 elif field_rank == 2: 1722 self.__qtcm.setbypy.getitem_real_array(key) 1723 if not self.__qtcm.setbypy.is_readable: 1724 self.__qtcm.setbypy.real_rank2_array = None 1725 raise FieldNotReadableFromCompiledModel, \ 1726 'Compiled model variable not readable' 1727 else: 1728 if N.rank(value) == 0: 1729 ashape = N.shape(self.__qtcm.setbypy.real_rank2_array) 1730 tmpa = N.zeros(ashape, float) 1731 tmpa.fill(value) 1732 self.__qtcm.setbypy.real_rank2_array = tmpa 1733 else: 1734 ashape = N.shape(value) 1735 self.__qtcm.setbypy.real_rank2_array = value 1736 self.__qtcm.setbypy.setitem_real_array(key) 1737 self.__qtcm.setbypy.real_rank2_array = None 1738 1739 elif field_rank == 3: 1740 self.__qtcm.setbypy.getitem_real_array(key) 1741 if not self.__qtcm.setbypy.is_readable: 1742 self.__qtcm.setbypy.real_rank3_array = None 1743 raise FieldNotReadableFromCompiledModel, \ 1744 'Compiled model variable not readable' 1745 else: 1746 if N.rank(value) == 0: 1747 ashape = N.shape(self.__qtcm.setbypy.real_rank3_array) 1748 tmpa = N.zeros(ashape, float) 1749 tmpa.fill(value) 1750 self.__qtcm.setbypy.real_rank3_array = tmpa 1751 else: 1752 ashape = N.shape(value) 1753 self.__qtcm.setbypy.real_rank3_array = value 1754 self.__qtcm.setbypy.setitem_real_array(key) 1755 self.__qtcm.setbypy.real_rank3_array = None 1756 1757 else: 1758 raise ValueError, 'bad rank value' 1759 1760 elif value_type in N.typecodes['Integer']: 1761 raise TypeError, 'array type not yet supported' 1762 1763 elif value_type in num.typecodes['Character']: 1764 raise TypeError, 'array type not yet supported' 1765 1766 else: 1767 raise TypeError, 'value is of unsupported type' 1768 1769 1770 #- Return the shape of the array that was set: 1771 1772 return ashape
1773 1774 1775 1776 1777 #----- Class Qtcm Private Method: _set_qtcm_scalar_item_in_model ------ 1778
1779 - def _set_qtcm_scalar_item_in_model(self, key, value):
1780 """Set Python scalar variable in the compiled QTCM model. 1781 1782 Sets value of Python scalar variable key in the compiled 1783 QTCM model. Nothing else is done (e.g., nothing is set on 1784 the Python side). Exception FieldNotReadableFromCompiledModel 1785 is raised if the compiled QTCM model variable is not 1786 readable/writable. 1787 1788 Positional Input Parameters: 1789 * key: Name of QTCM variable. String. Scalar. 1790 * value: Value of variable. String or numeric value. Must be 1791 a scalar. 1792 """ 1793 default_type = Field(key).typecode() 1794 value_type = num.typecode(value) 1795 1796 if value_type in N.typecodes['Float']: 1797 if default_type not in N.typecodes['Float']: 1798 raise TypeError, 'value type different from default for key' 1799 tmp = copy.copy(self.__qtcm.setbypy.getitem_real(key)) 1800 if not self.__qtcm.setbypy.is_readable: 1801 raise FieldNotReadableFromCompiledModel, \ 1802 'Compiled model variable not readable' 1803 self.__qtcm.setbypy.setitem_real(key, value) 1804 1805 elif value_type in N.typecodes['Integer']: 1806 if default_type not in N.typecodes['Integer']: 1807 raise TypeError, 'value type different from default for key' 1808 tmp = copy.copy(self.__qtcm.setbypy.getitem_int(key)) 1809 if not self.__qtcm.setbypy.is_readable: 1810 raise FieldNotReadableFromCompiledModel, \ 1811 'Compiled model variable not readable' 1812 self.__qtcm.setbypy.setitem_int(key, value) 1813 1814 elif value_type in num.typecodes['Character']: 1815 if default_type not in num.typecodes['Character']: 1816 raise TypeError, 'value type different from default for key' 1817 tmp = self.__qtcm.setbypy.getitem_str(key) 1818 if not self.__qtcm.setbypy.is_readable: 1819 raise FieldNotReadableFromCompiledModel, \ 1820 'Compiled model variable not readable' 1821 self.__qtcm.setbypy.setitem_str(key, value) 1822 1823 else: 1824 raise TypeError, 'value is of unsupported type'
1825 1826 1827 1828 1829 #-------------------------- Main: Test Module ------------------------- 1830 1831 #- Execute doctest if module is run from command line: 1832 1833 if __name__ == "__main__": 1834 """Test the module. 1835 1836 Note: To help ensure that module testing of this file works, the 1837 parent directory to the current directory is added to sys.path. 1838 """ 1839 import doctest, sys, os 1840 sys.path.append(os.pardir) 1841 doctest.testmod(sys.modules[__name__]) 1842 1843 1844 1845 1846 # ===== end file ===== 1847