# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/04_classes.ipynb (unless otherwise specified).

__all__ = ['ClockStructClass', 'OutputClass', 'ParamStructClass', 'SoilClass', 'CropClass', 'FarmerClass',
           'FieldMngtClass', 'GwClass', 'InitWCClass', 'InitCondClass', 'WevapClass', 'SoilCompClass', 'SoilLayerClass',
           'SoilProfileClass', 'KswClass', 'KsaClass', 'TAWClass', 'DrClass', 'thRZClass', 'KswClass', 'KstClass',
           'CO2Class']

# Cell
import numpy as np
import pandas as pd

# Cell
class ClockStructClass:
    '''
    Contains model information regarding dates and step times etc.

    Atributes:\n

    `TimeStepCounter` : `int`: Keeps track of current timestep

    `ModelTermination` : `Bool`: False unless model has finished

    `SimulationStartDate` : `np.Datetime64`: Date of simulation start

    `SimulationEndDate` : `np.Datetime64`:
        Date of simulation end

    `TimeStep` : `int`
        time step (not sure why this is in yet)

    `nSteps` : `int`
        total number of days of simulation

    `TimeSpan` : `np.array`
        all dates (np.Datetime64) that lie within the start and end dates of simulation

    `StepStartTime` : `np.Datetime64`
        Date at start of timestep

    `StepEndTime` : `np.Datetime64`
        Date at end of timestep

    `EvapTimeSteps` : `int`
        Number of time-steps (per day) for soil evaporation calculation

    `SimOffSeason` : `str`
        'Y' if you want to simulate the off season, 'N' otherwise

    `PlantingDates` : `list-like`
        list of planting dates in datetime format

    `HarvestDates` : `list-like`
        list of harvest dates in datetime format

    `nSeasons` : `int`
        Total number of seasons to be simulated

    `SeasonCounter` : `int`
        counter to keep track of which season we are currenlty simulating


        '''

    def __init__(self):

        self.TimeStepCounter = 0 # Keeps track of current timestep
        self.ModelTermination = False # False unless model has finished
        self.SimulationStartDate = 0 # Date of simulation start
        self.SimulationEndDate = 0 # Date of simulation end
        self.TimeStep = 0 # time step (not sure why this is in yet)
        self.nSteps = 0 # total number of days of simulation
        self.TimeSpan = 0 # all dates that lie within the start and end dates of simulation
        self.StepStartTime = 0 # Date at start of timestep
        self.StepEndTime = 0 # Number of time-steps (per day) for soil evaporation calculation
        self.EvapTimeSteps = 20 # 'Y' if you want to simulate the off season, 'N' otherwise
        self.SimOffSeason = 0
        self.PlantingDates = []
        self.HarvestDates = []
        self.nSeasons = 0
        self.SeasonCounter = 0

# Cell
class OutputClass():
    '''
    Class to hold output data

    **Atributes**:\n

    `Water` : `pandas.DataFrame` : Water storage in soil

    `Flux` : `pandas.DataFrame` : Water flux

    `Growth` : `pandas.DataFrame` : crop growth

    `Final` : `pandas.DataFrame` : final stats

    '''
    def __init__(self):

        self.Water = []
        self.Flux = []
        self.Growth = []
        self.Final = []




# Cell
class ParamStructClass:
    '''
    The ParamStruct class contains the bulk of model Paramaters


    **Attributes**:\n

    `Soil` : `SoilClass` : Soil object contains data and paramaters related to the soil

    `Crop List` : `list` : List of Crop Objects which contain paramaters for all crops used in simulations

    `FallowFieldMngt` : `pandas.Series` : Dictionary containing field management variables for the off season (fallow periods)

    `NCrops` : `int` : Number of crop types to be simulated

    `SpecifiedPlantCalander` : `str` :  Specified crop rotation calendar (Y or N)


    `CropChoices` : `list` : List of crop type names in each simulated season, to be matched with planting and harvest dates, dtype str

    `CO2data` : `list` : CO2 data

    `CO2RefConc` : `float` : reference co2 concentration

    `CO2CurrentConc` : `float` : current co2 concentration

    `WaterTable` : `int` : Water table present (1=yes, 0=no)

    `zGW` : `pandas.Series` : WaterTable depth (mm) for each day of simulation

        '''

    def __init__(self):

        # soil and croplist
        self.Soil = 0
        self.CropList = []

        # field management
        self.FallowFieldMngt = []

        # variables extracted from cropmix.txt
        self.NCrops = 0
        self.SpecifiedPlantCalander = ""
        self.RotationFilename = ""

        # calculated Co2 variables
        self.CO2data = []
        self.CO2RefConc = 0
        self.CO2CurrentConc = 0
        self.Seasonal_Crop_List =[]

        #water table
        self.WaterTable = 0
        self.zGW = []



