Module sarra_py.bilan_carbo

View Source
import numpy as np

import xarray as xr

def variable_dict():

    """

    Retrieve the dictionary of variables in the dataset with their respective units.

    Returns:

        dict: A dictionary containing the variables and their units, where the keys are the variable names and the values are the respective units.

    """

    variables = {

        # climate

        "ddj": ["daily thermal time", "°C.j"],

        "sdj": ["sum of thermal time since beginning of emergence", "°C.j"],

        # phenology

        "changePhase": ["indicator of phase transition day", "binary"],

        "numPhase": ["number of phenological stage", "arbitrary units"],

        "initPhase": ["indicator of performed phase transition", "binary"],

        "phasePhotoper": ["photoperiodic phase indicator", "binary"],

        "seuilTempPhaseSuivante": ["sum of thermal time needed to reach the next phenological phase", "°C.j"],

        "sommeDegresJourPhasePrec": ["sum of thermal time needed to reach the previous phenological phase", "°C.j"],

        "seuilTempPhasePrec": ["sum of thermal time needed to reach the previous phenological phase", "°C.j"],

        # carbon balance

        "assim": ["plant biomass assimilation", "kg/ha"],

        "assimPot": ["plant potential biomass assimilation", "kg/ha"],

        "bM": ["net growth rate of living biomass", "kg/(m².d)"],

        "cM": ["net growth rate of dead biomass", "kg/(m².d)"],

        "rdt": ["grain yield", "kg/ha"],

        "rdtPot": ["potential grain yield", "kg/ha"],

        "reallocation": ["amount of assimilates reallocated to the yield (supply < demand)", "kg/ha"],

        "respMaint": ["amount of assimilates consumed by maintainance respiration", "kg/ha"],

        "manqueAssim": ["deficit in assimilates (demand - supply)", "kg/ha"],

        # biomass

        "biomTotStadeFloraison": ["total biomass of the plant at the end of the flowering stage", "kg/ha"],

        "biomTotStadeIp": ["total biomass at the panicle initiation stage", "kg/ha"],

        "deltaBiomasseAerienne": ["increment of aerial biomass in one day", "kg/(ha.d)"],

        "deltaBiomasseFeuilles": ["increment of leaf biomass in one day", "kg/(ha.d)"],

        "biomasseAerienne": ["total aerial biomass", "kg/ha"],

        "biomasseVegetative": ["total vegetative biomass", "kg/ha"],

        "biomasseTotale": ["total biomass", "kg/ha"],

        "biomasseTige": ["total stem biomass", "kg/ha"],

        "biomasseRacinaire": ["total root biomass", "kg/ha"],

        "biomasseFeuille": ["total leaf biomass", "kg/ha"],

        "deltaBiomasseTotale": ["increment of total biomass in one day", "kg/(ha.d)"],

        # evapotranspiration

        "kce": ["fraction of kc attributable to soil evaporation","decimal percentage"],

        "kcp": ["fraction of kc attributable to plant transpiration","decimal percentage"],

        "kcTot": ["total crop coefficient",""],

        "tr": ["actual crop transpiration","mm/d"],

        "trPot": ["potential crop transpiration","mm/d"],

        "trSurf": ["",""],

        # water balance

        "consoRur": ["consumption of water stored in the root system", "mm"],

        "water_captured_by_mulch" : ["water captured by the mulch in one day","mm"],

        "available_water" : ["available water, sum of rainfall and total irrigation for the day","mm"],

        "eauTranspi": ["water available for transpiration from the surface reservoir","mm"],

        "correctedIrrigation" : ["corrected irrigation amount","mm/d"],

        "cstr" : ["drought stress coefficient", "arbitrary unit"],

        "dayVrac" : ["modulated daily root growth","mm/day"],

        "delta_root_tank_capacity": ["change in root system water reserve","mm"],

        "drainage": ["drainage","mm"],

        #// "etm": ["evapotranspiration from the soil moisture","mm/d"],

        "etp": ["potential evapotranspiration from the soil moisture","mm/d"],

        #// "etr": ["reference evapotranspiration","mm/d"],

        "evap": ["evaporation from the soil moisture","mm/d"],

        "evapPot": ["potential evaporation from the soil moisture","mm/d"],

        "FEMcW": ["water fraction in soil volume explored by the root system","none"],

        "fesw": ["fraction of available surface water","decimal percentage"],

        "irrigTotDay" : ["total irrigation for the day","mm"],

        "vRac" : ["reference daily root growth","mm/day"],

        "ftsw": ["fraction of transpirable surface water","decimal percentage"],

        "runoff" : ["daily water runoff","mm/d"],

        "pFact": ["FAO reference for critical FTSW value for transpiration response","none"],

        # water tanks

        "irrigation_tank_stock" : ["current stock of water in the irrigation tank","mm"], #! renaming stockIrr to irrigation_tank_stock

        "mulch_water_stock" : ["water stored in crop residues (mulch)","mm"], #! renaming stockMc to mulch_water_stock

        "root_tank_stock": ["current stock of water in the root system tank","mm"], #! renaming stRu to root_tank_stock

        "total_tank_capacity": ["total capacity of the root system tank","mm"], #! renaming stRuMax to total_tank_capacity

        "stRur": ["",""], # ["previous season's root system tank stock","mm"],

        "previous_root_tank_capacity": ["previous season's root system tank capacity","mm"], #! renaming stRurMaxPrec to previous_root_tank_capacity

        "previous_root_tank_stock": ["previous day's root system tank stock","mm"],

        "stRurSurf": ["surface root system tank stock","mm"],

        "surface_tank_stock": ["current stock of water in the surface root system tank","mm"], #! renaming stRuSurf to surface_tank_stock

        "stRuSurfPrec": ["previous day's surface root system tank stock","mm"],

        "delta_total_tank_stock": ["change in the total root system tank stock","mm"], #! renaming stRuVar to delta_total_tank_stock

        "irrigation_tank_capacity" : ["irrigation tank capacity","mm"], #! renaming ruIrr to irrigation_tank_capacity

        "ruRac": ["Water column that can potentially be strored in soil volume explored by root system","mm"],

        "conv": ["",""],

        "KAssim": ["",""],

        "dayBiomLeaf": ["daily growth of leaf biomass","kg/ha/d"],

        "dRdtPot": ["daily potential demand from yield","kg/ha/d"],

        "FeuilleUp": ["",""],

        "kRespMaint": ["",""],

        "LitFeuille": ["",""],

        "nbJourCompte": ["",""],

        "nbjStress": ["",""],

        "NbUBT": ["",""],

        "sla": ["",""],

        "stockRac": ["",""],

        "sumPP": ["",""],

        "TigeUp": ["",""],

        "UBTCulture": ["",""],

        "lai":["leaf area index","m2/m2"],

        # experimental

        "Ncrit": ["",""],

    }

    return variables

def initialize_simulation(data, grid_width, grid_height, duration, paramVariete, paramITK, date_start):

    """

    This function initializes variables related to crop growth in the data

    xarray dataset. As the rain is the first variable to be initialized in the

    data xarray dataset, its dimensions are used to initialize the other

    variables.

    ![no caption](../../docs/images/sla.png)

    This code has been adapted from the original InitiationCulture procedure, from the `MilBilanCarbone.pas` code of the

    SARRA model.

    Args:

        data (_type_): _description_ grid_width (_type_): _description_

        grid_height (_type_): _description_ duration (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    ### variables to be initialized with values from parameters

    # from paramVariete : maximum daily thermal time (°C.j) -> #? unused ?

    #// data["sommeDegresJourMaximale"] = (data["rain"].dims, np.full(

    #//     (duration, grid_width, grid_height),

    #//     (paramVariete["SDJLevee"] + paramVariete["SDJBVP"] + paramVariete["SDJRPR"] + paramVariete["SDJMatu1"] + paramVariete["SDJMatu2"])

    #// ))

    #// data["sommeDegresJourMaximale"].attrs = {"units":"°C.j", "long_name":"Maximum thermal time"}

    # from paramITK : sowing date

    data["sowing_date"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), (paramITK["DateSemis"] - date_start).days))

    # from paramITK : automatic irrigation indicator

    data["irrigAuto"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), paramITK["irrigAuto"]))

    data["irrigAuto"].attrs = {"units":"binary", "long_name":"automatic irrigation indicator"}

    ####### variables qui viennent de initplotMc

    # Initial biomass of crop residues (mulch) (kg/ha)

    # Biomasse initiale des résidus de culture (mulch) (kg/ha)

    #   BiomMc := BiomIniMc;

    data["biomMc"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), paramITK["biomIniMc"]))

    data["biomMc"].attrs = {"units": "kg/ha", "long_name": "Initial biomass of crop residues (mulch)"}

    data["biomMc"] = data["biomMc"].astype("float32")

    # ?

    #   StSurf := StockIniSurf;

    # data["stSurf"] = np.full((grid_width, grid_height, duration), paramTypeSol["stockIniSurf"])

    # ?

    #   Ltr := 1;

    data["ltr"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), 1.0))

    data["ltr"] = data["ltr"].astype("float32")

    # Initial biomass of stem residues as litter (kg/ha)

    # Biomasse initiale des résidus de tiges sous forme de litière (kg/ha)

    #   LitTiges := BiomIniMc;

    data["LitTige"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), paramITK["biomIniMc"]))

    data["LitTige"].attrs = {"units": "kg/ha", "long_name": "Initial biomass of stem residues as litter"}

    data["LitTige"] = data["LitTige"].astype("float32")

    ####### fin variables qui viennent de initplotMc

    ####### variables eau depuis InitPlotMc

    # Initializes variables related to crop residues boimass (mulch) in the data

    # xarray dataset. This code has been adapted from the original InitPlotMc

    # procedure, Bileau.pas code. Comments with tab indentation are from the

    # original code. As the rain is the first variable to be initialized in the

    # data xarray dataset, its dimensions are used to initialize the other

    # variables.

    # Soil maximum water storage capacity (mm)

    # Capacité maximale de la RU (mm)

    #   StRurMax := Ru * ProfRacIni / 1000;

    #! renaming stRurMax with root_tank_capacity

    #// data["stRurMax"] = data["ru"] * paramITK["profRacIni"] / 1000

    data["root_tank_capacity"] = (data["rain"].dims, np.repeat(np.array(data["ru"] * paramITK["profRacIni"] / 1000)[np.newaxis,:,:], duration, axis=0))

    #// data["stRurMax"].attrs = {"units": "mm", "long_name": "Soil maximum water storage capacity"}

    data["root_tank_capacity"].attrs = {"units": "mm", "long_name": "Soil maximum water storage capacity"}

    # Maximum water capacity of surface tank (mm)

    # Reserve utile de l'horizon de surface (mm)

    #   RuSurf := EpaisseurSurf / 1000 * Ru;

    #! renaming ruSurf with surface_tank_capacity

    #// data["ruSurf"] = data["epaisseurSurf"] / 1000 * data["ru"]

    data["surface_tank_capacity"] = data["epaisseurSurf"] / 1000 * data["ru"]

    #// data["ruSurf"].attrs = {"units": "mm", "long_name": "Maximum water capacity of surface tank"}

    data["surface_tank_capacity"].attrs = {"units": "mm", "long_name": "Maximum water capacity of surface tank"}

    # ?

    #   //    PfTranspi := EpaisseurSurf * HumPf;

    #   //    StTot := StockIniSurf - PfTranspi/2 + StockIniProf;

    #   StTot := StockIniSurf  + StockIniProf;

    # data["stTot"] = np.full((grid_width, grid_height, duration), (paramTypeSol["stockIniSurf"] + paramTypeSol["stockIniProf"]))

    #! modifié pour faire correspondre les résultats de simulation, à remettre en place pour un calcul correct dès que possible

    # data["stTot"] = np.full((grid_width, grid_height, duration), (paramTypeSol["stockIniProf"]))

    #! renaming stTot to total_tank_stock

    #// data["stTot"] = data["stockIniProf"]

    #//data["total_tank_stock"] = data["stockIniProf"]

    #! coorecting total_tank_stock initialization as it did not have the time dimensions that are required as stock evolves through time

    data["total_tank_stock"] = (data["rain"].dims, np.repeat(np.array(data["stockIniProf"])[np.newaxis,:,:], duration, axis=0))

    #// data["stTot"].attrs = {"units": "mm", "long_name": "?"}

    data["total_tank_stock"].attrs = {"units": "mm", "long_name": "?"}

    # Soil maximal depth (mm)

    # Profondeur maximale de sol (mm)

    #   ProfRU := EpaisseurSurf + EpaisseurProf;

    #! data["profRu"] = data["epaisseurProf"] + data["epaisseurSurf"]

    #! data["profRu"].attrs = {"units": "mm", "long_name": "Soil maximal depth"}

    # déplacé dans l'initialisation du sol

    # Maximum water capacity to humectation front (mm)

    # Quantité d'eau maximum jusqu'au front d'humectation (mm)

    #   // modif 10/06/2015  resilience stock d'eau

    #   // Front d'humectation egal a RuSurf trop de stress initial

    #   //    Hum := max(StTot, StRurMax);

    #   Hum := max(RuSurf, StRurMax);

    #   // Hum mis a profRuSurf

    #   Hum := max(StTot, Hum);

    data["humectation_front"] = (data["rain"].dims, np.full((duration, grid_width, grid_height),

        np.maximum(

            np.maximum(

                #! renaming ruSurf with surface_tank_capacity

                #// data["ruSurf"],

                data["surface_tank_capacity"].expand_dims({"time":duration}),

                #! renaming stRurMax with root_tank_capacity

                #// data["stRurMax"],

                data["root_tank_capacity"],

            ),

            #! renaming stTot with total_tank_stock

            #// data["stTot"],

            data["total_tank_stock"],

        )

    ))

    data["humectation_front"].attrs = {"units": "mm", "long_name": "Maximum water capacity to humectation front"}

    # Previous value for Maximum water capacity to humectation front (mm)

    #  HumPrec := Hum;

    data["previous_humectation_front"] = data["humectation_front"]

    # ?

    #   StRurPrec := 0;

    # Previous value for stTot

    #   StRurMaxPrec := 0;

    #   //modif 10/06/2015 resilience stock d'eau

    #! renaming stTot with total_tank_stock

    #! renaminog stRuPrec with previous_total_tank_stock

    #// data["stRuPrec"] =  data["stTot"]

    data["previous_total_tank_stock"] =  data["total_tank_stock"]

    ####### fin variables eau depuis InitPlotMc

    # depuis meteo.pas

    kpar = 0.5

    data["par"] = kpar * data["rg"]

    data["par"].attrs = {"units":"MJ/m2", "long_name":"par"}

    # crop density

    if ~np.isnan(paramVariete["densOpti"]) :

        data["rapDensite"] = data["rain"] * 0 + compute_rapDensite(paramITK, paramVariete)

        data["rapDensite"].attrs = {"units":"none", "long_name":"sowing density adjustement factor"}

    # initialize variables with values at 0

    variables = variable_dict()

    for variable in variables :

        data[variable] = (data["rain"].dims, np.zeros(shape=(duration, grid_width, grid_height)))

        data[variable].attrs = {"units":variables[variable][1], "long_name":variables[variable][0]}

        data[variable] = data[variable].astype("float32")

    return data

def estimate_kcp(j, data, paramVariete):

    """

    Estimate the kcp coefficient based on the maximum crop coefficient `kcMax` and plant cover `ltr`.

    The computation of `kcp` is based on the EvolKcpKcIni procedure from the biomasse.pas and exmodules 1 & 2.pas files of the original PASCAL code.

    Args:

        j (int): The starting index for updating `kcp` in the `data` dataset.

        data (xarray.Dataset): A dataset containing the data used in the computation of `kcp`. The dataset should contain the following variables:

            - 'numPhase': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the number of phases in the crop cycle.

            - 'kcp': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the coefficient of crop growth.

            - 'ltr': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the plant cover.

        paramVariete (dict): A dictionary containing the parameters for estimating `kcp`. The dictionary should contain the following key:

            - 'kcMax': A float, representing the maximum crop coefficient.

    Returns:

        xarray.Dataset: The updated `data` dataset with the new `kcp` values.

    """

    data["kcp"][j:,:,:] = np.where(

        data["numPhase"][j,:,:] >= 1,

        np.maximum(0.3, paramVariete["kcMax"] * (1 - data["ltr"][j,:,:])),

        data["kcp"][j,:,:],

    )

    return data

def estimate_ltr(j, data, paramVariete):

    """

    Estimate the fraction of radiation transmitted to the soil `ltr` based on the leaf area index `lai`.

    `ltr` is used as a proxy for plant covering of the soil in the water balance calculation, where 1 represents no plant cover and 0 represents full plant cover. `ltr` is computed as an exponential decay function of `lai` with a decay coefficient `kdf`.

    This function is adapted from the EvalLtr procedure from the biomasse.pas and exmodules 1 & 2.pas files of the original PASCAL code.

    Args:

        j (int): The starting index for updating `ltr` in the `data` dataset.

        data (xarray.Dataset): A dataset containing the data used in the computation of `ltr`. The dataset should contain the following variables:

            - 'lai': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the leaf area index.

            - 'ltr': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the fraction of radiation transmitted to the soil.

        paramVariete (dict): A dictionary containing the parameters for estimating `ltr`. The dictionary should contain the following key:

            - 'kdf': A float, representing the decay coefficient for `ltr`.

    Returns:

        xarray.Dataset: The updated `data` dataset with the new `ltr` values.

    """

    # group 80

    data["ltr"][j:,:,:] = np.exp(-paramVariete["kdf"] * data["lai"][j,:,:])

    return data

def estimate_KAssim(j, data, paramVariete):

    """

    This function calculates the conversion factor `KAssim`, which is used to convert assimilates into biomass.

    The value of `KAssim` depends on the phase of the crop.

    The conversion factor is calculated based on a lookup table that maps crop phases to values. The crop phase is

    determined by the `numPhase` field in the `data` argument, and the corresponding `KAssim` value is set in the

    `KAssim` field of the `data` argument.

    Args:

        j (int): An integer index specifying the time step.

        data (xarray.Dataset): A dataset containing the variables used in the calculation of `KAssim`. The dataset

            should include the fields `numPhase`, `sdj`, `seuilTemp PhasePrec`, and `seuilTemp PhaseSuivante`. The

            `KAssim` field of the dataset will be updated by this function.

        paramVariete (dict): A dictionary of parameters. It should include the fields `txAssimBVP`, `txAssimMatu1`,

            and `txAssimMatu2`.

    Returns:

        xarray.Dataset: The updated `data` dataset, with the `KAssim` field set to the calculated values.

    """

    phase_equivalences = {

        2: 1,

        3: paramVariete['txAssimBVP'],

        4: paramVariete['txAssimBVP'],

        #! replacing sommeDegresJourPhasePrec with seuilTempPhasePrec

        #// 5: paramVariete["txAssimBVP"] + (data['sdj'][j,:,:] - data['sommeDegresJourPhasePrec'][j,:,:]) * (paramVariete['txAssimMatu1'] -  paramVariete['txAssimBVP']) / (data['seuilTempPhaseSuivante'][j,:,:] - data['sommeDegresJourPhasePrec'][j,:,:]),

        5: paramVariete["txAssimBVP"] + (data['sdj'][j,:,:] - data['seuilTempPhasePrec'][j,:,:]) * (paramVariete['txAssimMatu1'] -  paramVariete['txAssimBVP']) / (data['seuilTempPhaseSuivante'][j,:,:] - data['seuilTempPhasePrec'][j,:,:]),

        #// 6: paramVariete["txAssimMatu1"] + (data["sdj"][j,:,:] - data["sommeDegresJourPhasePrec"][j,:,:]) * (paramVariete["txAssimMatu2"] - paramVariete["txAssimMatu1"]) / (data["seuilTempPhaseSuivante"][j,:,:] - data["sommeDegresJourPhasePrec"][j,:,:]),

        6: paramVariete["txAssimMatu1"] + (data["sdj"][j,:,:] - data["seuilTempPhasePrec"][j,:,:]) * (paramVariete["txAssimMatu2"] - paramVariete["txAssimMatu1"]) / (data["seuilTempPhaseSuivante"][j,:,:] - data["seuilTempPhasePrec"][j,:,:]),

    }

    for phase in range(2,7):

        data["KAssim"][j:,:,:] = np.where(

            data["numPhase"][j,:,:] == phase,

            phase_equivalences[phase],

            data["KAssim"][j,:,:],

        )

    return data

def estimate_conv(j,data,paramVariete):

    """

    This function calculates the conversion of assimilates into biomass.

    The conversion factor is determined by multiplying the KAssim value, which

    is dependent on the phase of the crop, with the conversion rate (txConversion)

    specified in the `paramVariete` argument.

    Args:

        j (int): The starting index of the calculation

        data (dict): A dictionary containing information on the crop growth, including

                     the phase of the crop and the KAssim value.

        paramVariete (dict): A dictionary containing parameters relevant to the crop

                             growth, including the conversion rate.

    Returns:

        dict: The input `data` dictionary with the calculated "conv" value added.

    """

    data["conv"][j:,:,:] = (data["KAssim"][j,:,:] * paramVariete["txConversion"])

    return data

def BiomDensOptSarraV4(j, data, paramITK):

    """

    si densité plus faible alors on considére qu'il faut augmenter les biomasses, LAI etc

    en regard de cette situation au niveau de chaque plante (car tout est rapporté é des kg/ha).

    Si elle est plus forte on ne change rien pour lors.

    Valeur fixe en ref au maés é déf en paramétre par variétésé rapDensite := Max(1, 70000/densite);

    """

    """

    if ~np.isnan(paramVariete["densOpti"]) :

        paramITK["rapDensite"] = np.maximum(1,paramVariete["densOpti"]/paramITK["densite"])

        data["rdt"][j,:,:] = data["rdt"][j,:,:] * paramITK["rapDensite"]

        data["biomasseRacinaire"][j,:,:] = data["biomasseRacinaire"][j,:,:] * paramITK["rapDensite"]

        data["biomasseTige"][j,:,:] = data["biomasseTige"][j,:,:] * paramITK["rapDensite"]

        data["biomasseFeuille"][j,:,:] = data["biomasseFeuille"][j,:,:] * paramITK["rapDensite"]

        data["biomasseAerienne"][j,:,:] = data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:]

        data["lai"][j,:,:]  = data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:]

        data["biomasseTotale"][j,:,:] = data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:]

    return data

    """

    return data

def compute_rapDensite(paramITK, paramVariete):

    """

    It basically calculates a correction factor (rapDensite).

    This correction factor Is calculated with an equation of form

    a + p * exp( -(x/(o / -log((1-a)/p) )) )

    with a the densiteA parameter

    p the densiteP parameter$

    x the actual crop density

    o the densOpti parameter

    See

    https://www.wolframalpha.com/input?i=a+%2B+p+*+exp%28-%28x+%2F+%28+o%2F-+log%28%281+-+a%29%2F+p%29%29%29%29

    for equation visualization.

    This equation is probably too complex for the problem at hand.

    Args:

        j (_type_): _description_

        data (_type_): _description_

        paramITK (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    rapDensite = paramVariete["densiteA"] + paramVariete["densiteP"] * np.exp(-(paramITK["densite"] / ( paramVariete["densOpti"]/- np.log((1 - paramVariete['densiteA'])/ paramVariete["densiteP"]))))

    return rapDensite

def adjust_for_sowing_density(j, data, paramVariete, direction):

    """

    This function translates the effect of sowing density on biomass and LAI.

    This function is adapted from the BiomDensOptSarV42 and BiomDensiteSarraV42

    procedures, from the bilancarbonsarra.pas original Pascal code.

    Notes from CB :

    if density is lower than the optimal density, then we consider that we need

    to increase the biomass, LAI etc in regard of this situation at each plant

    level (because everything is related to kg/ha). If it is higher, it

    increases asymptotically.

    Args:

        j (_type_): _description_

        data (_type_): _description_

        paramITK (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    if direction == "in" :

        if ~np.isnan(paramVariete["densOpti"]) :

            data["rdt"][j:,:,:] = data["rdt"][j,:,:] * data["rapDensite"][j,:,:]

            data["rdtPot"][j:,:,:] = (data["rdtPot"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseRacinaire"][j:,:,:] = (data["biomasseRacinaire"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseTige"][j:,:,:] = (data["biomasseTige"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseFeuille"][j:,:,:] = (data["biomasseFeuille"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseAerienne"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:])

            data["lai"][j:,:,:]  = (data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:])

            data["biomasseTotale"][j:,:,:] = (data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:])

        return data

    if direction == "out":

        if ~np.isnan(paramVariete["densOpti"]):

            data["rdt"][j:,:,:] = (data["rdt"][j,:,:] / data["rapDensite"][j,:,:])

            data["rdtPot"][j:,:,:] = (data["rdtPot"][j,:,:]/ data["rapDensite"][j,:,:])

            data["biomasseRacinaire"][j:,:,:] = (data["biomasseRacinaire"][j,:,:] / data["rapDensite"][j,:,:])

            data["biomasseTige"][j:,:,:] = (data["biomasseTige"][j,:,:] / data["rapDensite"][j,:,:])

            data["biomasseFeuille"][j:,:,:] = (data["biomasseFeuille"][j,:,:] / data["rapDensite"][j,:,:])

            data["biomasseAerienne"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:])

            #? conflit avec fonction evolLAIphase ?

            #data["lai"][j:,:,:]  = data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:]

            data["lai"][j:,:,:]  = data["lai"][j:,:,:]  / data["rapDensite"][j,:,:]

            data["biomasseTotale"][j:,:,:] = (data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:])#[...,np.newaxis]

            #data["biomasseTotale"][j:,:,:] = data["biomasseTotale"][j:,:,:] / data["rapDensite"]

        return data

def EvalAssimSarrahV4(j, data):

    """

    data["parIntercepte"][j,:,:] = 0.5 * (1 - data["ltr"][j,:,:]) * data["rg"][j,:,:]

    data["assimPot"][j:,:,:] = data["parIntercepte"][j,:,:] * data["conv"][j,:,:] * 10

    data["assim"][j,:,:] = np.where(

        data["trPot"][j,:,:] > 0,

        data["assimPot"][j,:,:] * data["tr"][j,:,:] / data["trPot"][j,:,:],

        0,

    )

    """

    return data

def update_assimPot(j, data, paramVariete, paramITK):

    """

    Update the assimPot value based on the intensification level (NI).

    If the intensification level `NI` is defined in `paramITK`, the conversion rate `txConversion` is computed using a formula based on `NIYo`, `NIp`, `LGauss`, and `AGauss`. If `NI` is not defined, `assimPot` is updated using `conv`, which is updated in the `estimate_conv` function using the variables `KAssim` and `txConversion`.

    When NI parameter is used (from to 4), conversion rate txConversion

    is computed using the following formula :

    NIYo + NIp * (1-exp(-NIp * NI)) - (exp(-0.5*((NI - LGauss)/AGauss)* (NI- LGauss)/AGauss))/(AGauss*2.506628274631)

    This function is adapted from the `EvalAssimSarraV42` procedure in the `bilancarbonsarra.pas` file of the original Pascal code.

    Note from

    CB : correction of the conversion rate depending on the intensification

    level

    notes from CB reharding the EvalAssimSarraV42 procedure :

    Modif du 04/03/2021 : Prise en compte en plus de la densit� de semis de

    l'effet niveau d'intensification NI NI = 1 quand on est � l'optimum du

    niveau d'intensification. Dans le cas de situation contr�l� c'est la

    fertilit� qui est la clef principale en prenant en r�f�rence la qt� d'azote

    (�quivalent phosphore...) optimum Il peut aller � 0 ou �tre sup�rieur � 1 si

    situation sur optimum, ie un peu plus de rdt mais � cout trop �lev�... On

    �value un nouveau tx de conversion en fn du Ni au travers d'une double

    �quation : asympote x gaussienne invers�e Et d'un NI d�fini en fn du

    sc�nario de simulation ou des donn�es observ�es. NIYo = D�calage en Y de

    l'asymptote NIp  = pente de l'asymptote LGauss = Largeur de la Guaussienne

    AGauss = Amplitude de la Guaussienne

    Conversion qui est la valeur du taux de conversion en situation optimum n'a

    plus besoin d'�tre utilis� sinon dans la calibration des param�tres de cette

    �quation en absence de donn�es sur ces param�tres on ne met aucune valeur �

    NI CF fichier ex IndIntensite_txConv_eq.xls}

    Args:

    - j (int): An index that represents the current iteration.

    - data (dict): A dictionary containing data arrays with the following keys:

        - "assimPot" (np.ndarray): An array representing the potential assimilation rate.

        - "par" (np.ndarray): An array representing photosynthetically active radiation.

        - "lai" (np.ndarray): An array representing the leaf area index.

        - "conv" (np.ndarray): An array representing the conversion rate.

    - paramVariete (dict): A dictionary containing parameters for the computation of the conversion rate, including:

        - "txConversion" (float): The conversion rate.

        - "NIYo" (float): The shift in the Y-axis of the asymptote.

        - "NIp" (float): The slope of the asymptote.

        - "LGauss" (float): The width of the Gaussian curve.

        - "AGauss" (float): The amplitude of the Gaussian curve.

        - "kdf" (float): The constant used in the computation of `assimPot`.

    - paramITK (dict): A dictionary containing the intensification level `NI` (float).

    Returns:

    - data (dict): The input `data` dictionary with the updated "assimPot" array.

    """

    if ~np.isnan(paramITK["NI"]):

        #? the following (stupidly long) line was found commented, need to check why and if this is correct

        paramVariete["txConversion"] = paramVariete["NIYo"] + paramVariete["NIp"] * (1-np.exp(-paramVariete["NIp"] * paramITK["NI"])) - (np.exp(-0.5*((paramITK["NI"] - paramVariete["LGauss"])/paramVariete["AGauss"])* (paramITK["NI"]- paramVariete["LGauss"])/paramVariete["AGauss"]))/(paramVariete["AGauss"]*2.506628274631)

        # NIYo + NIp * (1-exp(-NIp * NI)) - (exp(-0.5*((NI - LGauss)/AGauss)* (NI- LGauss)/AGauss))/(AGauss*2.506628274631)

        data["assimPot"][j,:,:] = data["par"][j,:,:] * \

            (1-np.exp(-paramVariete["kdf"] * data["lai"][j,:,:])) * \

            paramVariete["txConversion"] * 10

    else :

        data["assimPot"][j,:,:] = data["par"][j,:,:] * \

            (1-np.exp(-paramVariete["kdf"] * data["lai"][j,:,:])) * \

            data["conv"][j,:,:] * 10

    return data

def update_assim(j, data):

    """

    This function updates assim. If trPot (potential transpiration from the

    plant, mm) is greater than 0, then assim equals assimPot, multiplied by the

    ratio of effective transpiration over potential transpiration.

    If potential transpiration is null, then assim is null as well.

    Is it adapted from the EvalAssimSarraV42 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["assim"][j,:,:] = np.where(

        data["trPot"][j,:,:] > 0,

        data["assimPot"][j,:,:] * data["tr"][j,:,:] / data["trPot"][j,:,:],

        0,

    )

    return data

