def update_water_storage(self, tolerance=1E-08): """ Calculate root and top soil plant available water and runoff. Soil drainage is estimated using a "leaky-bucket" approach with two soil layers. In reality this is a combined drainage and runoff calculation, i.e. "outflow". There is no drainage out of the "bucket" soil. Returns: -------- outflow : float outflow [mm d-1] """ # reduce transpiration from the top soil if it is dry trans_frac = (self.params.fractup_soil * self.state.wtfac_topsoil) # Total soil layer self.state.pawater_topsoil += (self.fluxes.erain - (self.fluxes.transpiration * trans_frac) - self.fluxes.soil_evap) self.state.pawater_topsoil = clip(self.state.pawater_topsoil, min=0.0, max=self.params.wcapac_topsoil) # Total root zone previous = self.state.pawater_root self.state.pawater_root += (self.fluxes.erain - self.fluxes.transpiration - self.fluxes.soil_evap) # calculate runoff and remove any excess from rootzone if self.state.pawater_root > self.params.wcapac_root: runoff = self.state.pawater_root - self.params.wcapac_root self.state.pawater_root -= runoff else: runoff = 0.0 if float_le(self.state.pawater_root, 0.0): self.fluxes.transpiration = 0.0 self.fluxes.soil_evap = 0.0 self.fluxes.et = self.fluxes.interception self.state.pawater_root = clip(self.state.pawater_root, min=0.0, max=self.params.wcapac_root) self.state.delta_sw_store = self.state.pawater_root - previous return runoff
def update_water_storage(self, tolerance=1e-08): """ Calculate root and top soil plant available water and runoff. Soil drainage is estimated using a "leaky-bucket" approach with two soil layers. In reality this is a combined drainage and runoff calculation, i.e. "outflow". There is no drainage out of the "bucket" soil. Returns: -------- outflow : float outflow [mm d-1] """ # reduce transpiration from the top soil if it is dry trans_frac = self.params.fractup_soil * self.state.wtfac_topsoil # Total soil layer self.state.pawater_topsoil += ( self.fluxes.erain - (self.fluxes.transpiration * trans_frac) - self.fluxes.soil_evap ) self.state.pawater_topsoil = clip(self.state.pawater_topsoil, min=0.0, max=self.params.wcapac_topsoil) # Total root zone previous = self.state.pawater_root self.state.pawater_root += self.fluxes.erain - self.fluxes.transpiration - self.fluxes.soil_evap # calculate runoff and remove any excess from rootzone if self.state.pawater_root > self.params.wcapac_root: runoff = self.state.pawater_root - self.params.wcapac_root self.state.pawater_root -= runoff else: runoff = 0.0 if float_le(self.state.pawater_root, 0.0): self.fluxes.transpiration = 0.0 self.fluxes.soil_evap = 0.0 self.fluxes.et = self.fluxes.interception self.state.pawater_root = clip(self.state.pawater_root, min=0.0, max=self.params.wcapac_root) self.state.delta_sw_store = self.state.pawater_root - previous return runoff
def calc_carbon_allocation_fracs(self, nitfac): """Carbon allocation fractions to move photosynthate through the plant. Parameters: ----------- nitfac : float leaf N:C as a fraction of 'Ncmaxfyoung' (max 1.0) Returns: -------- alleaf : float allocation fraction for shoot alroot : float allocation fraction for fine roots albranch : float allocation fraction for branches alstem : float allocation fraction for stem References: ----------- Corbeels, M. et al (2005) Ecological Modelling, 187, 449-474. McMurtrie, R. E. et al (2000) Plant and Soil, 224, 135-152. """ if self.control.alloc_model == "FIXED": self.fluxes.alleaf = (self.params.c_alloc_fmax + nitfac * (self.params.c_alloc_fmax - self.params.c_alloc_fmin)) self.fluxes.alroot = (self.params.c_alloc_rmax + nitfac * (self.params.c_alloc_rmax - self.params.c_alloc_rmin)) self.fluxes.albranch = (self.params.c_alloc_bmax + nitfac * (self.params.c_alloc_bmax - self.params.c_alloc_bmin)) # allocate remainder to stem self.fluxes.alstem = (1.0 - self.fluxes.alleaf - self.fluxes.alroot - self.fluxes.albranch) self.fluxes.alcroot = self.params.c_alloc_cmax * self.fluxes.alstem self.fluxes.alstem -= self.fluxes.alcroot elif self.control.alloc_model == "GRASSES": # if combining grasses with the deciduous model this calculation # is done only during the leaf out period. See above. if not self.control.deciduous_model: self.calculate_growth_stress_limitation() # figure out root allocation given available water & nutrients # hyperbola shape to allocation min_root_alloc = 0.4 self.fluxes.alroot = (self.params.c_alloc_rmax * min_root_alloc / (min_root_alloc + (self.params.c_alloc_rmax - min_root_alloc) * self.state.prev_sma)) self.fluxes.alstem = 0.0 self.fluxes.albranch = 0.0 self.fluxes.alcroot = 0.0 self.fluxes.alleaf = (1.0 - self.fluxes.alroot) elif self.control.alloc_model == "ALLOMETRIC": if not self.control.deciduous_model: self.calculate_growth_stress_limitation() else: # reset the buffer at the end of the growing season self.sma.reset_stream() # figure out root allocation given available water & nutrients # hyperbola shape to allocation min_root_alloc = 0.1 self.fluxes.alroot = (self.params.c_alloc_rmax * min_root_alloc / (min_root_alloc + (self.params.c_alloc_rmax - min_root_alloc) * self.state.prev_sma)) #self.fluxes.alroot = (self.params.c_alloc_rmin + # (self.params.c_alloc_rmax - # self.params.c_alloc_rmin) * # self.state.prev_sma) # Calculate tree height: allometric reln using the power function # (Causton, 1985) self.state.canht = (self.params.heighto * self.state.stem**self.params.htpower) # LAI to stem sapwood cross-sectional area (As m-2 m-2) # (dimensionless) # Assume it varies between LS0 and LS1 as a linear function of tree # height (m) arg1 = self.state.sapwood * const.TONNES_AS_KG * const.M2_AS_HA arg2 = self.state.canht * self.params.density * self.params.cfracts sap_cross_sec_area = arg1 / arg2 if not self.control.deciduous_model: leaf2sap = self.state.lai / sap_cross_sec_area else: leaf2sap = self.state.max_lai / sap_cross_sec_area # Allocation to leaves dependant on height. Modification of pipe # theory, leaf-to-sapwood ratio is not constant above a certain # height, due to hydraulic constraints (Magnani et al 2000; Deckmyn # et al. 2006). if float_le(self.state.canht, self.params.height0): leaf2sa_target = self.params.leafsap0 elif float_ge(self.state.canht, self.params.height1): leaf2sa_target = self.params.leafsap1 else: arg1 = self.params.leafsap0 arg2 = self.params.leafsap1 - self.params.leafsap0 arg3 = self.state.canht - self.params.height0 arg4 = self.params.height1 - self.params.height0 leaf2sa_target = arg1 + (arg2 * arg3 / arg4) self.fluxes.alleaf = self.alloc_goal_seek(leaf2sap, leaf2sa_target, self.params.c_alloc_fmax, self.params.targ_sens) # Allocation to branch dependent on relationship between the stem # and branch target_branch = (self.params.branch0 * self.state.stem**self.params.branch1) self.fluxes.albranch = self.alloc_goal_seek(self.state.branch, target_branch, self.params.c_alloc_bmax, self.params.targ_sens) coarse_root_target = (self.params.croot0 * self.state.stem**self.params.croot1) self.fluxes.alcroot = self.alloc_goal_seek(self.state.croot, coarse_root_target, self.params.c_alloc_cmax, self.params.targ_sens) self.fluxes.alstem = (1.0 - self.fluxes.alroot - self.fluxes.albranch - self.fluxes.alleaf - self.fluxes.alcroot) # allocation to stem is the residual #self.fluxes.alstem = (1.0 - self.fluxes.alroot - # self.fluxes.albranch - # self.fluxes.alleaf) #self.fluxes.alcroot = 0.2 * self.fluxes.alstem #self.fluxes.alstem -= self.fluxes.alcroot # Because I have allowed the max fracs sum > 1, possibility # stem frac would be negative. Perhaps the above shouldn't be # allowed...? But this will stop wood allocation in such a # situation. #if self.fluxes.alstem < 0.0: # extra = self.fluxes.alstem # self.fluxes.alstem = 0.0 # self.fluxes.alleaf -= extra # minimum allocation to leaves - without it tree would die, as this # is done annually. if self.control.deciduous_model: if self.fluxes.alleaf < 0.1: min_leaf_alloc = 0.1 self.fluxes.alstem -= min_leaf_alloc self.fluxes.alleaf = min_leaf_alloc else: raise AttributeError('Unknown C allocation model') #print self.fluxes.alleaf, self.fluxes.alstem, self.fluxes.albranch, \ # self.fluxes.alroot, self.state.prev_sma, self.state.canht # Total allocation should be one, if not print warning: total_alloc = (self.fluxes.alroot + self.fluxes.alleaf + self.fluxes.albranch + self.fluxes.alstem + self.fluxes.alcroot) if float_gt(total_alloc, 1.0): raise RuntimeError, "Allocation fracs > 1"