# Cell
class SoilClass:
    '''
    The Soil Class contains Paramaters and variables of the soil used in the simulation


    **Attributes**:\n


    `profile` : `pandas.DataFrame` : holds soil profile information take from SoilProfile.txt

    `Hydrology` : `pandas.DataFrame`: holds soil layer hydrology informaiton

    `Comp` : `pandas.DataFrame` : holds soil compartment information

    A number of float attributes specified in the initialisation of the class

        '''
    def __init__(self,soilType,dz=[0.1]*6+[0.2]*4+[0.3]*2,CalcSHP = 0,nLayer=0,
                 zSoil=0.0,nComp=0,AdjREW= 1,REW= 9.0,CalcCN=1,CN=61.0,zRes=-999,
                 EvapZsurf = 0.04, EvapZmin = 0.15, EvapZmax = 0.30,
                 Kex = 1.1, fevap = 4, fWrelExp = 0.4, fwcc = 50,
                 zCN = 0.3, zGerm = 0.3,AdjCN=1, fshape_cr = 16, zTop = 0.1,):



        # Assign default program properties (should not be changed without expert knowledge)
        self.EvapZsurf = EvapZsurf # Thickness of soil surface skin evaporation layer (m)
        self.EvapZmin = EvapZmin # Minimum thickness of full soil surface evaporation layer (m)
        self.EvapZmax = EvapZmax # Maximum thickness of full soil surface evaporation layer (m)
        self.Kex = Kex # Maximum soil evaporation coefficient
        self.fevap = fevap # Shape factor describing reduction in soil evaporation in stage 2.
        self.fWrelExp = fWrelExp # Proportional value of Wrel at which soil evaporation layer expands
        self.fwcc = fwcc # Maximum coefficient for soil evaporation reduction due to sheltering effect of withered canopy
        self.zCN = zCN # Thickness of soil surface (m) used to calculate water content to adjust curve number
        self.zGerm = zGerm # Thickness of soil surface (m) used to calculate water content for germination
        self.AdjCN = AdjCN # Adjust curve number for antecedent moisture content (0: No, 1: Yes)
        self.fshape_cr = fshape_cr # Capillary rise shape factor
        self.zTop = zTop # Thickness of soil surface layer for water stress comparisons (m)


        # define Soil attributes that will be assigned in AOSP_ReadModelParameters

        self.CalcSHP= CalcSHP # Calculate soil hydraulic properties (0 = No, 1 = Yes)
        self.zSoil= zSoil # Total thickness of soil profile (m)
        self.nComp= nComp # Total number of soil compartments
        self.nLayer= nLayer # Total number of soil layers
        self.AdjREW= AdjREW # Adjust default value for readily evaporable water (0 = No, 1 = Yes)
        self.REW= REW # Readily evaporable water (mm) (only used if adjusting from default value)
        self.CalcCN= CalcCN # adjust Curve number
        self.CN= CN # Curve number
        self.zRes= zRes # Depth of restrictive soil layer (set to negative value if not present)

        if soilType == 'custom':
            self.create_df(dz)

        # estimates taken from saxton rawls 2006

        elif soilType == 'sand':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),88,5,2.5,100)

        elif soilType == 'loamy_sand':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),80,5,2.5,100)

        elif soilType == 'sandy_loam':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),65,10,2.5,100)

        elif soilType == 'loam':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),40,20,2.5,100)

        elif soilType == 'silt_loam':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),20,15,2.5,100)

        elif soilType == 'silt':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),10,5,2.5,100)

        elif soilType == 'sandy_clay_loam':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),60,25,2.5,100)

        elif soilType == 'clay_loam':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),30,35,2.5,100)

        elif soilType == 'silt_clay_loam':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),10,35,2.5,100)

        elif soilType == 'silt_clay':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),10,45,2.5,100)

        elif soilType == 'sandy_clay':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),50,40,2.5,100)

        elif soilType == 'clay':
            self.create_df(dz)
            self.add_layer_from_texture(sum(dz),25,50,2.5,100)

        elif soilType == 'ac_SandyLoam':
            self.CN = 46
            self.REW = 7
            self.create_df(dz)
            self.add_layer(sum(dz), 0.10, 0.22, 0.41, 1200, 100)

        elif soilType == 'ac_Paddy':
            self.CN = 77
            self.REW = 10
            dz = [0.1]*12
            #dz = [0.1]*5 + [0.2]*6 + [0.3]
            self.create_df(dz)
            self.add_layer(0.5, 0.32, 0.50, 0.54, 15, 100)
            self.add_layer(1.5, 0.39, 0.54, 0.55, 2, 100)

        elif soilType == 'ac_TunisLocal':
            self.CN = 46
            self.REW = 7
            dz = [0.1]*6 + [0.15]*5 + [0.2]
            self.create_df(dz)
            self.add_layer(0.3, 0.24, 0.40, 0.50, 155, 100)
            self.add_layer(1.7, 0.11, 0.33, 0.46, 500, 100)

        elif soilType == 'ac_Loam':
            self.CN = 61
            self.REW = 9
            dz = [0.1]*12
            self.create_df(dz)
            self.add_layer(sum(dz), 0.15, 0.31, 0.46, 500, 100)




        elif soilType == 'default':
            self.profile = pd.DataFrame(np.empty((12,10)),
                                        columns=["Comp","Layer","dz","dzsum","th_dry","th_wp","th_fc","th_s","Ksat","penetrability"])

            ksat = 500

            self.profile.dz = [0.1]*2 + [0.2]*8 + [0.3]*2
            self.profile.dzsum = np.cumsum(self.profile.dz)
            self.profile.Comp = np.arange(len(dz))
            self.profile.Layer = 1
            self.profile.th_dry = 0.15/2
            self.profile.th_wp = 0.15
            self.profile.th_fc = 0.31
            self.profile.th_s = 0.46
            self.profile.Ksat = ksat
            self.profile.penetrability = 100

            # Calculate drainage characteristic (tau)
            # Calculations use equation given by Raes et al. 2012
            tau = round(0.0866*(ksat**0.35),2)
            if tau > 1:
                tau = 1
            elif tau < 0:
                tau = 0

            self.profile["tau"] = tau

        else:
            print('wrong soil type')
            assert 1==2

    def create_df(self,dz):

        self.profile = pd.DataFrame(np.empty((len(dz),4)),columns=["Comp","Layer","dz","dzsum"])
        self.profile.dz = dz
        self.profile.dzsum = np.cumsum(self.profile.dz)
        self.profile.Comp = np.arange(len(dz))
        self.profile.Layer = np.nan

        self.profile["zBot"] = self.profile.dzsum
        self.profile["zTop"] = self.profile["zBot"]-self.profile.dz
        self.profile["zMid"] = (self.profile["zTop"]+self.profile["zBot"])/2

    def calculate_soil_hydraulic_properties(self,Sand,Clay,OrgMat,DF=1):

        """
        Function to calculate soil hydraulic properties, given textural inputs.
        Calculations use pedotransfer function equations described in Saxton and Rawls (2006)


        """


        # do calculations



        #Water content at permanent wilting point
        Pred_thWP = ( -(0.024*Sand) + (0.487*Clay) + (0.006*OrgMat)
                      +(0.005*Sand*OrgMat) - (0.013*Clay*OrgMat)
                      +(0.068*Sand*Clay) + 0.031 )

        th_wp = Pred_thWP+(0.14*Pred_thWP)-0.02

        # Water content at field capacity and saturation
        Pred_thFC = ( -(0.0251*Sand) + (0.195*Clay) + (0.011*OrgMat)
                      +(0.006*Sand*OrgMat) - (0.027*Clay*OrgMat)
                      +(0.452*Sand*Clay) + 0.299 )

        PredAdj_thFC = Pred_thFC+((1.283*(np.power(Pred_thFC,2)))-(0.374*Pred_thFC)-0.015)

        Pred_thS33 = ( (0.0278*Sand) + (0.034*Clay) + (0.022*OrgMat)
                      -(0.018*Sand*OrgMat) - (0.027*Clay*OrgMat)
                      +(0.584*Sand*Clay) + 0.078 )

        PredAdj_thS33 = Pred_thS33+((0.636*Pred_thS33)-0.107)
        Pred_thS = (PredAdj_thFC+PredAdj_thS33)+((-0.097*Sand)+0.043)

        pN = (1-Pred_thS)*2.65
        pDF = pN*DF
        PorosComp = (1-(pDF/2.65))-(1-(pN/2.65))
        PorosCompOM = 1-(pDF/2.65)

        DensAdj_thFC = PredAdj_thFC+(0.2*PorosComp)
        DensAdj_thS = PorosCompOM

        th_fc = DensAdj_thFC
        th_s = DensAdj_thS

        # Saturated hydraulic conductivity (mm/day)
        lmbda = 1/((np.log(1500)-np.log(33))/(np.log(th_fc)-np.log(th_wp)))
        Ksat = (1930*(th_s-th_fc)**(3-lmbda))*24

        # Water content at air dry
        th_dry = th_wp / 2

        #round values
        th_dry = round(10_000*th_dry)/10_000
        th_wp = round(1000*th_wp)/1000
        th_fc = round(1000*th_fc)/1000
        th_s = round(1000*th_s)/1000
        Ksat = round(10*Ksat)/10



        return th_wp,th_fc,th_s,Ksat


    def add_layer_from_texture(self,thickness,Sand,Clay,OrgMat,Penetrability):

        th_wp,th_fc,th_s,Ksat=self.calculate_soil_hydraulic_properties(Sand/100,Clay/100,OrgMat)

        self.add_layer(thickness, th_wp, th_fc, th_s, Ksat, Penetrability)


    def add_layer(self,thickness, thWP, thFC, thS, Ksat, penetrability):

        self.nLayer +=1


        num_layers = len(self.profile.dropna().Layer.unique())

        new_layer = num_layers+1

        if new_layer==1:
            self.profile.loc[(round(thickness,2)>=round(self.profile.dzsum,2)),"Layer"] = new_layer
        else:
            last = self.profile[self.profile.Layer==new_layer-1].dzsum.values[-1]
            self.profile.loc[(thickness+last>=self.profile.dzsum) & (self.profile.Layer.isna()),"Layer"] = new_layer


        self.profile.loc[self.profile.Layer==new_layer,"th_dry"] = self.profile.Layer.map({new_layer:thWP/2})
        self.profile.loc[self.profile.Layer==new_layer,"th_wp"] = self.profile.Layer.map({new_layer:thWP})
        self.profile.loc[self.profile.Layer==new_layer,"th_fc"] = self.profile.Layer.map({new_layer:thFC})
        self.profile.loc[self.profile.Layer==new_layer,"th_s"] = self.profile.Layer.map({new_layer:thS})
        self.profile.loc[self.profile.Layer==new_layer,"Ksat"] = self.profile.Layer.map({new_layer:Ksat})
        self.profile.loc[self.profile.Layer==new_layer,"penetrability"] = self.profile.Layer.map({new_layer:penetrability})



        # Calculate drainage characteristic (tau)
        # Calculations use equation given by Raes et al. 2012
        tau = round(0.0866*(Ksat**0.35),2)
        if tau > 1:
            tau = 1
        elif tau < 0:
            tau = 0

        self.profile.loc[self.profile.Layer==new_layer,"tau"] = self.profile.Layer.map({new_layer:tau})



    def fill_nan(self,):


        self.profile = self.profile.fillna(method='ffill')

        self.zSoil = round(self.profile.dzsum.iloc[-1],2)

        self.nComp = len(self.profile)


    def add_capillary_rise_params(self,):
        # Calculate capillary rise parameters for all soil layers
        # Only do calculation if water table is present. Calculations use equations
        # described in Raes et al. (2012)
        prof = self.profile

        hydf = prof.groupby('Layer').mean().drop(['dz','dzsum'],axis=1)

        hydf["aCR"] = 0
        hydf["bCR"] = 0

        for layer in hydf.index.unique():
            layer = int(layer)

            soil=hydf.loc[layer]

            thwp = soil.th_wp
            thfc = soil.th_fc
            ths = soil.th_s
            Ksat = soil.Ksat

            aCR = 0
            bCR = 0

            if (thwp >= 0.04) and (thwp <= 0.15) and (thfc >= 0.09) and \
                    (thfc <= 0.28) and (ths >= 0.32) and (ths <= 0.51):

                # Sandy soil class
                if (Ksat >= 200) and (Ksat <= 2000):
                    aCR = -0.3112-(Ksat*(1e-5))
                    bCR = -1.4936+(0.2416*np.log(Ksat))
                elif Ksat < 200:
                    aCR = -0.3112-(200*(1e-5));
                    bCR = -1.4936+(0.2416*np.log(200))
                elif Ksat > 2000:
                    aCR = -0.3112-(2000*(1e-5));
                    bCR = -1.4936+(0.2416*np.log(2000));

            elif (thwp >= 0.06) and (thwp <= 0.20) and (thfc >= 0.23) and \
                    (thfc <= 0.42) and (ths >= 0.42) and (ths <= 0.55):

                # Loamy soil class
                if (Ksat >= 100) and (Ksat <= 750):
                    aCR = -0.4986+(9*(1e-5)*Ksat)
                    bCR = -2.132+(0.4778*np.log(Ksat))
                elif Ksat < 100:
                    aCR = -0.4986+(9*(1e-5)*100)
                    bCR = -2.132+(0.4778*np.log(100))
                elif Ksat > 750:
                    aCR = -0.4986+(9*(1e-5)*750)
                    bCR = -2.132+(0.4778*np.log(750))

            elif (thwp >= 0.16) and (thwp <= 0.34) and (thfc >= 0.25) and \
                    (thfc <= 0.45) and (ths >= 0.40) and (ths <= 0.53):

                # Sandy clayey soil class
                if (Ksat >= 5) and (Ksat <= 150):
                    aCR = -0.5677-(4*(1e-5)*Ksat)
                    bCR = -3.7189+(0.5922*np.log(Ksat))
                elif Ksat < 5:
                    aCR = -0.5677-(4*(1e-5)*5)
                    bCR = -3.7189+(0.5922*np.log(5))
                elif Ksat > 150:
                    aCR = -0.5677-(4*(1e-5)*150)
                    bCR = -3.7189+(0.5922*np.log(150))

            elif (thwp >= 0.20) and (thwp <= 0.42) and (thfc >= 0.40) and \
                    (thfc <= 0.58) and (ths >= 0.49) and (ths <= 0.58):

                # Silty clayey soil class
                if (Ksat >= 1) and (Ksat <= 150):
                    aCR = -0.6366+(8*(1e-4)*Ksat)
                    bCR = -1.9165+(0.7063*np.log(Ksat))
                elif Ksat < 1:
                    aCR = -0.6366+(8*(1e-4)*1)
                    bCR = -1.9165+(0.7063*np.log(1))
                elif Ksat > 150:
                    aCR = -0.6366+(8*(1e-4)*150)
                    bCR = -1.9165+(0.7063*np.log(150))


            assert aCR != 0
            assert bCR != 0




            prof.loc[prof.Layer==layer,"aCR"] = prof.Layer.map({layer:aCR})
            prof.loc[prof.Layer==layer,"bCR"] = prof.Layer.map({layer:bCR})


        self.profile=prof

