Esempio n. 1
0
 def computeNet(b, a, co, dia=gy.WMeanDiam, survival=None, weed=False):
     #net per tree
     #b - biomass, a-age, co -concentration
     if survival is None: survival = np.ones(len(a))
     idx_seedling = [0, 1, 2, 3, 4]  #np.where(dia<3.5)
     mass = interS(a, b / survival, k=1)
     dm = mass.derivative()
     rate = dm(a) * survival * dt
     if weed is False: rate[idx_seedling] = b[idx_seedling] * 0.2 * dt
     return np.maximum(co * rate, 0.0)
Esempio n. 2
0
def CWTr(nLyrs, z, dz, pF, Ksat, direction='positive'):
    """
    Returns interpolation functions 
        sto=f(gwl)  profile water storage as a function ofground water level
        gwl=f(sto)  ground water level
        tra=f(gwl)  transissivity
    Input:
        nLyrs number of soil layers
        d depth of layer midpoint
        dz layer thickness
        pF van Genuchten water retention parameters: ThetaS, ThetaR, alfa, n
        Ksat saturated hydraulic conductivity in m s-1. K in m/day.
        direction: positive or negative downwards
    """
    #-------Parameters ---------------------
    z = np.array(z)   
    dz =np.array(dz)
    #--------- Connection between gwl and water storage------------

    if direction =='positive':
        print 'no positive direction available'
        import sys; sys.exit()
        gwl=np.linspace(0.,sum(dz),500)
        sto = [sum(wrc(pF, x = np.minimum(z-g, 0.0))*dz) for g in gwl]     #equilibrium head m                
    else:
        gwl=np.linspace(0.,-sum(dz),100)        
        sto = [sum(wrc(pF, x = np.minimum(z+g, 0.0))*dz) for g in gwl]     #equilibrium head m
        gwlabove = [2.0,1.5,0.5]        
        stoabove = [sto[0]+0.75*gwlabove[0], sto[0]+0.3*gwlabove[1], sto[0]+0.2*gwlabove[2]]
        stoT=list(stoabove)+list(sto)
        gwlT=list(gwlabove)+list(gwl)                
        gwlToSto = interp1d(np.array(gwlT), np.array(stoT), fill_value='extrapolate')
        stoT = list(stoT); gwl= list(gwlT)        
        sto.reverse(); gwl.reverse()
        stoToGwl =interp1d(np.array(stoT), np.array(gwlT), fill_value='extrapolate')

        cc=np.gradient(gwlToSto(gwlT))/np.gradient(gwlT)
#        cc = np.gradient(gwlToSto(gwlT), gwlT) # Iñaki
        cc[cc<0.2]=0.2
#        C = interp1d(np.array(gwlT), cc, bounds_error=False, fill_value=(0.,1.) )  #storage coefficient function
        
        # Iñaki's way:
        zeta = -z
        C = interp1d(zeta, wrc(pF, zeta), bounds_error=False, fill_value=(wrc(pF[-1], zeta[-1]), 1.))

        

    #import matplotlib.pylab as plt
    #plt.plot(gwlT, cc, 'ro')
    #plt.plot(gwlT, C(gwlT), 'b-')
    #import sys; sys.exit()
    gwlToSto = interp1d(np.array(gwlT), np.array(stoT), fill_value='extrapolate') 
    stoT = list(stoT); gwlT= list(gwlT)        
    stoT.reverse(); gwlT.reverse()
    stoToGwl =interp1d(np.array(stoT), np.array(gwlT), fill_value='extrapolate')

    del gwlT, stoT
        
    #----------Transmissivity-------------------
    K = np.array(Ksat*86400.)   #from m/s to m/day
    tr =[sum(K[t:] * dz[t:]) for t in range(nLyrs)]        
    if direction=='positive':        
        gwlToTra = interS(z, np.array(tr))            
    else:
        z= list(z);  z.reverse(); tr.reverse()
        gwlToTra = interS(-np.array(z), np.array(tr), k=3, ext='const') # returns limiting value outside interpolation domain 
    del tr
    return gwlToSto, K, gwlToTra, C
