def calculate_soil_water_fac(self):
        """ Estimate a relative water availability factor [0..1]

        A drying soil results in physiological stress that can induce stomatal
        closure and reduce transpiration. Further N mineralisation depends on 
        top soil moisture.

        References:
        -----------
        * Pepper et al. (2008) Functional Change Biology, 35, 493-508

        But similarly see:
        * van Genuchten (1981) Soil Sci. Soc. Am. J, 44, 892--898.
        * Wang and Leuning (1998) Ag Forest Met, 91, 89-111.

        Returns:
        --------
        wtfac_tsoil : float
            water availability factor for the top soil [0,1]
        wtfac_root : float
            water availability factor for the root zone [0,1]    
        """
        # turn into fraction...
        smc_root = self.state.pawater_root / self.params.wcapac_root
        smc_topsoil = self.state.pawater_tsoil / self.params.wcapac_topsoil

        # Calculate a soil moisture availability factor, used to adjust
        # ci/ca ratio in the face of limited water supply.
        arg = self.params.fwpmax - self.params.fwpmin
        wtfac_tsoil = (smc_topsoil - self.params.fwpmin) / arg
        wtfac_root = (smc_root - self.params.fwpmin) / arg
        
        return (clip(wtfac_tsoil, min=0.0, max=1.0), 
                clip(wtfac_root, min=0.0, max=1.0))
Esempio n. 2
0
 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 
Esempio n. 3
0
    def calc_infiltration(self, rain):
        """ Estimate "effective" rain, or infiltration I guess.

        Simple assumption that infiltration relates to leaf area
        and therefore canopy storage capacity (wetloss). Interception is
        likely to be ("more") erroneous if a canopy is subject to frequent daily
        rainfall I would suggest.

        Parameters:
        -------
        rain : float
            rainfall [mm d-1]

        """

        if float_gt(rain, 0.0):
            self.fluxes.interception = self.state.lai * self.params.wetloss
            self.fluxes.interception = clip(self.fluxes.interception,
                                            min=self.fluxes.interception,
                                            max=rain)

            self.fluxes.erain = (rain * self.params.rfmult -
                                    self.fluxes.interception)
        else:
            self.fluxes.interception = 0.0
            self.fluxes.erain = 0.0
Esempio n. 4
0
    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

        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):
        """ 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]
        """
        # Total root zone
        
        prev = self.state.pawater_root
        self.state.pawater_root += (self.fluxes.erain -
                                    self.fluxes.transpiration -
                                    self.fluxes.soil_evap)
       
        if self.state.pawater_root > self.params.wcapac_root:
            runoff = self.state.pawater_root - self.params.wcapac_root 
            
        else:
            runoff = 0.0
            
        self.state.pawater_root = clip(self.state.pawater_root, min=0.0,
                                        max=self.params.wcapac_root)
        
        self.delta_store = self.state.pawater_root - prev
       
        # Total soil layer
        self.state.pawater_tsoil += (self.fluxes.erain -
                                     self.fluxes.transpiration *
                                     self.params.fractup_soil -
                                     self.fluxes.soil_evap)

        self.state.pawater_tsoil = clip(self.state.pawater_tsoil, min=0.0,
                                        max=self.params.wcapac_topsoil) 
        
        return runoff
Esempio n. 6
0
    def calc_soil_evaporation(self, avg_temp, net_rad, press):
        """ Use Penman eqn to calculate top soil evaporation flux at the
        potential rate.

        Soil evaporation is dependent upon soil wetness and plant cover. The net
        radiation term is scaled for the canopy cover passed to this func and
        the impact of soil wetness is accounted for in the wtfac term. As the
        soil dries the evaporation component reduces significantly.

        Key assumptions from Ritchie...

        * When plant provides shade for the soil surface, evaporation will not
        be the same as bare soil evaporation. Wind speed, net radiation and VPD
        will all belowered in proportion to the canopy density. Following
        Ritchie role ofwind, VPD are assumed to be negligible and are therefore
        ignored.

        These assumptions are based on work with crops and whether this holds
        for tree shading where the height from the soil to the base of the
        crown is larger is questionable.

        units = (mm/day)

        References:
        -----------
        * Ritchie, 1972, Water Resources Research, 8, 1204-1213.

        Parameters:
        -----------
        avg_temp : float
            average daytime temp [degC]
        net_rad : float
            net radiation [mj m-2 day-1]
        press : float
            average daytime pressure [kPa]

        Returns:
        --------
        soil_evap : float
            soil evaporation [mm d-1]

        """
        P = Penman()
        soil_evap = P.calc_evaporation(net_rad, avg_temp, press)

        # if the available soil moisture is low the soil evaporation needs to
        # be reduced as well
        wtfac = (((self.state.pawater_tsoil / self.params.wcapac_topsoil) -
                    self.params.fwpmin) /
                    (self.params.fwpmax - self.params.fwpmin))

        soil_evap *= clip(wtfac, min=0.0, max=1.0)
        return soil_evap