# Cell
class CropClass:
    '''
    The Crop Class contains Paramaters and variables of the crop used in the simulation


    **Attributes**:\n


    `CropType` : `int` : Crop Type (1 = Leafy vegetable, 2 = Root/tuber, 3 = Fruit/grain)

    `PlantMethod` : `int` : Planting method (0 = Transplanted, 1 =  Sown)

    `CalendarType` : `int` : Calendar Type (1 = Calendar days, 2 = Growing degree days)

    `SwitchGDD` : `int` : Convert calendar to GDD mode if inputs are given in calendar days (0 = No; 1 = Yes)

    `PlantingDate` : `str` : Planting Date (dd/mm)

    `HarvestDate` : `str` : Latest Harvest Date (dd/mm)

    `IrrMngt`: `dict` :  dictionary containting irrigation management information. Added in AOSP_ReadIrrigationManagement

    `IrrSchd` : `pandas.DataFrame` :  pandas DataFrame containing the Irrigation Schedule if predefined

    `FieldMngt` : `dict` :   Dictionary containing field management variables for the growing season of the crop

    `Params` : `Dict` :  Dictionary containing crop paramaters read from file of type float

     A number of default program properties of type float are also specified during initialisation

    '''



    def __init__(self,c_name,**kwargs):

        self.Name = ''
        self.CropFilename = ''
        self.IrrigationFilename = ''
        self.FieldMngtFilename = ''

        # Assign default program properties (should not be changed without expert knowledge)

        self.fshape_b = 13.8135 # Shape factor describing the reduction in biomass production for insufficient growing degree days
        self.PctZmin = 70 # Initial percentage of minimum effective rooting depth
        self.fshape_ex = -6 # Shape factor describing the effects of water stress on root expansion
        self.ETadj = 1 # Adjustment to water stress thresholds depending on daily ET0 (0 = No, 1 = Yes)
        self.Aer = 5 # Vol (%) below saturation at which stress begins to occur due to deficient aeration
        self.LagAer = 3 # Number of days lag before aeration stress affects crop growth
        self.beta = 12 # Reduction (%) to p_lo3 when early canopy senescence is triggered
        self.a_Tr = 1 # Exponent parameter for adjustment of Kcx once senescence is triggered
        self.GermThr = 0.2 # Proportion of total water storage needed for crop to germinate
        self.CCmin = 0.05 # Minimum canopy size below which yield formation cannot occur
        self.MaxFlowPct = 100/3 # Proportion of total flowering time (%) at which peak flowering occurs
        self.HIini = 0.01 # Initial harvest index
        self.bsted = 0.000138 # WP co2 adjustment parameter given by Steduto et al. 2007
        self.bface = 0.001165 # WP co2 adjustment parameter given by FACE experiments


        if c_name == 'maize':

            self.Name = 'maize'

            # added in Read_Model_Paramaters
            self.CropType= 3 # Crop Type (1 = Leafy vegetable, 2 = Root/tuber, 3 = Fruit/grain)
            self.PlantMethod= 1 # Planting method (0 = Transplanted, 1 =  Sown)
            self.CalendarType= 2 # Calendar Type (1 = Calendar days, 2 = Growing degree days)
            self.SwitchGDD= 0 # Convert calendar to GDD mode if inputs are given in calendar days (0 = No; 1 = Yes)

            self.PlantingDate= '05/01' # Planting Date (mm/dd)
            self.HarvestDate= '10/30' # Latest Harvest Date (mm/dd)


            self.Emergence = 80 # Growing degree/Calendar days from sowing to emergence/transplant recovery
            self.MaxRooting = 1420 # Growing degree/Calendar days from sowing to maximum rooting
            self.Senescence = 1420 # Growing degree/Calendar days from sowing to senescence
            self.Maturity = 1670 # Growing degree/Calendar days from sowing to maturity
            self.HIstart = 850 # Growing degree/Calendar days from sowing to start of yield formation
            self.Flowering = 190 # Duration of flowering in growing degree/calendar days (-999 for non-fruit/grain crops)
            self.YldForm = 775 # Duration of yield formation in growing degree/calendar days
            self.GDDmethod = 2 # Growing degree day calculation method
            self.Tbase = 8 # Base temperature (degC) below which growth does not progress
            self.Tupp = 30 # Upper temperature (degC) above which crop development no longer increases
            self.PolHeatStress = 1 # Pollination affected by heat stress (0 = No, 1 = Yes)
            self.Tmax_up = 40 # Maximum air temperature (degC) above which pollination begins to fail
            self.Tmax_lo = 45 # Maximum air temperature (degC) at which pollination completely fails
            self.PolColdStress = 1 # Pollination affected by cold stress (0 = No, 1 = Yes)
            self.Tmin_up = 10 # Minimum air temperature (degC) below which pollination begins to fail
            self.Tmin_lo = 5 # Minimum air temperature (degC) at which pollination completely fails
            self.TrColdStress = 1 # Transpiration affected by cold temperature stress (0 = No, 1 = Yes)
            self.GDD_up = 12 # Minimum growing degree days (degC/day) required for full crop transpiration potential
            self.GDD_lo = 0 # Growing degree days (degC/day) at which no crop transpiration occurs
            self.Zmin = 0.3 # Minimum effective rooting depth (m)
            self.Zmax = 1.7 # Maximum rooting depth (m)
            self.fshape_r = 1.3 # Shape factor describing root expansion
            self.SxTopQ = 0.0480 # Maximum root water extraction at top of the root zone (m3/m3/day)
            self.SxBotQ = 0.0117 # Maximum root water extraction at the bottom of the root zone (m3/m3/day)
            self.SeedSize = 6.5 # Soil surface area (cm2) covered by an individual seedling at 90% emergence
            self.PlantPop = 75_000 # Number of plants per hectare
            self.CCx = 0.96 # Maximum canopy cover (fraction of soil cover)
            self.CDC = 0.01 # Canopy decline coefficient (fraction per GDD/calendar day)
            self.CGC = 0.0125 # Canopy growth coefficient (fraction per GDD)
            self.Kcb = 1.05 # Crop coefficient when canopy growth is complete but prior to senescence
            self.fage = 0.3 #  Decline of crop coefficient due to ageing (%/day)
            self.WP = 33.7 # Water productivity normalized for ET0 and C02 (g/m2)
            self.WPy = 100 # Adjustment of water productivity in yield formation stage (% of WP)
            self.fsink = 0.5 # Crop performance under elevated atmospheric CO2 concentration (%/100)
            self.HI0 = 0.48 # Reference harvest index
            self.dHI_pre = 0 # Possible increase of harvest index due to water stress before flowering (%)
            self.a_HI = 7 # Coefficient describing positive impact on harvest index of restricted vegetative growth during yield formation
            self.b_HI = 3 # Coefficient describing negative impact on harvest index of stomatal closure during yield formation
            self.dHI0 = 15 # Maximum allowable increase of harvest index above reference value
            self.Determinant = 1 # Crop Determinancy (0 = Indeterminant, 1 = Determinant)
            self.exc = 50 # Excess of potential fruits
            self.p_up1 = 0.14 # Upper soil water depletion threshold for water stress effects on affect canopy expansion
            self.p_up2 = 0.69 # Upper soil water depletion threshold for water stress effects on canopy stomatal control
            self.p_up3 = 0.69 # Upper soil water depletion threshold for water stress effects on canopy senescence
            self.p_up4 = 0.8 # Upper soil water depletion threshold for water stress effects on canopy pollination
            self.p_lo1 = 0.72 # Lower soil water depletion threshold for water stress effects on canopy expansion
            self.p_lo2 = 1 # Lower soil water depletion threshold for water stress effects on canopy stomatal control
            self.p_lo3 = 1 #  Lower soil water depletion threshold for water stress effects on canopy senescence
            self.p_lo4 = 1 # Lower soil water depletion threshold for water stress effects on canopy pollination
            self.fshape_w1 = 2.9 # Shape factor describing water stress effects on canopy expansion
            self.fshape_w2 = 6 # Shape factor describing water stress effects on stomatal control
            self.fshape_w3 = 2.7 # Shape factor describing water stress effects on canopy senescence
            self.fshape_w4 = 1 # Shape factor describing water stress effects on pollination



        elif c_name == 'wheat':

            self.Name = 'wheat'

            self.CropType= 3; self.PlantMethod= 1; self.CalendarType= 2
            self.SwitchGDD= 0; self.PlantingDate= '10/15'; self.HarvestDate= '04/30'

            self.Emergence = 150; self.MaxRooting = 864; self.Senescence = 1700
            self.Maturity = 2400; self.HIstart = 1250; self.Flowering = 200
            self.YldForm = 1100; self.GDDmethod = 3; self.Tbase = 0
            self.Tupp = 26; self.PolHeatStress = 1; self.Tmax_up = 35
            self.Tmax_lo = 40; self.PolColdStress = 1; self.Tmin_up = 5
            self.Tmin_lo = 0; self.TrColdStress = 1; self.GDD_up = 14
            self.GDD_lo = 0; self.Zmin = 0.3; self.Zmax = 1.5
            self.fshape_r = 1.5; self.SxTopQ = 0.0480; self.SxBotQ = 0.012
            self.SeedSize = 1.5; self.PlantPop = 4_500_000; self.CCx = 0.96
            self.CDC = 0.004; self.CGC = 0.005001; self.Kcb = 1.1
            self.fage = 0.15; self.WP = 15; self.WPy = 100
            self.fsink = 0.5; self.HI0 = 0.48; self.dHI_pre = 5
            self.a_HI = 10; self.b_HI = 7; self.dHI0 = 15
            self.Determinant = 1; self.exc = 100; self.p_up1 = 0.2
            self.p_up2 = 0.65; self.p_up3 = 0.7; self.p_up4 = 0.85
            self.p_lo1 = 0.65; self.p_lo2 = 1; self.p_lo3 = 1
            self.p_lo4 = 1; self.fshape_w1 = 5.; self.fshape_w2 = 2.5
            self.fshape_w3 = 2.5; self.fshape_w4 = 1.



        elif c_name == 'potato':

            self.Name = 'potato'

            self.CropType= 2; self.PlantMethod= 0; self.CalendarType= 1
            self.SwitchGDD= 0; self.PlantingDate= '04/25'; self.HarvestDate= '08/30'

            self.Emergence = 15; self.MaxRooting = 50; self.Senescence = 105
            self.Maturity = 125; self.HIstart = 46; self.Flowering = -999
            self.YldForm = 77; self.GDDmethod = 3; self.Tbase = 2
            self.Tupp = 26; self.PolHeatStress = 0; self.Tmax_up = -999
            self.Tmax_lo = -999; self.PolColdStress = 0; self.Tmin_up = -999
            self.Tmin_lo = -999; self.TrColdStress = 1; self.GDD_up = 7
            self.GDD_lo = 0; self.Zmin = 0.3; self.Zmax = 0.6
            self.fshape_r = 1.5; self.SxTopQ = 0.0480; self.SxBotQ = 0.012
            self.SeedSize = 15; self.PlantPop = 40_000; self.CCx = 0.92
            self.CDC = 0.01884; self.CGC = 0.126; self.Kcb = 1.1
            self.fage = 0.15; self.WP = 18; self.WPy = 100
            self.fsink = 0.5; self.HI0 = 0.85; self.dHI_pre = 2
            self.a_HI = 0; self.b_HI = 10; self.dHI0 = 5
            self.Determinant = 0; self.exc = 0; self.p_up1 = 0.2
            self.p_up2 = 0.6; self.p_up3 = 0.7; self.p_up4 = 0.8
            self.p_lo1 = 0.6; self.p_lo2 = 1; self.p_lo3 = 1
            self.p_lo4 = 1; self.fshape_w1 = 3.; self.fshape_w2 = 3
            self.fshape_w3 = 3; self.fshape_w4 = 0


        elif c_name == 'rice':

            self.Name = 'rice'

            self.CropType= 3; self.PlantMethod= 0; self.CalendarType= 2
            self.SwitchGDD= 0; self.PlantingDate= '08/01'; self.HarvestDate= '11/30'

            self.Emergence = 102; self.MaxRooting = 381; self.Senescence = 1450
            self.Maturity = 1707; self.HIstart = 1088; self.Flowering = 318
            self.YldForm = 577; self.GDDmethod = 3; self.Tbase = 8
            self.Tupp = 30; self.PolHeatStress = 1; self.Tmax_up = 35
            self.Tmax_lo = 40; self.PolColdStress = 1; self.Tmin_up = 8
            self.Tmin_lo = 3; self.TrColdStress = 1; self.GDD_up = 10
            self.GDD_lo = 0; self.Zmin = 0.3; self.Zmax = 0.5
            self.fshape_r = 2.5; self.SxTopQ = 0.0480; self.SxBotQ = 0.012
            self.SeedSize = 6; self.PlantPop = 1_000_000; self.CCx = 0.95
            self.CDC = 0.006172; self.CGC = 0.006163; self.Kcb = 1.1
            self.fage = 0.15; self.WP = 19; self.WPy = 100
            self.fsink = 0.5; self.HI0 = 0.43; self.dHI_pre = 0
            self.a_HI = 10; self.b_HI = 7; self.dHI0 = 15
            self.Determinant = 1; self.exc = 100; self.p_up1 = 0
            self.p_up2 = 0.5; self.p_up3 = 0.55; self.p_up4 = 0.75
            self.p_lo1 = 0.4; self.p_lo2 = 1; self.p_lo3 = 1
            self.p_lo4 = 1; self.fshape_w1 = 3.; self.fshape_w2 = 3
            self.fshape_w3 = 3; self.fshape_w4 = 2.7

            # no aeration stress for rice
            self.Aer = -1e10; self.LagAer = 1e10


        elif c_name == 'custom':
            # temporary solution for new crops
            # if using this ensure that all paramaters in 'allowed_keys'
            # are passed in as arguments at initialization
            _=1

        else:
            assert 1==2, 'wrong crop name'

        # set any paramaters specified by user
        allowed_keys = {'fshape_b','PctZmin','fshape_ex','ETadj','Aer','LagAer',
                        'beta','a_Tr','GermThr','CCmin','MaxFlowPct','HIini',
                        'bsted','bface','CropType','PlantMethod','CalendarType','SwitchGDD','PlantingDate',
                        'HarvestDate','Emergence','MaxRooting','Senescence','Maturity',
                        'HIstart','Flowering','YldForm','GDDmethod','Tbase','Tupp',
                        'PolHeatStress','Tmax_up','Tmax_lo','PolColdStress','Tmin_up',
                        'Tmin_lo','TrColdStress','GDD_up','GDD_lo','Zmin','Zmax',
                        'fshape_r','SxTopQ','SxBotQ','SeedSize','PlantPop','CCx','CDC',
                        'CGC','Kcb','fage','WP','WPy','fsink','HI0','dHI_pre','a_HI','b_HI',
                        'dHI0','Determinant','exc','p_up1','p_up2','p_up3','p_up4',
                        'p_lo1','p_lo2','p_lo3','p_lo4','fshape_w1','fshape_w2','fshape_w3',
                        'fshape_w4'}

        self.__dict__.update((k, v) for k, v in kwargs.items() if k in allowed_keys)




    def calculate_additional_params(self,):

        # Calculate additional parameters for all self types in mix


        # Fractional canopy cover size at emergence
        self.CC0 = self.PlantPop*self.SeedSize*1e-8
        # Root extraction terms
        SxTopQ = self.SxTopQ
        SxBotQ = self.SxBotQ
        S1 = self.SxTopQ
        S2 = self.SxBotQ
        if S1 == S2:
            SxTop = S1
            SxBot = S2
        else:
            if SxTopQ < SxBotQ:
                S1 = SxBotQ
                S2 = SxTopQ

            xx = 3*(S2/(S1-S2))
            if xx < 0.5:
                SS1 = (4/3.5)*S1
                SS2 = 0
            else:
                SS1 = (xx+3.5)*(S1/(xx+3))
                SS2 = (xx-0.5)*(S2/xx)

            if SxTopQ > SxBotQ:
                SxTop = SS1
                SxBot = SS2
            else:
                SxTop = SS2
                SxBot = SS1


        self.SxTop = SxTop
        self.SxBot = SxBot

        # Water stress thresholds
        self.p_up = [self.p_up1,self.p_up2,self.p_up3,self.p_up4]

        self.p_lo = [self.p_lo1,self.p_lo2,self.p_lo3,self.p_lo4]

        self.fshape_w = [self.fshape_w1,self.fshape_w2,self.fshape_w3,self.fshape_w4]


    def flowerfun(self,xx):
        assert self.CropType == 3
        return (0.00558*(xx**0.63))-(0.000969*xx)-0.00383


