Adding a New Crop Process¶
While the WOFOST crop growth model modelled by PCSE contains many crop and soil processes, it may sometimes be the case that the user wants to modify the underlying crop growth model in PCSE to add a new process (e.g. nutrient leeching from the subsoil). Adding a new crop process enables new responses to be studied in WOFOSTGym. This guide outlines how to add a new process to the PCSE model so that its variables can be modified through the WOFOSTGym API.
All crop submodules are contained within the pcse/pcse/crop
and pcse/pcse/soil
folders. First,
choose which submodule the new process should be computed within. Then, add the required states, rates, and parameters
to the subclasses defined within the parent module as shown below:
class NPK_Soil_Dynamics(SimulationObject):
class Parameters(ParamTemplate):
NEW_PARAM = Float(-99.0)
class StateVariables(StatesTemplate):
NEW_STATE = Float(-99.0)
class RateVariables(RatesTemplate):
NEW_RATE = Float(-99.0)
Then, add the new states and rates to the StateVariables
and RateVariables
constructor in the
initialize and reset functions as so:
self.states = self.StateVariables(
kiosk,
publish=["NEW_STATE"],
NEW_STATE=NEW_STATE_INIT_VALUE
)
self.rates = self.RateVariables(
kiosk,
publish=["NEW_RATE"]
)
This will ensure that your new states and rates can be accessed by other submodules in PCSE and also by the
WOFOSTGym API. Then, define the new crop subprocess in the calc_rates()
and integrate()
functions.
Rates can be accessed by r.NEW_RATE
and states can be accessed by s.NEW_STATE
. Parameters can be accessed
by p.NEW_PARAM
.
Now the new crop or soil process is completely defined! The next step is ensuring that WOFOSTGym can access it. In the
pcse_gym/pcse_gym/utils.py
file, add the new states and rates to the OUTPUT_VARS
list exactly as it was defined
in the StateVariables or RateVariables class.
For a new parameter, it is recommended to add this line of code to the set_params()
function in the same utils.py file:
if args.NEW_PARAM is not None:
env.parameterprovider.set_override("NEW_PARAM", args.NEW_PARAM, check=False)
and the below line of code in the pcse_gym/args.py
in the WOFOST_Args
class:
class WOFOST_Args:
""" NEW PARAM FOR XX"""
NEW_PARAM: Optional[float] = None
These additions will enable the parameter to be set via command line.
Caution
If a new parameter has been added, it must also be added to the crop and site yaml files. Otherwise, a missing parameter error will be thrown by the PCSE module.
Now that the WOFOSTGym API is aware of the new state and rate variables, they can be included as part of the observation
space by modifying the npk.output_vars
parameter via command line.
Adding a new Crop or Soil Submodules¶
To add a new Crop or Soil submodule, create a new file in the crop/
or soil/
folders.
Every submodule needs to inherit from the SimulationObject class. A template is included below:
from datetime import date
from pcse.util import AfgenTrait
from pcse.utils.traitlets import Float
from pcse.utils.decorators import prepare_rates, prepare_states
from pcse.base import ParamTemplate, StatesTemplate, RatesTemplate, SimulationObject
from pcse.utils import signals
from pcse.base import VariableKiosk
from pcse.nasapower import WeatherDataContainer
class NPK_Soil_Dynamics(SimulationObject):
class Parameters(ParamTemplate):
class StateVariables(StatesTemplate):
class RateVariables(RatesTemplate):
def initialize(self, day: date, kiosk: VariableKiosk, parvalues: dict) -> None:
self.params = self.Parameters(parvalues)
self.kiosk = kiosk
self.states = self.StateVariables(
kiosk,
publish=[],
)
self.rates = self.RateVariables(
kiosk,
publish=[]
)
self._connect_signal(self._on_APPLY_NPK, signals.apply_npk)
@prepare_rates
def calc_rates(self, day: date, drv: WeatherDataContainer) -> None:
"""Compute Rates for model"""
pass
@prepare_states
def integrate(self, day: date, delt: float = 1.0) -> None:
"""Integrate states with rates"""
pass
All Parameters, States, and Rates should be defined in their respective subclasses as so:
class NPK_Soil_Dynamics(SimulationObject):
class Parameters(ParamTemplate):
PARAM = Float(-99.0)
class StateVariables(StatesTemplate):
STATE = Float(-99.0)
class RateVariables(RatesTemplate):
RATE = Float(-99.0)
Any state and rate that should be visible to the kiosk or to the WOFOSTGym wrapper should be published. See the above section on adding states and rates.
After defining all the module processes, its integrate()
and calc_rates()
methods need to be called.
For soil submodules, these are in the soil_wrappers.py
file. Add an instance of the new object to the constructor,
and add its method calls to the high level integrate()
and calc_rates()
calls. Ensure that the corresponding
reset()
function is called in the reset wrapper as well.
For crop submodules, these are defined in the wofost8.py
file. Follow the same process for adding the functions listed above.