def calculate_maintainance_respiration(j, data, paramVariete):

    """

    This function calculates the maintenance respiration `respMaint` (in kg/ha/j in equivalent dry matter) of the plant.

    The maintenance respiration is calculated by summing the maintenance respiration associated with total biomass and

    leaves biomass. If the plant's growth phase is above 4 and there is no leaf biomass, `respMaint` is set to 0.

    The calculation is based on the equation:

      coefficient_temp = 2^((average_temp - tempMaint) / 10)

      respiration = kRespMaint * biomass * coefficient_temp

    where `average_temp` is the average temperature for the day, `tempMaint` is the maintenance temperature from

    `variety_params`, `kRespMaint` is the maintenance respiration coefficient from `variety_params`, and `biomass`

    is the total or leaf biomass.

    Args:

        j (int): The time step of the calculation.

        data (xarray.Dataset): The input data containing the variables `tpMoy`, `biomasseTotale`, `biomasseFeuille`, and

            `numPhase`. The output `respMaint` will also be stored in this dataset.

        variety_params (dict): The parameters related to the plant variety, containing the keys `tempMaint` and

            `kRespMaint`.

    Returns:

        xarray.Dataset: The input `data` with the updated `respMaint` variable.

    """

    coefficient_temp = 2**((data["tpMoy"][j,:,:] - paramVariete["tempMaint"]) / 10)

    resp_totale = paramVariete["kRespMaint"] * data["biomasseTotale"][j,:,:] * coefficient_temp

    resp_feuille = paramVariete["kRespMaint"] * data["biomasseFeuille"][j,:,:] * coefficient_temp

    data["respMaint"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 4) & (data["biomasseFeuille"][j,:,:]==0),

        0,

        resp_totale + resp_feuille,

    )

    return data

def update_total_biomass(j, data, paramVariete, paramITK):

    """

    Update the Total Biomass of the Plant.

    The total biomass is updated based on the plant's current phase and other parameters.

    If the plant is in phase 2 and there's a change in phase, the total biomass is initialized using

    crop density, grain yield per plant, and the dry weight of the grain.

    If the plant is not in phase 2 or there's no change in phase, the total biomass is incremented with

    the difference between the plant's assimilation and maintenance respiration.

    When passing from phase 1 to phase 2, total biomass is initialized.

    Initialization value is computed from crop density (plants/ha), txResGrain

    (grain yield per plant), and poidsSecGrain. Otherwise, total biomass is

    incremented with the difference between plant assimilation assim and

    maintainance respiration respMaint.

    This function is adapted from the EvolBiomTotSarrahV4 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code.

    Args:

        j (int): The current time step.

        data (xarray.Dataset): The data for the plant, including variables like

            "biomasseTotale", "assim", "respMaint", "numPhase", and "changePhase".

        paramVariete (dict): A dictionary of parameters specific to the plant variety.

        paramITK (dict): A dictionary of inter-tropical convergence zone parameters.

    Returns:

        xarray.Dataset: The updated data for the plant, including the updated "biomasseTotale"

            and "deltaBiomasseTotale" variables.

    """

    data["biomasseTotale"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:]==2) & (data["changePhase"][j,:,:]==1),

        paramITK["densite"] *  np.maximum(1,paramVariete['densOpti']/paramITK['densite']) * paramVariete["txResGrain"] *  paramVariete["poidsSecGrain"] / 1000,

        data["biomasseTotale"][j,:,:]  + (data["assim"][j,:,:] - data["respMaint"][j,:,:]),

    )

    # we may want to drop this variable and use the raw computation instead

    data["deltaBiomasseTotale"][j:,:,:] = (data["assim"][j,:,:] - data["respMaint"][j,:,:])

    return data

def update_total_biomass_stade_ip(j, data):

    """

    Update the total biomass of the plant at the end of the vegetative phase (ip = "initiation paniculaire").

    If the plant has reached phase 4 and has just changed phase, the current

    total biomass will be copied to the "biomTotStadeIp" variable, which represents

    the total biomass at the end of the vegetative phase (initiation paniculaire).

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of

    the bilancarbonsarra.pas file from the original Pascal code.

    Args:

    j (int): Timestep index.

    data (xarray.Dataset): Input dataset.

    Returns:

    xarray.Dataset: The updated dataset with the "biomTotStadeIp" variable updated.

    """

    data["biomTotStadeIp"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 4) & (data["changePhase"][j,:,:] == 1),

        data["biomasseTotale"][j,:,:],

        data["biomTotStadeIp"][j,:,:],

    )

    return data

def update_total_biomass_at_flowering_stage(j, data):

    """

    This function updates the total biomass of the plant at the end of the

    flowering stage (biomTotStadeFloraison).

    If the plant is in phase 5, and the phase has changed, then the total

    biomass is copied to the biomTotStadeFloraison variable.

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of

    the bilancarbonsarra.pas file from the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomTotStadeFloraison"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5) & (data["changePhase"][j,:,:] == 1),

        data["biomasseTotale"][j,:,:],

        data["biomTotStadeFloraison"][j,:,:],

    )

    return data

def update_potential_yield(j, data, paramVariete):

    """

    Update the potential yield of the plant.

    The potential yield is initialized as an affine function of the delta

    between the end of the vegetative phase and the end of the flowering stage,

    plus a linear function of the total biomass at the end of the flowering stage.

    The potential yield is capped to twice the biomass of the stem to avoid unrealistic

    values.

    The update occurs if the plant is in phase 5 and its phase has changed

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code.

    Args:

        j (int): An index representing the current time step.

        data (xarray.Dataset): A dataset containing plant data.

        paramVariete (dict): A dictionary containing parameters for the plant variety.

    Returns:

        xarray.Dataset: The input `data` with the potential yield updated.

    """

    delta_biomass_flowering_ip = data["biomTotStadeFloraison"][j,:,:] - data["biomTotStadeIp"][j,:,:]

    data["rdtPot"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5) & (data["changePhase"][j,:,:] == 1),

        (paramVariete["KRdtPotA"] * delta_biomass_flowering_ip + paramVariete["KRdtPotB"]) + paramVariete["KRdtBiom"] * data["biomTotStadeFloraison"][j,:,:],

        data["rdtPot"][j,:,:],

    )

    #! phaseDevVeg pas utilisé ? attention c'est un paramètre variétal et pas un jeu de donées

    data["rdtPot"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5) & (data["changePhase"][j,:,:] == 1) & (data["rdtPot"][j,:,:] > data["biomasseTige"][j,:,:] * 2) & (paramVariete["phaseDevVeg"] < 6),

        data["biomasseTige"][j,:,:] * 2,

        data["rdtPot"][j,:,:],

    )

    return data

def update_potential_yield_delta(j, data, paramVariete):

    """

    This function updates the delta potential yield (dRdtPot) of the plant, which is the rate at which the

    plant's yield is changing over time. The delta potential yield is calculated as the product of the potential

    yield, the ratio of actual degree days to maturity, and the ratio of actual transpiration to potential transpiration.

    The calculation is only done if the plant is in phase 5 and the potential transpiration is above 0.

    If the potential transpiration is not above 0, the delta potential yield is set to 0.

    For all other phases, the delta potential yield is unchanged.

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code.

    Args:

    - j (int): an integer index, representing the current step of the simulation

    - data (xarray dataset): the simulation data, including the current state of the plant

    - paramVariete (dict): the variety-specific parameters used in the simulation

    Returns:

    - data (xarray dataset): the updated simulation data, including the updated delta potential yield

    """

    data["dRdtPot"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5),

        np.where(

            (data["trPot"][j,:,:] > 0),

            np.maximum(

                data["rdtPot"][j,:,:] * (data["ddj"][j,:,:] / paramVariete["SDJMatu1"]) * (data["tr"][j,:,:] / data["trPot"][j,:,:]),

                data["respMaint"][j,:,:] * 0.15,

            ),

            0,

        ),

        data["dRdtPot"][j,:,:],

    )

    return data

def update_aboveground_biomass(j, data, paramVariete):

    """

    Update the aboveground biomass of the plant.

    The aboveground biomass is either updated based on a linear function of the total biomass, if the plant is in phase 2, 3, or 4, or incremented with the total biomass delta if the plant is in any other phase.

    This function is based on the EvolBiomAeroSarrahV3 procedure, of the

    ***bilancarbonsarra***, exmodules 1 & 2.pas file from the original Pascal

    code.

    Args:

        j (int): The current iteration step in the simulation.

        data (xarray.Dataset): The simulation data, including the current phase of the plant and various biomass values.

        paramVariete (dict): The parameters of the plant variety.

    Returns:

        xarray.Dataset: The updated simulation data, including the updated aboveground biomass and delta aboveground biomass.

    """

    #// data["deltaBiomasseAerienne"][j:,:,:] = np.copy(data["biomasseAerienne"][j,:,:])

    data["biomasseAerienne"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] >= 2) & (data["numPhase"][j,:,:] <= 4),

        np.minimum(0.9, paramVariete["aeroTotPente"] * data["biomasseTotale"][j,:,:] + paramVariete["aeroTotBase"]) * data["biomasseTotale"][j,:,:],

        data["biomasseAerienne"][j,:,:] + data["deltaBiomasseTotale"][j,:,:],

    )

    #//data["deltaBiomasseAerienne"][j:,:,:] = (data["biomasseAerienne"][j,:,:] - data["deltaBiomasseAerienne"][j,:,:])#[...,np.newaxis]

    data["deltaBiomasseAerienne"][j:,:,:] = data["biomasseAerienne"][j,:,:] - data["biomasseAerienne"][j-1,:,:]

    return data

def estimate_reallocation(j, data, paramVariete):

    """

    Estimate the daily biomass reallocation between stem and leaves.

    This function computes the daily biomass reallocation between stem and leaves for the plant. The computation

    only occurs when the plant is in phase 5. The amount of biomass that can be reallocated is estimated as

    follows:

    1. The difference between the potential yield delta and the aboveground biomass delta, bound by 0, is

    calculated and referred to as manqueAssim. manqueAssim represents the daily variation in biomass that

    remains after the plant has built its aboveground biomass.

    2. The reallocation is computed as the minimum of the product of manqueAssim and the reallocation rate and

    the difference between the leaf biomass and 30, also bound by 0. The value of 30 is an arbitrary

    threshold which ensures that reallocation is 0 if the leaf biomass is below 30. If the leaf biomass is

    above 30, reallocation is bounded by biomasseFeuille - 30.

    If the plant is not in phase 5, reallocation is set to 0.

    This function is based on the EvalReallocationSarrahV3 procedure from the bilancarbonsarra.pas and

    exmodules 1 & 2.pas files from the original Pascal code.

    Args:

        j (int): Current time step of the simulation.

        data (xarray.Dataset): The dataset containing all the simulation data.

        paramVariete (dict): A dictionary containing the parameters for the simulation.

    Returns:

        xarray.Dataset: The updated dataset with the reallocation values.

    """

    condition = (data["numPhase"][j,:,:] == 5)

    data["manqueAssim"][j:,:,:] = np.where(

        condition,

        np.maximum(0, (data["dRdtPot"][j,:,:] -  np.maximum(0.0, data["deltaBiomasseAerienne"][j,:,:]))),

        0,

    )

    data["reallocation"][j:,:,:] = np.where(

        condition,

        np.minimum(

            data["manqueAssim"][j,:,:] * paramVariete["txRealloc"],

            np.maximum(0.0, data["biomasseFeuille"][j,:,:] - 30),

        ),

        0,

    )

    return data

def update_root_biomass(j, data):

    """

    Update the root biomass (biomasseRacinaire) for a given time step.

    The root biomass is computed as the difference between the total biomass

    and the aboveground biomass.

    This function is based on the EvalBiomasseRacinaire procedure, of the

    milbilancarbone, exmodules 1 & 2, ***milbilancarbone***.pas file from the

    original Pascal code

    Args:

        j (int): Time step index.

        data (xarray.Dataset): Input dataset containing relevant variables.

    Returns:

        xarray.Dataset: Updated dataset with the root biomass variable.

    """

    data["biomasseRacinaire"][j,:,:] = data["biomasseTotale"][j,:,:] - data["biomasseAerienne"][j,:,:]

    return data

def update_leaf_biomass(j, data, paramVariete):

    """

    For phase above 1 and if the delta of aerial biomass is negative,

    meaning that the plant is losing aerial biomass, the leaf biomass is

    updated as the difference between the leaf biomass and the reallocation

    minus the delta of aerial biomass multiplied by the reallocation rate in

    leaves. This value is bound in 0.00000001.

    Otherwise, the leaf biomass is not updated.

    This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of

    the bilancarbonsarra.pas and exmodules 1 & 2.pas files from the original

    Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseFeuille"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] < 0),

        np.maximum(

            0.00000001,

            data["biomasseFeuille"][j,:,:] - (data["reallocation"][j,:,:] - data["deltaBiomasseAerienne"][j,:,:]) * paramVariete["pcReallocFeuille"]

        ),

        data["biomasseFeuille"][j,:,:],

    )

    return data