# Cell
class FarmerClass:

    """
    Farmer Class defines irrigation strategy

    **Attributes:**\n


    `Name` : `str` :  name

    `IrrMethod` : `int` :  Irrigation method {0: rainfed, 1: soil moisture targets, 2: set time interval,
                                              3: predifined schedule, 4: net irrigation, 5: constant depth }

    `WetSurf` : `int` : Soil surface wetted by irrigation (%)

    `AppEff` : `int` : Irrigation application efficiency (%)

    `MaxIrr` : `float` : Maximum depth (mm) that can be applied each day

    `SMT` : `list` :  Soil moisture targets (%TAW) to maintain in each growth stage (only used if irrigation method is equal to 1)

    `IrrInterval` : `int` : Irrigation interval in days (only used if irrigation method is equal to 2)

    `Schedule` : `pandas.DataFrame` : DataFrame containing dates and depths

    `NetIrrSMT` : `float` : Net irrigation threshold moisture level (% of TAW that will be maintained, for IrrMethod=4)

    `Depth` : `float` : constant depth to apply on each day

    """

    def __init__(self,IrrMethod,**kwargs):
        self.Name = 'Farmer'
        self.IrrMethod=IrrMethod

        self.WetSurf = 100
        self.AppEff = 100
        self.MaxIrr = 25


        if IrrMethod == 1:
            self.SMT=[100]*4

        if IrrMethod == 2:
            self.IrrInterval = 3


        if IrrMethod == 3:
            #wants a pandas dataframe with Date and Depth, pd.Datetime and float
            """
            dates = pd.DatetimeIndex(['20/10/1979','20/11/1979','20/12/1979'])
            depths = [25,25,25]
            irr=pd.DataFrame([dates,depths]).T
            irr.columns=['Date','Depth']
            """
            self.Schedule = pd.DataFrame(columns=['Date','Depth'])


        if IrrMethod == 4:
            self.NetIrrSMT = 80


        if IrrMethod == 5:
            self.Depth = 0


        allowed_keys = {'name','WetSurf','AppEff','MaxIrr','SMT','IrrInterval','NetIrrSMT','Schedule'}

        self.__dict__.update((k, v) for k, v in kwargs.items() if k in allowed_keys)


    def get_irr_depth(self,NewCond):


        if self.IrrMethod ==0:

            return 0


        elif self.IrrMethod ==1:

            Dr  = NewCond.Depletion / NewCond.TAW

            index = int(NewCond.GrowthStage)-1
            #index = int(NewCond.DAP/10)


            if Dr > 1-self.SMT[index]/100:
                # Irrigation occurs
                IrrReq = max(0,NewCond.Depletion)
                # Adjust irrigation requirements for application efficiency
                EffAdj = ((100-self.AppEff)+100)/100
                IrrReq = IrrReq*EffAdj
                # Limit irrigation to maximum depth
                Irr = min(self.MaxIrr,IrrReq)

                return Irr

            else:
                return 0

        elif self.IrrMethod ==2: # Irrigation - fixed interval

            Dr = NewCond.Depletion

            # Get number of days in growing season so far (subtract 1 so that
            # always irrigate first on day 1 of each growing season)
            nDays = NewCond.DAP-1


            if nDays % self.IrrInterval == 0:
                # Irrigation occurs
                IrrReq = max(0,Dr)
                # Adjust irrigation requirements for application efficiency
                EffAdj = ((100-self.AppEff)+100)/100
                IrrReq = IrrReq*EffAdj
                # Limit irrigation to maximum depth
                Irr = min(self.MaxIrr,IrrReq)
            else:
                # No irrigation
                Irr = 0

            return Irr


        elif self.IrrMethod == 3: # Irrigation - pre-defined schedule
            # Get current date
            CurrentDate = NewCond.CurrentDate
            # Find irrigation value corresponding to current date
            Irr = self.Schedule.loc[CurrentDate].Depth

            assert Irr>0; assert Irr < 10_000

            return min(self.MaxIrr,IrrReq)


        elif self.IrrMethod ==4: # Irrigation - net irrigation
        # Net irrigation calculation performed after transpiration, so
        # irrigation is zero here

            return 0

        elif self.IrrMethod ==5: # depth applied each day (usually specified outside of model)

            return self.depth

        else:
            print(f'somethings gone wrong in irrigation method:{self.IrrMethod}')