Esempio n. 3
0
    def fWeeds(self, rowGrowth=0, rowGen=0):
        """
        Computes weed biomass, LAI, and litterfall. Allows setting the maximum green mass (stand foliage + weeds) \n
        limit; if exceeded, weed mass is reduced. Weeding is given as time list (time in months); in weeding \n 
        all weed biomass is located to litter from dead plants (no retranslocation). Litter produced  \n
        because of weed longevity is located to litter from living plants (retranslocation allowed):  \n
            Kwargs: \n 
            - rowGrowth - row index in parameter file growth and yield page \n
            - rowGen - row index in parameter file general parameters \n            
            Input: \n
            - maxGreenMass - maximum allowed sum of tree stand foliage and weed above ground mass, kg ha-1  \n
            - weedings - time of weeding, as list, month at when weeding is applied \n
            - appAge - apparent age in yrs, np.array (adjusted age because of Type 1 growth response) \n
            - BiFoliage  - mass of tree stand foliage, kg ha-1 \n
            Output: \n
            - weedAbove - above ground weed biomass, kg ha-1
            - weedBelow - below ground weed biomass, kg ha-1
            - weedALitL - weed litter from living above ground plant parts, allows retranslocation, kg ha-1 timestep-1
            - weedALitD - weed litter from dead above ground plant parts, no retranslocation, kg ha-1 timestep-1
            - weedBLitL - weed litter from living below ground plant parts, allows retranslocation, kg ha-1 timestep-1
            - weedBLitD - weed litter from dead below ground plant parts, no retranslocation, kg ha-1 timestep-1
            - weedLAI - weed leaf area index, m2 m-2
                     
        """
        print('    + Weeds')
        #-----These to arguments--------------
        if len(self.weedings) == 0:
            self.weedings = np.array(
                [0, 3, 6, 9,
                 18])  #give the age when weeding is done, in months
        weedings = self.weedings
        age = self.age
        nsteps = int(len(age) / (self.rotation + 1))
        appAge = self.appAge[-nsteps:]
        BiFoliage = self.BiFoliage[-nsteps:]
        weedALitD = np.zeros(nsteps)
        weedBLitD = np.zeros(nsteps)

        #--------Parameters---------------
        r = rowGrowth
        rg = rowGen
        maxGreenMass = self.g['maxGreenMass'][
            rg]  #maximum green mass includes above ground weed mass and tree stand foliage mass
        self.maxGreenMass = maxGreenMass
        b0 = self.p['Wbeta0'][r]
        b1 = self.p['Wbeta1'][
            r]  #Weed biomass growth parameters, result in kg/ha
        sa = self.g['WeedLeafA'][rg]
        rsr = self.g['WeedRSRatio'][
            rg]  #Specific leaf area weeds kg/m2, root to shoot-ratio
        wl = self.g['WeedsLon'][rg]  #weed logevity
        dt = self.g['dt'][0] / 12.0
        Nrotat = int(self.g['Nrotat'][0])
        a_age = np.arange(dt, dt * nsteps + dt,
                          dt)  #age between 0...end of rotation yrs
        fW = lambda a: b0 * np.exp(-b1 / a)

        #----Construction of weed age series, in weeding set to 0. Apparent age is used in the growth computation, allows consuderation of fertilization,
        a1 = np.diff(appAge)
        a1 = np.insert(a1, 0, a1[0])
        n = 0
        aa = 0
        tmpAges = []
        k = 0
        for a in range(nsteps):
            if n < len(weedings):
                if a == weedings[n]:
                    aa = 0
                    n += 1
                else:
                    aa += a1[k]
                tmpAges.append(aa)
            else:
                aa += a1[k]
                tmpAges.append(aa)
        tmpAges = np.array(tmpAges) + 0.001  #to avoid zero division

        #---weed biomass computation ----------------------
        WLAI = fW(tmpAges) * sa / 10000.0  # Leaf area index m2/m2
        weedAbove = fW(tmpAges)  # Above ground weed mass kg/ha

        weedBelow = weedAbove * rsr  # Below ground weed mass kg/ha
        potTot = weedAbove + BiFoliage  # potential total green biomass in site
        cutTot = np.minimum(
            potTot,
            maxGreenMass)  # cut the fraction exceeding the maximum green mass
        cutWeed = np.where(
            cutTot - potTot < 0, cutTot - potTot,
            0.0)  # the exceeding part is subtracted from weed mass
        weedAbove = np.maximum(weedAbove + cutWeed,
                               0.0)  # adjust the weed mass
        weedBelow = weedAbove * rsr

        t = interS(a_age, weedAbove, k=1)
        #t = interS(tmpAges, weedAbove, k=1)
        dW = t.derivative()
        #-------Litter production by age and longevity (retranslocation allowed) ---------------
        weedALitL = self.computeWeedLitter(
            age, nsteps, wl, dt,
            weedAbove)  # above ground litter production, kg ha-1 timestep-1
        weedBLitL = weedALitL * rsr  # below ground litter production, kg ha-a timestep-1
        weedDecline = np.where(dW(a_age) < 0, dW(a_age), 0.0) * dt * -1.0

        weedALitL = weedALitL + weedDecline
        weedBLitL = weedBLitL + weedDecline * rsr

        #-------In weeding, locate weed biomass to litter from dead plants (no retranslocation)
        n = 0
        for a in range(nsteps):
            if a == weedings[n]:
                aa = tmpAges[a - 1] + dt if a != 0 else 0.001
                weedALitL[a] = 0.0
                weedBLitL[a] = 0.0
                weedALitD[a] = fW(aa)
                weedBLitD[a] = weedALitD[a] * rsr
                n += 1
                if n == len(weedings):
                    break

        #-------Initialize litter with final biomass
        if self.rotation == 0:
            weedALitD[0] = weedAbove[-1]
            weedBLitD[0] = weedBelow[-1]
        if self.rotation < Nrotat - 1:
            weedALitD[nsteps - 1] = weedAbove[-1]
            weedBLitD[nsteps - 1] = weedBelow[-1]

        #-------append results into instance variables --------------
        self.weedAbove = np.append(self.weedAbove, weedAbove)
        self.weedBelow = np.append(self.weedBelow, weedBelow)
        self.weedALitL = np.append(self.weedALitL, weedALitL)
        self.weedBLitL = np.append(self.weedBLitL, weedBLitL)
        self.weedALitD = np.append(self.weedALitD, weedALitD)
        self.weedBLitD = np.append(self.weedBLitD, weedBLitD)
        self.weedLAI = np.append(self.weedLAI, WLAI)