def update_stem_biomass(j, data, paramVariete):

    """

    For phase above 1 and if the delta of aerial biomass is negative,

    meaning that the plant is losing aerial biomass, the stem biomass is

    updated as the difference between the leaf biomass and the reallocation

    minus the delta of aerial biomass multiplied by (1-reallocation rate in

    leaves) (if it's not leaves, it's stems...). This value is bound in 0.00000001.

    Otherwise, the stem biomass is not updated.

    This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of

    the bilancarbonsarra.pas and exmodules 1 & 2.pas files from the original

    Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    # group 122

    data["biomasseTige"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] < 0),

        np.maximum(

            0.00000001,

            data["biomasseTige"][j,:,:] - (data["reallocation"][j,:,:] - data["deltaBiomasseAerienne"][j,:,:]) * (1 - paramVariete["pcReallocFeuille"]),

            ),

        data["biomasseTige"][j,:,:],

    )

    return data

def condition_positive_delta_biomass(j, data, paramVariete):

        condition = (data["numPhase"][j,:,:] > 1) & \

            (data["deltaBiomasseAerienne"][j,:,:] >= 0) & \

            ((data["numPhase"][j,:,:] <= 4) | (data["numPhase"][j,:,:] <= paramVariete["phaseDevVeg"]))

            # (data["numPhase"][j,:,:] <= 4)

        return condition

def update_bM_and_cM(j, data, paramVariete):

    """

    This function returns the updated values of bM and cM.

    bM and cM are updated if the delta of aerial biomass is positive,

    meaning that the plant is gaining aerial biomass, and if the phase is

    above 1 and below 4 or the phase is below the vegetative phase.

    This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of

    the bilancarbonsarra.pas files from the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["bM"][j,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        paramVariete["feuilAeroBase"] - 0.1,

        data["bM"][j,:,:],

    )

    data["cM"][j,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        ((paramVariete["feuilAeroPente"] * 1000)/ data["bM"][j,:,:] + 0.78) / 0.75,

        data["cM"][j,:,:],

    )

    return data

def update_leaf_biomass_positive_delta_aboveground_biomass(j, data, paramVariete):

    """

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseFeuille"][j:,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        (0.1 + data["bM"][j,:,:] * data["cM"][j,:,:] ** ((data["biomasseAerienne"][j,:,:] - data["rdt"][j,:,:]) / 1000)) \

            * (data["biomasseAerienne"][j,:,:] - data["rdt"][j,:,:]),

        data["biomasseFeuille"][j,:,:],

    )

    return data

def update_stem_biomass_positive_delta_aboveground_biomass(j, data, paramVariete):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseTige"][j:,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        data["biomasseAerienne"][j,:,:] - data["biomasseFeuille"][j,:,:] - data["rdt"][j,:,:],

        data["biomasseTige"][j,:,:],

    )

    return data

def condition_positive_delta_aboveground_biomass_all_phases(j, data):

        #// condition = (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] >= 0)

    condition = (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] > 0)

    return condition

def update_leaf_biomass_all_phases(j, data, paramVariete):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseFeuille"][j:,:,:] = np.where(

        condition_positive_delta_aboveground_biomass_all_phases(j, data),

        data["biomasseFeuille"][j,:,:] - data["reallocation"][j,:,:] * paramVariete["pcReallocFeuille"],

        data["biomasseFeuille"][j,:,:],

    )

    return data

def update_stem_biomass_all_phases(j, data, paramVariete):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseTige"][j:,:,:] = np.where(

        condition_positive_delta_aboveground_biomass_all_phases(j, data),

        data["biomasseTige"][j,:,:] - (data["reallocation"][j,:,:] * (1- paramVariete["pcReallocFeuille"])),

        data["biomasseTige"][j,:,:],

    )

    return data

def update_aboveground_biomass_step_2(j, data):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseAerienne"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 1),

        data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:],

        data["biomasseAerienne"][j,:,:],

    )

    return data

def EvalFeuilleTigeSarrahV4(j, data, paramVariete):

    """

    This function is a wrapper

    It is adapted from the EvalFeuilleTigeSarrahV4 procedure from the bilancarbonsarra.pas file

    of the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    # data["deltaBiomasseFeuilles"][j:,:,:] = np.where(

    #     (data["numPhase"][j,:,:] > 1),

    #     data["biomasseFeuille"][j,:,:],

    #     data["deltaBiomasseFeuilles"][j,:,:],

    # )

    # if (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] < 0)

    data = update_leaf_biomass(j, data, paramVariete)

    data = update_stem_biomass(j, data, paramVariete)

    # if deltaBiomasseAerienne >= 0 and (numPhase <= 4 or numPhase <= phaseDevVeg)

    data = update_bM_and_cM(j, data, paramVariete)

    data = update_leaf_biomass_positive_delta_aboveground_biomass(j, data, paramVariete)

    data = update_stem_biomass_positive_delta_aboveground_biomass(j, data, paramVariete)

    # if deltaBiomasseAerienne > 0 and numPhase > 1

    data = update_leaf_biomass_all_phases(j, data, paramVariete)

    data = update_stem_biomass_all_phases(j, data, paramVariete)

    # condition = (data["numPhase"][j,:,:] > 1)

    # data["deltaBiomasseFeuilles"][j:,:,:] = np.where(

    #     (data["numPhase"][j,:,:] > 1),

    #     data["biomasseFeuille"][j,:,:] - data["deltaBiomasseFeuilles"][j,:,:],

    #     data["deltaBiomasseFeuilles"][j,:,:],

    # )

    # simpler formulation for updating the deltaBiomasseFeuilles

    data["deltaBiomasseFeuilles"][j:,:,:] = data["biomasseFeuille"][j,:,:] - data["biomasseFeuille"][j-1,:,:]

    data = update_aboveground_biomass_step_2(j, data)

    return data

