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

__all__ = ['ClockStructClass', 'OutputClass', 'ParamStructClass', 'SoilClass', 'CropClass', 'IrrMngtClass',
           'IrrMngtStruct', 'spec', 'FieldMngtClass', 'FieldMngtStruct', 'spec', 'GwClass', 'InitWCClass', 'CropStruct',
           'spec', 'InitCondClass', 'spec', 'WevapClass', 'spec', 'SoilProfileClass', 'spec', 'TAWClass', 'spec',
           'DrClass', 'spec', 'thRZClass', 'spec', 'KswClass', 'spec', 'KstClass', 'spec', 'CO2Class', 'spec']

# Cell
import numpy as np
import pandas as pd
from numba.experimental import jitclass
from numba import float64, int64, boolean

# 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 (evaluation needed)

    `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 (evaluaiton needed)
        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 # Date at start of timestep
        self.EvapTimeSteps = 20 # Number of time-steps (per day) for soil evaporation calculation
        self.SimOffSeason = 'N' # 'Y' if you want to simulate the off season, 'N' otherwise
        self.PlantingDates = [] # list of crop planting dates during simulation
        self.HarvestDates = [] # list of crop planting dates during simulation
        self.nSeasons = 0 # total number of seasons (plant and harvest)
        self.SeasonCounter = -1 # running counter of seasons

# 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. In general these will not change over the course of the simulation


    **Attributes**:\n

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

    `FallowFieldMngt` : `FieldMngtClass` : Object 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

    `CO2data` : `pd.Series` : CO2 data indexed by year

    `CO2` : `CO2Class` : object containing reference and current co2 concentration

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

    `zGW` : `np.array` : WaterTable depth (mm) for each day of simulation

    `zGW_dates` : `np.array` : Corresponding dates to the zGW values

    `WTMethod` : `str` : 'Constant' or 'Variable'

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

    `python_crop_list` : `list` : List of Crop Objects, one for each season

    `python_fallow_crop` : `CropClass` : Crop object for off season

    `Seasonal_Crop_List` : `list` : List of CropStructs, one for each season (jit class objects)

    `crop_name_list` : `list` : List of crop names, one for each season

    `Fallow_Crop` : `CropStruct` : CropStruct object (jit class) for off season

    `Fallow_Crop_Name` : `str` : name of fallow crop

        '''

    def __init__(self):

        # soil
        self.Soil = 0

        # field management
        self.FallowFieldMngt = 0

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

        # calculated Co2 variables
        self.CO2data = []
        self.CO2 = 0

        #water table
        self.WaterTable = 0
        self.zGW = []
        self.zGW_dates = []
        self.WTMethod = ""

        #crops
        self.CropList = []
        self.python_crop_list = []
        self.python_fallow_crop = 0
        self.Seasonal_Crop_List =[]
        self.crop_name_list =[]
        self.Fallow_Crop =0
        self.Fallow_Crop_Name =""


# 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

    `Profile` : `SoilProfileClass` : jit class object holdsing soil profile information

    `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]*12,
                 AdjREW= 1,REW= 9.0,CalcCN=0,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,):


        self.Name=soilType


        self.zSoil= sum(dz) # Total thickness of soil profile (m)
        self.nComp= len(dz) # Total number of soil compartments
        self.nLayer= 0 # 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 based on Ksat
        self.CN= CN # Curve number  (0 = No, 1 = Yes)
        self.zRes= zRes # Depth of restrictive soil layer (set to negative value if not present)


        # 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 = max(zTop,dz[0]) # Thickness of soil surface layer for water stress comparisons (m)

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


        elif soilType == 'Clay':
            self.CN = 77
            self.CalcCN = 0
            self.REW = 14
            self.create_df(dz)
            self.add_layer(sum(dz), 0.39, 0.54, 0.55, 35, 100)

        elif soilType == 'ClayLoam':
            self.CN = 72
            self.CalcCN = 0
            self.REW = 11
            self.create_df(dz)
            self.add_layer(sum(dz), 0.23, 0.39, 0.5, 125, 100)

        elif soilType == 'Loam':
            self.CN = 61
            self.CalcCN = 0
            self.REW = 9
            self.create_df(dz)
            self.add_layer(sum(dz), 0.15, 0.31, 0.46, 500, 100)

        elif soilType == 'LoamySand':
            self.CN = 46
            self.CalcCN = 0
            self.REW = 5
            self.create_df(dz)
            self.add_layer(sum(dz), 0.08, 0.16, 0.38, 2200, 100)

        elif soilType == 'Sand':
            self.CN = 46
            self.CalcCN = 0
            self.REW = 4
            self.create_df(dz)
            self.add_layer(sum(dz), 0.06, 0.13, 0.36, 3000, 100)

        elif soilType == 'SandyClay':
            self.CN = 77
            self.CalcCN = 0
            self.REW = 10
            self.create_df(dz)
            self.add_layer(sum(dz), 0.27, 0.39, 0.5, 35, 100)

        elif soilType == 'SandyClayLoam':
            self.CN = 72
            self.CalcCN = 0
            self.REW = 9
            self.create_df(dz)
            self.add_layer(sum(dz), 0.20, 0.32, 0.47, 225, 100)

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

        elif soilType == 'Silt':
            self.CN = 61
            self.CalcCN = 0
            self.REW = 11
            self.create_df(dz)
            self.add_layer(sum(dz), 0.09, 0.33, 0.43, 500, 100)

        elif soilType == 'SiltClayLoam':
            self.CN = 72
            self.CalcCN = 0
            self.REW = 13
            self.create_df(dz)
            self.add_layer(sum(dz), 0.23, 0.44, 0.52, 150, 100)

        elif soilType == 'SiltLoam':
            self.CN = 61
            self.CalcCN = 0
            self.REW = 11
            self.create_df(dz)
            self.add_layer(sum(dz), 0.13, 0.33, 0.46, 575, 100)

        elif soilType == 'SiltClay':
            self.CN = 72
            self.CalcCN = 0
            self.REW = 14
            self.create_df(dz)
            self.add_layer(sum(dz), 0.32, 0.50, 0.54, 100, 100)

        elif soilType == 'Paddy':
            self.CN = 77
            self.CalcCN = 0
            self.REW = 10
            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.CalcCN = 0
            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)


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

    def __repr__(self):
        for key in self.__dict__:
            if key != 'profile':
                print(f"{key}: {getattr(self,key)}")

        return ' '

    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).round(2)
        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.profile.dz = self.profile.dz.round(2)

        self.profile.dzsum = self.profile.dz.cumsum().round(2)

        self.zSoil = round(self.profile.dz.sum(),2)

        self.nComp = len(self.profile)

        self.profile.Layer = self.profile.Layer.astype(int)

    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

    `c_name`: `str`: crop name ('custom' or one of built in defaults e.g. 'Maize')

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

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

    `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)



    `IrrMngt`: `dict` :  dictionary containting irrigation management information

    `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

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

    '''



    def __init__(self,c_name,PlantingDate,HarvestDate=None,**kwargs):

        self.Name = ''

        # 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= PlantingDate # Planting Date (mm/dd)
            self.HarvestDate= HarvestDate # 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= PlantingDate # Planting Date (mm/dd)
            self.HarvestDate= HarvestDate # Latest Harvest Date (mm/dd)
            #self.PlantingDate= '10/15'; self.HarvestDate= '05/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= PlantingDate # Planting Date (mm/dd)
            self.HarvestDate= HarvestDate # Latest Harvest Date (mm/dd)
            #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= PlantingDate # Planting Date (mm/dd)
            self.HarvestDate= HarvestDate # Latest Harvest Date (mm/dd)
            #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)

        self.calculate_additional_params()




    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 = np.array([self.p_up1,self.p_up2,self.p_up3,self.p_up4])

        self.p_lo = np.array([self.p_lo1,self.p_lo2,self.p_lo3,self.p_lo4])

        self.fshape_w = np.array([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 IrrMngtClass:

    """
    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.IrrMethod=IrrMethod

        self.WetSurf = 100.
        self.AppEff = 100.
        self.MaxIrr = 25.
        self.MaxIrrSeason = 10_000.
        self.SMT=np.zeros(4)
        self.IrrInterval = 0
        self.Schedule=[]
        self.NetIrrSMT = 80.
        self.depth = 0.

        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','MaxIrrSeason','SMT','IrrInterval','NetIrrSMT','Schedule'}

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


# Cell
spec = [
    ('IrrMethod', int64),
    ('WetSurf', float64),
    ('AppEff', float64),
    ('MaxIrr', float64),
    ('MaxIrrSeason', float64),
    ('SMT', float64[:]),
    ('IrrInterval', int64),
    ('Schedule', float64[:]),
    ('NetIrrSMT', float64),
    ('depth', float64),
]
@jitclass(spec)
class IrrMngtStruct:

    """


    """

    def __init__(self,sim_len):
        self.IrrMethod=0

        self.WetSurf = 100.
        self.AppEff = 100.
        self.MaxIrr = 25.
        self.MaxIrrSeason = 10_000
        self.SMT=np.zeros(4)
        self.IrrInterval = 0
        self.Schedule=np.zeros(sim_len)
        self.NetIrrSMT = 80.
        self.depth = 0.

# Cell
class FieldMngtClass:
    '''
    Field Management Class

    **Attributes:**\n


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

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

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

    `SRinhb` : `bool` : 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=False,Bunds=False,CNadj=False,SRinhb=False,
                 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