Esempio n. 7
0
    def update_water_storage(self):
        """ Calculate root and top soil plant available water

        Soil drainage is estimated using a "leaky-bucket" approach with two
        soil layers. My visualisation is of 2 seperate buckets and the plant
        available water encompasses the top soil as well.
        """
        # Total root zone
        self.state.pawater_root += (self.fluxes.erain -
                                        self.fluxes.transpiration -
                                        self.fluxes.soil_evap)
        self.state.pawater_root = clip(self.state.pawater_root, min=0.0,
                                        max=self.params.wcapac_root)

        # Total soil layer
        self.state.pawater_tsoil += (self.fluxes.erain -
                                        self.fluxes.transpiration *
                                        self.params.fractup_soil -
                                        self.fluxes.soil_evap)

        self.state.pawater_tsoil = clip(self.state.pawater_tsoil, min=0.0,
                                        max=self.params.wcapac_topsoil)
Esempio n. 8
0
def iterationClip(cand, minLimit, maxLimit, limitless):
    """
    Adayın limitless değerine göre clip edilip edilmeyeceğine karar verir ve gerekliyse clip edip döndürür.
    Gerekli değil ise clip edilmemiş halini döndürür.

    Zorunlu Argümanlar:
    cand:       Adayın kendisi (list)
    minLimit:   İstenen minimum değer (int or float)
    maxLimit:   İstenen maximum değer (int or float)
    limitless:  Adayın alabileceği değerlerin sınırsız olduğunu söyler (bool)
    """
    if limitless:
        return cand
    else:
        return ut.clip(cand, minLimit, maxLimit)