def update_vegetative_biomass(j, data):

    """_summary_

    This function is adapted from the EvalBiomasseVegetati procedure from the copie milbilancarbon, exmodules 1 & 2, ***milbilancarbone*** file

    of the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseVegetative"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:])

    return data

def calculate_canopy_specific_leaf_area(j, data, paramVariete):

    """

    Calculate the specific leaf area (SLA) of the canopy.

    If the leaf biomass is positive, and if we are at the transition day between

    phases 1 and 2 (numPhase = 2 and changePhase = 1), then the SLA is set to

    `slaMax`.

    If the leaf biomass is positive and increasing (deltaBiomasseFeuilles is

    positive), the SLA for existing leaves is calculated by reducing it by an

    amount proportional to the current SLA, while the SLA for new leaves is

    calculated as the average between SLA and `slaMax`. The SLA for the entire

    canopy is then calculated as the weighted average of the SLAs for existing

    and new leaves.

    If there is no increase in leaf biomass (deltaBiomasseFeuilles is negative),

    only the SLA for existing leaves is calculated.

    If the leaf biomass is negative, the SLA is unchanged.

    The calculated SLA value is bounded between `slaMin` and `slaMax`.

    This function is adapted from the EvalSlaSarrahV3 procedure in the

    bilancarbonsarra.pas and exmodules 1 & 2.pas files of the original Pascal

    code.  This calculation method assumes that young leaves have a higher SLA

    than old leaves and that the fraction of young leaves makes the canopy SLA

    increase. The `penteSLA` parameter causes a general decrease in SLA

    (penteSLA = relative decrease per day = fraction of difference between

    `slaMax` and `slaMin`).

    Expected parameters:

    SLAmax [0.001, 0.01]

    SLAmin [0.001, 0.01]

    penteSLA [0, 0.2]

    SLAini = SLAmax

    This function estimates the specific leaf area (SLA) of the canopy.

    First, if the leaf biomass is positive, if numPhase = 2 and changePhase = 1,

    which means we are at the transition day between phases 1 and 2, sla is set

    to be equal to slaMax.

    Then, if the leaf biomass is positive, and if deltaBiomasseFeuilles is

    positive (meaning that the leaf biomass is increasing), SLA for already

    existing leaves is calculated by removing a value that is an affine function

    of SLA itself, and SLA for new leaves is calculated as the mean between SLA

    and slaMax ; then the SLA is calculated as the weighted mean of the two SLA

    values.

    Logically, if there is no newly produced leaf biomass (deltaBiomasseFeuilles

    is negative), only the SLA for already existing leaves is calculated.

    If biomasseFeuille is negative, SLA is unchanged.

    Finally, if biomasseFeuille is positive, SLA value is bounded between slaMin

    and slaMax.

    This function is adapted from the EvalSlaSarrahV3 procedure from the

    bilancarbonsarra.pas and  exmodules 1 & 2.pas file of the original Pascal

    code.  We note that multiple versions of the calculation methods have been

    used in the original procecure. We may want to go back to that if this

    function is problematic.

    Notes :

    In this approach, it is assumed that young leaves have a higher SLA than old

    leaves. The fraction of young leaves makes the canopy SLA increase. The

    penteSLA parameter causes a general decrease in SLA (penteSLA = relative

    decrease per day = fraction of difference between SLAmax and SLAmin). This

    approach is known for legumes, but can also be adapted to other species.

    Generic/expected parameters :

    SLAmax [0.001, 0.01]

    SLAmin [0.001, 0.01]

    penteSLA [0, 0.2]

    SLAini = SLAmax

    Args:

    - j (int): The time step.

    - data (xarray.Dataset): The data for all variables.

    - paramVariete (dict): Parameters for the calculation.

    Returns:

    - data (xarray.Dataset): The updated data with the calculated SLA.

    """

    condition = (data["biomasseFeuille"][j,:,:] > 0) & \

                (data["numPhase"][j,:,:] == 2) & \

                (data["changePhase"][j,:,:] == 1)

    data["sla"][j:,:,:] = np.where(

        condition,

        paramVariete["slaMax"],

        data["sla"][j,:,:],

    )

    ratio_old_leaf_biomass = data["biomasseFeuille"][j-1,:,:] / data["biomasseFeuille"][j,:,:]

    ratio_new_leaf_biomass = data["deltaBiomasseFeuilles"][j,:,:] / data["biomasseFeuille"][j,:,:]

    sla_decrease_step = paramVariete["slaPente"] * (data["sla"][j,:,:] - paramVariete["slaMin"])

    # Modif du 10/07/2018, DeltaBiomasse neg si reallocation ne pas fair l'evol du SLA dans ces conditions

    data["sla"][j:,:,:] = np.where(

        (data["biomasseFeuille"][j,:,:] > 0),

        np.where(

            (data["deltaBiomasseFeuilles"][j,:,:] > 0),

            #// (data["sla"][j,:,:] - paramVariete["slaPente"] * (data["sla"][j,:,:] - paramVariete["slaMin"])) * (data["biomasseFeuille"][j,:,:] - data["deltaBiomasseFeuilles"][j,:,:]) / data["biomasseFeuille"][j,:,:] + (paramVariete["slaMax"] + data["sla"][j,:,:])/2 * (data["deltaBiomasseFeuilles"][j,:,:] / data["biomasseFeuille"][j,:,:]),

            (data["sla"][j,:,:] - sla_decrease_step) * ratio_old_leaf_biomass + (paramVariete["slaMax"] + data["sla"][j,:,:])/2 * ratio_new_leaf_biomass,

            #//(data["sla"][j,:,:] - paramVariete["slaPente"] * (data["sla"][j,:,:] - paramVariete["slaMin"])) * (data["biomasseFeuille"][j,:,:] / data["biomasseFeuille"][j,:,:]),

            (data["sla"][j,:,:] - sla_decrease_step) * ratio_old_leaf_biomass,

        ),

        data["sla"][j,:,:],

    )

    data["sla"][j:,:,:] = np.where(

        (data["biomasseFeuille"][j,:,:] > 0),

        #// np.minimum(paramVariete["slaMin"], np.maximum(paramVariete["slaMax"], data["sla"][j,:,:])), # according to original

        # according to ocelet version

        np.minimum(

            paramVariete["slaMax"],

            np.maximum(

                paramVariete["slaMin"],

                data["sla"][j,:,:],

            ),

        ),

        data["sla"][j,:,:],

    )

    return data

def calculate_leaf_area_index(j, data):

    """

    Calculate the leaf area index (LAI) for a given time step.

    If the number of growth phase (numPhase) is less than or equal to 1, the LAI is set to 0.

    If the number of growth phase is between 2 and 6, the LAI is calculated as the product of

    the leaf biomass (biomasseFeuille) and specific leaf area (sla).

    If the number of growth phase is greater than 6, the LAI is set back to 0.

    This function is adapted from the EvolLAIPhases procedure from the

    milbilancarbone.pas and exmodules 1 & 2.pas file of the original Pascal

    code.

    Args:

        timestep (int): The time step to calculate the LAI for.

        data (xarray.Dataset): The xarray dataset that contains the relevant data.

    Returns:

        xarray.Dataset: The updated xarray dataset with the calculated LAI.

    """

    data["lai"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] <= 1),

        0,

        np.where(

            data["numPhase"][j,:,:] <= 6,

            data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:],

            0,

        )

    )

    return data

def update_yield_during_filling_phase(j, data):

    """

    This function updates the yield value during the filling phase.

    During the filling phase (numPhase == 5), the yield is updated by

    incrementing it with the sum of `deltaBiomasseAerienne` and `reallocation`,

    bounded by 0 and `dRdtPot` (daily potential yield). The construction of yield

    is done during phase 5 only, from the variation of aerial biomass and

    reallocation, with a maximum of `dRdtPot`.

    This function is adapted from the EvolDayRdtSarraV3 procedure from the

    ***bilancarbonesarra***, exmodules 1 & 2.pas file of the original Pascal

    code.

    Notes :

    On tend vers le potentiel en fn du rapport des degresJours/sumDegresJours

    pour la phase de remplissage. Frein sup fn du flux de sève estimé par le

    rapport Tr/TrPot.

    dRdtPot = RdtPotDuJour

    Args:

        j (int): The time step at which the calculation starts.

        data (xarray.Dataset): The data that contains all variables.

    Returns:

        xarray.Dataset: The input data with updated yield values.

    """

    data["rdt"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5),

        data["rdt"][j,:,:] + np.minimum(data["dRdtPot"][j,:,:],  np.maximum(0.0, data["deltaBiomasseAerienne"][j,:,:]) + data['reallocation'][j,:,:]),

        data["rdt"][j,:,:],

    )

    return data

def BiomDensiteSarraV42(j, data, paramITK, paramVariete):

    # depuis bilancarbonsarra.pas

    if ~np.isnan(paramVariete["densOpti"]):

        data["rdt"][j:,:,:] = (data["rdt"][j,:,:] / data["rapDensite"])

        data["rdtPot"][j:,:,:] = (data["rdtPot"][j,:,:]/ data["rapDensite"])

        data["biomasseRacinaire"][j:,:,:] = (data["biomasseRacinaire"][j,:,:] / data["rapDensite"])

        data["biomasseTige"][j:,:,:] = (data["biomasseTige"][j,:,:] / data["rapDensite"])

        data["biomasseFeuille"][j:,:,:] = (data["biomasseFeuille"][j,:,:] / data["rapDensite"])

        data["biomasseAerienne"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:])

        #? conflit avec fonction evolLAIphase ?

        #data["lai"][j:,:,:]  = data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:]

        data["lai"][j:,:,:]  = data["lai"][j:,:,:]  / data["rapDensite"]

        data["biomasseTotale"][j:,:,:] = (data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:])#[...,np.newaxis]

        #data["biomasseTotale"][j:,:,:] = data["biomasseTotale"][j:,:,:] / data["rapDensite"]

    return data

def BiomMcUBTSV3(j, data, paramITK):

    """

    depuis bilancarbonsarra.pas

    group 174

    Pendant la croissance des cultures la d�gradation des r�sidusest calcul�e sans les UBT

    Ici c'est pendant la saion s�che quand il n'y a des cultures pas de b�tes.

    Sur le mulch dress� (Up) ou couch� Lit), on calcul sa d�gradation journali�re

    sur les feuilles et les tiges en fn de coef KN (climat, termites...),

    KI ingestion par les b�tes pression en UBT seulement pour les feuilles, KT (effet pi�tinement) qui va faire passer

    du stade lev� en couch� et du stade couch� en ensevelissement pression en UBT

    Par D�faut :

    KNUp = 0.001 /jour

    KNLit = 0.011

    KN est soit une constante soit peut varier en fn climat (pas fait ref STEP)

    KT = 0.003

    KI = 0.005

    NbUBT = 10 (zone Fakara)

    """

    condition = (data["numPhase"][j,:,:] > 0)

    #   group 161

    data["UBTCulture"][j:,:,:] = np.where(condition, 0, data["NbUBT"][j,:,:])#[...,np.newaxis]

    #  group 162

    data["LitFeuille"][j:,:,:] = np.where(condition, data["LitFeuille"][j,:,:] + data["FeuilleUp"][j,:,:], data["LitFeuille"][j,:,:])#[...,np.newaxis]

    # group 163

    data["LitTige"][j:,:,:] = np.where(condition, data["LitTige"][j,:,:] + data["TigeUp"][j,:,:], data["LitTige"][j,:,:])#[...,np.newaxis]

    # group 164

    data["FeuilleUp"][j:,:,:] = np.where(condition, 0, data["FeuilleUp"][j,:,:])#[...,np.newaxis]

    # group 165

    data["TigeUp"][j:,:,:] = np.where(condition, 0, data["TigeUp"][j,:,:])#[...,np.newaxis]

    # group 166

    data["biomMc"][j:,:,:] = np.where(condition, data["LitFeuille"][j,:,:] + data["LitTige"][j,:,:], data["biomMc"][j,:,:])#[...,np.newaxis]

    #// D�gradation des feuilles et tiges dress�es

    # FeuilleUp := max(0, (FeuilleUp -  FeuilleUp * KNUp - FeuilleUp * KI * UBTCulture  - FeuilleUp * KT * UBTCulture));

    # group 167

    data["FeuilleUp"][j:,:,:] = np.maximum(

        0,

        data["FeuilleUp"][j,:,:] - data["FeuilleUp"][j,:,:] * paramITK["KNUp"] - data["FeuilleUp"][j,:,:] \

            * paramITK["KI"] * data["UBTCulture"][j,:,:] - data["FeuilleUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    # group 168

    # TigeUp := max(0, (TigeUp -  TigeUp * KNUp - TigeUp * KT * UBTCulture));

    data["TigeUp"][j:,:,:] = np.maximum(

        0,

        data["TigeUp"][j,:,:] - data["TigeUp"][j,:,:] * paramITK["KNUp"] - data["TigeUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    #// D�gradation des feuilles et tiges couch�es (liti�re)

    # group 169

    # LitFeuille :=  max(0, (LitFeuille -  LitFeuille * KNLit - LitFeuille * KI * UBTCulture  - LitFeuille * KT * UBTCulture));

    data["LitFeuille"][j:,:,:] = np.maximum(

        0,

        data["LitFeuille"][j,:,:] - data["LitFeuille"][j,:,:] * paramITK["KNLit"] - data["LitFeuille"][j,:,:] * paramITK["KI"] \

            * data["UBTCulture"][j,:,:] - data["LitFeuille"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    # group 170

    # LitTige :=  max(0, (LitTige -  LitTige * KNLit - LitTige * KT * UBTCulture));

    data["LitTige"][j:,:,:] = np.maximum(

        0,

        data["LitTige"][j,:,:] - data["LitTige"][j,:,:] * paramITK["KNLit"] - data["LitTige"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    # group 171

    #BiomMc := LitFeuille + LitTige;

    data["biomMc"][j:,:,:] = (data["LitFeuille"][j,:,:] + data["LitTige"][j,:,:])#[...,np.newaxis]

    # #// transfert dress� � liti�re effet pi�tinement

    # LitFeuille :=  LitFeuille + FeuilleUp * KT * UBTCulture;

    # group 172

    data["LitFeuille"][j:,:,:] = (data["LitFeuille"][j,:,:] + data["FeuilleUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:])#[...,np.newaxis]

    # LitTige :=  LitTige + TigeUp * KT * UBTCulture;

    # group 173

    data["LitTige"][j:,:,:] = (data["LitTige"][j,:,:] + data["TigeUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:])#[...,np.newaxis]

    # // le 01/03 on consid�re que toutes les pailles et feuilles dressees sont couchees

    #       if (trunc(DayOfTheYear(DateEnCours)) = 61) then

    #   begin

    #     LitFeuille :=  LitFeuille + FeuilleUp;

    #     LitTige :=  LitTige + TigeUp;

    #     FeuilleUp :=  0;

    #     TigeUp :=  0;

    #     BiomMc := LitFeuille + LitTige;

    #  end;

    return data

def MAJBiomMcSV3(data):

    """

    groupe 182

 A la Recolte, on calcul la part des biomasses qui restent sur place (Up), non r�colt�es

 et la part qui est mise � terre (Liti�re) sur ce qui est laiss� sur place

 On met a jour aussi la biomasse des liti�res pour les calculs effet mulch sue lr bilan hydrique

    """

#         if (NumPhase =7) then

#     begin

        # groupe 175

#       FeuilleUp := FeuilleUp +  BiomasseFeuilles * (1-TxRecolte);

        # groupe 176

#       TigeUp := TigeUp + BiomasseTiges *  (1-TxRecolte);

        # groupe 177

#       LitFeuille := LitFeuille + FeuilleUp * TxaTerre;

        # groupe 178

#       LitTige := LitTige + TigeUp * TxaTerre;

        # groupe 179

#       FeuilleUp := FeuilleUp * (1-TxaTerre);

        # groupe 180

#       TigeUp := TigeUp * (1-TxaTerre);

# //      LitTige := LitTige + BiomMc;

        # groupe 181

#       BiomMC := LitFeuille + LitTige;

#  {     BiomasseFeuilles := 0;

#       BiomasseTiges := 0;

    return data

def estimate_critical_nitrogen_concentration(j, data):

    # estimate critical nitrogen concentration from plant dry matter using the Justes et al (1994) relationship

    data["Ncrit"][j,:,:] = 5.35 * (data["biomasseTotale"][j,:,:]/1000) ** (-0.44)

    return data

Functions

BiomDensOptSarraV4

def BiomDensOptSarraV4(
    j,
    data,
    paramITK
)

si densité plus faible alors on considére qu'il faut augmenter les biomasses, LAI etc

en regard de cette situation au niveau de chaque plante (car tout est rapporté é des kg/ha). Si elle est plus forte on ne change rien pour lors. Valeur fixe en ref au maés é déf en paramétre par variétésé rapDensite := Max(1, 70000/densite);

View Source
def BiomDensOptSarraV4(j, data, paramITK):

    """

    si densité plus faible alors on considére qu'il faut augmenter les biomasses, LAI etc

    en regard de cette situation au niveau de chaque plante (car tout est rapporté é des kg/ha).

    Si elle est plus forte on ne change rien pour lors.

    Valeur fixe en ref au maés é déf en paramétre par variétésé rapDensite := Max(1, 70000/densite);

    """

    """

    if ~np.isnan(paramVariete["densOpti"]) :

        paramITK["rapDensite"] = np.maximum(1,paramVariete["densOpti"]/paramITK["densite"])

        data["rdt"][j,:,:] = data["rdt"][j,:,:] * paramITK["rapDensite"]

        data["biomasseRacinaire"][j,:,:] = data["biomasseRacinaire"][j,:,:] * paramITK["rapDensite"]

        data["biomasseTige"][j,:,:] = data["biomasseTige"][j,:,:] * paramITK["rapDensite"]

        data["biomasseFeuille"][j,:,:] = data["biomasseFeuille"][j,:,:] * paramITK["rapDensite"]

        data["biomasseAerienne"][j,:,:] = data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:]

        data["lai"][j,:,:]  = data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:]

        data["biomasseTotale"][j,:,:] = data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:]

    return data

    """

    return data

BiomDensiteSarraV42

def BiomDensiteSarraV42(
    j,
    data,
    paramITK,
    paramVariete
)
View Source
def BiomDensiteSarraV42(j, data, paramITK, paramVariete):

    # depuis bilancarbonsarra.pas

    if ~np.isnan(paramVariete["densOpti"]):

        data["rdt"][j:,:,:] = (data["rdt"][j,:,:] / data["rapDensite"])

        data["rdtPot"][j:,:,:] = (data["rdtPot"][j,:,:]/ data["rapDensite"])

        data["biomasseRacinaire"][j:,:,:] = (data["biomasseRacinaire"][j,:,:] / data["rapDensite"])

        data["biomasseTige"][j:,:,:] = (data["biomasseTige"][j,:,:] / data["rapDensite"])

        data["biomasseFeuille"][j:,:,:] = (data["biomasseFeuille"][j,:,:] / data["rapDensite"])

        data["biomasseAerienne"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:])

        #? conflit avec fonction evolLAIphase ?

        #data["lai"][j:,:,:]  = data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:]

        data["lai"][j:,:,:]  = data["lai"][j:,:,:]  / data["rapDensite"]

        data["biomasseTotale"][j:,:,:] = (data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:])#[...,np.newaxis]

        #data["biomasseTotale"][j:,:,:] = data["biomasseTotale"][j:,:,:] / data["rapDensite"]

    return data

BiomMcUBTSV3

def BiomMcUBTSV3(
    j,
    data,
    paramITK
)

depuis bilancarbonsarra.pas

group 174

Pendant la croissance des cultures la d�gradation des r�sidusest calcul�e sans les UBT Ici c'est pendant la saion s�che quand il n'y a des cultures pas de b�tes. Sur le mulch dress� (Up) ou couch� Lit), on calcul sa d�gradation journali�re sur les feuilles et les tiges en fn de coef KN (climat, termites...), KI ingestion par les b�tes pression en UBT seulement pour les feuilles, KT (effet pi�tinement) qui va faire passer du stade lev� en couch� et du stade couch� en ensevelissement pression en UBT Par D�faut : KNUp = 0.001 /jour KNLit = 0.011 KN est soit une constante soit peut varier en fn climat (pas fait ref STEP) KT = 0.003 KI = 0.005 NbUBT = 10 (zone Fakara)

View Source
def BiomMcUBTSV3(j, data, paramITK):

    """

    depuis bilancarbonsarra.pas

    group 174

    Pendant la croissance des cultures la d�gradation des r�sidusest calcul�e sans les UBT

    Ici c'est pendant la saion s�che quand il n'y a des cultures pas de b�tes.

    Sur le mulch dress� (Up) ou couch� Lit), on calcul sa d�gradation journali�re

    sur les feuilles et les tiges en fn de coef KN (climat, termites...),

    KI ingestion par les b�tes pression en UBT seulement pour les feuilles, KT (effet pi�tinement) qui va faire passer

    du stade lev� en couch� et du stade couch� en ensevelissement pression en UBT

    Par D�faut :

    KNUp = 0.001 /jour

    KNLit = 0.011

    KN est soit une constante soit peut varier en fn climat (pas fait ref STEP)

    KT = 0.003

    KI = 0.005

    NbUBT = 10 (zone Fakara)

    """

    condition = (data["numPhase"][j,:,:] > 0)

    #   group 161

    data["UBTCulture"][j:,:,:] = np.where(condition, 0, data["NbUBT"][j,:,:])#[...,np.newaxis]

    #  group 162

    data["LitFeuille"][j:,:,:] = np.where(condition, data["LitFeuille"][j,:,:] + data["FeuilleUp"][j,:,:], data["LitFeuille"][j,:,:])#[...,np.newaxis]

    # group 163

    data["LitTige"][j:,:,:] = np.where(condition, data["LitTige"][j,:,:] + data["TigeUp"][j,:,:], data["LitTige"][j,:,:])#[...,np.newaxis]

    # group 164

    data["FeuilleUp"][j:,:,:] = np.where(condition, 0, data["FeuilleUp"][j,:,:])#[...,np.newaxis]

    # group 165

    data["TigeUp"][j:,:,:] = np.where(condition, 0, data["TigeUp"][j,:,:])#[...,np.newaxis]

    # group 166

    data["biomMc"][j:,:,:] = np.where(condition, data["LitFeuille"][j,:,:] + data["LitTige"][j,:,:], data["biomMc"][j,:,:])#[...,np.newaxis]

    #// D�gradation des feuilles et tiges dress�es

    # FeuilleUp := max(0, (FeuilleUp -  FeuilleUp * KNUp - FeuilleUp * KI * UBTCulture  - FeuilleUp * KT * UBTCulture));

    # group 167

    data["FeuilleUp"][j:,:,:] = np.maximum(

        0,

        data["FeuilleUp"][j,:,:] - data["FeuilleUp"][j,:,:] * paramITK["KNUp"] - data["FeuilleUp"][j,:,:] \

            * paramITK["KI"] * data["UBTCulture"][j,:,:] - data["FeuilleUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    # group 168

    # TigeUp := max(0, (TigeUp -  TigeUp * KNUp - TigeUp * KT * UBTCulture));

    data["TigeUp"][j:,:,:] = np.maximum(

        0,

        data["TigeUp"][j,:,:] - data["TigeUp"][j,:,:] * paramITK["KNUp"] - data["TigeUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    #// D�gradation des feuilles et tiges couch�es (liti�re)

    # group 169

    # LitFeuille :=  max(0, (LitFeuille -  LitFeuille * KNLit - LitFeuille * KI * UBTCulture  - LitFeuille * KT * UBTCulture));

    data["LitFeuille"][j:,:,:] = np.maximum(

        0,

        data["LitFeuille"][j,:,:] - data["LitFeuille"][j,:,:] * paramITK["KNLit"] - data["LitFeuille"][j,:,:] * paramITK["KI"] \

            * data["UBTCulture"][j,:,:] - data["LitFeuille"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    # group 170

    # LitTige :=  max(0, (LitTige -  LitTige * KNLit - LitTige * KT * UBTCulture));

    data["LitTige"][j:,:,:] = np.maximum(

        0,

        data["LitTige"][j,:,:] - data["LitTige"][j,:,:] * paramITK["KNLit"] - data["LitTige"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:],

    )#[...,np.newaxis]

    # group 171

    #BiomMc := LitFeuille + LitTige;

    data["biomMc"][j:,:,:] = (data["LitFeuille"][j,:,:] + data["LitTige"][j,:,:])#[...,np.newaxis]

    # #// transfert dress� � liti�re effet pi�tinement

    # LitFeuille :=  LitFeuille + FeuilleUp * KT * UBTCulture;

    # group 172

    data["LitFeuille"][j:,:,:] = (data["LitFeuille"][j,:,:] + data["FeuilleUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:])#[...,np.newaxis]

    # LitTige :=  LitTige + TigeUp * KT * UBTCulture;

    # group 173

    data["LitTige"][j:,:,:] = (data["LitTige"][j,:,:] + data["TigeUp"][j,:,:] * paramITK["KT"] * data["UBTCulture"][j,:,:])#[...,np.newaxis]

    # // le 01/03 on consid�re que toutes les pailles et feuilles dressees sont couchees

    #       if (trunc(DayOfTheYear(DateEnCours)) = 61) then

    #   begin

    #     LitFeuille :=  LitFeuille + FeuilleUp;

    #     LitTige :=  LitTige + TigeUp;

    #     FeuilleUp :=  0;

    #     TigeUp :=  0;

    #     BiomMc := LitFeuille + LitTige;

    #  end;

    return data

EvalAssimSarrahV4

def EvalAssimSarrahV4(
    j,
    data
)

data["parIntercepte"][j,:,:] = 0.5 * (1 - data["ltr"][j,:,:]) * data["rg"][j,:,:]

data["assimPot"][j:,:,:] = data["parIntercepte"][j,:,:] * data["conv"][j,:,:] * 10

data["assim"][j,:,:] = np.where( data["trPot"][j,:,:] > 0, data["assimPot"][j,:,:] * data["tr"][j,:,:] / data["trPot"][j,:,:], 0, )

View Source
def EvalAssimSarrahV4(j, data):

    """

    data["parIntercepte"][j,:,:] = 0.5 * (1 - data["ltr"][j,:,:]) * data["rg"][j,:,:]

    data["assimPot"][j:,:,:] = data["parIntercepte"][j,:,:] * data["conv"][j,:,:] * 10

    data["assim"][j,:,:] = np.where(

        data["trPot"][j,:,:] > 0,

        data["assimPot"][j,:,:] * data["tr"][j,:,:] / data["trPot"][j,:,:],

        0,

    )

    """

    return data

EvalFeuilleTigeSarrahV4

def EvalFeuilleTigeSarrahV4(
    j,
    data,
    paramVariete
)

This function is a wrapper

It is adapted from the EvalFeuilleTigeSarrahV4 procedure from the bilancarbonsarra.pas file of the original Pascal code.

Parameters:

Name Type Description Default
j type description None
data type description None
paramVariete type description None

Returns:

Type Description
type description
View Source
def EvalFeuilleTigeSarrahV4(j, data, paramVariete):

    """

    This function is a wrapper

    It is adapted from the EvalFeuilleTigeSarrahV4 procedure from the bilancarbonsarra.pas file

    of the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    # data["deltaBiomasseFeuilles"][j:,:,:] = np.where(

    #     (data["numPhase"][j,:,:] > 1),

    #     data["biomasseFeuille"][j,:,:],

    #     data["deltaBiomasseFeuilles"][j,:,:],

    # )

    # if (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] < 0)

    data = update_leaf_biomass(j, data, paramVariete)

    data = update_stem_biomass(j, data, paramVariete)

    # if deltaBiomasseAerienne >= 0 and (numPhase <= 4 or numPhase <= phaseDevVeg)

    data = update_bM_and_cM(j, data, paramVariete)

    data = update_leaf_biomass_positive_delta_aboveground_biomass(j, data, paramVariete)

    data = update_stem_biomass_positive_delta_aboveground_biomass(j, data, paramVariete)

    # if deltaBiomasseAerienne > 0 and numPhase > 1

    data = update_leaf_biomass_all_phases(j, data, paramVariete)

    data = update_stem_biomass_all_phases(j, data, paramVariete)

    # condition = (data["numPhase"][j,:,:] > 1)

    # data["deltaBiomasseFeuilles"][j:,:,:] = np.where(

    #     (data["numPhase"][j,:,:] > 1),

    #     data["biomasseFeuille"][j,:,:] - data["deltaBiomasseFeuilles"][j,:,:],

    #     data["deltaBiomasseFeuilles"][j,:,:],

    # )

    # simpler formulation for updating the deltaBiomasseFeuilles

    data["deltaBiomasseFeuilles"][j:,:,:] = data["biomasseFeuille"][j,:,:] - data["biomasseFeuille"][j-1,:,:]

    data = update_aboveground_biomass_step_2(j, data)

    return data

MAJBiomMcSV3

def MAJBiomMcSV3(
    data
)

groupe 182

A la Recolte, on calcul la part des biomasses qui restent sur place (Up), non r�colt�es et la part qui est mise � terre (Liti�re) sur ce qui est laiss� sur place On met a jour aussi la biomasse des liti�res pour les calculs effet mulch sue lr bilan hydrique

View Source
def MAJBiomMcSV3(data):

    """

    groupe 182

 A la Recolte, on calcul la part des biomasses qui restent sur place (Up), non r�colt�es

 et la part qui est mise � terre (Liti�re) sur ce qui est laiss� sur place

 On met a jour aussi la biomasse des liti�res pour les calculs effet mulch sue lr bilan hydrique

    """

#         if (NumPhase =7) then

#     begin

        # groupe 175

#       FeuilleUp := FeuilleUp +  BiomasseFeuilles * (1-TxRecolte);

        # groupe 176

#       TigeUp := TigeUp + BiomasseTiges *  (1-TxRecolte);

        # groupe 177

#       LitFeuille := LitFeuille + FeuilleUp * TxaTerre;

        # groupe 178

#       LitTige := LitTige + TigeUp * TxaTerre;

        # groupe 179

#       FeuilleUp := FeuilleUp * (1-TxaTerre);

        # groupe 180

#       TigeUp := TigeUp * (1-TxaTerre);

# //      LitTige := LitTige + BiomMc;

        # groupe 181

#       BiomMC := LitFeuille + LitTige;

#  {     BiomasseFeuilles := 0;

#       BiomasseTiges := 0;

    return data

adjust_for_sowing_density

def adjust_for_sowing_density(
    j,
    data,
    paramVariete,
    direction
)

This function translates the effect of sowing density on biomass and LAI.

This function is adapted from the BiomDensOptSarV42 and BiomDensiteSarraV42 procedures, from the bilancarbonsarra.pas original Pascal code.

