AJMR-Python-Baird/LAVegMOD_DM/model.py

1436 lines
62 KiB
Python

#!/usr/bin/env python
##\file
##\brief Top level file for the ecological modeling.
##\detail This file describes the model proper and the major
# model-specific coding elements used to build this model.
##\section Includes Included Libraries
# The file makes use of the following libraries
# - Standard Python Libraries
# - copy
# - exceptions
# - itertools
# - re
# - StringIO
# - sys
# - time
# - Third Party Libraries
# - pandas version 0.13.1
# - xlrd
# - Model defined libraries
# - config
# - event
# - function
# - landscape
# STD Python modules
import copy
import csv
import exceptions
import itertools
import math
import numpy
from collections import OrderedDict
import re
import StringIO
import sys
import time
# Third party modules
import pandas
import xlrd
# This model's modules
import config
import event
import landscape
import function
##\class SpeciesModel
##\brief Base class for all species models.
##\detail This is a generic base class defining the functionality
# of a single species model for a single location in the landscape.
# All species models should inherit
# from this class to make sure they get the basic elements.
##\role{Ecology/Machinery}
class SpeciesModel(object):
##\brief A reference to the model Params object
params = None
##\brief A reference to the model DispersalModel object
dspModel = None
## Class constructor.
#\param [in] self The object's reference to itself.
#\param [in] index The species index.
#\param [in] abr The species USDA alpha-numeric vegetation code.
#\param [in] name The full species name.
#\param [in] modelType The type of a model.
#\param [in] cover The fraction of model cell that is covered by a species.
#\param [in] loc The x,y location of a cell. Given as the row,column index with in the model grid.
def __init__(self, index=0, name='', abr='', modelType='', cover=0, cellID=0):
##\brief Species index
##\details An integer that is a unique numerical species index.
## - Type: integer
## - Range: [0, inf)
self.index = index
##\brief The long name for a species
##\details A string used to make output pretty
## - Type: string
self.name = name
##\brief Species code
##\details A string abbreviation for the species. The value is the species' USDA alpha-numeric vegetation code.
# This is an important member value in the model. It is used as the key in the dictionary that stores the list
# of species in a patch.
## - Type: string
self.abr = abr
##\brief The model type
##\details A string containing the model type.
##\n
## This member should one of the following values (as a string):\n
# - BarrierIslandSpeciesModel
# - EmergentWetlandModel
# - UplandForestModel
# - SAVModel
# - NullModel
##\n
self.modelType = modelType
##\brief The area occupied by a species
##\details This is the fraction of a patch covered by a species.
## - Type: float
## - Range: [0, inf)
self.cover = cover
##\brief Species Location
##\details This is the location of a species. It is stored as a the index of a cell.
## - Type: int
self.cellID = cellID
##\brief Object configuration.
#\param self The object's reference to itself.
#\param index The species index.
#\param abr The species USDA alpha-numeric vegetation code.
#\param name The full species name.
#\param modelType The type of a model.
#\param cover The fraction of model cell that is covered by a species.
#\param loc The x,y location of a cell. Given as the row,column index with in the model grid.
##\detail This function is used to configure an object after it has been created.
# this is used SpeciesModelList when it is creating the list of species from
# the species growth/senescence tables and by PatchModel when creating the
# individual species model for each location.
def config(self, index=0, name='', abr='', modelType='', cover=0, cellID=0):
self.index = index
self.name = name
self.abr = abr
self.modelType = modelType
self.cover = cover
self.cellID = cellID
##\brief Computes the probability of senescence.
##\detail This is a generic function that returns the probability of plants senesencing.
# This function should be redefined by each class that inherits form SpeciesModel.
def senescence(self):
return 0.0
##\brief Computes the probability of establishment
##\details This is a generic function that returns the probability of plats growing.
# This function should be redefined by each class that inherits from SpeciesModel.
def growth(self):
return 0.0
def __float__(self):
return self.cover
def __str__(self):
ret = str(self.index) + ', '
ret += self.name + ', '
ret += self.abr + ', '
ret += self.modelType + ', '
ret += str(self.cover) + ', '
ret += str(self.cellID)
return ret
##\class NullModel
##\brief A place holder.
##\role{Machinery}
##\detail A place holder for habitat and vegetation types that
# do not really do anything. Having this class makes
# the rest of the model code simpler.
class NullModel(SpeciesModel):
def __init__(self, index=0, name='', abr='', cover=0, cellID=0):
SpeciesModel.__init__(self, index, name, abr, 'NullModel', cover, cellID)
##\brief
##\detail
def senescence(self):
return 0.0
##\brief
##\detail
def growth(self):
return 0.0
##\class UplandForestModel
##\brief This class handles the ecology for the upland forest species.
##\role{Ecology}
##\detail Upland forest types change state based on the
# height of the habitat above mean water surface.
class UplandForestModel(SpeciesModel):
def __init__(self, index=0, name='', abr='', cover=0, cellID=0, Pdata=None, Ddata=None):
SpeciesModel.__init__(self, index, name, abr, 'UplandForestModel', cover, cellID)
self.P = function.Function(Pdata['elvValue'], Pdata['rate'])
self.D = function.Function(Ddata['elvValue'], Ddata['rate'])
## Senescence function
# This function determines the probability that an upland forest species
# will experience senescence. The probability of senescence is a function
# of the elevation of the local habitat above the mean water surface. The
# exact functional relationship between height above water and the probability
# of senescence is determined by the senescence table for each species.
# The senescence tables are defined in a model input file.
def senescence(self):
try:
#elv = SpeciesModel.params.heighAboveWater[self.cellID]
#sal = SpeciesModel.params.meanSal[self.cellID]
#bi = SpeciesModel.params.biEstCond[self.cellID]
#if bi or sal > 1.0:
#return 1.0
#return self.D[elv]
return 0.0
except exceptions.RuntimeError as error:
msg = 'UplandForestModel.senescence(): Error: location out of range. Additional info follows\n'
msg += str(error)
raise exceptions.RuntimeError(msg)
## Growth function
# This function determines the probability that an upland forest species will become
# established at the current location. The probability of an upland forest species becoming
# established is depended on three major factors. First, the salinity must be below 1.0 ppt.
# Second, there must be a period of 14 days with no flooding followed by a period of 14 with
# water depths below 14 cm. Finally, the height of the habitat above mean water level determines
# the final probability.
# I'm not entirely happy with the current state of this function because the computation
# of the basic establishment conditions (14 day no flood, 14 days water depth < 14 cm) is
# handled external to the model. The problem is that the current approach divides
# the responsibilities for representing the ecology of upland forest species. An external program
# determines the establishment conditions while the model proper handles the final computation of the
# probability of establishment. I would like to fix this.
def growth(self):
try:
#elv = SpeciesModel.params.heighAboveWater[self.cellID]
#sal = SpeciesModel.params.meanSal[self.cellID]
#est = SpeciesModel.params.treeEstCond[self.cellID]
#bi = SpeciesModel.params.biEstCond[self.cellID]
#dsp = SpeciesModel.dspModel[self.cellID][self.abr]
#dsp = 1.0
#if bi or est == 0 or sal > 1.0:
#return 0
#return self.P[elv] * dsp
return 0.0
except exceptions.RuntimeError as error:
msg = 'UplandForestModel.growth(): Error: location out of range. Additional info follows\n'
msg += str(error)
raise exceptions.RuntimeError(msg)
##\class EmergentWetlandModel
##\brief This class handles the ecology for the emergent wetland species.
##\role{Ecology}
##\detail
#
class EmergentWetlandModel(SpeciesModel):
def __init__(self, index=0, name='', abr='', cover=0, cellID=0, Pdata=None, Ddata=None):
SpeciesModel.__init__(self, index, name, abr, 'EmergentWetlandModel', cover, cellID)
self.P = function.Function2DFast(Pdata['waValue'], Pdata['salValue'], Pdata['rate'])
self.D = function.Function2DFast(Ddata['waValue'], Ddata['salValue'], Ddata['rate'])
## Senescence function
# This function determines the probability that an emergent wetland species
# will experience senescence. The probability of senescence is a function
# of variation in water stage height (computed as the standard deviation of stage)
# and salinity. The exact relationship between the probability of senescence and
# the environmental factors is describe the by scenescence table for each species.
# The senescence tables are defined in a model input file.
def senescence(self):
waveAmp = SpeciesModel.params.hydrology[self.cellID][SpeciesModel.params.WAVEAMP]
meanSal = SpeciesModel.params.hydrology[self.cellID][SpeciesModel.params.SAL]
return self.D[waveAmp,meanSal]
## Growth function
# This function determines the probability that an emergent wetland species will become
# established at the current location. The probability of an wetland species becoming
# established is depended on
# three major factors. First, the salinity must be below 1.0 ppt.
# Second, there must be a period of 14 days with no flooding followed by a period of 14 with
# water depths below 14 cm. Finally, the height of the habitat above mean water level determines
# the final probability.
def growth(self):
waveAmp = SpeciesModel.params.hydrology[self.cellID][SpeciesModel.params.WAVEAMP]
meanSal = SpeciesModel.params.hydrology[self.cellID][SpeciesModel.params.SAL]
return self.P[waveAmp, meanSal]
##\class SAVModel
##\brief This class handles the ecology for the SAV species.
##\role{Ecology}
##\detail
#
class SAVModel(SpeciesModel):
def __init__(self, index=0, name='', abr='', cover=0, cellID=0, SAVData=None):
SpeciesModel.__init__(self, index, name, abr, 'SAVModel', cover, cellID)
if SAVData == None:
errorMessage = 'SAVModel: Error: No SAVData object passed to constructor (i.e. __init__() )\n'
raise exceptions.RuntimeError(errorMessage)
self.betaIntercept = SAVData['Intercept']
self.betaTemp = SAVData['Temp']
self.betaSal = SAVData['Sal']
self.betaDepth = SAVData['Depth']
def senescence(self):
return 0;
def growth(self):
d = SpeciesModel.params.hydrology[self.cellID][SpeciesModel.params.SUMMERWD]
s = SpeciesModel.params.hydrology[self.cellID][SpeciesModel.params.SUMMERSAL]
t = SpeciesModel.params.hydrology[self.cellID][SpeciesModel.params.SUMMERTEMP]
return min( 1.0, max(0.0, self.betaIntercept + self.betaTemp*t + self.betaSal*s + self.betaDepth*d ) )
##\class BarrierIslandSpeciesModel
##\brief This class handles the ecology for the barrier island species.
##\role{Ecology}
##\detail
#
class BarrierIslandSpeciesModel(SpeciesModel):
def __init__(self, index=0, name='', abr='', cover=0, cellID=0, Pdata=None, Ddata=None):
SpeciesModel.__init__(self, index, name, abr, 'BarrierIslandSpeciesModel', cover, cellID)
self.P = function.Function(Pdata['elvValue'], Pdata['rate'])
self.D = function.Function(Ddata['elvValue'], Ddata['rate'])
def senescence(self):
#elv = SpeciesModel.params.heighAboveWater[self.cellID]
#bi = SpeciesModel.params.biEstCond[self.cellID]
#if not bi:
#return 1.0
#return self.D[elv]
return 0.0
def growth(self):
#elv = SpeciesModel.params.heighAboveWater[self.cellID]
#bi = SpeciesModel.params.biEstCond[self.cellID]
#dsp = SpeciesModel.dspModel[self.cellID][self.abr]
#dsp = 1.0
#if not bi:
#return 0.0
#return self.P[elv] * dsp
return 0.0
##\brief Growth/Senescence table storage
##\detail This class stores the growth and senescence tables
# for each species. The class is structured as a dictionary.
# Each species is identified by a unique abbreviation. For
# example, _Phragmities australis_ (Roseau cane) is represented
# by the abbreviation PAHU7. The abbreviations are based on the
# USDA species abbreviations. The abbreviations for each of the species
# used in this model are defined in the establishment table file.
#
# Each unique abbreviation is associated with the growth and senescence
# tables. These tables are defined in the establishment and senescence file.
#
# This class' job is to make the data from the establishment and senescence
# tables available to the other model components.
#
class SpeciesModelList(dict):
##\brief Class constructor
def __init__(self):
dict.__init__(self)
##\brief Load data from establishment and senescence files.
##@param [in] estFilename The name of the file containing the establishment tables.
##@param [in] mortFilename The name of the file containing the senescence tables.
##\details This member function loads the establishment and senescence tables
# from the filenames passed in as arguments. The files are expected to be
# MSExcel spreadsheets stored in the XLSX format. The data in each file
# should be divided into a set of separate tabbed sheets. One sheet should
# be named "VegTypeNames". This sheet should list all the species to include
# in the model. There should also be one table for each of the species included in the
# model. There are three exceptions to this: SAV, the Upland forest species and the
# barrier island species. SAV parameters are read from the model configuration file.
# The barrier island species and the upland forest species are each represented by a single
# sheet.
#
# The function performs the following steps:\n
def config(self, estFilename, mortFilename):
errorMessage = ''
###################################################
##
# - __Step 1__: Read the VegTypeNames sheet from the
# establishment and senescence files.
#
# The VegTypeNames file is where we get the
# names of the species in the model. If this
# sheet does not exist in the Excel files,
# or if the files do not exist, raise and
# exception.
###################################################
try:
estData = pandas.read_excel(estFilename, 'VegTypeNames', index_col=None)
except exceptions.IOError as error:
errorMessage += 'SpeciesModelList: Error: Could not open file for reading: ' + str(estFilename) + '\n'
errorMessage += 'SpeciesModelList: Error: Additional error info: ' + str(error) + '\n';
except xlrd.biffh.XLRDError as error:
errorMessage += 'SpeciesModelList: Error: While opening : ' + str(estFilename) + '\n'
errorMessage += 'SpeciesModelList: Error: Additional error info: ' + str(error) + '\n';
try:
mortData = pandas.read_excel(mortFilename, 'VegTypeNames', index_col=None)
except exceptions.IOError as error:
errorMessage += 'SpeciesModelList: Error: Could not open file for reading: ' + str(estFilename) + '\n'
errorMessage += 'SpeciesModelList: Error: Additional error info: ' + str(error) + '\n';
except xlrd.biffh.XLRDError as error:
errorMessage += 'SpeciesModelList: Error: While opening : ' + str(estFilename) + '\n'
errorMessage += 'SpeciesModelList: Error: Additional error info: ' + str(error) + '\n';
if len(errorMessage):
raise exceptions.RuntimeError(errorMessage);
###################################################
##
# - __Step 2__: Get the list of species from the 'Symbol'
# column. Raise exceptions as needed.
#
###################################################
try:
speciesList = estData['Symbol']
except exceptions.KeyError as error:
errorMessage += 'SpeciesModelList: Error: Symbol column not defined in establishment tables \n'
raise exceptions.RuntimeError(errorMessage);
###################################################
##
# - __Step 3__: Read the establishment and senescence tables.
#
# For each species, configure the P & D
# Functions for each species and push everything
# into a dictionary (dict).
#
###################################################
for spSymbol in speciesList:
print 'SpeciesModelList: Msg: Configuring model for species ' + str(spSymbol)
spInfo = estData[ estData['Symbol'] == spSymbol ].to_dict('record')[0]
spID = spInfo['ID']
spName = spInfo['Common Name']
spModelType = spInfo['ModelType']
###########################################
# Upland Forest Model
#
###########################################
if ( spModelType == 'UplandForestModel'):
reader = function.ReadVegTable1D()
Pdata = None
Ddata = None
try:
Pdata = reader.read(estFilename, 'UplandForest', spSymbol)
except exceptions.RuntimeError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
except exceptions.ValueError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
try:
Ddata = reader.read(mortFilename, 'UplandForest', spSymbol)
except exceptions.RuntimeError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
except exceptions.ValueError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
spModel = UplandForestModel(index=spID, name=spName, abr=spSymbol, cover=0, cellID=-1, Pdata=Pdata, Ddata=Ddata)
self.__setitem__(spSymbol, spModel)
###########################################
# Emergent Wetland Model
#
###########################################
elif (spModelType == 'EmergentWetlandModel'):
reader = function.ReadVegTable2D()
Pdata = None
Ddata = None
try:
Pdata = reader.read(estFilename, spSymbol)
except exceptions.RuntimeError as error:
errorMessage += str(error) + '\n';
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
except exceptions.ValueError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
try:
Ddata = reader.read(mortFilename, spSymbol)
except exceptions.RuntimeError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(species) + '\n'
except exceptions.ValueError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(species) + '\n'
spModel = EmergentWetlandModel(index=spID, name=spName, abr=spSymbol, cover=0, cellID=-1, Pdata=Pdata, Ddata=Ddata)
self.__setitem__(spSymbol, spModel)
###########################################
# Submerged Aquatic Vegetation Model
#
###########################################
elif ( spModelType == 'SAVModel'):
savData = None
try:
savData = pandas.read_excel(estFilename, spSymbol, index_col=0 ).to_dict('dict')['Value']
except exceptions.IOError as error:
errorMessage += 'SpeciesModelList: Error : Could not open file for reading : ' + estFilename + '\n'
errorMessage += 'SpeciesModelList: Extra error info: ' + str(error) + '\n'
except xlrd.biffh.XLRDError as error:
errorMessage += 'SpeciesModelList: Error : Could not find sheet name ' + species + ' in file ' + estFilename + '\n'
errorMessage += 'SpeciesModelList: Extra error info: ' + str(error) + '\n'
except exceptions.KeyError as error:
errorMessage += 'SpeciesModelList: Error : Dictionary key error. SAV table probably has wrong column headings\n'
errorMessage += 'SpeciesModelList: Error : '
spModel = SAVModel(index=spID, name=spName, abr=spSymbol, cover=0, cellID=-1, SAVData=savData)
self.__setitem__(spSymbol, spModel)
###########################################
# Barrier Island Model
#
###########################################
elif ( spModelType == 'BarrierIslandSpeciesModel'):
reader = function.ReadVegTable1D()
Pdata = None
Ddata = None
try:
Pdata = reader.read(estFilename, 'BarrierIsland', spSymbol)
except exceptions.RuntimeError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
except exceptions.ValueError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
try:
Ddata = reader.read(mortFilename, 'BarrierIsland', spSymbol)
except exceptions.RuntimeError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
except exceptions.ValueError as error:
errorMessage += str(error) + '\n'
errorMessage += 'SpeciesModelList: Error: error while working on species ' + str(spSymbol) + '\n'
spModel = BarrierIslandSpeciesModel(index=spID, name=spName, abr=spSymbol, cover=0, cellID=-1, Pdata=Pdata, Ddata=Ddata)
self.__setitem__(spSymbol, spModel)
###########################################
# Null Model
#
###########################################
elif ( spModelType == 'NullModel'):
spModel = NullModel(index=spID, name=spName, abr=spSymbol, cover=0, cellID=-1)
self.__setitem__(spSymbol, spModel)
###########################################
# Error State
#
###########################################
else:
errorMessage += 'SpeciesModelList: Error: Unknown model type specified : ' + spModelType + '\n'
if len(errorMessage):
raise exceptions.RuntimeError(errorMessage)
##\class Params
##\brief This class is the interface between the outside world and the model.
##\role{Machinery}
##\detail This class is an interface between the internal components of the model
# and the outside world. That is, everything that is read in from outside the
# model is stored in an object of class Params. Params then makes all of this
# information available to the other model components.
#
# It is important to note that
# there is only meant to be ONE object derived from this class, and that object
# is owned by the object of class Model.
#
# Params handles reading values from the model configuration file. Params
# also manages all of the model IO streams, as well as the landscape.Landscape
# objects that hold the spatial data files used by the model, including the
# hydrology, salinity, elevation, initial conditions and other spatial data sets. Params also
# holds a special object of type model.SpeciesModelList. This class is
# used to store the the growth and senescence tables for each species along with
# any other species specific information.
class Params(object):
##\brief Class constructor
def __init__(self):
##\brief Holds the configuration information
self.configDict = config.Config()
##\brief Model start year
self.startYear = 0
##\brief Model end year
self.endYear = 0
##\brief List of model input filenames.
#
self.inputStrm = {'HydrologyFile' :None}
##\brief List of model output filenames.
#
self.outputStrm = { 'OutputFile' :None}
##\brief The annual hydrology data
self.hydrology = landscape.Landscape()
##\brief The model initial conditions.
#
self.initCond = landscape.Landscape() # InitialConditionFile
##\brief The species model list (SpeciesModelList)
#
self.spModelList = SpeciesModelList()
##\brief The elevation threshold for tree establishment (soon to be deprecated).
#
self.elevationThreshold = 0.1525
self.numLocs = 0
##\brief These are column names used in the hydrology file. The values
# assigned here are the default values. However, if they change, you can
# reset them in the configuration file. There is error checking associated
# with these names at the end of the params.config()
self.YEAR = 'year'
self.CELLID = 'CellID'
self.SAL = 'mean(sal_year)'
self.WAVEAMP = 'std(dep_year)'
self.SUMMERWD = 'mean(dep_summer)'
self.SUMMERSAL = 'mean(sal_summer)'
self.SUMMERTEMP = 'mean(temp_summer)'
self.LAND = 'land/water'
##\brief Open input/output files
##\param key The keyword name for the file as it appears in the configuration file.
##\param mode Should the file be opened for reading or writing.
##\details This is a convenience function. It checks to make sure the
# file actually exists and opens if it does. If any problems are encountered,
# such as file not existing or cannot be opened, an exception is raised.
def open_file(self, key, mode):
try:
filename = self.configDict[key];
except exceptions.KeyError as error:
errorMessage = 'Params: Error: ' + str(key) + ' is not defined in the configuration file.\n'
errorMessage += 'Params: Error: Additional error info : ' + str(error) + '\n'
raise exceptions.RuntimeError(errorMessage);
try:
strm = open(filename, mode)
except exceptions.IOError as error:
errorMessage = 'Params: Error: Could not open the file ' + filename + (' for reading' if mode == 'r' else ' for writing') +'\n'
errorMessage += 'Params: Error: Additional error info : ' + str(error) + '\n'
raise exceptions.RuntimeError(errorMessage);
return strm
##\brief Read the model configuration information.
##\param argv A object containing the name of the configuration file.
##\details This member function reads in the information
# contained in the model configuration file and sets up
# a number of program elements based on this information
#
# The function performs the follwing steps:
#
def config(self, argv):
errorMessage = ''
###################################################
##
# - __Step 1__: Read in the configuration data from the
# configuration file.
#
###################################################
try:
self.configDict.config(argv);
except exceptions.RuntimeError as error:
errorMessage = 'Params: Error: An error occurred reading the configuration file.\n' + str(error)
raise exceptions.RuntimeError(errorMessage)
###################################################
##
# - __Step 2__: Get the start end end year of the simulation
#
###################################################
try:
self.startYear = int( self.configDict['StartYear'] )
print 'Params: Msg: StartYear = ' + str(self.startYear)
except exceptions.KeyError as error:
errorMessage += 'Params: Error: StartYear is not defined in the configuration file\n'
try:
self.endYear = int( self.configDict['EndYear'])
print 'Params: Msg: EndYear = ' + str(self.endYear)
except exceptions.KeyError as error:
errorMessage += 'Params: Error: EndYear is not defined in the configuration file\n'
###################################################
##
# - __Step 3__: Read the initial conditions file
#
###################################################
try:
print 'Params: Msg: Reading initial conditions from ' + self.configDict['InitialConditionFile'] + '\n'
initCondStrm = self.open_file('InitialConditionFile','r')
reader = landscape.ReadLandscape()
reader.read(initCondStrm, self.initCond)
initCondStrm.close();
except exceptions.RuntimeError as error:
print(error)
errorMessage += str(error)
###################################################
##
# - __Step 4__: Configure the SpeciesModelList object
#
###################################################
try:
estFilename = self.configDict['EstFilename']
except exceptions.KeyError as error:
errorMessage += 'Params: Error: ' + str(error) + '\n'
except exceptions.RuntimeError as error:
errorMessage += str(error);
try:
mortFilename = self.configDict['MortFilename']
except exceptions.KeyError as error:
errorMessage += 'Params: Error: ' + str(error) + '\n'
except exceptions.RuntimeError as error:
errorMessage += str(error);
try:
self.spModelList.config(estFilename, mortFilename)
except exceptions.KeyError as error:
errorMessage += 'Params: Error: ' + str(error) + '\n'
except exceptions.RuntimeError as error:
errorMessage += str(error);
###################################################
##
# - __Step 5__: Open all the input files for reading.
#
###################################################
for key in self.inputStrm.iterkeys():
try:
self.inputStrm[key] = self.open_file(key, 'r')
print 'Params: Msg: Opened file for reading: ' + str(key) + ' = ' + self.configDict[key]
except exceptions.RuntimeError as error:
errorMessage += str(error)
###################################################
##
# - __Step 6__: Open all the output files for writing.
#
###################################################
# Steps 6.1 to 6.3: Set up the single year files.
# Step 6.1: Get the filename template for the single year files.
try:
outputTemplate = None
outputTemplate = self.configDict['OutputTemplate']
print 'Params: Msg: OutputTemplate = ' + self.configDict['OutputTemplate']
except exceptions.KeyError as error:
errorMessage += 'Params: Error: OutputTemplate is not defined in the configuration file\n'
errorMessage += 'Params: Error: Additional error info: ' + str(error)
# Step 6.2: Get the years we want output stored in separate files
outputYear = range(self.startYear, self.endYear+1)
#try:
# outputYear = None
# outputYear = self.configDict['OutputYears']
# outputYear = [ int(x) for x in outputYear.split(',')]
# print 'Params: Msg: OutputYears = ' + str(outputYear)
#except exceptions.KeyError as error:
# errorMessage += 'Params: Error: OutputYears is not defined in the configuration file\n'
# errorMessage += 'Params: Error: Additional error info: ' + str(error)
# Step 6.3: Build the file names for the separate files.
if outputTemplate != None and outputYear != None:
for year in outputYear:
filename = re.sub(r'<YEAR>', str(year), outputTemplate);
self.configDict['__Single_'+str(year)] = filename
self.outputStrm['__Single_'+str(year)] = None
# Step 6.7: Open all the output files.
for key in self.outputStrm.iterkeys():
try:
self.outputStrm[key] = self.open_file(key, 'w+')
print 'Params: Msg: Output file opened ' + str(self.configDict[key])
except exceptions.RuntimeError as error:
errorMessage += str(error)
errorMessage += 'Params: Error: This is very odd\n'
###################################################
##
# - __Step 7__: If any errors have occurred, raise an
# exception
#
###################################################
if len(errorMessage):
raise exceptions.RuntimeError(errorMessage)
###################################################
##
# - __Step X__: Find the number of mesh cells
#
###################################################
self.numLocs = len(self.initCond.data)
###################################################
##
# - __Step 8__: Give all of the landscape objects their
# correct size
###################################################
print 'Params: Msg: Giving all data layers their proper size'
reader = landscape.ReadLandscape()
reader.read(self.inputStrm['HydrologyFile'], self.hydrology, self.numLocs)
print 'Params: Msg: Rewinding all the input streams'
for strm in self.inputStrm.itervalues():
strm.seek(0,0)
###################################################
##
# - __Step 9__: Read the column names for the
# hydrology file. They the column names have not
# been redefined in the configuration file, they
# just use the defaults (already set in __init___().
#
# Check that the column names actually appear in the
# hydrology file. If they do not, throw and exception
#
###################################################
if 'YEAR' in self.configDict.keys(): self.YEAR = config['YEAR']
if 'CELLID' in self.configDict.keys(): self.CELLID = config['CELLID']
if 'SAL' in self.configDict.keys(): self.SAL = config['SAL']
if 'WAVEAMP' in self.configDict.keys(): self.WAVEAMP = config['WAVEAMP']
if 'SUMMERWD' in self.configDict.keys(): self.SUMMERWD = config['SUMMERWD']
if 'SUMMERSAL' in self.configDict.keys(): self.SUMMERSAL = config['SUMMERSAL']
if 'SUMMERTEMP' in self.configDict.keys(): self.SUMMERTEMP = config['SUMMERTEMP']
for name in [self.YEAR, self.SAL, self.WAVEAMP, self.SUMMERWD, self.SUMMERSAL, self.SUMMERTEMP]:
if not name in self.hydrology.data.columns:
errorMessage += 'Params: Error: Column name ' + name + ' is not defined in the hydrology file.\n'
if len(errorMessage):
raise exceptions.RuntimeError(errorMessage)
##\brief Actions take at the end of the simulation.
##\details The primary responsibility of this function
# is to close all of the open file streams. It is
# called at the end of the simulation as the program
# is getting ready to exit.
def done(self):
for iter in self.inputStrm.itervalues():
iter.close();
print 'Params: Msg: Fixing output file format'
columnOrder = self.initCond.columns
index = pandas.DataFrame( self.initCond.data['index'] )
for strm in self.outputStrm.itervalues():
filename = strm.name
strm.close()
data = pandas.read_csv(filename, delimiter='\s*,\s*')
data = pandas.merge(index, data, left_index=True, right_on='CellID')
data = data[columnOrder]
data.to_csv(filename, index=False, quoting=csv.QUOTE_NONNUMERIC)
strm.close()
def __del__(self):
pass
##\class PatchModel
##\brief This class handles the plant community dynamics at a single location
##\role {Ecology/Machinery}
##\detail This class handles the ecology of a single location in the landscape.
# The information for each location is stored as a Python dictionary. They
# keys for the dictionary are the abbreviated names, stored as a string, for
# each of the species. The value for each entry in the dictionary is an object
# instantiated from a class that inherits from SpeciesModel. That is, each value
# is an object of one of the following classes: UplandForestModel, EmergentWetlandModel,
# SAVModel, BarierIslandModel, or NullModel.
class PatchModel(dict):
params = None
def __init__(self, cellID, initCond, spModelList):
dict.__init__(self)
self.cellID = cellID
for spName,spCover in initCond.iteritems():
spModel = copy.copy(spModelList[spName])
spModel.cover = spCover
spModel.cellID = cellID
dict.__setitem__(self, spName, spModel)
def update(self):
occupied = 0.0
lost = 0.0
growthLikelihood = 0.0
#######################################################
# WARNING: The order of steps here is important. Additional
# care is required here because you can change
# the order without actually causing the model to break.
# Changing the order will change the way the model simulates
# the ecology the plants. Unless you really know what
# you are doing, do not change the order. Even if
# you think you know what you are doing, think about
# what you are going to do carefully. Perhaps you should
# go take and nap and think things over before you make
# changes here. If you break things, it will be on your head.
#
#######################################################
#######################################################
# Step 1: Work out the increase and decrease in cover
# for all types except, SAV, WATER and
# Those types have to be
# handled in a special way.
#
#######################################################
# Step 1.0: The first thing we need to do is workout the effects of added or lost land
# There are four cases.
# Case 1: Land/Land: The cell is currently classified as land and the next state (obtained from the input file)
# is also land. In this case, we do not need to do anything.
#
# Case 2: Water/Water: The cell is currently classified as water and the next state is also water.
# In this case we do not need to do anything.
#
# Case 3: Land/Water: The cell is currently classified as land and the next state is water.
# In this case we need to set the cover of all plant species to zero and set the cover of water to 1.0
# the proceed as normal
#
# Case 4: Water/Land: The cell is currently classified as water and the next state is land.
# In this case we need to set the cover of water to zero (?) and set BAREGRND to 1.0. The proceed as
# normal.
#
# A cell that has some vegetation present will be classified as land.
nextLandState = PatchModel.params.hydrology[self.cellID][PatchModel.params.LAND]
# Step 1.0 a: Figure out if the cell is currently classified as land or water.
isLand = 1
#SAVModel = dict.__getitem__(self, 'SAV')
#waterModel = dict.__getitem__(self, 'WATER')
#totalWater = SAVModel.cover + waterModel.cover
totalWater = self['WATER'].cover + self['SAV'].cover
if totalWater == 1.0:
isLand = 0
if isLand == 1 and nextLandState == 0: # Case 3
for spName, spModel in self.iteritems():
spModel.cover = 0.0
self['WATER'].cover = 1.0
#waterModel = dict.__getitem__(self, 'WATER')
#waterModel.cover = 1.0
elif isLand == 0 and nextLandState == 1: # Case 4
self['BAREGRND'].cover = 1.0
self['WATER'].cover = 0.0
self['SAV'].cover = 0.0
else: # Case 1 and Case 2
pass
# Step 1.1: Work out the loss of cover for all species
unoccupied = self['BAREGRND'].cover
for spName, spModel in itertools.ifilterfalse(lambda (k,v): k == 'BAREGRND' or k =='SAV' or k == 'WATER', self.iteritems()):
cover = spModel.cover
death = spModel.senescence()
#occupied += cover
lost += death * cover
spModel.cover -= death * cover
growthLikelihood += spModel.growth()
#unoccupied = max( 0, min( (1.0-occupied)+lost, 1.0) )
unoccupied += lost
available = copy.copy(unoccupied)
# Step 1.2: Work out the gain in cover for all species
# The rounding of growth is complicated because what I really want to happen is for the
# digits below the thousandths place to be dropped. This isn't really rounding. The reason
# for this approach is to make growth slightly smaller than it might be. this should
# prevent unoccupied from becoming negative. This is a rather crude way to achieve the
# desired result, but it is simple and requires a minimum of recoding and new coding.
if growthLikelihood:
for spName, spModel in itertools.ifilterfalse(lambda (k,v): k == 'BAREGRND' or k=='SAV' or k=='WATER', self.iteritems()):
#spModel.cover += spModel.growth()/growthLikelihood * unoccupied
growth = math.floor( spModel.growth()/growthLikelihood * 1000.0 )/1000.0
#spModel.cover += growth * unoccupied
#unoccupied -= growth * unoccupied
spModel.cover += growth * available
unoccupied -= growth * available
self['BAREGRND'].cover = unoccupied
#######################################################
# Step 3: Work out the change in cover for SAV and WATER
#
#######################################################
SAVModel = dict.__getitem__(self, 'SAV')
waterModel = dict.__getitem__(self, 'WATER')
totalWater = SAVModel.cover + waterModel.cover
SAVModel.cover = SAVModel.growth() * totalWater
waterModel.cover = totalWater - SAVModel.cover
#######################################################
# Step 4: Check sum
#
#######################################################
errorMsg = str()
checkSum = 0.0
for spName, spModel in self.iteritems():
checkSum += spModel.cover
if spModel.cover < 0.0:
errorMsg += 'PatchModel: Error: Species cover < 0.0. This should not happen.\n'
errorMsg += 'PatchModel: Error: Species = ' + spName + '\n'
tol = 0.005
if not( (1.0 - tol) < checkSum and checkSum < (1.0 + tol) ):
errorMsg += 'PatchMod: Error: checkSum is out of range. checkSum = ' + str(checkSum) + ' should be 1.0\n'
if len(errorMsg) != 0:
raise exceptions.RuntimeError(errorMsg)
def write_to_str(self, dataFormat='{0}'):
ret = ''
sep = ''
for value in self.itervalues():
ret += sep + dataFormat.format(value.cover)
sep = ', '
return(ret)
def write_to_stream(self, stream = sys.stdout, dataFormat='{0}'):
sep=''
for key, value in self.iteritems():
print >> stream, sep + dataFormat.format(value)
sep = ', '
##\class DynamicsModel
##\brief This class coordinates the updating of plant community dynamics
##\role{Ecology}
##\detail This class handles updating the spatial distribution of species
# in response to environmental conditions and plant dispersal ability.
# This class defines the spatial structure of the model using a
# raster grid. The grid structure is inherited from landscape.LandscapePlus.
# Each element of the raster contains an object of type PatchModel.
# Local dynamics, those taking place within each raster cell, are
# computed by objects of class PatchModel.
class DynamicsModel(dict):
def __init__(self):
dict.__init__(self)
self.locInfo = None
def config(self, params):
# Get the species codes (e.g. 'SPPA', 'NYAQ2', etc.. ) from the
# from the species model list object.
speciesList = params.spModelList.keys()
# Pull off just the location information from the
# initial conditions files. This should be the
# the row and column indicies (m and n), the
# actual geographic coordinates (x and y) and the
# location index (CellID).
try:
self.locInfo = params.initCond.data[ ['m','n','x','y'] ]
except exceptions.KeyError:
errMsg = 'DynamicsModel: Error: One or more spatial location columns are not defined in the initialization\n'
errMsg += 'DynamicsModel: Error: file. Columns should be CellID, m, n, x and y (order does not matter)\n'
raise exceptions.RuntimeError(errMsg)
# Cycle over the locations in the initial conditions.
# Each row in the initial conditions csv file represents
# a single location/patch.
for cellID, patch in params.initCond.iterlocs():
# Get just the cover values for the current patch.
patchInitCond = patch[speciesList]
# Make a PatchModel object for the patch and initialize it
newPatch = PatchModel(cellID, patchInitCond, params.spModelList)
# Place the patch model in a dictionary referenced by the cellID.
dict.__setitem__(self, cellID, newPatch)
def __getitem__(self, cellID):
dict.__getitem__(self, cellID)
def update(self):
for index,patch in self.iteritems():
patch.update()
def write_to_stream(self, stream, header=True, time=None):
#if time is not None:
#cols = self.locInfo.columns.insert(0, 'year')
#self.locInfo['year'] = numpy.full( len(self.locInfo), time)
#self.locInfo = self.locInfo[ cols ]
if header:
line = 'CellID'
for name in self.locInfo.columns:
line += ', ' + str(name)
for name in self.itervalues().next().iterkeys():
line += ', ' + str(name)
print >> stream, line
for cellID,patch in self.iteritems():
try:
pandas.DataFrame( self.locInfo.ix[ cellID ] ).T.to_csv(stream, header=False, line_terminator='')
print >> stream, ', ' + patch.write_to_str()
except exceptions.IndexError as error:
print cellID
raise error
#if time is not None:
#del self.locInfo['year']
##\class DispersalModel
##\brief Computes the dispersal of each species over space
##\role{Ecology}
##\detail This class computes the dispersal kernel for each species.
class DispersalModel(object):
dynModel = None
def __init__(self):
self.patchIndexLandscape = landscape.Landscape()
self.patchDict = dict()
def config(self, params):
self.patchIndexLandscape.copy(params.initCond)
#for row in range(0, int(params.initCond.nrow) ):
#for col in range(0, int(params.initCond.ncol) ):
# if params.initCond.has_data_at(row,col):
for row,col in itertools.ifilter( lambda (r,c): params.initCond.has_data_at(r,c), itertools.product( range(0,int(params.initCond.nrow)), range(0,int(params.initCond.ncol)) ) ):
patchIndex = params.initCond.data[row,col]
patchInitCond = params.initCond[(row,col)]
neighborList = [ (nRow,nCol) for nRow,nCol in itertools.ifilter( lambda (r,c): params.initCond.has_data_at(r,c), itertools.product(range(row-1,row+2), range(col-1,col+2)) ) ] # This is slick as hell. I like python.
newPatch = { 'loc':(row,col), 'spFreq':patchInitCond, 'neighborList':neighborList }
self.patchDict[patchIndex] = newPatch
patchInitCond = params.initCond.table.itervalues().next()
for key in patchInitCond.iterkeys():
patchInitCond[key] = 0.0
newPatch = {'loc':(-1,-1), 'spFreq':patchInitCond, 'neighborList':[]}
self.patchDict[params.initCond.nodata_value] = newPatch
def summary(self):
print 'DispersalModel: Msg: Summary info start'
print self.patchIndexLandscape.header()
print 'len(patchDict) = ' + str(len(self.patchDict))
print next( self.patchDict.itervalues() )
patchAddress = self.patchIndexLandscape.data[241,44]
print 'DispersalModel: patchAddress = ' + str(patchAddress)
print 'DispersalModel: Stuff at patchAddress = ' + str( self.patchDict[patchAddress] )
print 'DispersalModel: Stuff at patchAddress[\'spFreq\'] = ' + str( self.patchDict[patchAddress]['spFreq'])
print 'DispersalModel: Msg: Summary info end'
def __getitem__(self, item):
patchAddress = self.patchIndexLandscape.data[item]
return self.patchDict[patchAddress]['spFreq']
def has_data_at(self,row,col):
return self.patchIndexLandscape.has_data_at(row,col)
def _compute_local_dsp(self, ret):
#row,col = ret['loc']
ptr = ret['spFreq']
neighborList = ret['neighborList']
total = 0.0
for sp in ptr.iterkeys():
ptr[sp] = 0
for neighbor in neighborList:
patchModel = DispersalModel.dynModel[neighbor]
for sp,spModel in patchModel.iteritems():
ptr[sp] += spModel.cover
total += spModel.cover
#for offsetRow in range(-1,2):
# for offsetCol in range(-1,2):
# neighborRow = row + offsetRow
# neighborCol = col + offsetCol
# if DispersalModel.dynModel.has_data_at(neighborRow, neighborCol):
# patchModel = DispersalModel.dynModel[neighborRow,neighborCol]
# for sp,spModel in patchModel.iteritems():
# try:
# ptr[sp] += spModel.cover
# total += spModel.cover
# except exceptions.TypeError as error:
# print 'DispersalModel::_compute_local_dsp(): Error: ret = ' + str(ret)
# print 'DispersalModel::_compute_local_dsp(): Error: row = ' + str(row)
# print 'DispersalModel::_compute_local_dsp(): Error: col = ' + str(col)
# print 'DispersalModel::_compute_local_dsp(): Error: ptr = ' + str(ptr)
# print 'DispersalModel::_compute_local_dsp(): Error: total = ' + str(total)
# print 'DispersalModel::_compute_local_dsp(): Error: offsetRow = ' + str(offsetRow)
# print 'DispersalModel::_compute_local_dsp(): Error: offsetCol = ' + str(offsetCol)
# print 'DispersalModel::_compute_local_dsp(): Error: neighborRow = ' + str(neighborRow)
# print 'DispersalModel::_compute_local_dsp(): Error: neighborCol = ' + str(neighborCol)
# print 'DispersalModel::_compute_local_dsp(): Error: patch = ' + str(patch)
# print 'DispersalModel::_compute_local_dsp(): Error: sp = ' + str(sp)
# print 'DispersalModel::_compute_local_dsp(): Error: cover = ' + str(cover)
# raise error
if total:
for sp in ptr.iterkeys():
ptr[sp] /= total
def update(self):
for loc in itertools.ifilter( lambda key: key != self.patchIndexLandscape.nodata_value, self.patchDict.iterkeys()):
self._compute_local_dsp(self.patchDict[loc])
##\class ModelUpdateEvent
##\brief An event class to update the dynamics model and the dispersal model.
##\role{Machinery}
##\detail This class will likely be removed in an upcoming version of the model.
#
class ModelUpdateEvent(event.Event):
def __init__(self, time, name, model):
event.Event.__init__(self, time, name)
self.model = model
def act(self):
print self.name
self.model.update()
##\class StopWatch
##\brief A class to measure elapsed wall clock time.
##\role{Machinery}
##\detail This class is used to measure how much time
# (wall clock time) is used for different aspects of the model.
# This provides some rough profiling information so we can estimate
# how long future runs of the model might take.
class StopWatch:
def __init__(self):
self.running = 0
self._start = 0
self._end = 0
def start(self):
self.running = 1
self._start = time.time()
self._stop = 0
def stop(self):
self._end = time.time()
self.running = 0
print 'StopWatch: Msg: Delta t = ' + str(self._end - self._start)
def act(self):
if self.running:
self.stop()
else:
self.start()
def __str__(self):
ret = str(id(self)) + ' '
ret += str(self.running) + ' '
ret += str(self._start) + ' '
ret += str(self._end) + ' '
ret += str(self._end - self._start)
return ret
#class StopWatchEvent(event.Event):
#def __init__(self, time, name='StopWatchEvent', stopwatch=None):
event.Event.__init__(self, time, name)
self.stopwatch = stopwatch
#
#def act(self):
#if self.stopwatch.running:
#self.stopwatch.stop()
#else:
#self.stopwatch.start()
# This is the top level class for the LAVegMod. To get the model going you need
# to do four things.
# 1) Include the model module in the Python file that will be used
# to coordinate the running of the various models.
# The include statement should look something like:
# import model
# Note that this assumes that the LAVegMod code is in the same
# directory as the toplevel script.
#
# 2) Instantiate an object of class Model
# This will create a copy of the model and all of its subcomponents.
# Instantiating model should look something like:
#
# laVegMod = model.Model()
#
#
# 3) Call Model.config(self, argv) for the instantated object
# Model.config takes a single (non-self) argument that contains the
# name of the configuration file for the model. The call to Model.config()
# should look something like:
#
# laVegMod.config( <configFilename> )
#
# where <configFilename> is a Python string containing the full path
# and filename of the configuration file for the LAVegMod.
#
# The call to Model.config() will bring the model up to a state where it
# is ready to run. It is possible that more or more errors may occur while
# configuring the model. You can capture these and processes them yourself
# or you can just let them go and they will halt the code. To capture the
# errors from the LAVegMod you need code that looks something like:
#
# try:
# laVegMod.config( <configFilename> )
# except exception.RunTimeError as error:
# <do something with the error>
#
# 4) Call Model.step() repeatedly for the instantated object.
# Each time you call model.step() the model will advance by one year.
#
# The call to Model.step() should look something like:
#
# laVegMod.step()
#
# Again, this code may throw exceptions if something goes wrong. You can
# capture these errors using code that looks like this:
#
# try:
# laVegMod.step()
# except exception.RunTimeError as error:
# <do something with the error>
#
# Note that the LAVegMod only throws, (or should only throw)
# exception.RunTimeError()
#
# The thrown error contains one or error messages stored in a string that describe what
# went wrong. You can print these with something like:
#
# print error
#
# If the model has thrown an exception, then something is broken and
# there is no way to fix it during run time. You or I, or someone, will
# have to chase down the error and fix it. Often the error is going to be
# in the input files. So I would check those first.
#
# I have tried to make the error checking and reporting as comprehensive
# and as detailed as possible.
#
#
class Model(object):
def __init__(self):
self.currentTime = event.Time(0,0)
self.params = Params()
self.eventQueue = event.EventQueue()
self.dynModel = DynamicsModel()
#self.dspModel = DispersalModel()
SpeciesModel.params = self.params # I'm not sure I like this.
PatchModel.params = self.params
#SpeciesModel.dspModel = self.dspModel #
DispersalModel.dynModel = self.dynModel #
def config(self, argv):
print 'Model: Msg: Reading configuration information'
self.params.config(argv)
print 'Model: Msg: Configuring the dynamics model'
self.dynModel.config(self.params)
print 'Model: Msg: Configuring the dispersal model'
#self.dspModel.config(self.params)
# self.dspModel.summary()
print 'Model: Msg: Configuring the update queue'
self.eventQueue.clear()
perYearSW = StopWatch()
totalSW = StopWatch()
self.eventQueue.add_event( event.GenericEvent(event.Time(self.params.startYear, 0), name='StopWatch', callable=totalSW) )
self.eventQueue.add_event( landscape.WriteLandscape(event.Time(self.params.startYear, 200), name='WriteASCIIGrid: Msg: Writing model output', stream=self.params.outputStrm['OutputFile'], landscape=self.dynModel, header=True))
for year in range(self.params.startYear+1, self.params.endYear+1):
self.eventQueue.add_event( event.GenericEvent(event.Time(year, 100), name='StopWatch', callable=perYearSW ) )
self.eventQueue.add_event( event.MsgEvent(event.Time(year, 200), name='MsgEvent', msg='Year = ' + str(year)) )
self.eventQueue.add_event( landscape.ReadLandscape(event.Time(year, 300), name='ReadASCIIGrid: Msg: Reading wave amp data', stream=self.params.inputStrm['HydrologyFile'], landscape=self.params.hydrology, numLocs=self.params.numLocs))
self.eventQueue.add_event( ModelUpdateEvent(event.Time(year, 1100), name='ModelUpdateEvent: Msg: Updating veg dynamics', model=self.dynModel ) )
self.eventQueue.add_event( landscape.WriteLandscape(event.Time(year, 1400), name='WriteLandscape: Msg: Writing model output', stream=self.params.outputStrm['OutputFile'], landscape=self.dynModel, header=False))
self.eventQueue.add_event( event.GenericEvent(event.Time(year, 1600), name='StopWatch', callable=perYearSW ) )
for yearKey,yearStream in itertools.ifilter( lambda (k,v): re.match(r'^__Single_',k) != None, self.params.outputStrm.iteritems() ):
year = int( yearKey.replace('__Single_','') )
self.eventQueue.add_event(landscape.WriteLandscape(event.Time(year, 1500), name='WriteLandscape: Msg: Writing model output for year ' + str(year), stream=yearStream, landscape=self.dynModel))
self.eventQueue.add_event( event.GenericEvent(event.Time(self.params.endYear,3000), name='StopWatch', callable=totalSW) )
def step(self):
self.eventQueue.run(while_condition=(lambda arg: arg.name != 'PauseEvent'))
return 0
def run(self):
try:
self.config(sys.argv)
except exceptions.RuntimeError as error:
print error
return 1
#except:
#print 'Model: Error: Caught an unknown error : ' , sys.exc_info()[0]
#return 1
self.eventQueue.run()
self.params.done()
return 0;