spec = [
    ('Mulches', boolean),
    ('Bunds', boolean),
    ('CNadj', boolean),
    ('SRinhb', boolean),
    ('MulchPct', float64),
    ('fMulch', float64),
    ('zBund', float64),
    ('BundWater', float64),
    ('CNadjPct', float64),
]
@jitclass(spec)
class FieldMngtStruct:

    """


    """

    def __init__(self):
        self.Mulches=False
        self.Bunds=False
        self.CNadj=False
        self.SRinhb=False

        self.MulchPct = 0.
        self.fMulch = 0.
        self.zBund = 0.
        self.BundWater = 0.
        self.CNadjPct = 0.



# 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
spec=[
    ('fshape_b',float64),
    ('PctZmin',float64),
    ('fshape_ex',float64),
    ('ETadj',float64),
    ('Aer',float64),
    ('LagAer',int64),
    ('beta',float64),
    ('a_Tr',float64),
    ('GermThr',float64),
    ('CCmin',float64),
    ('MaxFlowPct',float64),
    ('HIini',float64),
    ('bsted',float64),
    ('bface',float64),
    ('CropType',int64),
    ('PlantMethod',int64),
    ('CalendarType',int64),
    ('SwitchGDD',int64),
    ('EmergenceCD', int64),
    ('Canopy10PctCD', int64),
    ('MaxRootingCD', int64),
    ('SenescenceCD', int64),
    ('MaturityCD', int64),
    ('MaxCanopyCD', int64),
    ('CanopyDevEndCD', int64),
    ('HIstartCD', int64),
    ('HIendCD', int64),
    ('YldFormCD', int64),
    ('Emergence',float64),
    ('MaxRooting',float64),
    ('Senescence',float64),
    ('Maturity',float64),
    ('HIstart',float64),
    ('Flowering',float64),
    ('YldForm',float64),
    ('HIend',float64),
    ('CanopyDevEnd',float64),
    ('MaxCanopy',float64),
    ('GDDmethod',int64),
    ('Tbase',float64),
    ('Tupp',float64),
    ('PolHeatStress',int64),
    ('Tmax_up',float64),
    ('Tmax_lo',float64),
    ('PolColdStress',int64),
    ('Tmin_up',float64),
    ('Tmin_lo',float64),
    ('TrColdStress',int64),
    ('GDD_up',float64),
    ('GDD_lo',float64),
    ('Zmin',float64),
    ('Zmax',float64),
    ('fshape_r',float64),
    ('SxTopQ',float64),
    ('SxBotQ',float64),
    ('SxTop',float64),
    ('SxBot',float64),
    ('SeedSize',float64),
    ('PlantPop',int64),
    ('CCx',float64),
    ('CDC',float64),
    ('CGC',float64),
    ('Kcb',float64),
    ('fage',float64),
    ('WP',float64),
    ('WPy',float64),
    ('fsink',float64),
    ('HI0',float64),
    ('dHI_pre',float64),
    ('a_HI',float64),
    ('b_HI',float64),
    ('dHI0',float64),
    ('Determinant',int64),
    ('exc',float64),
    ('p_up',float64[:]),
    ('p_lo',float64[:]),
    ('fshape_w',float64[:]),

    ('Canopy10Pct',int64),
    ('CC0',float64),

    ('HIGC',float64),
    ('tLinSwitch',int64),
    ('dHILinear',float64),

    ('fCO2',float64),

    ('FloweringCD',int64),

    ('FloweringEnd',float64),




]
@jitclass(spec)
class CropStruct(object):
    '''
    The Crop Class contains Paramaters and variables of the crop used in the simulation


    **Attributes**:\n



    '''



    def __init__(self,):

        # 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


        # 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.EmergenceCD = 0
        self.Canopy10PctCD = 0
        self.MaxRootingCD = 0
        self.SenescenceCD = 0
        self.MaturityCD = 0
        self.MaxCanopyCD = 0
        self.CanopyDevEndCD = 0
        self.HIstartCD = 0
        self.HIendCD = 0
        self.YldFormCD = 0

        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.HIend = 0
        self.MaxCanopy = 0
        self.CanopyDevEnd = 0
        self.Canopy10Pct = 0

        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.SxTop = 0.
        self.SxBot = 0.

        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_up = np.zeros(4) # Upper soil water depletion threshold for water stress effects on affect canopy expansion
        self.p_lo = np.zeros(4) # Lower soil water depletion threshold for water stress effects on canopy expansion
        self.fshape_w = np.ones(4) # Shape factor describing water stress effects on canopy expansion


        self.CC0 = 0.

        self.HIGC = 0.
        self.tLinSwitch = 0
        self.dHILinear = 0.

        self.fCO2 = 0.

        self.FloweringCD = 0
        self.FloweringEnd=0.