Notes from CB : if density is lower than the optimal density, then we consider that we need to increase the biomass, LAI etc in regard of this situation at each plant level (because everything is related to kg/ha). If it is higher, it increases asymptotically.

Parameters:

Name Type Description Default
j type description None
data type description None
paramITK type description None
paramVariete type description None

Returns:

Type Description
type description
View Source
def adjust_for_sowing_density(j, data, paramVariete, direction):

    """

    This function translates the effect of sowing density on biomass and LAI.

    This function is adapted from the BiomDensOptSarV42 and BiomDensiteSarraV42

    procedures, from the bilancarbonsarra.pas original Pascal code.

    Notes from CB :

    if density is lower than the optimal density, then we consider that we need

    to increase the biomass, LAI etc in regard of this situation at each plant

    level (because everything is related to kg/ha). If it is higher, it

    increases asymptotically.

    Args:

        j (_type_): _description_

        data (_type_): _description_

        paramITK (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    if direction == "in" :

        if ~np.isnan(paramVariete["densOpti"]) :

            data["rdt"][j:,:,:] = data["rdt"][j,:,:] * data["rapDensite"][j,:,:]

            data["rdtPot"][j:,:,:] = (data["rdtPot"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseRacinaire"][j:,:,:] = (data["biomasseRacinaire"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseTige"][j:,:,:] = (data["biomasseTige"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseFeuille"][j:,:,:] = (data["biomasseFeuille"][j,:,:] * data["rapDensite"][j,:,:])

            data["biomasseAerienne"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:])

            data["lai"][j:,:,:]  = (data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:])

            data["biomasseTotale"][j:,:,:] = (data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:])

        return data

    if direction == "out":

        if ~np.isnan(paramVariete["densOpti"]):

            data["rdt"][j:,:,:] = (data["rdt"][j,:,:] / data["rapDensite"][j,:,:])

            data["rdtPot"][j:,:,:] = (data["rdtPot"][j,:,:]/ data["rapDensite"][j,:,:])

            data["biomasseRacinaire"][j:,:,:] = (data["biomasseRacinaire"][j,:,:] / data["rapDensite"][j,:,:])

            data["biomasseTige"][j:,:,:] = (data["biomasseTige"][j,:,:] / data["rapDensite"][j,:,:])

            data["biomasseFeuille"][j:,:,:] = (data["biomasseFeuille"][j,:,:] / data["rapDensite"][j,:,:])

            data["biomasseAerienne"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:])

            #? conflit avec fonction evolLAIphase ?

            #data["lai"][j:,:,:]  = data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:]

            data["lai"][j:,:,:]  = data["lai"][j:,:,:]  / data["rapDensite"][j,:,:]

            data["biomasseTotale"][j:,:,:] = (data["biomasseAerienne"][j,:,:] + data["biomasseRacinaire"][j,:,:])#[...,np.newaxis]

            #data["biomasseTotale"][j:,:,:] = data["biomasseTotale"][j:,:,:] / data["rapDensite"]

        return data

calculate_canopy_specific_leaf_area

def calculate_canopy_specific_leaf_area(
    j,
    data,
    paramVariete
)

Calculate the specific leaf area (SLA) of the canopy.

If the leaf biomass is positive, and if we are at the transition day between phases 1 and 2 (numPhase = 2 and changePhase = 1), then the SLA is set to slaMax.

If the leaf biomass is positive and increasing (deltaBiomasseFeuilles is positive), the SLA for existing leaves is calculated by reducing it by an amount proportional to the current SLA, while the SLA for new leaves is calculated as the average between SLA and slaMax. The SLA for the entire canopy is then calculated as the weighted average of the SLAs for existing and new leaves.

If there is no increase in leaf biomass (deltaBiomasseFeuilles is negative), only the SLA for existing leaves is calculated.

If the leaf biomass is negative, the SLA is unchanged.

The calculated SLA value is bounded between slaMin and slaMax.

This function is adapted from the EvalSlaSarrahV3 procedure in the bilancarbonsarra.pas and exmodules 1 & 2.pas files of the original Pascal code. This calculation method assumes that young leaves have a higher SLA than old leaves and that the fraction of young leaves makes the canopy SLA increase. The penteSLA parameter causes a general decrease in SLA (penteSLA = relative decrease per day = fraction of difference between slaMax and slaMin).

Expected parameters: SLAmax [0.001, 0.01] SLAmin [0.001, 0.01] penteSLA [0, 0.2] SLAini = SLAmax

This function estimates the specific leaf area (SLA) of the canopy.

First, if the leaf biomass is positive, if numPhase = 2 and changePhase = 1, which means we are at the transition day between phases 1 and 2, sla is set to be equal to slaMax.

Then, if the leaf biomass is positive, and if deltaBiomasseFeuilles is positive (meaning that the leaf biomass is increasing), SLA for already existing leaves is calculated by removing a value that is an affine function of SLA itself, and SLA for new leaves is calculated as the mean between SLA and slaMax ; then the SLA is calculated as the weighted mean of the two SLA values.

Logically, if there is no newly produced leaf biomass (deltaBiomasseFeuilles is negative), only the SLA for already existing leaves is calculated.

If biomasseFeuille is negative, SLA is unchanged.

Finally, if biomasseFeuille is positive, SLA value is bounded between slaMin and slaMax.

This function is adapted from the EvalSlaSarrahV3 procedure from the bilancarbonsarra.pas and exmodules 1 & 2.pas file of the original Pascal code. We note that multiple versions of the calculation methods have been used in the original procecure. We may want to go back to that if this function is problematic.

Notes : In this approach, it is assumed that young leaves have a higher SLA than old leaves. The fraction of young leaves makes the canopy SLA increase. The penteSLA parameter causes a general decrease in SLA (penteSLA = relative decrease per day = fraction of difference between SLAmax and SLAmin). This approach is known for legumes, but can also be adapted to other species.

Generic/expected parameters : SLAmax [0.001, 0.01] SLAmin [0.001, 0.01] penteSLA [0, 0.2] SLAini = SLAmax

Args: - j (int): The time step. - data (xarray.Dataset): The data for all variables. - paramVariete (dict): Parameters for the calculation.

Returns: - data (xarray.Dataset): The updated data with the calculated SLA.

View Source
def calculate_canopy_specific_leaf_area(j, data, paramVariete):

    """

    Calculate the specific leaf area (SLA) of the canopy.

    If the leaf biomass is positive, and if we are at the transition day between

    phases 1 and 2 (numPhase = 2 and changePhase = 1), then the SLA is set to

    `slaMax`.

    If the leaf biomass is positive and increasing (deltaBiomasseFeuilles is

    positive), the SLA for existing leaves is calculated by reducing it by an

    amount proportional to the current SLA, while the SLA for new leaves is

    calculated as the average between SLA and `slaMax`. The SLA for the entire

    canopy is then calculated as the weighted average of the SLAs for existing

    and new leaves.

    If there is no increase in leaf biomass (deltaBiomasseFeuilles is negative),

    only the SLA for existing leaves is calculated.

    If the leaf biomass is negative, the SLA is unchanged.

    The calculated SLA value is bounded between `slaMin` and `slaMax`.

    This function is adapted from the EvalSlaSarrahV3 procedure in the

    bilancarbonsarra.pas and exmodules 1 & 2.pas files of the original Pascal

    code.  This calculation method assumes that young leaves have a higher SLA

    than old leaves and that the fraction of young leaves makes the canopy SLA

    increase. The `penteSLA` parameter causes a general decrease in SLA

    (penteSLA = relative decrease per day = fraction of difference between

    `slaMax` and `slaMin`).

    Expected parameters:

    SLAmax [0.001, 0.01]

    SLAmin [0.001, 0.01]

    penteSLA [0, 0.2]

    SLAini = SLAmax

    This function estimates the specific leaf area (SLA) of the canopy.

    First, if the leaf biomass is positive, if numPhase = 2 and changePhase = 1,

    which means we are at the transition day between phases 1 and 2, sla is set

    to be equal to slaMax.

    Then, if the leaf biomass is positive, and if deltaBiomasseFeuilles is

    positive (meaning that the leaf biomass is increasing), SLA for already

    existing leaves is calculated by removing a value that is an affine function

    of SLA itself, and SLA for new leaves is calculated as the mean between SLA

    and slaMax ; then the SLA is calculated as the weighted mean of the two SLA

    values.

    Logically, if there is no newly produced leaf biomass (deltaBiomasseFeuilles

    is negative), only the SLA for already existing leaves is calculated.

    If biomasseFeuille is negative, SLA is unchanged.

    Finally, if biomasseFeuille is positive, SLA value is bounded between slaMin

    and slaMax.

    This function is adapted from the EvalSlaSarrahV3 procedure from the

    bilancarbonsarra.pas and  exmodules 1 & 2.pas file of the original Pascal

    code.  We note that multiple versions of the calculation methods have been

    used in the original procecure. We may want to go back to that if this

    function is problematic.

    Notes :

    In this approach, it is assumed that young leaves have a higher SLA than old

    leaves. The fraction of young leaves makes the canopy SLA increase. The

    penteSLA parameter causes a general decrease in SLA (penteSLA = relative

    decrease per day = fraction of difference between SLAmax and SLAmin). This

    approach is known for legumes, but can also be adapted to other species.

    Generic/expected parameters :

    SLAmax [0.001, 0.01]

    SLAmin [0.001, 0.01]

    penteSLA [0, 0.2]

    SLAini = SLAmax

    Args:

    - j (int): The time step.

    - data (xarray.Dataset): The data for all variables.

    - paramVariete (dict): Parameters for the calculation.

    Returns:

    - data (xarray.Dataset): The updated data with the calculated SLA.

    """

    condition = (data["biomasseFeuille"][j,:,:] > 0) & \

                (data["numPhase"][j,:,:] == 2) & \

                (data["changePhase"][j,:,:] == 1)

    data["sla"][j:,:,:] = np.where(

        condition,

        paramVariete["slaMax"],

        data["sla"][j,:,:],

    )

    ratio_old_leaf_biomass = data["biomasseFeuille"][j-1,:,:] / data["biomasseFeuille"][j,:,:]

    ratio_new_leaf_biomass = data["deltaBiomasseFeuilles"][j,:,:] / data["biomasseFeuille"][j,:,:]

    sla_decrease_step = paramVariete["slaPente"] * (data["sla"][j,:,:] - paramVariete["slaMin"])

    # Modif du 10/07/2018, DeltaBiomasse neg si reallocation ne pas fair l'evol du SLA dans ces conditions

    data["sla"][j:,:,:] = np.where(

        (data["biomasseFeuille"][j,:,:] > 0),

        np.where(

            (data["deltaBiomasseFeuilles"][j,:,:] > 0),

            #// (data["sla"][j,:,:] - paramVariete["slaPente"] * (data["sla"][j,:,:] - paramVariete["slaMin"])) * (data["biomasseFeuille"][j,:,:] - data["deltaBiomasseFeuilles"][j,:,:]) / data["biomasseFeuille"][j,:,:] + (paramVariete["slaMax"] + data["sla"][j,:,:])/2 * (data["deltaBiomasseFeuilles"][j,:,:] / data["biomasseFeuille"][j,:,:]),

            (data["sla"][j,:,:] - sla_decrease_step) * ratio_old_leaf_biomass + (paramVariete["slaMax"] + data["sla"][j,:,:])/2 * ratio_new_leaf_biomass,

            #//(data["sla"][j,:,:] - paramVariete["slaPente"] * (data["sla"][j,:,:] - paramVariete["slaMin"])) * (data["biomasseFeuille"][j,:,:] / data["biomasseFeuille"][j,:,:]),

            (data["sla"][j,:,:] - sla_decrease_step) * ratio_old_leaf_biomass,

        ),

        data["sla"][j,:,:],

    )

    data["sla"][j:,:,:] = np.where(

        (data["biomasseFeuille"][j,:,:] > 0),

        #// np.minimum(paramVariete["slaMin"], np.maximum(paramVariete["slaMax"], data["sla"][j,:,:])), # according to original

        # according to ocelet version

        np.minimum(

            paramVariete["slaMax"],

            np.maximum(

                paramVariete["slaMin"],

                data["sla"][j,:,:],

            ),

        ),

        data["sla"][j,:,:],

    )

    return data

calculate_leaf_area_index

def calculate_leaf_area_index(
    j,
    data
)

Calculate the leaf area index (LAI) for a given time step.

If the number of growth phase (numPhase) is less than or equal to 1, the LAI is set to 0. If the number of growth phase is between 2 and 6, the LAI is calculated as the product of the leaf biomass (biomasseFeuille) and specific leaf area (sla). If the number of growth phase is greater than 6, the LAI is set back to 0.

This function is adapted from the EvolLAIPhases procedure from the milbilancarbone.pas and exmodules 1 & 2.pas file of the original Pascal code.

Parameters:

Name Type Description Default
timestep int The time step to calculate the LAI for. None
data xarray.Dataset The xarray dataset that contains the relevant data. None

Returns:

Type Description
xarray.Dataset The updated xarray dataset with the calculated LAI.
View Source
def calculate_leaf_area_index(j, data):

    """

    Calculate the leaf area index (LAI) for a given time step.

    If the number of growth phase (numPhase) is less than or equal to 1, the LAI is set to 0.

    If the number of growth phase is between 2 and 6, the LAI is calculated as the product of

    the leaf biomass (biomasseFeuille) and specific leaf area (sla).

    If the number of growth phase is greater than 6, the LAI is set back to 0.

    This function is adapted from the EvolLAIPhases procedure from the

    milbilancarbone.pas and exmodules 1 & 2.pas file of the original Pascal

    code.

    Args:

        timestep (int): The time step to calculate the LAI for.

        data (xarray.Dataset): The xarray dataset that contains the relevant data.

    Returns:

        xarray.Dataset: The updated xarray dataset with the calculated LAI.

    """

    data["lai"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] <= 1),

        0,

        np.where(

            data["numPhase"][j,:,:] <= 6,

            data["biomasseFeuille"][j,:,:] * data["sla"][j,:,:],

            0,

        )

    )

    return data

calculate_maintainance_respiration

def calculate_maintainance_respiration(
    j,
    data,
    paramVariete
)

This function calculates the maintenance respiration respMaint (in kg/ha/j in equivalent dry matter) of the plant.

The maintenance respiration is calculated by summing the maintenance respiration associated with total biomass and leaves biomass. If the plant's growth phase is above 4 and there is no leaf biomass, respMaint is set to 0.

The calculation is based on the equation: coefficient_temp = 2^((average_temp - tempMaint) / 10) respiration = kRespMaint * biomass * coefficient_temp

where average_temp is the average temperature for the day, tempMaint is the maintenance temperature from variety_params, kRespMaint is the maintenance respiration coefficient from variety_params, and biomass is the total or leaf biomass.

Parameters:

Name Type Description Default
j int The time step of the calculation. None
data xarray.Dataset The input data containing the variables tpMoy, biomasseTotale, biomasseFeuille, and
numPhase. The output respMaint will also be stored in this dataset.
None
variety_params dict The parameters related to the plant variety, containing the keys tempMaint and
kRespMaint.
None

Returns:

Type Description
xarray.Dataset The input data with the updated respMaint variable.
View Source
def calculate_maintainance_respiration(j, data, paramVariete):

    """

    This function calculates the maintenance respiration `respMaint` (in kg/ha/j in equivalent dry matter) of the plant.

    The maintenance respiration is calculated by summing the maintenance respiration associated with total biomass and

    leaves biomass. If the plant's growth phase is above 4 and there is no leaf biomass, `respMaint` is set to 0.

    The calculation is based on the equation:

      coefficient_temp = 2^((average_temp - tempMaint) / 10)

      respiration = kRespMaint * biomass * coefficient_temp

    where `average_temp` is the average temperature for the day, `tempMaint` is the maintenance temperature from

    `variety_params`, `kRespMaint` is the maintenance respiration coefficient from `variety_params`, and `biomass`

    is the total or leaf biomass.

    Args:

        j (int): The time step of the calculation.

        data (xarray.Dataset): The input data containing the variables `tpMoy`, `biomasseTotale`, `biomasseFeuille`, and

            `numPhase`. The output `respMaint` will also be stored in this dataset.

        variety_params (dict): The parameters related to the plant variety, containing the keys `tempMaint` and

            `kRespMaint`.

    Returns:

        xarray.Dataset: The input `data` with the updated `respMaint` variable.

    """

    coefficient_temp = 2**((data["tpMoy"][j,:,:] - paramVariete["tempMaint"]) / 10)

    resp_totale = paramVariete["kRespMaint"] * data["biomasseTotale"][j,:,:] * coefficient_temp

    resp_feuille = paramVariete["kRespMaint"] * data["biomasseFeuille"][j,:,:] * coefficient_temp

    data["respMaint"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 4) & (data["biomasseFeuille"][j,:,:]==0),

        0,

        resp_totale + resp_feuille,

    )

    return data

compute_rapDensite

def compute_rapDensite(
    paramITK,
    paramVariete
)

It basically calculates a correction factor (rapDensite).

This correction factor Is calculated with an equation of form

a + p * exp( -(x/(o / -log((1-a)/p) )) )

with a the densiteA parameter p the densiteP parameter$ x the actual crop density o the densOpti parameter

See https://www.wolframalpha.com/input?i=a+%2B+p+*+exp%28-%28x+%2F+%28+o%2F-+log%28%281+-+a%29%2F+p%29%29%29%29 for equation visualization.

This equation is probably too complex for the problem at hand.

Parameters:

Name Type Description Default
j type description None
data type description None
paramITK type description None
paramVariete type description None

Returns:

Type Description
type description
View Source
def compute_rapDensite(paramITK, paramVariete):

    """

    It basically calculates a correction factor (rapDensite).

    This correction factor Is calculated with an equation of form

    a + p * exp( -(x/(o / -log((1-a)/p) )) )

    with a the densiteA parameter

    p the densiteP parameter$

    x the actual crop density

    o the densOpti parameter

    See

    https://www.wolframalpha.com/input?i=a+%2B+p+*+exp%28-%28x+%2F+%28+o%2F-+log%28%281+-+a%29%2F+p%29%29%29%29

    for equation visualization.

    This equation is probably too complex for the problem at hand.

    Args:

        j (_type_): _description_

        data (_type_): _description_

        paramITK (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    rapDensite = paramVariete["densiteA"] + paramVariete["densiteP"] * np.exp(-(paramITK["densite"] / ( paramVariete["densOpti"]/- np.log((1 - paramVariete['densiteA'])/ paramVariete["densiteP"]))))

    return rapDensite

condition_positive_delta_aboveground_biomass_all_phases

def condition_positive_delta_aboveground_biomass_all_phases(
    j,
    data
)
View Source
def condition_positive_delta_aboveground_biomass_all_phases(j, data):

        #// condition = (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] >= 0)

    condition = (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] > 0)

    return condition

condition_positive_delta_biomass

def condition_positive_delta_biomass(
    j,
    data,
    paramVariete
)
View Source
def condition_positive_delta_biomass(j, data, paramVariete):

        condition = (data["numPhase"][j,:,:] > 1) & \

            (data["deltaBiomasseAerienne"][j,:,:] >= 0) & \

            ((data["numPhase"][j,:,:] <= 4) | (data["numPhase"][j,:,:] <= paramVariete["phaseDevVeg"]))

            # (data["numPhase"][j,:,:] <= 4)

        return condition

estimate_KAssim

def estimate_KAssim(
    j,
    data,
    paramVariete
)

This function calculates the conversion factor KAssim, which is used to convert assimilates into biomass.

The value of KAssim depends on the phase of the crop.

The conversion factor is calculated based on a lookup table that maps crop phases to values. The crop phase is determined by the numPhase field in the data argument, and the corresponding KAssim value is set in the KAssim field of the data argument.

Parameters:

Name Type Description Default
j int An integer index specifying the time step. None
data xarray.Dataset A dataset containing the variables used in the calculation of KAssim. The dataset
should include the fields numPhase, sdj, seuilTemp PhasePrec, and seuilTemp PhaseSuivante. The
KAssim field of the dataset will be updated by this function.
None
paramVariete dict A dictionary of parameters. It should include the fields txAssimBVP, txAssimMatu1,
and txAssimMatu2.
None