# Cell
class FieldMngtClass:
    '''
    Field Management Class

    **Attributes:**\n


    `Mulches` : `str` :  Soil surface covered by mulches (Y or N)

    `Bunds` : `str` :  Surface bunds present (Y or N)

    `CNadj` : `str` : Field conditions affect curve number (Y or N)

    `SRinhb` : `str` : Management practices fully inhibit surface runoff (Y or N)



    `MulchPct` : `float` :  Area of soil surface covered by mulches (%)

    `fMulch` : `float` : Soil evaporation adjustment factor due to effect of mulches

    `zBund` : `float` : Bund height (m)

    `BundWater` : `float` : Initial water height in surface bunds (mm)

    `CNadjPct` : `float` : Percentage change in curve number (positive or negative)

    '''
    def __init__(self,Mulches='N',Bunds='N',CNadj='N',SRinhb='N',
                 MulchPct=50,fMulch=0.5,zBund=0,BundWater=0,
                 CNadjPct=0):

        self.Mulches = Mulches #  Soil surface covered by mulches (Y or N)
        self.Bunds = Bunds #  Surface bunds present (Y or N)
        self.CNadj = CNadj # Field conditions affect curve number (Y or N)
        self.SRinhb = SRinhb # Management practices fully inhibit surface runoff (Y or N)

        self.MulchPct = MulchPct #  Area of soil surface covered by mulches (%)
        self.fMulch = fMulch # Soil evaporation adjustment factor due to effect of mulches
        self.zBund = zBund # Bund height (m)
        self.BundWater = BundWater # Initial water height in surface bunds (mm)
        self.CNadjPct = CNadjPct # Percentage change in curve number (positive or negative)