# Cell
spec=[
    ('AgeDays', float64),
    ('AgeDays_NS', float64),
    ('AerDays', float64),
    ('AerDaysComp', float64[:]),
    ('IrrCum', float64),
    ('DelayedGDDs', float64),
    ('DelayedCDs', float64),
    ('PctLagPhase', float64),
    ('tEarlySen', float64),
    ('GDDcum', float64),
    ('DaySubmerged', float64),
    ('IrrNetCum', float64),
    ('DAP', int64),
    ('Epot', float64),
    ('Tpot', float64),
    ('PreAdj', boolean),
    ('CropMature', boolean),
    ('CropDead', boolean),
    ('Germination', boolean),
    ('PrematSenes', boolean),
    ('HarvestFlag', boolean),
    ('GrowingSeason', boolean),
    ('YieldForm', boolean),
    ('Stage2', boolean),
    ('WTinSoil', boolean),
    ('Stage', float64),
    ('Fpre', float64),
    ('Fpost', float64),
    ('fpost_dwn', float64),
    ('fpost_upp', float64),
    ('HIcor_Asum', float64),
    ('HIcor_Bsum', float64),
    ('Fpol', float64),
    ('sCor1', float64),
    ('sCor2', float64),
    ('HIref', float64),
    ('GrowthStage', float64),
    ('TrRatio', float64),
    ('rCor', float64),
    ('CC', float64),
    ('CCadj', float64),
    ('CC_NS', float64),
    ('CCadj_NS', float64),
    ('B', float64),
    ('B_NS', float64),
    ('HI', float64),
    ('HIadj', float64),
    ('CCxAct', float64),
    ('CCxAct_NS', float64),
    ('CCxW', float64),
    ('CCxW_NS', float64),
    ('CCxEarlySen', float64),
    ('CCprev', float64),
    ('ProtectedSeed', int64),
    ('Y', float64),
    ('Zroot', float64),
    ('CC0adj', float64),
    ('SurfaceStorage', float64),
    ('zGW', float64),
    ('th_fc_Adj', float64[:]),
    ('th', float64[:]),
    ('thini', float64[:]),
    ('TimeStepCounter', int64),
    ('P', float64),
    ('Tmax', float64),
    ('Tmin', float64),
    ('Et0', float64),
    ('GDD', float64),
    ('Wsurf', float64),
    ('EvapZ', float64),
    ('Wstage2', float64),
    ('Depletion', float64),
    ('TAW', float64),

]
@jitclass(spec)
class InitCondClass:
    '''
    The InitCond Class contains all Paramaters and variables used in the simulation

    updated each timestep with the name NewCond


    '''

    def __init__(self,num_comp):
        # counters
        self.AgeDays = 0
        self.AgeDays_NS = 0
        self.AerDays = 0
        self.AerDaysComp = np.zeros(num_comp)
        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
        self.GrowingSeason = False
        self.YieldForm = False
        self.Stage2 = False

        self.WTinSoil = 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

        self.Zroot = 0
        self.CC0adj = 0
        self.SurfaceStorage = 0
        self.zGW = -999

        self.th_fc_Adj = np.zeros(num_comp)
        self.th = np.zeros(num_comp)
        self.thini = np.zeros(num_comp)

        self.TimeStepCounter=0

        self.P=0
        self.Tmax=0
        self.Tmin=0
        self.Et0=0
        self.GDD=0


        self.Wsurf=0
        self.EvapZ=0
        self.Wstage2=0

        self.Depletion=0
        self.TAW=0