Returns:

Type Description
xarray.Dataset The updated data dataset, with the KAssim field set to the calculated values.
View Source
def estimate_KAssim(j, data, paramVariete):

    """

    This function calculates the conversion factor `KAssim`, which is used to convert assimilates into biomass.

    The value of `KAssim` depends on the phase of the crop.

    The conversion factor is calculated based on a lookup table that maps crop phases to values. The crop phase is

    determined by the `numPhase` field in the `data` argument, and the corresponding `KAssim` value is set in the

    `KAssim` field of the `data` argument.

    Args:

        j (int): An integer index specifying the time step.

        data (xarray.Dataset): A dataset containing the variables used in the calculation of `KAssim`. The dataset

            should include the fields `numPhase`, `sdj`, `seuilTemp PhasePrec`, and `seuilTemp PhaseSuivante`. The

            `KAssim` field of the dataset will be updated by this function.

        paramVariete (dict): A dictionary of parameters. It should include the fields `txAssimBVP`, `txAssimMatu1`,

            and `txAssimMatu2`.

    Returns:

        xarray.Dataset: The updated `data` dataset, with the `KAssim` field set to the calculated values.

    """

    phase_equivalences = {

        2: 1,

        3: paramVariete['txAssimBVP'],

        4: paramVariete['txAssimBVP'],

        #! replacing sommeDegresJourPhasePrec with seuilTempPhasePrec

        #// 5: paramVariete["txAssimBVP"] + (data['sdj'][j,:,:] - data['sommeDegresJourPhasePrec'][j,:,:]) * (paramVariete['txAssimMatu1'] -  paramVariete['txAssimBVP']) / (data['seuilTempPhaseSuivante'][j,:,:] - data['sommeDegresJourPhasePrec'][j,:,:]),

        5: paramVariete["txAssimBVP"] + (data['sdj'][j,:,:] - data['seuilTempPhasePrec'][j,:,:]) * (paramVariete['txAssimMatu1'] -  paramVariete['txAssimBVP']) / (data['seuilTempPhaseSuivante'][j,:,:] - data['seuilTempPhasePrec'][j,:,:]),

        #// 6: paramVariete["txAssimMatu1"] + (data["sdj"][j,:,:] - data["sommeDegresJourPhasePrec"][j,:,:]) * (paramVariete["txAssimMatu2"] - paramVariete["txAssimMatu1"]) / (data["seuilTempPhaseSuivante"][j,:,:] - data["sommeDegresJourPhasePrec"][j,:,:]),

        6: paramVariete["txAssimMatu1"] + (data["sdj"][j,:,:] - data["seuilTempPhasePrec"][j,:,:]) * (paramVariete["txAssimMatu2"] - paramVariete["txAssimMatu1"]) / (data["seuilTempPhaseSuivante"][j,:,:] - data["seuilTempPhasePrec"][j,:,:]),

    }

    for phase in range(2,7):

        data["KAssim"][j:,:,:] = np.where(

            data["numPhase"][j,:,:] == phase,

            phase_equivalences[phase],

            data["KAssim"][j,:,:],

        )

    return data

estimate_conv

def estimate_conv(
    j,
    data,
    paramVariete
)

This function calculates the conversion of assimilates into biomass.

The conversion factor is determined by multiplying the KAssim value, which is dependent on the phase of the crop, with the conversion rate (txConversion) specified in the paramVariete argument.

Parameters:

Name Type Description Default
j int The starting index of the calculation None
data dict A dictionary containing information on the crop growth, including
the phase of the crop and the KAssim value.
None
paramVariete dict A dictionary containing parameters relevant to the crop
growth, including the conversion rate.
None

Returns:

Type Description
dict The input data dictionary with the calculated "conv" value added.
View Source
def estimate_conv(j,data,paramVariete):

    """

    This function calculates the conversion of assimilates into biomass.

    The conversion factor is determined by multiplying the KAssim value, which

    is dependent on the phase of the crop, with the conversion rate (txConversion)

    specified in the `paramVariete` argument.

    Args:

        j (int): The starting index of the calculation

        data (dict): A dictionary containing information on the crop growth, including

                     the phase of the crop and the KAssim value.

        paramVariete (dict): A dictionary containing parameters relevant to the crop

                             growth, including the conversion rate.

    Returns:

        dict: The input `data` dictionary with the calculated "conv" value added.

    """

    data["conv"][j:,:,:] = (data["KAssim"][j,:,:] * paramVariete["txConversion"])

    return data

estimate_critical_nitrogen_concentration

def estimate_critical_nitrogen_concentration(
    j,
    data
)
View Source
def estimate_critical_nitrogen_concentration(j, data):

    # estimate critical nitrogen concentration from plant dry matter using the Justes et al (1994) relationship

    data["Ncrit"][j,:,:] = 5.35 * (data["biomasseTotale"][j,:,:]/1000) ** (-0.44)

    return data

estimate_kcp

def estimate_kcp(
    j,
    data,
    paramVariete
)

Estimate the kcp coefficient based on the maximum crop coefficient kcMax and plant cover ltr.

The computation of kcp is based on the EvolKcpKcIni procedure from the biomasse.pas and exmodules 1 & 2.pas files of the original PASCAL code.

Parameters:

Name Type Description Default
j int The starting index for updating kcp in the data dataset. None
data xarray.Dataset A dataset containing the data used in the computation of kcp. The dataset should contain the following variables:
- 'numPhase': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the number of phases in the crop cycle.
- 'kcp': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the coefficient of crop growth.
- 'ltr': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the plant cover.
None
paramVariete dict A dictionary containing the parameters for estimating kcp. The dictionary should contain the following key:
- 'kcMax': A float, representing the maximum crop coefficient.
None

Returns:

Type Description
xarray.Dataset The updated data dataset with the new kcp values.
View Source
def estimate_kcp(j, data, paramVariete):

    """

    Estimate the kcp coefficient based on the maximum crop coefficient `kcMax` and plant cover `ltr`.

    The computation of `kcp` is based on the EvolKcpKcIni procedure from the biomasse.pas and exmodules 1 & 2.pas files of the original PASCAL code.

    Args:

        j (int): The starting index for updating `kcp` in the `data` dataset.

        data (xarray.Dataset): A dataset containing the data used in the computation of `kcp`. The dataset should contain the following variables:

            - 'numPhase': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the number of phases in the crop cycle.

            - 'kcp': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the coefficient of crop growth.

            - 'ltr': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the plant cover.

        paramVariete (dict): A dictionary containing the parameters for estimating `kcp`. The dictionary should contain the following key:

            - 'kcMax': A float, representing the maximum crop coefficient.

    Returns:

        xarray.Dataset: The updated `data` dataset with the new `kcp` values.

    """

    data["kcp"][j:,:,:] = np.where(

        data["numPhase"][j,:,:] >= 1,

        np.maximum(0.3, paramVariete["kcMax"] * (1 - data["ltr"][j,:,:])),

        data["kcp"][j,:,:],

    )

    return data

estimate_ltr

def estimate_ltr(
    j,
    data,
    paramVariete
)

Estimate the fraction of radiation transmitted to the soil ltr based on the leaf area index lai.

ltr is used as a proxy for plant covering of the soil in the water balance calculation, where 1 represents no plant cover and 0 represents full plant cover. ltr is computed as an exponential decay function of lai with a decay coefficient kdf.

This function is adapted from the EvalLtr procedure from the biomasse.pas and exmodules 1 & 2.pas files of the original PASCAL code.

Parameters:

Name Type Description Default
j int The starting index for updating ltr in the data dataset. None
data xarray.Dataset A dataset containing the data used in the computation of ltr. The dataset should contain the following variables:
- 'lai': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the leaf area index.
- 'ltr': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the fraction of radiation transmitted to the soil.
None
paramVariete dict A dictionary containing the parameters for estimating ltr. The dictionary should contain the following key:
- 'kdf': A float, representing the decay coefficient for ltr.
None

Returns:

Type Description
xarray.Dataset The updated data dataset with the new ltr values.
View Source
def estimate_ltr(j, data, paramVariete):

    """

    Estimate the fraction of radiation transmitted to the soil `ltr` based on the leaf area index `lai`.

    `ltr` is used as a proxy for plant covering of the soil in the water balance calculation, where 1 represents no plant cover and 0 represents full plant cover. `ltr` is computed as an exponential decay function of `lai` with a decay coefficient `kdf`.

    This function is adapted from the EvalLtr procedure from the biomasse.pas and exmodules 1 & 2.pas files of the original PASCAL code.

    Args:

        j (int): The starting index for updating `ltr` in the `data` dataset.

        data (xarray.Dataset): A dataset containing the data used in the computation of `ltr`. The dataset should contain the following variables:

            - 'lai': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the leaf area index.

            - 'ltr': A 3-dimensional data variable with shape (num_timesteps, num_rows, num_columns), representing the fraction of radiation transmitted to the soil.

        paramVariete (dict): A dictionary containing the parameters for estimating `ltr`. The dictionary should contain the following key:

            - 'kdf': A float, representing the decay coefficient for `ltr`.

    Returns:

        xarray.Dataset: The updated `data` dataset with the new `ltr` values.

    """

    # group 80

    data["ltr"][j:,:,:] = np.exp(-paramVariete["kdf"] * data["lai"][j,:,:])

    return data

estimate_reallocation

def estimate_reallocation(
    j,
    data,
    paramVariete
)

Estimate the daily biomass reallocation between stem and leaves.

This function computes the daily biomass reallocation between stem and leaves for the plant. The computation only occurs when the plant is in phase 5. The amount of biomass that can be reallocated is estimated as follows:

  1. The difference between the potential yield delta and the aboveground biomass delta, bound by 0, is calculated and referred to as manqueAssim. manqueAssim represents the daily variation in biomass that remains after the plant has built its aboveground biomass.

  2. The reallocation is computed as the minimum of the product of manqueAssim and the reallocation rate and the difference between the leaf biomass and 30, also bound by 0. The value of 30 is an arbitrary threshold which ensures that reallocation is 0 if the leaf biomass is below 30. If the leaf biomass is above 30, reallocation is bounded by biomasseFeuille - 30.

If the plant is not in phase 5, reallocation is set to 0.

This function is based on the EvalReallocationSarrahV3 procedure from the bilancarbonsarra.pas and exmodules 1 & 2.pas files from the original Pascal code.

Parameters:

Name Type Description Default
j int Current time step of the simulation. None
data xarray.Dataset The dataset containing all the simulation data. None
paramVariete dict A dictionary containing the parameters for the simulation. None

Returns:

Type Description
xarray.Dataset The updated dataset with the reallocation values.
View Source
def estimate_reallocation(j, data, paramVariete):

    """

    Estimate the daily biomass reallocation between stem and leaves.

    This function computes the daily biomass reallocation between stem and leaves for the plant. The computation

    only occurs when the plant is in phase 5. The amount of biomass that can be reallocated is estimated as

    follows:

    1. The difference between the potential yield delta and the aboveground biomass delta, bound by 0, is

    calculated and referred to as manqueAssim. manqueAssim represents the daily variation in biomass that

    remains after the plant has built its aboveground biomass.

    2. The reallocation is computed as the minimum of the product of manqueAssim and the reallocation rate and

    the difference between the leaf biomass and 30, also bound by 0. The value of 30 is an arbitrary

    threshold which ensures that reallocation is 0 if the leaf biomass is below 30. If the leaf biomass is

    above 30, reallocation is bounded by biomasseFeuille - 30.

    If the plant is not in phase 5, reallocation is set to 0.

    This function is based on the EvalReallocationSarrahV3 procedure from the bilancarbonsarra.pas and

    exmodules 1 & 2.pas files from the original Pascal code.

    Args:

        j (int): Current time step of the simulation.

        data (xarray.Dataset): The dataset containing all the simulation data.

        paramVariete (dict): A dictionary containing the parameters for the simulation.

    Returns:

        xarray.Dataset: The updated dataset with the reallocation values.

    """

    condition = (data["numPhase"][j,:,:] == 5)

    data["manqueAssim"][j:,:,:] = np.where(

        condition,

        np.maximum(0, (data["dRdtPot"][j,:,:] -  np.maximum(0.0, data["deltaBiomasseAerienne"][j,:,:]))),

        0,

    )

    data["reallocation"][j:,:,:] = np.where(

        condition,

        np.minimum(

            data["manqueAssim"][j,:,:] * paramVariete["txRealloc"],

            np.maximum(0.0, data["biomasseFeuille"][j,:,:] - 30),

        ),

        0,

    )

    return data

initialize_simulation

def initialize_simulation(
    data,
    grid_width,
    grid_height,
    duration,
    paramVariete,
    paramITK,
    date_start
)

This function initializes variables related to crop growth in the data

xarray dataset. As the rain is the first variable to be initialized in the data xarray dataset, its dimensions are used to initialize the other variables.

no caption

This code has been adapted from the original InitiationCulture procedure, from the MilBilanCarbone.pas code of the SARRA model.

Parameters:

Name Type Description Default
data type description grid_width (type): description None
grid_height type description duration (type): description None
paramVariete type description None

Returns:

Type Description
type description
View Source
def initialize_simulation(data, grid_width, grid_height, duration, paramVariete, paramITK, date_start):

    """

    This function initializes variables related to crop growth in the data

    xarray dataset. As the rain is the first variable to be initialized in the

    data xarray dataset, its dimensions are used to initialize the other

    variables.

    ![no caption](../../docs/images/sla.png)

    This code has been adapted from the original InitiationCulture procedure, from the `MilBilanCarbone.pas` code of the

    SARRA model.

    Args:

        data (_type_): _description_ grid_width (_type_): _description_

        grid_height (_type_): _description_ duration (_type_): _description_

        paramVariete (_type_): _description_

    Returns:

        _type_: _description_

    """

    ### variables to be initialized with values from parameters

    # from paramVariete : maximum daily thermal time (°C.j) -> #? unused ?

    #// data["sommeDegresJourMaximale"] = (data["rain"].dims, np.full(

    #//     (duration, grid_width, grid_height),

    #//     (paramVariete["SDJLevee"] + paramVariete["SDJBVP"] + paramVariete["SDJRPR"] + paramVariete["SDJMatu1"] + paramVariete["SDJMatu2"])

    #// ))

    #// data["sommeDegresJourMaximale"].attrs = {"units":"°C.j", "long_name":"Maximum thermal time"}

    # from paramITK : sowing date

    data["sowing_date"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), (paramITK["DateSemis"] - date_start).days))

    # from paramITK : automatic irrigation indicator

    data["irrigAuto"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), paramITK["irrigAuto"]))

    data["irrigAuto"].attrs = {"units":"binary", "long_name":"automatic irrigation indicator"}

    ####### variables qui viennent de initplotMc

    # Initial biomass of crop residues (mulch) (kg/ha)

    # Biomasse initiale des résidus de culture (mulch) (kg/ha)

    #   BiomMc := BiomIniMc;

    data["biomMc"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), paramITK["biomIniMc"]))

    data["biomMc"].attrs = {"units": "kg/ha", "long_name": "Initial biomass of crop residues (mulch)"}

    data["biomMc"] = data["biomMc"].astype("float32")

    # ?

    #   StSurf := StockIniSurf;

    # data["stSurf"] = np.full((grid_width, grid_height, duration), paramTypeSol["stockIniSurf"])

    # ?

    #   Ltr := 1;

    data["ltr"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), 1.0))

    data["ltr"] = data["ltr"].astype("float32")

    # Initial biomass of stem residues as litter (kg/ha)

    # Biomasse initiale des résidus de tiges sous forme de litière (kg/ha)

    #   LitTiges := BiomIniMc;

    data["LitTige"] = (data["rain"].dims, np.full((duration, grid_width, grid_height), paramITK["biomIniMc"]))

    data["LitTige"].attrs = {"units": "kg/ha", "long_name": "Initial biomass of stem residues as litter"}

    data["LitTige"] = data["LitTige"].astype("float32")

    ####### fin variables qui viennent de initplotMc

    ####### variables eau depuis InitPlotMc

    # Initializes variables related to crop residues boimass (mulch) in the data

    # xarray dataset. This code has been adapted from the original InitPlotMc

    # procedure, Bileau.pas code. Comments with tab indentation are from the

    # original code. As the rain is the first variable to be initialized in the

    # data xarray dataset, its dimensions are used to initialize the other

    # variables.

    # Soil maximum water storage capacity (mm)

    # Capacité maximale de la RU (mm)

    #   StRurMax := Ru * ProfRacIni / 1000;

    #! renaming stRurMax with root_tank_capacity

    #// data["stRurMax"] = data["ru"] * paramITK["profRacIni"] / 1000

    data["root_tank_capacity"] = (data["rain"].dims, np.repeat(np.array(data["ru"] * paramITK["profRacIni"] / 1000)[np.newaxis,:,:], duration, axis=0))

    #// data["stRurMax"].attrs = {"units": "mm", "long_name": "Soil maximum water storage capacity"}

    data["root_tank_capacity"].attrs = {"units": "mm", "long_name": "Soil maximum water storage capacity"}

    # Maximum water capacity of surface tank (mm)

    # Reserve utile de l'horizon de surface (mm)

    #   RuSurf := EpaisseurSurf / 1000 * Ru;

    #! renaming ruSurf with surface_tank_capacity

    #// data["ruSurf"] = data["epaisseurSurf"] / 1000 * data["ru"]

    data["surface_tank_capacity"] = data["epaisseurSurf"] / 1000 * data["ru"]

    #// data["ruSurf"].attrs = {"units": "mm", "long_name": "Maximum water capacity of surface tank"}

    data["surface_tank_capacity"].attrs = {"units": "mm", "long_name": "Maximum water capacity of surface tank"}

    # ?

    #   //    PfTranspi := EpaisseurSurf * HumPf;

    #   //    StTot := StockIniSurf - PfTranspi/2 + StockIniProf;

    #   StTot := StockIniSurf  + StockIniProf;

    # data["stTot"] = np.full((grid_width, grid_height, duration), (paramTypeSol["stockIniSurf"] + paramTypeSol["stockIniProf"]))

    #! modifié pour faire correspondre les résultats de simulation, à remettre en place pour un calcul correct dès que possible

    # data["stTot"] = np.full((grid_width, grid_height, duration), (paramTypeSol["stockIniProf"]))

    #! renaming stTot to total_tank_stock

    #// data["stTot"] = data["stockIniProf"]

    #//data["total_tank_stock"] = data["stockIniProf"]

    #! coorecting total_tank_stock initialization as it did not have the time dimensions that are required as stock evolves through time

    data["total_tank_stock"] = (data["rain"].dims, np.repeat(np.array(data["stockIniProf"])[np.newaxis,:,:], duration, axis=0))

    #// data["stTot"].attrs = {"units": "mm", "long_name": "?"}

    data["total_tank_stock"].attrs = {"units": "mm", "long_name": "?"}

    # Soil maximal depth (mm)

    # Profondeur maximale de sol (mm)

    #   ProfRU := EpaisseurSurf + EpaisseurProf;

    #! data["profRu"] = data["epaisseurProf"] + data["epaisseurSurf"]

    #! data["profRu"].attrs = {"units": "mm", "long_name": "Soil maximal depth"}

    # déplacé dans l'initialisation du sol

    # Maximum water capacity to humectation front (mm)

    # Quantité d'eau maximum jusqu'au front d'humectation (mm)

    #   // modif 10/06/2015  resilience stock d'eau

    #   // Front d'humectation egal a RuSurf trop de stress initial

    #   //    Hum := max(StTot, StRurMax);

    #   Hum := max(RuSurf, StRurMax);

    #   // Hum mis a profRuSurf

    #   Hum := max(StTot, Hum);

    data["humectation_front"] = (data["rain"].dims, np.full((duration, grid_width, grid_height),

        np.maximum(

            np.maximum(

                #! renaming ruSurf with surface_tank_capacity

                #// data["ruSurf"],

                data["surface_tank_capacity"].expand_dims({"time":duration}),

                #! renaming stRurMax with root_tank_capacity

                #// data["stRurMax"],

                data["root_tank_capacity"],

            ),

            #! renaming stTot with total_tank_stock

            #// data["stTot"],

            data["total_tank_stock"],

        )

    ))

    data["humectation_front"].attrs = {"units": "mm", "long_name": "Maximum water capacity to humectation front"}

    # Previous value for Maximum water capacity to humectation front (mm)

    #  HumPrec := Hum;

    data["previous_humectation_front"] = data["humectation_front"]

    # ?

    #   StRurPrec := 0;

    # Previous value for stTot

    #   StRurMaxPrec := 0;

    #   //modif 10/06/2015 resilience stock d'eau

    #! renaming stTot with total_tank_stock

    #! renaminog stRuPrec with previous_total_tank_stock

    #// data["stRuPrec"] =  data["stTot"]

    data["previous_total_tank_stock"] =  data["total_tank_stock"]

    ####### fin variables eau depuis InitPlotMc

    # depuis meteo.pas

    kpar = 0.5

    data["par"] = kpar * data["rg"]

    data["par"].attrs = {"units":"MJ/m2", "long_name":"par"}

    # crop density

    if ~np.isnan(paramVariete["densOpti"]) :

        data["rapDensite"] = data["rain"] * 0 + compute_rapDensite(paramITK, paramVariete)

        data["rapDensite"].attrs = {"units":"none", "long_name":"sowing density adjustement factor"}

    # initialize variables with values at 0

    variables = variable_dict()

    for variable in variables :

        data[variable] = (data["rain"].dims, np.zeros(shape=(duration, grid_width, grid_height)))

        data[variable].attrs = {"units":variables[variable][1], "long_name":variables[variable][0]}

        data[variable] = data[variable].astype("float32")

    return data