Esempio n. 4
0
    def fLitter(self, rowGen=0):
        """
        Computes litterfall from longevity (life span, years) and quantity of biomass components.  
        Makes distinction between litter generated from living trees and that from dead stems.
        Composes continous function from acculumation of biomass fractions, computes its derivative, and uses it with 
        biomass fraction life span to compute the litter output. \n
        Output: \n
            FoLitL - foliage litter from living trees (kg/ha/time step) \n
            BaLitL - bark litter from living trees (kg/ha/timestep) \n
            BrLitL - branch litter from living trees (kg/ha/timestep) \n
            FineRLitL - fine root litter from living trees (kg/ha/timestep) \n
            \n
            FoLitD - foliage litter from dead trees (kg/ha/timestep) \n
            BaLitD - bark litter from dead trees (kg/ha/timestep) \n 
            BrLitD - branch litter from dead trees (kg/ha/timestep) \n
            RootLitD - root litter from dead trees (kg/ha/timestep) \n
            CWD - stems (coarse woody debris, kg/ha/timestep)
        Kwargs: \n
            rowGen  - row index in parameterfile General parameters
        """
        #-----Parameters-------------------------
        print('    + Litter')
        rg = rowGen
        BarkLon = self.g['BarkLon'][rg]
        BranchLon = self.g['BranchLon'][rg]
        LeafLon = self.g['LeafLon'][rg]
        FineRLon = self.g['FineRLon'][rg]
        FineRootPr = self.g['FineRootPr'][rg]
        nsteps = int(len(self.age) / (self.rotation + 1))
        dt = self.g['dt'][0] / 12.0
        Nrotat = int(self.g['Nrotat'][0])
        age = self.age[-nsteps:]
        Survival = self.Survival[-nsteps:]
        BiFoliage = self.BiFoliage[-nsteps:]
        BiBark = self.BiBark[-nsteps:]
        BiBranch = self.BiBranch[-nsteps:]
        RootMass = self.RootMass[-nsteps:]
        MerchMass = self.MerchMass[-nsteps:]

        #-----compute litter production from living trees
        FoGrTree = self.FoGrTree[-nsteps:]
        BaGrTree = self.BaGrTree[-nsteps:]
        BrGrTree = self.BrGrTree[-nsteps:]
        RootGrTree = self.RootGrTree[-nsteps:]
        FineRootGrTree = self.FineRootGrTree[-nsteps:]

        FoLitL = shift(FoGrTree, int(LeafLon / dt),
                       cval=0.0) * Survival  # litterfall kg/ha/timestep
        BaLitL = shift(BaGrTree, int(BarkLon / dt), cval=0.0) * Survival
        BrLitL = shift(BrGrTree, int(BranchLon / dt), cval=0.0) * Survival
        FineRLitL = shift(FineRootGrTree, int(FineRLon / dt),
                          cval=0.0) * Survival

        #-------compute litter production caused by mortality
        fsur = interS(age, Survival, k=1)
        dS = fsur.derivative()  #rate of mortailty
        M = dS(age) * dt * -1.0  #Number of dead trees in time step
        FoLitD = BiFoliage * M / Survival
        BaLitD = BiBark * M / Survival
        BrLitD = BiBranch * M / Survival
        RootLitD = RootMass * M / Survival
        CWD = MerchMass * M / Survival

        #-------add logging residues to litter production, initialization and then at the end of rotation ------------
        optLResid = True  #in Ireland, no previous stand
        if optLResid:
            if self.rotation == 0:
                FoLitD[0] = BiFoliage[-1]
                BaLitD[0] = BiBark[-1] * 0.3
                BrLitD[0] = BiBranch[-1]
                RootLitD[0] = RootMass[-1] * 0.5  #stumps & roots
                CWD[0] = sum(CWD) * 0.5 + RootMass[-1] * 0.5
            if self.rotation < Nrotat - 1:
                FoLitD[nsteps - 1] = BiFoliage[-1]
                BaLitD[nsteps - 1] = BiBark[-1] * 0.3
                BrLitD[nsteps - 1] = BiBranch[-1]
                RootLitD[nsteps - 1] = RootMass[-1] * 0.5
                CWD[nsteps - 1] = RootMass[-1] * 0.5

        #-------append results into instance variables --------------
        self.FoLitL = np.append(self.FoLitL, FoLitL)
        self.BaLitL = np.append(self.BaLitL, BaLitL)
        self.BrLitL = np.append(self.BrLitL, BrLitL)
        self.FineRLitL = np.append(self.FineRLitL, FineRLitL)
        self.FoLitD = np.append(self.FoLitD, FoLitD)
        self.BaLitD = np.append(self.BaLitD, BaLitD)
        self.BrLitD = np.append(self.BrLitD, BrLitD)
        self.RootLitD = np.append(self.RootLitD, RootLitD)
        self.CWD = np.append(self.CWD, CWD)

        del FoLitL, FoLitD, BaLitL, BaLitD, BrLitL, BrLitD, FineRLitL, RootLitD, CWD