Esempio n. 9
0
    def calc_carbon_allocation_fracs(self, nitfac, yr_index, project_day):
        """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.state.alleaf = (
                self.params.c_alloc_fmax + nitfac *
                (self.params.c_alloc_fmax - self.params.c_alloc_fmin))

            self.state.alroot = (
                self.params.c_alloc_rmax + nitfac *
                (self.params.c_alloc_rmax - self.params.c_alloc_rmin))

            self.state.albranch = (
                self.params.c_alloc_bmax + nitfac *
                (self.params.c_alloc_bmax - self.params.c_alloc_bmin))

            # allocate remainder to stem
            self.state.alstem = (1.0 - self.state.alleaf - self.state.alroot -
                                 self.state.albranch)
            #print self.state.alleaf, self.state.alstem, self.state.albranch, self.state.alroot

        elif self.control.alloc_model == "GRASSES":

            # calculate the N limitation based on available canopy N
            # this logic appears counter intuitive, but it works out when
            # applied with the perhaps backwards logic below
            nf = self.state.shootnc

            # case - completely limited by N availability
            if nf < self.params.nf_min:
                nlim = 0.0
            elif nf < self.params.nf_crit:

                nlim = ((nf - self.params.nf_min) /
                        (self.params.nf_crit - self.params.nf_min))
            # case - no N limitation
            else:
                nlim = 1.0

            # no constraint on water uptake via root mass, so makes no sense
            #limitation = self.sma(min(nlim, self.state.wtfac_root))

            # to increase allocation if water stressed.
            # dependent on lifespan of the roots...
            limitation = self.sma(nlim)
            self.state.prev_sma = limitation

            # figure out root allocation given available water & nutrients
            # hyperbola shape to allocation
            self.state.alroot = (
                self.params.c_alloc_rmax * self.params.c_alloc_rmin /
                (self.params.c_alloc_rmin +
                 (self.params.c_alloc_rmax - self.params.c_alloc_rmin) *
                 limitation))

            self.state.alstem = 0.0
            self.state.albranch = 0.0
            self.state.alleaf = (1.0 - self.state.alroot)
            #print nlim, limitation, self.state.alleaf, self.state.alroot
        elif self.control.alloc_model == "ALLOMETRIC":

            # calculate the N limitation based on available canopy N
            # this logic appears counter intuitive, but it works out when
            # applied with the perhaps backwards logic below
            nf = self.state.shootnc

            # case - completely limited by N availability
            if nf < self.params.nf_min:
                nlim = 0.0
            elif nf < self.params.nf_crit:

                nlim = ((nf - self.params.nf_min) /
                        (self.params.nf_crit - self.params.nf_min))
            # case - no N limitation
            else:
                nlim = 1.0

            # no constraint on water uptake via root mass, so makes no sense
            #limitation = self.sma(min(nlim, self.state.wtfac_root))

            # to increase allocation if water stressed.
            # dependent on lifespan of the roots...
            limitation = self.sma(nlim)
            self.state.prev_sma = limitation

            # figure out root allocation given available water & nutrients
            # hyperbola shape to allocation
            self.state.alroot = (
                self.params.c_alloc_rmax * self.params.c_alloc_rmin /
                (self.params.c_alloc_rmin +
                 (self.params.c_alloc_rmax - self.params.c_alloc_rmin) *
                 limitation))

            #self.state.alroot = (self.params.c_alloc_rmin +
            #                    (self.params.c_alloc_rmax -
            #                     self.params.c_alloc_rmin) * limitation)

            # 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)
            sap_cross_sec_area = (
                ((self.state.sapwood * const.TONNES_AS_KG * const.M2_AS_HA) /
                 self.params.cfracts) / self.state.canht / self.params.density)

            leaf2sap = self.state.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 self.params.leafsap0 < self.params.leafsap1:
                min_target = self.params.leafsap0
            else:
                min_target = self.params.leafsap1

            if self.params.leafsap0 > self.params.leafsap1:
                max_target = self.params.leafsap0
            else:
                max_target = self.params.leafsap1

            leaf2sa_target = (self.params.leafsap0 +
                              (self.params.leafsap1 - self.params.leafsap0) *
                              (self.state.canht - self.params.height0) /
                              (self.params.height1 - self.params.height0))
            leaf2sa_target = clip(leaf2sa_target,
                                  min=min_target,
                                  max=max_target)

            self.state.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.state.albranch = self.alloc_goal_seek(
                self.state.branch, target_branch, self.params.c_alloc_bmax,
                self.params.targ_sens)

            #target_coarse_roots = 0.34 * self.state.stem**0.84
            #self.state.alcroot = self.alloc_goal_seek(self.state.croot,
            #                                           target_coarse_roots,
            #                                           self.params.c_alloc_crmax,
            #                                           self.params.targ_sens)

            # allocation to stem is the residual
            self.state.alstem = (1.0 - self.state.alroot -
                                 self.state.albranch - self.state.alleaf)

            #print self.state.alleaf, self.state.albranch, self.state.alstem, self.state.alroot
        else:
            raise AttributeError('Unknown C allocation model')

        # Total allocation should be one, if not print warning:
        total_alloc = (self.state.alroot + self.state.alleaf +
                       self.state.albranch + self.state.alstem)
        if float_gt(total_alloc, 1.0):
            raise RuntimeError, "Allocation fracs > 1"
Esempio n. 10
0
    def calc_carbon_allocation_fracs(self, nitfac, yr_index, project_day):
        """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.state.alleaf = (self.params.c_alloc_fmax + nitfac *
                                (self.params.c_alloc_fmax - 
                                 self.params.c_alloc_fmin))
            
            self.state.alroot = (self.params.c_alloc_rmax + nitfac *
                                (self.params.c_alloc_rmax - 
                                 self.params.c_alloc_rmin))

            self.state.albranch = (self.params.c_alloc_bmax + nitfac *
                                  (self.params.c_alloc_bmax - 
                                   self.params.c_alloc_bmin))
        
            # allocate remainder to stem
            self.state.alstem = (1.0 - self.state.alleaf - self.state.alroot - 
                                 self.state.albranch)
            #print self.state.alleaf, self.state.alstem, self.state.albranch, self.state.alroot
        
        elif self.control.alloc_model == "GRASSES":
            
            # calculate the N limitation based on available canopy N
            # this logic appears counter intuitive, but it works out when
            # applied with the perhaps backwards logic below
            nf = self.state.shootnc
            
            # case - completely limited by N availability
            if nf < self.params.nf_min:
                nlim = 0.0
            elif nf < self.params.nf_crit:
               
                nlim = ((nf - self.params.nf_min) / 
                        (self.params.nf_crit - self.params.nf_min))
            # case - no N limitation
            else:
                nlim = 1.0
            
            # no constraint on water uptake via root mass, so makes no sense
            #limitation = self.sma(min(nlim, self.state.wtfac_root))
            
            # to increase allocation if water stressed.
            # dependent on lifespan of the roots...
            limitation = self.sma(nlim)
            self.state.prev_sma = limitation
            
            # figure out root allocation given available water & nutrients
            # hyperbola shape to allocation
            self.state.alroot = (self.params.c_alloc_rmax * 
                                 self.params.c_alloc_rmin / 
                                (self.params.c_alloc_rmin + 
                                (self.params.c_alloc_rmax - 
                                 self.params.c_alloc_rmin) * limitation))
            
            
            self.state.alstem = 0.0
            self.state.albranch = 0.0
            self.state.alleaf = (1.0 - self.state.alroot)
            #print nlim, limitation, self.state.alleaf, self.state.alroot
        elif self.control.alloc_model == "ALLOMETRIC":
            
            # calculate the N limitation based on available canopy N
            # this logic appears counter intuitive, but it works out when
            # applied with the perhaps backwards logic below
            nf = self.state.shootnc
            
            # case - completely limited by N availability
            if nf < self.params.nf_min:
                nlim = 0.0
            elif nf < self.params.nf_crit:
               
                nlim = ((nf - self.params.nf_min) / 
                        (self.params.nf_crit - self.params.nf_min))
            # case - no N limitation
            else:
                nlim = 1.0
           
           
            # no constraint on water uptake via root mass, so makes no sense
            #limitation = self.sma(min(nlim, self.state.wtfac_root))
            
            # to increase allocation if water stressed.
            # dependent on lifespan of the roots...
            limitation = self.sma(nlim)
            self.state.prev_sma = limitation
            
            
            # figure out root allocation given available water & nutrients
            # hyperbola shape to allocation
            self.state.alroot = (self.params.c_alloc_rmax * 
                                 self.params.c_alloc_rmin / 
                                (self.params.c_alloc_rmin + 
                                (self.params.c_alloc_rmax - 
                                 self.params.c_alloc_rmin) * limitation))
            
            #self.state.alroot = (self.params.c_alloc_rmin + 
            #                    (self.params.c_alloc_rmax - 
            #                     self.params.c_alloc_rmin) * limitation)
            
            
            # 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) 
            sap_cross_sec_area = (((self.state.sapwood * 
                                    const.TONNES_AS_KG * 
                                    const.M2_AS_HA) / 
                                    self.params.cfracts) / 
                                    self.state.canht / 
                                    self.params.density)
            
            leaf2sap = self.state.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 self.params.leafsap0 < self.params.leafsap1:
                min_target = self.params.leafsap0
            else:
                min_target = self.params.leafsap1
            
            if self.params.leafsap0 > self.params.leafsap1:
                max_target = self.params.leafsap0
            else:
                max_target = self.params.leafsap1
          
            leaf2sa_target = (self.params.leafsap0 + 
                             (self.params.leafsap1 - self.params.leafsap0) * 
                             (self.state.canht - self.params.height0) / 
                             (self.params.height1 - self.params.height0))
            leaf2sa_target = clip(leaf2sa_target, min=min_target, max=max_target)
        
            self.state.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.state.albranch = self.alloc_goal_seek(self.state.branch, 
                                                       target_branch, 
                                                       self.params.c_alloc_bmax, 
                                                       self.params.targ_sens) 

            #target_coarse_roots = 0.34 * self.state.stem**0.84
            #self.state.alcroot = self.alloc_goal_seek(self.state.croot, 
            #                                           target_coarse_roots, 
            #                                           self.params.c_alloc_crmax, 
            #                                           self.params.targ_sens)
            
            # allocation to stem is the residual
            self.state.alstem = (1.0 - self.state.alroot - 
                                       self.state.albranch - 
                                       self.state.alleaf)
                                       
            #print self.state.alleaf, self.state.albranch, self.state.alstem, self.state.alroot 
        else:
            raise AttributeError('Unknown C allocation model')
        
        # Total allocation should be one, if not print warning:
        total_alloc = (self.state.alroot + self.state.alleaf + 
                       self.state.albranch + self.state.alstem)
        if float_gt(total_alloc, 1.0):
            raise RuntimeError, "Allocation fracs > 1"