Source code for pcse.soil.n_soil_dynamics

# -*- coding: utf-8 -*-
# Copyright (c) 2004-2024 Wageningen Environmental Research, Wageningen-UR
# Allard de Wit (allard.dewit@wur.nl), March 2024

from pcse.traitlets import Float
from pcse.decorators import prepare_rates, prepare_states
from pcse.base import ParamTemplate, StatesTemplate, RatesTemplate, \
    SimulationObject
from pcse import signals


class N_PotentialProduction(SimulationObject):
    """Provides unlimited soil N/P/K for potential production simulations.

    NAVAIL just remains 100 kg/ha whatever the crop takes.
    """

    class StateVariables(StatesTemplate):
        NAVAIL = Float(-99.)  # total mineral N from soil and fertiliser  kg N ha-1

    def initialize(self, day, kiosk, parvalues):
        """
        :param day: start date of the simulation
        :param kiosk: variable kiosk of this PCSE instance
        :param cropdata: dictionary with WOFOST cropdata key/value pairs
        """
        self.states = self.StateVariables(kiosk, publish=["NAVAIL"], NAVAIL=100.)

    def calc_rates(self, day, drv):
        pass

    @prepare_states
    def integrate(self, day, delt=1.0):
        self.touch()


[docs]class N_Soil_Dynamics(SimulationObject): """A simple module for soil N dynamics. This modules represents the soil as a bucket for available N consisting of two components: 1) a native soil supply which consists of an initial amount of N which will become available with a fixed fraction every day and 2) an external supply which is computed as an amount of N supplied and multiplied by a recovery fraction in order to have an effective amount of N that is available for crop growth. This module does not simulate any soil physiological processes and is only a book-keeping approach for N availability. On the other hand, it requires no detailed soil parameters. Only an initial soil amount, the fertilizer inputs, a recovery fraction and a background supply. **Simulation parameters** ============ ============================================= ======= ============== Name Description Type Unit ============ ============================================= ======= ============== NSOILBASE Base soil supply of N available through SSi |kg ha-1| mineralisation NSOILBASE_FR Fraction of base soil N that comes available SSi - every day NAVAILI Initial N available in the N pool SSi |kg ha-1| BG_N_SUPPLY Background supply of N through atmospheric SSi |kg ha-1 d-1| deposition. ============ ============================================= ======= ============== **State variables** ======= ================================================= ==== ============ Name Description Pbl Unit ======= ================================================= ==== ============ NSOIL total mineral soil N available at start of N [kg ha-1] growth period NAVAIL Total mineral N from soil and fertiliser Y |kg ha-1| ======= ================================================= ==== ============ **Rate variables** ============== ================================================= ==== ============= Name Description Pbl Unit ============== ================================================= ==== ============= RNSOIL Rate of change on total soil mineral N N |kg ha-1 d-1| RNAVAIL Total change in N availability N |kg ha-1 d-1| # Rate of fertilizer supply for N/P/K [kg/ha/day] FERT_N_SUPPLY Supply of fertilizer N. This will be supplied N |kg ha-1 d-1| by the AgroManager module through the event mechanism. See the section on signals below. ============== ================================================= ==== ============= **Signals send or handled** `N_Soil_Dynamics` receives the following signals: * APPLY_N: Is received when an external input from N fertilizer is provided. See `_on_APPLY_N()` for details. **External dependencies:** ========= =================================== =================== ============== Name Description Provided by Unit ========= =================================== =================== ============== DVS Crop development stage DVS_Phenology - TRA Actual crop transpiration Evapotranspiration |cm| increase TRAMX Potential crop transpiration Evapotranspiration |cm| increase RNuptake Rate of N uptake by the crop NPK_Demand_Uptake |kg ha-1 d-1| ========= =================================== =================== ============== """ NSOILI = Float(-99.) # initial soil N amount class Parameters(ParamTemplate): NSOILBASE = Float(-99.) # total mineral soil N available at start of growth period [kg N/ha] NSOILBASE_FR = Float(-99.) # fraction of soil mineral N coming available per day [day-1] NAVAILI = Float() BG_N_SUPPLY = Float() class StateVariables(StatesTemplate): NSOIL = Float(-99.) # mineral N available from soil for crop kg N ha-1 NAVAIL = Float(-99.) # total mineral N from soil and fertiliser kg N ha-1 class RateVariables(RatesTemplate): RNSOIL = Float(-99.) RNAVAIL = Float(-99.) # Rate of fertilizer supply for N [kg/ha/day] FERT_N_SUPPLY = Float() def initialize(self, day, kiosk, parvalues): """ :param day: start date of the simulation :param kiosk: variable kiosk of this PCSE instance :param cropdata: dictionary with WOFOST cropdata key/value pairs """ self.params = self.Parameters(parvalues) self.rates = self.RateVariables(kiosk) self.kiosk = kiosk # INITIAL STATES p = self.params self.NSOILI = p.NSOILBASE self.states = self.StateVariables(kiosk, publish=["NAVAIL"], NSOIL=p.NSOILBASE, NAVAIL=p.NAVAILI) self._connect_signal(self._on_APPLY_N, signals.apply_n) @prepare_rates def calc_rates(self, day, drv): r = self.rates s = self.states p = self.params k = self.kiosk r.RNSOIL = -max(0., min(p.NSOILBASE_FR * self.NSOILI, s.NSOIL)) # Check uptake rates from crop, if a crop is actually growing RNuptake = k.RNuptake if "RNuptake" in self.kiosk else 0. r.RNAVAIL = r.FERT_N_SUPPLY + p.BG_N_SUPPLY - RNuptake - r.RNSOIL @prepare_states def integrate(self, day, delt=1.0): rates = self.rates states = self.states # mineral N amount in the soil states.NSOIL += rates.RNSOIL * delt # total (soil + fertilizer) N amount in soil states.NAVAIL += rates.RNAVAIL * delt def _on_APPLY_N(self, N_amount=None,N_recovery=None): r = self.rates r.unlock() r.FERT_N_SUPPLY = N_amount * N_recovery r.lock()