# Cell
spec = [
    ('Act', float64),
    ('Sat', float64),
    ('Fc', float64),
    ('Wp', float64),
    ('Dry', float64),
]
@jitclass(spec)
class WevapClass(object):
    """
    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
spec = [
    ('Comp', int64[:]),
    ('dz', float64[:]),
    ('Layer', int64[:]),
    ('dzsum', float64[:]),
    ('th_fc', float64[:]),
    ('Layer_dz', float64[:]),
    ('th_s', float64[:]),
    ('th_wp', float64[:]),
    ('Ksat', float64[:]),
    ('Penetrability', float64[:]),
    ('th_dry', float64[:]),
    ('tau', float64[:]),
    ('zBot', float64[:]),
    ('zTop', float64[:]),
    ('zMid', float64[:]),
    ('th_fc_Adj', float64[:]),
    ('aCR', float64[:]),
    ('bCR', float64[:]),
]
@jitclass(spec)
class SoilProfileClass:
    """

    **Attributes:**\n


    `Comp` : `list` :

    `Layer` : `list` :

    `dz` : `list` :

    `dzsum` : `list` :

    `zBot` : `list` :

    `zTop` : `list` :

    `zMid` : `list` :




    """
    def __init__(self,length):

        self.Comp = np.zeros(length,dtype=int64)
        self.dz = np.zeros(length,dtype=float64)
        self.Layer = np.zeros(length,dtype=int64)
        self.dzsum = np.zeros(length,dtype=float64)
        self.th_fc = np.zeros(length,dtype=float64)
        self.Layer_dz = np.zeros(length,dtype=float64)
        self.th_s = np.zeros(length,dtype=float64)
        self.th_wp = np.zeros(length,dtype=float64)
        self.Ksat = np.zeros(length,dtype=float64)
        self.Penetrability = np.zeros(length,dtype=float64)
        self.th_dry = np.zeros(length,dtype=float64)
        self.tau = np.zeros(length,dtype=float64)
        self.zBot = np.zeros(length,dtype=float64)
        self.zTop = np.zeros(length,dtype=float64)
        self.zMid = np.zeros(length,dtype=float64)
        self.th_fc_Adj = np.zeros(length,dtype=float64)
        self.aCR = np.zeros(length,dtype=float64)
        self.bCR = np.zeros(length,dtype=float64)

# Cell
spec = [
    ('Rz', float64),
    ('Zt', float64),
]
@jitclass(spec)
class TAWClass:
    """
    **Attributes:**\n



    `Rz` : `float` : .

    `Zt` : `float` : .




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