update_aboveground_biomass

def update_aboveground_biomass(
    j,
    data,
    paramVariete
)

Update the aboveground biomass of the plant.

The aboveground biomass is either updated based on a linear function of the total biomass, if the plant is in phase 2, 3, or 4, or incremented with the total biomass delta if the plant is in any other phase.

This function is based on the EvolBiomAeroSarrahV3 procedure, of the bilancarbonsarra, exmodules 1 & 2.pas file from the original Pascal code.

Parameters:

Name Type Description Default
j int The current iteration step in the simulation. None
data xarray.Dataset The simulation data, including the current phase of the plant and various biomass values. None
paramVariete dict The parameters of the plant variety. None

Returns:

Type Description
xarray.Dataset The updated simulation data, including the updated aboveground biomass and delta aboveground biomass.
View Source
def update_aboveground_biomass(j, data, paramVariete):

    """

    Update the aboveground biomass of the plant.

    The aboveground biomass is either updated based on a linear function of the total biomass, if the plant is in phase 2, 3, or 4, or incremented with the total biomass delta if the plant is in any other phase.

    This function is based on the EvolBiomAeroSarrahV3 procedure, of the

    ***bilancarbonsarra***, exmodules 1 & 2.pas file from the original Pascal

    code.

    Args:

        j (int): The current iteration step in the simulation.

        data (xarray.Dataset): The simulation data, including the current phase of the plant and various biomass values.

        paramVariete (dict): The parameters of the plant variety.

    Returns:

        xarray.Dataset: The updated simulation data, including the updated aboveground biomass and delta aboveground biomass.

    """

    #// data["deltaBiomasseAerienne"][j:,:,:] = np.copy(data["biomasseAerienne"][j,:,:])

    data["biomasseAerienne"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] >= 2) & (data["numPhase"][j,:,:] <= 4),

        np.minimum(0.9, paramVariete["aeroTotPente"] * data["biomasseTotale"][j,:,:] + paramVariete["aeroTotBase"]) * data["biomasseTotale"][j,:,:],

        data["biomasseAerienne"][j,:,:] + data["deltaBiomasseTotale"][j,:,:],

    )

    #//data["deltaBiomasseAerienne"][j:,:,:] = (data["biomasseAerienne"][j,:,:] - data["deltaBiomasseAerienne"][j,:,:])#[...,np.newaxis]

    data["deltaBiomasseAerienne"][j:,:,:] = data["biomasseAerienne"][j,:,:] - data["biomasseAerienne"][j-1,:,:]

    return data

update_aboveground_biomass_step_2

def update_aboveground_biomass_step_2(
    j,
    data
)

summary

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_aboveground_biomass_step_2(j, data):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseAerienne"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 1),

        data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:] + data["rdt"][j,:,:],

        data["biomasseAerienne"][j,:,:],

    )

    return data

update_assim

def update_assim(
    j,
    data
)

This function updates assim. If trPot (potential transpiration from the

plant, mm) is greater than 0, then assim equals assimPot, multiplied by the ratio of effective transpiration over potential transpiration.

If potential transpiration is null, then assim is null as well.

Is it adapted from the EvalAssimSarraV42 procedure, of the bilancarbonsarra.pas file from the original Pascal code

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_assim(j, data):

    """

    This function updates assim. If trPot (potential transpiration from the

    plant, mm) is greater than 0, then assim equals assimPot, multiplied by the

    ratio of effective transpiration over potential transpiration.

    If potential transpiration is null, then assim is null as well.

    Is it adapted from the EvalAssimSarraV42 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["assim"][j,:,:] = np.where(

        data["trPot"][j,:,:] > 0,

        data["assimPot"][j,:,:] * data["tr"][j,:,:] / data["trPot"][j,:,:],

        0,

    )

    return data

update_assimPot

def update_assimPot(
    j,
    data,
    paramVariete,
    paramITK
)

Update the assimPot value based on the intensification level (NI).

If the intensification level NI is defined in paramITK, the conversion rate txConversion is computed using a formula based on NIYo, NIp, LGauss, and AGauss. If NI is not defined, assimPot is updated using conv, which is updated in the estimate_conv function using the variables KAssim and txConversion.

When NI parameter is used (from to 4), conversion rate txConversion is computed using the following formula : NIYo + NIp * (1-exp(-NIp * NI)) - (exp(-0.5((NI - LGauss)/AGauss) (NI- LGauss)/AGauss))/(AGauss*2.506628274631)

This function is adapted from the EvalAssimSarraV42 procedure in the bilancarbonsarra.pas file of the original Pascal code.

Note from CB : correction of the conversion rate depending on the intensification level

notes from CB reharding the EvalAssimSarraV42 procedure :

Modif du 04/03/2021 : Prise en compte en plus de la densit� de semis de l'effet niveau d'intensification NI NI = 1 quand on est � l'optimum du niveau d'intensification. Dans le cas de situation contr�l� c'est la fertilit� qui est la clef principale en prenant en r�f�rence la qt� d'azote (�quivalent phosphore...) optimum Il peut aller � 0 ou �tre sup�rieur � 1 si situation sur optimum, ie un peu plus de rdt mais � cout trop �lev�... On �value un nouveau tx de conversion en fn du Ni au travers d'une double �quation : asympote x gaussienne invers�e Et d'un NI d�fini en fn du sc�nario de simulation ou des donn�es observ�es. NIYo = D�calage en Y de l'asymptote NIp = pente de l'asymptote LGauss = Largeur de la Guaussienne AGauss = Amplitude de la Guaussienne

Conversion qui est la valeur du taux de conversion en situation optimum n'a plus besoin d'�tre utilis� sinon dans la calibration des param�tres de cette �quation en absence de donn�es sur ces param�tres on ne met aucune valeur � NI CF fichier ex IndIntensite_txConv_eq.xls}

Args: - j (int): An index that represents the current iteration. - data (dict): A dictionary containing data arrays with the following keys: - "assimPot" (np.ndarray): An array representing the potential assimilation rate. - "par" (np.ndarray): An array representing photosynthetically active radiation. - "lai" (np.ndarray): An array representing the leaf area index. - "conv" (np.ndarray): An array representing the conversion rate. - paramVariete (dict): A dictionary containing parameters for the computation of the conversion rate, including: - "txConversion" (float): The conversion rate. - "NIYo" (float): The shift in the Y-axis of the asymptote. - "NIp" (float): The slope of the asymptote. - "LGauss" (float): The width of the Gaussian curve. - "AGauss" (float): The amplitude of the Gaussian curve. - "kdf" (float): The constant used in the computation of assimPot. - paramITK (dict): A dictionary containing the intensification level NI (float).

Returns: - data (dict): The input data dictionary with the updated "assimPot" array.

View Source
def update_assimPot(j, data, paramVariete, paramITK):

    """

    Update the assimPot value based on the intensification level (NI).

    If the intensification level `NI` is defined in `paramITK`, the conversion rate `txConversion` is computed using a formula based on `NIYo`, `NIp`, `LGauss`, and `AGauss`. If `NI` is not defined, `assimPot` is updated using `conv`, which is updated in the `estimate_conv` function using the variables `KAssim` and `txConversion`.

    When NI parameter is used (from to 4), conversion rate txConversion

    is computed using the following formula :

    NIYo + NIp * (1-exp(-NIp * NI)) - (exp(-0.5*((NI - LGauss)/AGauss)* (NI- LGauss)/AGauss))/(AGauss*2.506628274631)

    This function is adapted from the `EvalAssimSarraV42` procedure in the `bilancarbonsarra.pas` file of the original Pascal code.

    Note from

    CB : correction of the conversion rate depending on the intensification

    level

    notes from CB reharding the EvalAssimSarraV42 procedure :

    Modif du 04/03/2021 : Prise en compte en plus de la densit� de semis de

    l'effet niveau d'intensification NI NI = 1 quand on est � l'optimum du

    niveau d'intensification. Dans le cas de situation contr�l� c'est la

    fertilit� qui est la clef principale en prenant en r�f�rence la qt� d'azote

    (�quivalent phosphore...) optimum Il peut aller � 0 ou �tre sup�rieur � 1 si

    situation sur optimum, ie un peu plus de rdt mais � cout trop �lev�... On

    �value un nouveau tx de conversion en fn du Ni au travers d'une double

    �quation : asympote x gaussienne invers�e Et d'un NI d�fini en fn du

    sc�nario de simulation ou des donn�es observ�es. NIYo = D�calage en Y de

    l'asymptote NIp  = pente de l'asymptote LGauss = Largeur de la Guaussienne

    AGauss = Amplitude de la Guaussienne

    Conversion qui est la valeur du taux de conversion en situation optimum n'a

    plus besoin d'�tre utilis� sinon dans la calibration des param�tres de cette

    �quation en absence de donn�es sur ces param�tres on ne met aucune valeur �

    NI CF fichier ex IndIntensite_txConv_eq.xls}

    Args:

    - j (int): An index that represents the current iteration.

    - data (dict): A dictionary containing data arrays with the following keys:

        - "assimPot" (np.ndarray): An array representing the potential assimilation rate.

        - "par" (np.ndarray): An array representing photosynthetically active radiation.

        - "lai" (np.ndarray): An array representing the leaf area index.

        - "conv" (np.ndarray): An array representing the conversion rate.

    - paramVariete (dict): A dictionary containing parameters for the computation of the conversion rate, including:

        - "txConversion" (float): The conversion rate.

        - "NIYo" (float): The shift in the Y-axis of the asymptote.

        - "NIp" (float): The slope of the asymptote.

        - "LGauss" (float): The width of the Gaussian curve.

        - "AGauss" (float): The amplitude of the Gaussian curve.

        - "kdf" (float): The constant used in the computation of `assimPot`.

    - paramITK (dict): A dictionary containing the intensification level `NI` (float).

    Returns:

    - data (dict): The input `data` dictionary with the updated "assimPot" array.

    """

    if ~np.isnan(paramITK["NI"]):

        #? the following (stupidly long) line was found commented, need to check why and if this is correct

        paramVariete["txConversion"] = paramVariete["NIYo"] + paramVariete["NIp"] * (1-np.exp(-paramVariete["NIp"] * paramITK["NI"])) - (np.exp(-0.5*((paramITK["NI"] - paramVariete["LGauss"])/paramVariete["AGauss"])* (paramITK["NI"]- paramVariete["LGauss"])/paramVariete["AGauss"]))/(paramVariete["AGauss"]*2.506628274631)

        # NIYo + NIp * (1-exp(-NIp * NI)) - (exp(-0.5*((NI - LGauss)/AGauss)* (NI- LGauss)/AGauss))/(AGauss*2.506628274631)

        data["assimPot"][j,:,:] = data["par"][j,:,:] * \

            (1-np.exp(-paramVariete["kdf"] * data["lai"][j,:,:])) * \

            paramVariete["txConversion"] * 10

    else :

        data["assimPot"][j,:,:] = data["par"][j,:,:] * \

            (1-np.exp(-paramVariete["kdf"] * data["lai"][j,:,:])) * \

            data["conv"][j,:,:] * 10

    return data

update_bM_and_cM

def update_bM_and_cM(
    j,
    data,
    paramVariete
)

This function returns the updated values of bM and cM.

bM and cM are updated if the delta of aerial biomass is positive, meaning that the plant is gaining aerial biomass, and if the phase is above 1 and below 4 or the phase is below the vegetative phase.

This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of the bilancarbonsarra.pas files from the original Pascal code.

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_bM_and_cM(j, data, paramVariete):

    """

    This function returns the updated values of bM and cM.

    bM and cM are updated if the delta of aerial biomass is positive,

    meaning that the plant is gaining aerial biomass, and if the phase is

    above 1 and below 4 or the phase is below the vegetative phase.

    This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of

    the bilancarbonsarra.pas files from the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["bM"][j,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        paramVariete["feuilAeroBase"] - 0.1,

        data["bM"][j,:,:],

    )

    data["cM"][j,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        ((paramVariete["feuilAeroPente"] * 1000)/ data["bM"][j,:,:] + 0.78) / 0.75,

        data["cM"][j,:,:],

    )

    return data

update_leaf_biomass

def update_leaf_biomass(
    j,
    data,
    paramVariete
)

For phase above 1 and if the delta of aerial biomass is negative,

meaning that the plant is losing aerial biomass, the leaf biomass is updated as the difference between the leaf biomass and the reallocation minus the delta of aerial biomass multiplied by the reallocation rate in leaves. This value is bound in 0.00000001.

Otherwise, the leaf biomass is not updated.

This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of the bilancarbonsarra.pas and exmodules 1 & 2.pas files from the original Pascal code.

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_leaf_biomass(j, data, paramVariete):

    """

    For phase above 1 and if the delta of aerial biomass is negative,

    meaning that the plant is losing aerial biomass, the leaf biomass is

    updated as the difference between the leaf biomass and the reallocation

    minus the delta of aerial biomass multiplied by the reallocation rate in

    leaves. This value is bound in 0.00000001.

    Otherwise, the leaf biomass is not updated.

    This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of

    the bilancarbonsarra.pas and exmodules 1 & 2.pas files from the original

    Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseFeuille"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] < 0),

        np.maximum(

            0.00000001,

            data["biomasseFeuille"][j,:,:] - (data["reallocation"][j,:,:] - data["deltaBiomasseAerienne"][j,:,:]) * paramVariete["pcReallocFeuille"]

        ),

        data["biomasseFeuille"][j,:,:],

    )

    return data

update_leaf_biomass_all_phases

def update_leaf_biomass_all_phases(
    j,
    data,
    paramVariete
)

summary

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_leaf_biomass_all_phases(j, data, paramVariete):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseFeuille"][j:,:,:] = np.where(

        condition_positive_delta_aboveground_biomass_all_phases(j, data),

        data["biomasseFeuille"][j,:,:] - data["reallocation"][j,:,:] * paramVariete["pcReallocFeuille"],

        data["biomasseFeuille"][j,:,:],

    )

    return data

update_leaf_biomass_positive_delta_aboveground_biomass

def update_leaf_biomass_positive_delta_aboveground_biomass(
    j,
    data,
    paramVariete
)

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_leaf_biomass_positive_delta_aboveground_biomass(j, data, paramVariete):

    """

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseFeuille"][j:,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        (0.1 + data["bM"][j,:,:] * data["cM"][j,:,:] ** ((data["biomasseAerienne"][j,:,:] - data["rdt"][j,:,:]) / 1000)) \

            * (data["biomasseAerienne"][j,:,:] - data["rdt"][j,:,:]),

        data["biomasseFeuille"][j,:,:],

    )

    return data

update_potential_yield

def update_potential_yield(
    j,
    data,
    paramVariete
)

Update the potential yield of the plant.

The potential yield is initialized as an affine function of the delta between the end of the vegetative phase and the end of the flowering stage, plus a linear function of the total biomass at the end of the flowering stage. The potential yield is capped to twice the biomass of the stem to avoid unrealistic values.

The update occurs if the plant is in phase 5 and its phase has changed

This function is adapted from the EvalRdtPotRespSarV42 procedure, of the bilancarbonsarra.pas file from the original Pascal code.

Parameters:

Name Type Description Default
j int An index representing the current time step. None
data xarray.Dataset A dataset containing plant data. None
paramVariete dict A dictionary containing parameters for the plant variety. None

Returns:

Type Description
xarray.Dataset The input data with the potential yield updated.
View Source
def update_potential_yield(j, data, paramVariete):

    """

    Update the potential yield of the plant.

    The potential yield is initialized as an affine function of the delta

    between the end of the vegetative phase and the end of the flowering stage,

    plus a linear function of the total biomass at the end of the flowering stage.

    The potential yield is capped to twice the biomass of the stem to avoid unrealistic

    values.

    The update occurs if the plant is in phase 5 and its phase has changed

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code.

    Args:

        j (int): An index representing the current time step.

        data (xarray.Dataset): A dataset containing plant data.

        paramVariete (dict): A dictionary containing parameters for the plant variety.

    Returns:

        xarray.Dataset: The input `data` with the potential yield updated.

    """

    delta_biomass_flowering_ip = data["biomTotStadeFloraison"][j,:,:] - data["biomTotStadeIp"][j,:,:]

    data["rdtPot"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5) & (data["changePhase"][j,:,:] == 1),

        (paramVariete["KRdtPotA"] * delta_biomass_flowering_ip + paramVariete["KRdtPotB"]) + paramVariete["KRdtBiom"] * data["biomTotStadeFloraison"][j,:,:],

        data["rdtPot"][j,:,:],

    )

    #! phaseDevVeg pas utilisé ? attention c'est un paramètre variétal et pas un jeu de donées

    data["rdtPot"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5) & (data["changePhase"][j,:,:] == 1) & (data["rdtPot"][j,:,:] > data["biomasseTige"][j,:,:] * 2) & (paramVariete["phaseDevVeg"] < 6),

        data["biomasseTige"][j,:,:] * 2,

        data["rdtPot"][j,:,:],

    )

    return data

update_potential_yield_delta

def update_potential_yield_delta(
    j,
    data,
    paramVariete
)

This function updates the delta potential yield (dRdtPot) of the plant, which is the rate at which the

plant's yield is changing over time. The delta potential yield is calculated as the product of the potential yield, the ratio of actual degree days to maturity, and the ratio of actual transpiration to potential transpiration. The calculation is only done if the plant is in phase 5 and the potential transpiration is above 0. If the potential transpiration is not above 0, the delta potential yield is set to 0. For all other phases, the delta potential yield is unchanged.

This function is adapted from the EvalRdtPotRespSarV42 procedure, of the bilancarbonsarra.pas file from the original Pascal code.

Args: - j (int): an integer index, representing the current step of the simulation - data (xarray dataset): the simulation data, including the current state of the plant - paramVariete (dict): the variety-specific parameters used in the simulation

Returns: - data (xarray dataset): the updated simulation data, including the updated delta potential yield

View Source
def update_potential_yield_delta(j, data, paramVariete):

    """

    This function updates the delta potential yield (dRdtPot) of the plant, which is the rate at which the

    plant's yield is changing over time. The delta potential yield is calculated as the product of the potential

    yield, the ratio of actual degree days to maturity, and the ratio of actual transpiration to potential transpiration.

    The calculation is only done if the plant is in phase 5 and the potential transpiration is above 0.

    If the potential transpiration is not above 0, the delta potential yield is set to 0.

    For all other phases, the delta potential yield is unchanged.

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code.

    Args:

    - j (int): an integer index, representing the current step of the simulation

    - data (xarray dataset): the simulation data, including the current state of the plant

    - paramVariete (dict): the variety-specific parameters used in the simulation

    Returns:

    - data (xarray dataset): the updated simulation data, including the updated delta potential yield

    """

    data["dRdtPot"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5),

        np.where(

            (data["trPot"][j,:,:] > 0),

            np.maximum(

                data["rdtPot"][j,:,:] * (data["ddj"][j,:,:] / paramVariete["SDJMatu1"]) * (data["tr"][j,:,:] / data["trPot"][j,:,:]),

                data["respMaint"][j,:,:] * 0.15,

            ),

            0,

        ),

        data["dRdtPot"][j,:,:],

    )

    return data