Esempio n. 5
0
    def releaseFert(self, gy):
        """
         fertilizer parameters: N (%), P2O5 (%), K2O (%),
             release kinetics parameters (month^-1): Kn, Kp, Kk
        """

        nStems = gy.Survival[0]  # number of stems /ha
        dt = gy.g['dt'][0]
        Nrotat = int(
            gy.g['Nrotat'][0])  # time step (months), number of rotations
        lr = gy.g['Lrotat'][0] * 12.0
        dtInRot = lr / dt  # length of rotation (months), timesteps in rotation
        length = int(Nrotat * dtInRot * dt +
                     Nrotat)  # lenght of array including all timesteps
        self.ferSto = np.zeros(length)
        self.ferRel = np.zeros(
            length
        )  # undissonlved fertilizer in soil kg ha-1, nutrient release  kg ha-1 timestep-1

        ferSto = np.zeros(length)  # local: fertilizer nutrient storage
        ferRel = np.zeros(length)
        time = np.arange(0, length)

        if self.name == 'Nitrogen':
            name = 'N'
            k = 'Kn'
            conv = 1.0
        if self.name == 'Phosphorus':
            name = 'P'
            k = 'Kp'
            conv = 0.42
        if self.name == 'Potassium':
            name = 'K'
            k = 'Kk'
            conv = 0.83

        fpara = {
            'NPK': {
                'N': 23,
                'P': 13,
                'K': 7,
                'Kn': 0.94,
                'Kp': 0.04,
                'Kk': 0.3
            },
            'Suburin': {
                'N': 7,
                'P': 15,
                'K': 8,
                'Kn': 0.94,
                'Kp': 0.04,
                'Kk': 0.3
            },
            'Urea': {
                'N': 45,
                'P': 0,
                'K': 0,
                'Kn': 0.9,
                'Kp': 0,
                'Kk': 0
            },
            'RP': {
                'N': 0,
                'P': 9,
                'K': 0,
                'Kn': 0,
                'Kp': 0.04,
                'Kk': 0
            },
            'Kcl': {
                'N': 0,
                'P': 0,
                'K': 60,
                'Kn': 0,
                'Kp': 0,
                'Kk': 0.8
            },
            'Ash': {
                'N': 0,
                'P': 2.8 / 0.42,
                'K': 7.6 / 0.83,
                'Kn': 0,
                'Kp': 0.01,
                'Kk': 0.01
            },
        }

        for r in range(Nrotat):  # rotation
            for t in range(len(self.fer['time'])):  # times in input data frame
                m = int(self.fer['time'][t] + (r * lr))  # fertilization month
                for a in self.fer.keys():  # fertilizers in use
                    if self.fer[a][t] != 0 and a != 'time':  # dose is > 0
                        feNut = fpara[a][name]
                        kk = fpara[a][k]
                        dose = self.fer[a][t]
                        if kk > 0:  # nutrient is in the fertilizer
                            #print feNut, self.name, conv, nStems, kk
                            feNut = feNut * conv / 100. * dose * nStems / 1000.0
                            ts = time[m:] - m
                            ferSto[m:] = ferSto[m:] + feNut * np.exp(
                                -kk * ts * dt)
                            ferRel[m:] = ferRel[m:] + feNut * (
                                1 - np.exp(-kk * ts * dt))

        func = interS(time, ferRel, k=1)
        dfdt = func.derivative()
        self.ferRel = dfdt(time) * dt

        self.ferSto = ferSto
