Source code for pcse.crop.npk_dynamics

#!/usr/bin/env python

from .. import exceptions as exc
from ..traitlets import Float, Int, Instance
from ..decorators import prepare_rates, prepare_states
from ..base import ParamTemplate, StatesTemplate, RatesTemplate, \
    SimulationObject
from ..util import AfgenTrait
from .nutrients import NPK_Translocation
from .nutrients import NPK_Demand_Uptake


[docs]class NPK_Crop_Dynamics(SimulationObject): """Implementation of overall NPK crop dynamics. NPK_Crop_Dynamics implements the overall logic of N/P/K book-keeping within the crop. **Simulation parameters** ============= ================================================= ======================= Name Description Unit ============= ================================================= ======================= NMAXLV_TB Maximum N concentration in leaves as kg N kg-1 dry biomass function of dvs PMAXLV_TB As for P kg P kg-1 dry biomass KMAXLV_TB As for K kg K kg-1 dry biomass NMAXRT_FR Maximum N concentration in roots as fraction - of maximum N concentration in leaves PMAXRT_FR As for P - KMAXRT_FR As for K - NMAXST_FR Maximum N concentration in stems as fraction - of maximum N concentration in leaves KMAXST_FR As for K - PMAXST_FR As for P - NRESIDLV Residual N fraction in leaves kg N kg-1 dry biomass PRESIDLV Residual P fraction in leaves kg P kg-1 dry biomass KRESIDLV Residual K fraction in leaves kg K kg-1 dry biomass NRESIDRT Residual N fraction in roots kg N kg-1 dry biomass PRESIDRT Residual P fraction in roots kg P kg-1 dry biomass KRESIDRT Residual K fraction in roots kg K kg-1 dry biomass NRESIDST Residual N fraction in stems kg N kg-1 dry biomass PRESIDST Residual P fraction in stems kg P kg-1 dry biomass KRESIDST Residual K fraction in stems kg K kg-1 dry biomass ============= ================================================= ======================= **State variables** ========== ================================================== ============ Name Description Unit ========== ================================================== ============ NamountLV Actual N amount in living leaves |kg N ha-1| PamountLV Actual P amount in living leaves |kg P ha-1| KamountLV Actual K amount in living leaves |kg K ha-1| NamountST Actual N amount in living stems |kg N ha-1| PamountST Actual P amount in living stems |kg P ha-1| KamountST Actual K amount in living stems |kg K ha-1| NamountSO Actual N amount in living storage organs |kg N ha-1| PamountSO Actual P amount in living storage organs |kg P ha-1| KamountSO Actual K amount in living storage organs |kg K ha-1| NamountRT Actual N amount in living roots |kg N ha-1| PamountRT Actual P amount in living roots |kg P ha-1| KamountRT Actual K amount in living roots |kg K ha-1| Nuptake_T total absorbed N amount |kg N ha-1| Puptake_T total absorbed P amount |kg P ha-1| Kuptake_T total absorbed K amount |kg K ha-1| Nfix_T total biological fixated N amount |kg N ha-1| ========== ================================================== ============ **Rate variables** =========== ================================================= ================ Name Description Unit =========== ================================================= ================ RNamountLV Weight increase (N) in leaves |kg N ha-1 d-1| RPamountLV Weight increase (P) in leaves |kg P ha-1 d-1| RKamountLV Weight increase (K) in leaves |kg K ha-1 d-1| RNamountST Weight increase (N) in stems |kg N ha-1 d-1| RPamountST Weight increase (P) in stems |kg P ha-1 d-1| RKamountST Weight increase (K) in stems |kg K ha-1 d-1| RNamountRT Weight increase (N) in roots |kg N ha-1 d-1| RPamountRT Weight increase (P) in roots |kg P ha-1 d-1| RKamountRT Weight increase (K) in roots |kg K ha-1 d-1| RNamountSO Weight increase (N) in storage organs |kg N ha-1 d-1| RPamountSO Weight increase (P) in storage organs |kg P ha-1 d-1| RKamountSO Weight increase (K) in storage organs |kg K ha-1 d-1| RNdeathLV Rate of N loss in leaves |kg N ha-1 d-1| RPdeathLV as for P |kg P ha-1 d-1| RKdeathLV as for K |kg K ha-1 d-1| RNdeathST Rate of N loss in roots |kg N ha-1 d-1| RPdeathST as for P |kg P ha-1 d-1| RKdeathST as for K |kg K ha-1 d-1| RNdeathRT Rate of N loss in stems |kg N ha-1 d-1| RPdeathRT as for P |kg P ha-1 d-1| RKdeathRT as for K |kg K ha-1 d-1| RNloss N loss due to senescence |kg N ha-1 d-1| RPloss P loss due to senescence |kg P ha-1 d-1| RKloss K loss due to senescence |kg K ha-1 d-1| =========== ================================================= ================ **Signals send or handled** None **External dependencies** ======= =================================== ==================== ============== Name Description Provided by Unit ======= =================================== ==================== ============== DVS Crop development stage DVS_Phenology - WLV Dry weight of living leaves WOFOST_Leaf_Dynamics |kg ha-1| WRT Dry weight of living roots WOFOST_Root_Dynamics |kg ha-1| WST Dry weight of living stems WOFOST_Stem_Dynamics |kg ha-1| DRLV Death rate of leaves WOFOST_Leaf_Dynamics |kg ha-1 d-1| DRRT Death rate of roots WOFOST_Root_Dynamics |kg ha-1 d-1| DRST Death rate of stems WOFOST_Stem_Dynamics |kg ha-1 d-1| ======= =================================== ==================== ============== """ translocation = Instance(SimulationObject) demand_uptake = Instance(SimulationObject) NamountLVI = Float(-99.) # initial soil N amount in leaves NamountSTI = Float(-99.) # initial soil N amount in stems NamountRTI = Float(-99.) # initial soil N amount in roots NamountSOI = Float(-99.) # initial soil N amount in storage organs PamountLVI = Float(-99.) # initial soil P amount in leaves PamountSTI = Float(-99.) # initial soil P amount in stems PamountRTI = Float(-99.) # initial soil P amount in roots PamountSOI = Float(-99.) # initial soil P amount in storage organs KamountLVI = Float(-99.) # initial soil K amount in leaves KamountSTI = Float(-99.) # initial soil K amount in stems KamountRTI = Float(-99.) # initial soil K amount in roots KamountSOI = Float(-99.) # initial soil K amount in storage organs class Parameters(ParamTemplate): DVS_NPK_STOP = Float(-99.) NMAXLV_TB = AfgenTrait() PMAXLV_TB = AfgenTrait() KMAXLV_TB = AfgenTrait() NMAXST_FR = Float(-99.) NMAXRT_FR = Float(-99.) PMAXST_FR = Float(-99.) PMAXRT_FR = Float(-99.) KMAXST_FR = Float(-99.) KMAXRT_FR = Float(-99.) NRESIDLV = Float(-99.) # residual N fraction in leaves [kg N kg-1 dry biomass] NRESIDST = Float(-99.) # residual N fraction in stems [kg N kg-1 dry biomass] NRESIDRT = Float(-99.) # residual N fraction in roots [kg N kg-1 dry biomass] PRESIDLV = Float(-99.) # residual P fraction in leaves [kg P kg-1 dry biomass] PRESIDST = Float(-99.) # residual P fraction in stems [kg P kg-1 dry biomass] PRESIDRT = Float(-99.) # residual P fraction in roots [kg P kg-1 dry biomass] KRESIDLV = Float(-99.) # residual K fraction in leaves [kg K kg-1 dry biomass] KRESIDST = Float(-99.) # residual K fraction in stems [kg K kg-1 dry biomass] KRESIDRT = Float(-99.) # residual K fraction in roots [kg K kg-1 dry biomass] class StateVariables(StatesTemplate): NamountLV = Float(-99.) # N amount in leaves [kg N ha-1] PamountLV = Float(-99.) # P amount in leaves [kg P ] KamountLV = Float(-99.) # K amount in leaves [kg K ] NamountST = Float(-99.) # N amount in stems [kg N ] PamountST = Float(-99.) # P amount in stems [kg P ] KamountST = Float(-99.) # K amount in stems [kg K ] NamountSO = Float(-99.) # N amount in storage organs [kg N ] PamountSO = Float(-99.) # P amount in storage organs [kg P ] KamountSO = Float(-99.) # K amount in storage organs [kg K ] NamountRT = Float(-99.) # N amount in roots [kg N ] PamountRT = Float(-99.) # P amount in roots [kg P ] KamountRT = Float(-99.) # K amount in roots [kg K ] NuptakeTotal = Float(-99.) # total absorbed N amount [kg N ] PuptakeTotal = Float(-99.) # total absorbed P amount [kg P ] KuptakeTotal = Float(-99.) # total absorbed K amount [kg K ] NfixTotal = Float(-99.) # total biological fixated N amount [kg N ] NlossesTotal = Float(-99.) PlossesTotal = Float(-99.) KlossesTotal = Float(-99.) class RateVariables(RatesTemplate): RNamountLV = Float(-99.) # Net rates of NPK in different plant organs RPamountLV = Float(-99.) RKamountLV = Float(-99.) RNamountST = Float(-99.) RPamountST = Float(-99.) RKamountST = Float(-99.) RNamountRT = Float(-99.) RPamountRT = Float(-99.) RKamountRT = Float(-99.) RNamountSO = Float(-99.) RPamountSO = Float(-99.) RKamountSO = Float(-99.) RNdeathLV = Float(-99.) # N loss rate leaves [kg ha-1 d-1] RNdeathST = Float(-99.) # N loss rate stems [kg ha-1 d-1] RNdeathRT = Float(-99.) # N loss rate roots [kg ha-1 d-1] RPdeathLV = Float(-99.) # P loss rate leaves [kg ha-1 d-1] RPdeathST = Float(-99.) # P loss rate stems [kg ha-1 d-1] RPdeathRT = Float(-99.) # P loss rate roots [kg ha-1 d-1] RKdeathLV = Float(-99.) # K loss rate leaves [kg ha-1 d-1] RKdeathST = Float(-99.) # K loss rate stems [kg ha-1 d-1] RKdeathRT = Float(-99.) # K loss rate roots [kg ha-1 d-1] RNloss = Float(-99.) RPloss = Float(-99.) RKloss = Float(-99.) def initialize(self, day, kiosk, parvalues): """ :param kiosk: variable kiosk of this PCSE instance :param parvalues: dictionary with parameters as key/value pairs """ self.params = self.Parameters(parvalues) self.rates = self.RateVariables(kiosk) self.kiosk = kiosk # Initialize components of the npk_crop_dynamics self.translocation = NPK_Translocation(day, kiosk, parvalues) self.demand_uptake = NPK_Demand_Uptake(day, kiosk, parvalues) # INITIAL STATES params = self.params k = kiosk # Initial amounts self.NamountLVI = NamountLV = k.WLV * params.NMAXLV_TB(k.DVS) self.NamountSTI = NamountST = k.WST * params.NMAXLV_TB(k.DVS) * params.NMAXST_FR self.NamountRTI = NamountRT = k.WRT * params.NMAXLV_TB(k.DVS) * params.NMAXRT_FR self.NamountSOI = NamountSO = 0. self.PamountLVI = PamountLV = k.WLV * params.PMAXLV_TB(k.DVS) self.PamountSTI = PamountST = k.WST * params.PMAXLV_TB(k.DVS) * params.PMAXST_FR self.PamountRTI = PamountRT = k.WRT * params.PMAXLV_TB(k.DVS) * params.PMAXRT_FR self.PamountSOI = PamountSO = 0. self.KamountLVI = KamountLV = k.WLV * params.KMAXLV_TB(k.DVS) self.KamountSTI = KamountST = k.WST * params.KMAXLV_TB(k.DVS) * params.KMAXST_FR self.KamountRTI = KamountRT = k.WRT * params.KMAXLV_TB(k.DVS) * params.KMAXRT_FR self.KamountSOI = KamountSO = 0. self.states = self.StateVariables(kiosk, publish=["NamountLV", "NamountST", "NamountRT", "NamountSO", "PamountLV", "PamountST", "PamountRT", "PamountSO", "KamountLV", "KamountST", "KamountRT", "KamountSO"], NamountLV=NamountLV, NamountST=NamountST, NamountRT=NamountRT, NamountSO=NamountSO, PamountLV=PamountLV, PamountST=PamountST, PamountRT=PamountRT, PamountSO=PamountSO, KamountLV=KamountLV, KamountST=KamountST, KamountRT=KamountRT, KamountSO=KamountSO, NuptakeTotal=0, PuptakeTotal=0., KuptakeTotal=0., NfixTotal=0., NlossesTotal=0, PlossesTotal=0., KlossesTotal=0.) @prepare_rates def calc_rates(self, day, drv): rates = self.rates params = self.params k = self.kiosk self.demand_uptake.calc_rates(day, drv) self.translocation.calc_rates(day, drv) # Compute loss of NPK due to death of plant material rates.RNdeathLV = params.NRESIDLV * k.DRLV rates.RNdeathST = params.NRESIDST * k.DRST rates.RNdeathRT = params.NRESIDRT * k.DRRT rates.RPdeathLV = params.PRESIDLV * k.DRLV rates.RPdeathST = params.PRESIDST * k.DRST rates.RPdeathRT = params.PRESIDRT * k.DRRT rates.RKdeathLV = params.KRESIDLV * k.DRLV rates.RKdeathST = params.KRESIDST * k.DRST rates.RKdeathRT = params.KRESIDRT * k.DRRT # N rates in leaves, stems, root and storage organs computed as # uptake - translocation - death. # except for storage organs which only take up as a result of translocation. rates.RNamountLV = k.RNuptakeLV - k.RNtranslocationLV - rates.RNdeathLV rates.RNamountST = k.RNuptakeST - k.RNtranslocationST - rates.RNdeathST rates.RNamountRT = k.RNuptakeRT - k.RNtranslocationRT - rates.RNdeathRT rates.RNamountSO = k.RNuptakeSO # P rates in leaves, stems, root and storage organs rates.RPamountLV = k.RPuptakeLV - k.RPtranslocationLV - rates.RPdeathLV rates.RPamountST = k.RPuptakeST - k.RPtranslocationST - rates.RPdeathST rates.RPamountRT = k.RPuptakeRT - k.RPtranslocationRT - rates.RPdeathRT rates.RPamountSO = k.RPuptakeSO # K rates in leaves, stems, root and storage organs rates.RKamountLV = k.RKuptakeLV - k.RKtranslocationLV - rates.RKdeathLV rates.RKamountST = k.RKuptakeST - k.RKtranslocationST - rates.RKdeathST rates.RKamountRT = k.RKuptakeRT - k.RKtranslocationRT - rates.RKdeathRT rates.RKamountSO = k.RKuptakeSO rates.RNloss = rates.RNdeathLV + rates.RNdeathST + rates.RNdeathRT rates.RPloss = rates.RPdeathLV + rates.RPdeathST + rates.RPdeathRT rates.RKloss = rates.RKdeathLV + rates.RKdeathST + rates.RKdeathRT self._check_N_balance(day) self._check_P_balance(day) self._check_K_balance(day) @prepare_states def integrate(self, day, delt=1.0): rates = self.rates states = self.states k = self.kiosk # N amount in leaves, stems, root and storage organs states.NamountLV += rates.RNamountLV states.NamountST += rates.RNamountST states.NamountRT += rates.RNamountRT states.NamountSO += rates.RNamountSO # P amount in leaves, stems, root and storage organs states.PamountLV += rates.RPamountLV states.PamountST += rates.RPamountST states.PamountRT += rates.RPamountRT states.PamountSO += rates.RPamountSO # K amount in leaves, stems, root and storage organs states.KamountLV += rates.RKamountLV states.KamountST += rates.RKamountST states.KamountRT += rates.RKamountRT states.KamountSO += rates.RKamountSO self.translocation.integrate(day, delt) self.demand_uptake.integrate(day, delt) # total NPK uptake from soil states.NuptakeTotal += k.RNuptake states.PuptakeTotal += k.RPuptake states.KuptakeTotal += k.RKuptake states.NfixTotal += k.RNfixation states.NlossesTotal += rates.RNloss states.PlossesTotal += rates.RPloss states.KlossesTotal += rates.RKloss def _check_N_balance(self, day): s = self.states checksum = abs(s.NuptakeTotal + s.NfixTotal + (self.NamountLVI + self.NamountSTI + self.NamountRTI + self.NamountSOI) - (s.NamountLV + s.NamountST + s.NamountRT + s.NamountSO + s.NlossesTotal)) if abs(checksum) >= 1.0: msg = "N flows not balanced on day %s\n" % day msg += "Checksum: %f, Nuptake_T: %f, Nfix_T: %f\n" % (checksum, s.NuptakeTotal, s.NfixTotal) msg += "NamountLVI: %f, NamountSTI: %f, NamountRTI: %f, NamountSOI: %f\n" % \ (self.NamountLVI, self.NamountSTI, self.NamountRTI, self.NamountSOI) msg += "NamountLV: %f, NamountST: %f, NamountRT: %f, NamountSO: %f\n" % \ (s.NamountLV, s.NamountST, s.NamountRT, s.NamountSO) msg += "NLOSST: %f\n" % (s.NlossesTotal) raise exc.NutrientBalanceError(msg) def _check_P_balance(self, day): s = self.states checksum = abs(s.PuptakeTotal + (self.PamountLVI + self.PamountSTI + self.PamountRTI + self.PamountSOI) - (s.PamountLV + s.PamountST + s.PamountRT + s.PamountSO + s.PlossesTotal)) if abs(checksum) >= 1.: msg = "P flows not balanced on day %s\n" % day msg += "Checksum: %f, Puptake_T: %f\n" % (checksum, s.PuptakeTotal) msg += "PamountLVI: %f, PamountSTI: %f, PamountRTI: %f, PamountSOI: %f\n" % \ (self.PamountLVI, self.PamountSTI, self.PamountRTI, self.PamountSOI) msg += "PamountLV: %f, PamountST: %f, PamountRT: %f, PamountSO: %f\n" % \ (s.PamountLV, s.PamountST, s.PamountRT, s.PamountSO) msg += "PLOSST: %f\n" % (s.PlossesTotal) raise exc.NutrientBalanceError(msg) def _check_K_balance(self, day): s = self.states checksum = abs(s.KuptakeTotal + (self.KamountLVI + self.KamountSTI + self.KamountRTI + self.KamountSOI) - (s.KamountLV + s.KamountST + s.KamountRT + s.KamountSO + s.KlossesTotal)) if abs(checksum) >= 1.: msg = "K flows not balanced on day %s\n" % day msg += "Checksum: %f, Kuptake_T: %f\n" % (checksum, s.KuptakeTotal) msg += "KamountLVI: %f, KamountSTI: %f, KamountRTI: %f, KamountSOI: %f\n" % \ (self.KamountLVI, self.KamountSTI, self.KamountRTI, self.KamountSOI) msg += "KamountLV: %f, KamountST: %f, KamountRT: %f, KamountSO: %f\n" % \ (s.KamountLV, s.KamountST, s.KamountRT, s.KamountSO) msg += "KLOSST: %f\n" % (s.KlossesTotal) raise exc.NutrientBalanceError(msg)