update_root_biomass

def update_root_biomass(
    j,
    data
)

Update the root biomass (biomasseRacinaire) for a given time step.

The root biomass is computed as the difference between the total biomass and the aboveground biomass.

This function is based on the EvalBiomasseRacinaire procedure, of the milbilancarbone, exmodules 1 & 2, milbilancarbone.pas file from the original Pascal code

Parameters:

Name Type Description Default
j int Time step index. None
data xarray.Dataset Input dataset containing relevant variables. None

Returns:

Type Description
xarray.Dataset Updated dataset with the root biomass variable.
View Source
def update_root_biomass(j, data):

    """

    Update the root biomass (biomasseRacinaire) for a given time step.

    The root biomass is computed as the difference between the total biomass

    and the aboveground biomass.

    This function is based on the EvalBiomasseRacinaire procedure, of the

    milbilancarbone, exmodules 1 & 2, ***milbilancarbone***.pas file from the

    original Pascal code

    Args:

        j (int): Time step index.

        data (xarray.Dataset): Input dataset containing relevant variables.

    Returns:

        xarray.Dataset: Updated dataset with the root biomass variable.

    """

    data["biomasseRacinaire"][j,:,:] = data["biomasseTotale"][j,:,:] - data["biomasseAerienne"][j,:,:]

    return data

update_stem_biomass

def update_stem_biomass(
    j,
    data,
    paramVariete
)

For phase above 1 and if the delta of aerial biomass is negative,

meaning that the plant is losing aerial biomass, the stem biomass is updated as the difference between the leaf biomass and the reallocation minus the delta of aerial biomass multiplied by (1-reallocation rate in leaves) (if it's not leaves, it's stems...). This value is bound in 0.00000001.

Otherwise, the stem biomass is not updated.

This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of the bilancarbonsarra.pas and exmodules 1 & 2.pas files from the original Pascal code.

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_stem_biomass(j, data, paramVariete):

    """

    For phase above 1 and if the delta of aerial biomass is negative,

    meaning that the plant is losing aerial biomass, the stem biomass is

    updated as the difference between the leaf biomass and the reallocation

    minus the delta of aerial biomass multiplied by (1-reallocation rate in

    leaves) (if it's not leaves, it's stems...). This value is bound in 0.00000001.

    Otherwise, the stem biomass is not updated.

    This function is adapted from the EvalFeuilleTigeSarrahV4 procedure, of

    the bilancarbonsarra.pas and exmodules 1 & 2.pas files from the original

    Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    # group 122

    data["biomasseTige"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] > 1) & (data["deltaBiomasseAerienne"][j,:,:] < 0),

        np.maximum(

            0.00000001,

            data["biomasseTige"][j,:,:] - (data["reallocation"][j,:,:] - data["deltaBiomasseAerienne"][j,:,:]) * (1 - paramVariete["pcReallocFeuille"]),

            ),

        data["biomasseTige"][j,:,:],

    )

    return data

update_stem_biomass_all_phases

def update_stem_biomass_all_phases(
    j,
    data,
    paramVariete
)

summary

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_stem_biomass_all_phases(j, data, paramVariete):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseTige"][j:,:,:] = np.where(

        condition_positive_delta_aboveground_biomass_all_phases(j, data),

        data["biomasseTige"][j,:,:] - (data["reallocation"][j,:,:] * (1- paramVariete["pcReallocFeuille"])),

        data["biomasseTige"][j,:,:],

    )

    return data

update_stem_biomass_positive_delta_aboveground_biomass

def update_stem_biomass_positive_delta_aboveground_biomass(
    j,
    data,
    paramVariete
)

summary

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_stem_biomass_positive_delta_aboveground_biomass(j, data, paramVariete):

    """_summary_

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseTige"][j:,:,:] = np.where(

        condition_positive_delta_biomass(j, data, paramVariete),

        data["biomasseAerienne"][j,:,:] - data["biomasseFeuille"][j,:,:] - data["rdt"][j,:,:],

        data["biomasseTige"][j,:,:],

    )

    return data

update_total_biomass

def update_total_biomass(
    j,
    data,
    paramVariete,
    paramITK
)

Update the Total Biomass of the Plant.

The total biomass is updated based on the plant's current phase and other parameters. If the plant is in phase 2 and there's a change in phase, the total biomass is initialized using crop density, grain yield per plant, and the dry weight of the grain. If the plant is not in phase 2 or there's no change in phase, the total biomass is incremented with the difference between the plant's assimilation and maintenance respiration.

When passing from phase 1 to phase 2, total biomass is initialized. Initialization value is computed from crop density (plants/ha), txResGrain (grain yield per plant), and poidsSecGrain. Otherwise, total biomass is incremented with the difference between plant assimilation assim and maintainance respiration respMaint.

This function is adapted from the EvolBiomTotSarrahV4 procedure, of the bilancarbonsarra.pas file from the original Pascal code.

Parameters:

Name Type Description Default
j int The current time step. None
data xarray.Dataset The data for the plant, including variables like
"biomasseTotale", "assim", "respMaint", "numPhase", and "changePhase".
None
paramVariete dict A dictionary of parameters specific to the plant variety. None
paramITK dict A dictionary of inter-tropical convergence zone parameters. None

Returns:

Type Description
xarray.Dataset The updated data for the plant, including the updated "biomasseTotale"
and "deltaBiomasseTotale" variables.
View Source
def update_total_biomass(j, data, paramVariete, paramITK):

    """

    Update the Total Biomass of the Plant.

    The total biomass is updated based on the plant's current phase and other parameters.

    If the plant is in phase 2 and there's a change in phase, the total biomass is initialized using

    crop density, grain yield per plant, and the dry weight of the grain.

    If the plant is not in phase 2 or there's no change in phase, the total biomass is incremented with

    the difference between the plant's assimilation and maintenance respiration.

    When passing from phase 1 to phase 2, total biomass is initialized.

    Initialization value is computed from crop density (plants/ha), txResGrain

    (grain yield per plant), and poidsSecGrain. Otherwise, total biomass is

    incremented with the difference between plant assimilation assim and

    maintainance respiration respMaint.

    This function is adapted from the EvolBiomTotSarrahV4 procedure, of the

    bilancarbonsarra.pas file from the original Pascal code.

    Args:

        j (int): The current time step.

        data (xarray.Dataset): The data for the plant, including variables like

            "biomasseTotale", "assim", "respMaint", "numPhase", and "changePhase".

        paramVariete (dict): A dictionary of parameters specific to the plant variety.

        paramITK (dict): A dictionary of inter-tropical convergence zone parameters.

    Returns:

        xarray.Dataset: The updated data for the plant, including the updated "biomasseTotale"

            and "deltaBiomasseTotale" variables.

    """

    data["biomasseTotale"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:]==2) & (data["changePhase"][j,:,:]==1),

        paramITK["densite"] *  np.maximum(1,paramVariete['densOpti']/paramITK['densite']) * paramVariete["txResGrain"] *  paramVariete["poidsSecGrain"] / 1000,

        data["biomasseTotale"][j,:,:]  + (data["assim"][j,:,:] - data["respMaint"][j,:,:]),

    )

    # we may want to drop this variable and use the raw computation instead

    data["deltaBiomasseTotale"][j:,:,:] = (data["assim"][j,:,:] - data["respMaint"][j,:,:])

    return data

update_total_biomass_at_flowering_stage

def update_total_biomass_at_flowering_stage(
    j,
    data
)

This function updates the total biomass of the plant at the end of the

flowering stage (biomTotStadeFloraison).

If the plant is in phase 5, and the phase has changed, then the total biomass is copied to the biomTotStadeFloraison variable.

This function is adapted from the EvalRdtPotRespSarV42 procedure, of the bilancarbonsarra.pas file from the original Pascal code.

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_total_biomass_at_flowering_stage(j, data):

    """

    This function updates the total biomass of the plant at the end of the

    flowering stage (biomTotStadeFloraison).

    If the plant is in phase 5, and the phase has changed, then the total

    biomass is copied to the biomTotStadeFloraison variable.

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of

    the bilancarbonsarra.pas file from the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomTotStadeFloraison"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5) & (data["changePhase"][j,:,:] == 1),

        data["biomasseTotale"][j,:,:],

        data["biomTotStadeFloraison"][j,:,:],

    )

    return data

update_total_biomass_stade_ip

def update_total_biomass_stade_ip(
    j,
    data
)

Update the total biomass of the plant at the end of the vegetative phase (ip = "initiation paniculaire").

If the plant has reached phase 4 and has just changed phase, the current total biomass will be copied to the "biomTotStadeIp" variable, which represents the total biomass at the end of the vegetative phase (initiation paniculaire).

This function is adapted from the EvalRdtPotRespSarV42 procedure, of the bilancarbonsarra.pas file from the original Pascal code.

Args: j (int): Timestep index. data (xarray.Dataset): Input dataset.

Returns: xarray.Dataset: The updated dataset with the "biomTotStadeIp" variable updated.

View Source
def update_total_biomass_stade_ip(j, data):

    """

    Update the total biomass of the plant at the end of the vegetative phase (ip = "initiation paniculaire").

    If the plant has reached phase 4 and has just changed phase, the current

    total biomass will be copied to the "biomTotStadeIp" variable, which represents

    the total biomass at the end of the vegetative phase (initiation paniculaire).

    This function is adapted from the EvalRdtPotRespSarV42 procedure, of

    the bilancarbonsarra.pas file from the original Pascal code.

    Args:

    j (int): Timestep index.

    data (xarray.Dataset): Input dataset.

    Returns:

    xarray.Dataset: The updated dataset with the "biomTotStadeIp" variable updated.

    """

    data["biomTotStadeIp"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 4) & (data["changePhase"][j,:,:] == 1),

        data["biomasseTotale"][j,:,:],

        data["biomTotStadeIp"][j,:,:],

    )

    return data

update_vegetative_biomass

def update_vegetative_biomass(
    j,
    data
)

summary

This function is adapted from the EvalBiomasseVegetati procedure from the copie milbilancarbon, exmodules 1 & 2, milbilancarbone file of the original Pascal code.

Parameters:

Name Type Description Default
j type description None
data type description None

Returns:

Type Description
type description
View Source
def update_vegetative_biomass(j, data):

    """_summary_

    This function is adapted from the EvalBiomasseVegetati procedure from the copie milbilancarbon, exmodules 1 & 2, ***milbilancarbone*** file

    of the original Pascal code.

    Args:

        j (_type_): _description_

        data (_type_): _description_

    Returns:

        _type_: _description_

    """

    data["biomasseVegetative"][j:,:,:] = (data["biomasseTige"][j,:,:] + data["biomasseFeuille"][j,:,:])

    return data

update_yield_during_filling_phase

def update_yield_during_filling_phase(
    j,
    data
)

This function updates the yield value during the filling phase.

During the filling phase (numPhase == 5), the yield is updated by incrementing it with the sum of deltaBiomasseAerienne and reallocation, bounded by 0 and dRdtPot (daily potential yield). The construction of yield is done during phase 5 only, from the variation of aerial biomass and reallocation, with a maximum of dRdtPot.

This function is adapted from the EvolDayRdtSarraV3 procedure from the bilancarbonesarra, exmodules 1 & 2.pas file of the original Pascal code.

Notes : On tend vers le potentiel en fn du rapport des degresJours/sumDegresJours pour la phase de remplissage. Frein sup fn du flux de sève estimé par le rapport Tr/TrPot. dRdtPot = RdtPotDuJour

Parameters:

Name Type Description Default
j int The time step at which the calculation starts. None
data xarray.Dataset The data that contains all variables. None

Returns:

Type Description
xarray.Dataset The input data with updated yield values.
View Source
def update_yield_during_filling_phase(j, data):

    """

    This function updates the yield value during the filling phase.

    During the filling phase (numPhase == 5), the yield is updated by

    incrementing it with the sum of `deltaBiomasseAerienne` and `reallocation`,

    bounded by 0 and `dRdtPot` (daily potential yield). The construction of yield

    is done during phase 5 only, from the variation of aerial biomass and

    reallocation, with a maximum of `dRdtPot`.

    This function is adapted from the EvolDayRdtSarraV3 procedure from the

    ***bilancarbonesarra***, exmodules 1 & 2.pas file of the original Pascal

    code.

    Notes :

    On tend vers le potentiel en fn du rapport des degresJours/sumDegresJours

    pour la phase de remplissage. Frein sup fn du flux de sève estimé par le

    rapport Tr/TrPot.

    dRdtPot = RdtPotDuJour

    Args:

        j (int): The time step at which the calculation starts.

        data (xarray.Dataset): The data that contains all variables.

    Returns:

        xarray.Dataset: The input data with updated yield values.

    """

    data["rdt"][j:,:,:] = np.where(

        (data["numPhase"][j,:,:] == 5),

        data["rdt"][j,:,:] + np.minimum(data["dRdtPot"][j,:,:],  np.maximum(0.0, data["deltaBiomasseAerienne"][j,:,:]) + data['reallocation'][j,:,:]),

        data["rdt"][j,:,:],

    )

    return data

variable_dict

def variable_dict(

)

Retrieve the dictionary of variables in the dataset with their respective units.

Returns:

Type Description
dict A dictionary containing the variables and their units, where the keys are the variable names and the values are the respective units.
View Source
def variable_dict():

    """

    Retrieve the dictionary of variables in the dataset with their respective units.

    Returns:

        dict: A dictionary containing the variables and their units, where the keys are the variable names and the values are the respective units.

    """

    variables = {

        # climate

        "ddj": ["daily thermal time", "°C.j"],

        "sdj": ["sum of thermal time since beginning of emergence", "°C.j"],

        # phenology

        "changePhase": ["indicator of phase transition day", "binary"],

        "numPhase": ["number of phenological stage", "arbitrary units"],

        "initPhase": ["indicator of performed phase transition", "binary"],

        "phasePhotoper": ["photoperiodic phase indicator", "binary"],

        "seuilTempPhaseSuivante": ["sum of thermal time needed to reach the next phenological phase", "°C.j"],

        "sommeDegresJourPhasePrec": ["sum of thermal time needed to reach the previous phenological phase", "°C.j"],

        "seuilTempPhasePrec": ["sum of thermal time needed to reach the previous phenological phase", "°C.j"],

        # carbon balance

        "assim": ["plant biomass assimilation", "kg/ha"],

        "assimPot": ["plant potential biomass assimilation", "kg/ha"],

        "bM": ["net growth rate of living biomass", "kg/(m².d)"],

        "cM": ["net growth rate of dead biomass", "kg/(m².d)"],

        "rdt": ["grain yield", "kg/ha"],

        "rdtPot": ["potential grain yield", "kg/ha"],

        "reallocation": ["amount of assimilates reallocated to the yield (supply < demand)", "kg/ha"],

        "respMaint": ["amount of assimilates consumed by maintainance respiration", "kg/ha"],

        "manqueAssim": ["deficit in assimilates (demand - supply)", "kg/ha"],

        # biomass

        "biomTotStadeFloraison": ["total biomass of the plant at the end of the flowering stage", "kg/ha"],

        "biomTotStadeIp": ["total biomass at the panicle initiation stage", "kg/ha"],

        "deltaBiomasseAerienne": ["increment of aerial biomass in one day", "kg/(ha.d)"],

        "deltaBiomasseFeuilles": ["increment of leaf biomass in one day", "kg/(ha.d)"],

        "biomasseAerienne": ["total aerial biomass", "kg/ha"],

        "biomasseVegetative": ["total vegetative biomass", "kg/ha"],

        "biomasseTotale": ["total biomass", "kg/ha"],

        "biomasseTige": ["total stem biomass", "kg/ha"],

        "biomasseRacinaire": ["total root biomass", "kg/ha"],

        "biomasseFeuille": ["total leaf biomass", "kg/ha"],

        "deltaBiomasseTotale": ["increment of total biomass in one day", "kg/(ha.d)"],

        # evapotranspiration

        "kce": ["fraction of kc attributable to soil evaporation","decimal percentage"],

        "kcp": ["fraction of kc attributable to plant transpiration","decimal percentage"],

        "kcTot": ["total crop coefficient",""],

        "tr": ["actual crop transpiration","mm/d"],

        "trPot": ["potential crop transpiration","mm/d"],

        "trSurf": ["",""],

        # water balance

        "consoRur": ["consumption of water stored in the root system", "mm"],

        "water_captured_by_mulch" : ["water captured by the mulch in one day","mm"],

        "available_water" : ["available water, sum of rainfall and total irrigation for the day","mm"],

        "eauTranspi": ["water available for transpiration from the surface reservoir","mm"],

        "correctedIrrigation" : ["corrected irrigation amount","mm/d"],

        "cstr" : ["drought stress coefficient", "arbitrary unit"],

        "dayVrac" : ["modulated daily root growth","mm/day"],

        "delta_root_tank_capacity": ["change in root system water reserve","mm"],

        "drainage": ["drainage","mm"],

        #// "etm": ["evapotranspiration from the soil moisture","mm/d"],

        "etp": ["potential evapotranspiration from the soil moisture","mm/d"],

        #// "etr": ["reference evapotranspiration","mm/d"],

        "evap": ["evaporation from the soil moisture","mm/d"],

        "evapPot": ["potential evaporation from the soil moisture","mm/d"],

        "FEMcW": ["water fraction in soil volume explored by the root system","none"],

        "fesw": ["fraction of available surface water","decimal percentage"],

        "irrigTotDay" : ["total irrigation for the day","mm"],

        "vRac" : ["reference daily root growth","mm/day"],

        "ftsw": ["fraction of transpirable surface water","decimal percentage"],

        "runoff" : ["daily water runoff","mm/d"],

        "pFact": ["FAO reference for critical FTSW value for transpiration response","none"],

        # water tanks

        "irrigation_tank_stock" : ["current stock of water in the irrigation tank","mm"], #! renaming stockIrr to irrigation_tank_stock

        "mulch_water_stock" : ["water stored in crop residues (mulch)","mm"], #! renaming stockMc to mulch_water_stock

        "root_tank_stock": ["current stock of water in the root system tank","mm"], #! renaming stRu to root_tank_stock

        "total_tank_capacity": ["total capacity of the root system tank","mm"], #! renaming stRuMax to total_tank_capacity

        "stRur": ["",""], # ["previous season's root system tank stock","mm"],

        "previous_root_tank_capacity": ["previous season's root system tank capacity","mm"], #! renaming stRurMaxPrec to previous_root_tank_capacity

        "previous_root_tank_stock": ["previous day's root system tank stock","mm"],

        "stRurSurf": ["surface root system tank stock","mm"],

        "surface_tank_stock": ["current stock of water in the surface root system tank","mm"], #! renaming stRuSurf to surface_tank_stock

        "stRuSurfPrec": ["previous day's surface root system tank stock","mm"],

        "delta_total_tank_stock": ["change in the total root system tank stock","mm"], #! renaming stRuVar to delta_total_tank_stock

        "irrigation_tank_capacity" : ["irrigation tank capacity","mm"], #! renaming ruIrr to irrigation_tank_capacity

        "ruRac": ["Water column that can potentially be strored in soil volume explored by root system","mm"],

        "conv": ["",""],

        "KAssim": ["",""],

        "dayBiomLeaf": ["daily growth of leaf biomass","kg/ha/d"],

        "dRdtPot": ["daily potential demand from yield","kg/ha/d"],

        "FeuilleUp": ["",""],

        "kRespMaint": ["",""],

        "LitFeuille": ["",""],

        "nbJourCompte": ["",""],

        "nbjStress": ["",""],

        "NbUBT": ["",""],

        "sla": ["",""],

        "stockRac": ["",""],

        "sumPP": ["",""],

        "TigeUp": ["",""],

        "UBTCulture": ["",""],

        "lai":["leaf area index","m2/m2"],

        # experimental

        "Ncrit": ["",""],

    }

    return variables