# Cell
class GwClass:
    '''
    Ground Water Class stores information on water table params

    **Attributes:**\n


    `WaterTable` : `str` :  Water table considered (Y or N)

    `Method` : `str` :  Water table input data ('Constant' or 'Variable')

    `dates` : `list` : water table observation dates

    `values` : `list` : water table observation depths

    '''
    def __init__(self,WaterTable='N',Method='Constant',dates=[],values=[]):

        self.WaterTable = WaterTable
        self.Method = Method
        self.dates=dates
        self.values=values

# Cell
class InitWCClass:
    '''
    Initial water content Class defines water content at start of sim

    **Attributes:**\n

    `wc_type` : `str` :  Type of value ('Prop' = 'WP'/'FC'/'SAT'; 'Num' = XXX m3/m3; 'Pct' = % TAW))

    `Method` : `str` :  Method ('Depth' = Interpolate depth points; 'Layer' = Constant value for each soil layer)

    `depth_layer` : `list` : location in soil profile (soil layer or depth)

    `value` : `list` : value at that location

    '''
    def __init__(self,wc_type='Prop',Method='Layer',
                 depth_layer=[1],value=['FC']):

        assert len(depth_layer)==len(value)

        self.wc_type = wc_type
        self.Method = Method
        self.depth_layer = depth_layer
        self.value = value