# Cell
spec = [
    ('Rz', float64),
    ('Zt', float64),
]
@jitclass(spec)
class DrClass:

    """

    **Attributes:**\n

    `Rz` : `float` : .

    `Zt` : `float` : .


    """

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


# Cell
spec = [
    ('Act', float64),
    ('S', float64),
    ('FC', float64),
    ('WP', float64),
    ('Dry', float64),
    ('Aer', float64),
]
@jitclass(spec)
class thRZClass(object):
    """
    root zone water content

    **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
spec = [
    ('Exp', float64),
    ('Sto', float64),
    ('Sen', float64),
    ('Pol', float64),
    ('StoLin', float64),
]
@jitclass(spec)
class KswClass(object):

    """
    water stress coefficients

    **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
spec = [
    ('PolH', float64),
    ('PolC', float64),
]
@jitclass(spec)
class KstClass(object):

    """
    temperature stress coefficients

    **Attributes:**\n


    `PolH` : `float` : heat stress

    `PolC` : `float` : cold stress


    """


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


# Cell
spec = [
    ('RefConc', float64),
    ('CurrentConc', float64),
]
@jitclass(spec)
class CO2Class(object):

    """

    **Attributes:**\n


    `RefConc` : `float` : reference CO2 concentration

    `CurrentConc` : `float` : current CO2 concentration

    """

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