Esempio n. 6
0
def CWTr(nLyrs, z, dz, pF, Ksat, direction='positive'):
    """
    Returns interpolation functions 
        sto=f(gwl)  profile water storage as a function ofground water level
        gwl=f(sto)  ground water level
        tra=f(gwl)  transmissivity
    Input:
        nLyrs number of soil layers
        d depth of layer midpoint
        dz layer thickness
        pF van Genuchten water retention parameters: ThetaS, ThetaR, alfa, n
        Ksat saturated hydraulic conductivity in m s-1. K in m/day.
        direction: positive or negative downwards
    """
    #-------Parameters ---------------------
    z = np.array(z)
    dz = np.array(dz)
    #--------- Connection between gwl and water storage------------

    if direction == 'positive':
        print('no positive direction available')
        import sys
        sys.exit()
        gwl = np.linspace(0., sum(dz), 500)
        sto = [sum(wrc(pF, x=np.minimum(z - g, 0.0)) * dz)
               for g in gwl]  #equilibrium head m
    else:
        # Previously...
        gwl = np.linspace(0., -sum(dz), 100)
        sto = [sum(wrc(pF, x=np.minimum(z + g, 0.0)) * dz)
               for g in gwl]  #equilibrium head m
        gwlabove = [2.0, 1.5, 0.5]
        stoabove = [
            sto[0] + 0.75 * gwlabove[0], sto[0] + 0.3 * gwlabove[1],
            sto[0] + 0.2 * gwlabove[2]
        ]
        stoT = list(stoabove) + list(sto)
        gwlT = list(gwlabove) + list(gwl)
        gwlToSto = interp1d(np.array(gwlT),
                            np.array(stoT),
                            fill_value='extrapolate')
        stoT = list(stoT)
        gwl = list(gwlT)
        sto.reverse()
        gwl.reverse()
        stoToGwl = interp1d(np.array(stoT),
                            np.array(gwlT),
                            fill_value='extrapolate')

        cc = np.gradient(gwlToSto(gwlT)) / np.gradient(gwlT)
        #        cc = np.gradient(gwlToSto(gwlT), gwlT) # Iñaki
        cc[cc < 0.2] = 0.2
        C = interp1d(np.array(gwlT),
                     cc,
                     bounds_error=False,
                     fill_value=(0., 1.))  #storage coefficient function

        # New approach, 28.V: to test Boussinesq eq. with theta.
        # Idea of the new approach. C is not smooth enough with current parameters, so we take the liberty to return
        # theta as if it was the C = d \theta/ dh, because theta is smooth. This leads to the integral of theta
        # working as theta itself, because integral(C dh) = \theta by def of C.
        # All this is to test solving the Boussinesq eq in both the phi and the theta form and compare them.
        # theta is the volumetric water content
        # theta_integral is integral of theta over dh
        zeta = -z
        theta = interp1d(zeta,
                         wrc(pF, zeta),
                         bounds_error=False,
                         fill_value=(wrc(pF[-1], zeta[-1]), 1.))
        # C = interp1d(zeta, -np.gradient(theta(zeta), dz[0]))
        theta_integral = interp1d(zeta[::-1],
                                  np.cumsum(theta(zeta)[::-1]),
                                  bounds_error=False)

        # This is the line where the labyrinth happens
        C = theta
        theta = theta_integral

    #import matplotlib.pylab as plt
    #plt.plot(gwlT, cc, 'ro')
    #plt.plot(gwlT, C(gwlT), 'b-')
    #import sys; sys.exit()
    gwlToSto = interp1d(np.array(gwlT),
                        np.array(stoT),
                        fill_value='extrapolate')
    stoT = list(stoT)
    gwlT = list(gwlT)
    stoT.reverse()
    gwlT.reverse()
    stoToGwl = interp1d(np.array(stoT),
                        np.array(gwlT),
                        fill_value='extrapolate')

    del gwlT, stoT

    #----------Transmissivity-------------------
    K = np.array(Ksat * 86400.)  #from m/s to m/day
    tr = [sum(K[t:] * dz[t:]) for t in range(nLyrs)]
    if direction == 'positive':
        gwlToTra = interS(z, np.array(tr))
    else:
        z = list(z)
        z.reverse()
        tr.reverse()
        gwlToTra = interS(
            -np.array(z), np.array(tr), k=3,
            ext='const')  # returns limiting value outside interpolation domain
    del tr
    return gwlToSto, K, gwlToTra, C, theta
Esempio n. 7
0
 def composeDwtFromTs(self, time):
     from scipy.interpolate import InterpolatedUnivariateSpline as interS
     t = np.array(self.Time['time'])
     dwt = np.array(self.Time['wt']) * -1
     smooth = interS(t, dwt, k=1)
     return smooth(time)