# Cell
class InitCondClass:
    '''
    The InitCond Class contains all Paramaters and variables used in the simulation

    updated each timestep with the name NewCond


    '''

    def __init__(self):
        self.ParamStruct = []

        # counters
        self.AgeDays = 0
        self.AgeDays_NS = 0
        self.AerDays = 0
        self.IrrCum = 0
        self.DelayedGDDs = 0
        self.DelayedCDs = 0
        self.PctLagPhase = 0
        self.tEarlySen = 0
        self.GDDcum = 0
        self.DaySubmerged = 0
        self.IrrNetCum = 0
        self.DAP = 0
        self.Epot = 0
        self.Tpot = 0

        # States
        self.PreAdj = False
        self.CropMature = False
        self.CropDead = False
        self.Germination = False
        self.PrematSenes = False
        self.HarvestFlag = False

        # HI
        self.Stage = 1
        self.Fpre = 1
        self.Fpost = 1
        self.fpost_dwn = 1
        self.fpost_upp = 1

        self.HIcor_Asum = 0
        self.HIcor_Bsum = 0
        self.Fpol = 0
        self.sCor1 = 0
        self.sCor2 = 0
        self.HIref = 0.

        # GS
        self.GrowthStage = 0

        #Transpiration
        self.TrRatio = 1

        # crop growth
        self.rCor = 1

        self.CC = 0
        self.CCadj = 0
        self.CC_NS = 0
        self.CCadj_NS = 0
        self.B = 0
        self.B_NS = 0
        self.HI = 0
        self.HIadj = 0
        self.CCxAct = 0
        self.CCxAct_NS = 0
        self.CCxW = 0
        self.CCxW_NS = 0
        self.CCxEarlySen = 0
        self.CCprev = 0
        self.ProtectedSeed = 0
        self.Y = 0


# Cell
class WevapClass:
    """
    stores soil water contents in the evaporation layer

    **Attributes:**\n


    `Sat` : `float` :  Water storage in evaporation layer at saturation (mm)

    `Fc` : `float` :  Water storage in evaporation layer at Field Capacity (mm)

    `Wp` : `float`:  Water storage in evaporation layer at Wilting Point (mm)

    `Dry` : `float` : Water storage in evaporation layer at air dry (mm)

    `Act` : `float` : Actual Water storage in evaporation layer (mm)

    """

    def __init__(self):
        self.Sat = 0.
        self.Fc = 0.
        self.Wp = 0
        self.Dry = 0
        self.Act = 0


# Cell
class SoilCompClass:
    """
    Stores informaiton about an individual soil compartment

    These are extracted from the pandas dataframe at initialization to speed up the model

    **Attributes:**\n


    `Comp` : `float` : compartment number

    `dz` : `float` : thickness (m)

    `Layer` : `float` : layer of compartment

    `dzsum` : `float` : depth of compartment

    `th_fc` : `float` : water content at field capacity

    `th_s` : `float` : water content at saturation

    `th_wp` : `float` : water content at wilting point

    `Ksat` : `float` : saturated conductivity

    `Penetrability` : `float` : penetrability

    `th_dry` : `float` : water content at air dry

    `tau` : `float` : drainage characteristic

    `zBot` : `float` : depth at bottom of compartment

    `zTop` : `float` : depth at top of compartment

    `zMid` : `float` : depth at mid point of compartment

    `th_fc_Adj` : `float` : adjusted water content at field capacity

    """


    def __init__(self):
        self.Comp = 0
        self.dz = 0
        self.Layer = 0
        self.dzsum = 0
        self.th_fc = 0
        self.Layer_dz = 0
        self.th_s = 0
        self.th_wp = 0
        self.Ksat = 0
        self.Penetrability = 0
        self.th_dry = 0
        self.tau = 0
        self.zBot = 0
        self.zTop = 0
        self.zMid = 0
        self.th_fc_Adj = 0



# Cell

class SoilLayerClass:
    """

    Stores information about each soil layer


    **Attributes:**\n


    `dz` : `float` : thickness of layer

    `th_fc` : `float` : water content at field capacity

    `th_s` : `float` : water content at saturation

    `th_wp` : `float` : water content at wilting point

    `Ksat` : `float` : saturated conductivity

    `Penetrability` : `float` : penetrability

    `th_dry` : `float` : water content at air dry

    `tau` : `float` : drainage characteristic

    `aCR` : `float` : capillary rise param a

    `bCR` : `float` : capillary rise param b




    """

    def __init__(self):
        self.dz = 0
        self.th_fc = 0
        self.th_s = 0
        self.th_wp = 0
        self.Ksat = 0
        self.Penetrability = 0
        self.th_dry = 0
        self.tau = 0
        self.aCR = 0
        self.bCR = 0




# Cell

class SoilProfileClass:
    """

    **Attributes:**\n


    `Comp` : `list` :

    `Layer` : `list` :

    `dz` : `list` :

    `dzsum` : `list` :

    `zBot` : `list` :

    `zTop` : `list` :

    `zMid` : `list` :




    """
    def __init__(self):
        self.Comp = []
        self.Layer = []
        self.dz = []
        self.dzsum = []
        self.zBot = []
        self.zTop = []
        self.zMid = []



# Cell

class KswClass:
    """

    **Attributes:**\n



    `Exp` : `float` : .

    `Sto` : `float` : .

    `Sen` : `float` : .

    `Pol` : `float` : .

    `StoLin` : `float` : .



"""
    def __init__(self):
        self.Exp = 1.
        self.Sto = 1.
        self.Sen = 1.
        self.Pol = 1.
        self.StoLin = 1.



# Cell
class KsaClass:
    """
    **Attributes:**\n

    `Aer` : `float` : .


    """
    def __init__(self):
        self.Aer = 1.

# Cell

class TAWClass:
    """
    **Attributes:**\n



    `Rz` : `float` : .

    `Zt` : `float` : .




    """
    def __init__(self):
        self.Rz = 0.
        self.Zt = 0.




# Cell

class DrClass:

    """

    **Attributes:**\n



    `Rz` : `float` : .

    `Zt` : `float` : .


    """

    def __init__(self):
        self.Rz = 0.
        self.Zt = 0.




# Cell

class thRZClass:
    """

    **Attributes:**\n



    `Act` : `float` : .

    `S` : `float` : .

    `FC` : `float` : .

    `WP` : `float` : .

    `Dry` : `float` : .

    `Aer` : `float` : .



    """
    def __init__(self):
        self.Act = 0.
        self.S = 0.
        self.FC = 0.
        self.WP = 0.
        self.Dry = 0.
        self.Aer = 0.


# Cell

class KswClass:

    """
    **Attributes:**\n


    `Exp` : `float` : .

    `Sto` : `float` : .

    `Sen` : `float` : .

    `Pol` : `float` : .

    `StoLin` : `float` : .



    """


    def __init__(self):
        self.Exp = 1.
        self.Sto = 1.
        self.Sen = 1.
        self.Pol = 1.
        self.StoLin = 1.


# Cell

class KstClass:

    """

    **Attributes:**\n


    `PolH` : `float` : .

    `PolC` : `float` : .




    """


    def __init__(self):
        self.PolH = 1.
        self.PolC = 1.




# Cell

class CO2Class:

    """

    **Attributes:**\n


    `RefConc` : `float` : .

    `CurrentConc` : `float` : .


    """

    def __init__(self):
        self.RefConc = 0.
        self.CurrentConc = 0.