Skip to content
Snippets Groups Projects
params_samir_class.py 12.5 KiB
Newer Older
#! /usr/bin/env python
#-*- coding: utf-8 -*-
"""
11-07-2023 adapted from modspa-parcel code
@author: jeremy auclair

Classes to load and store SAMIR parameters.
"""

from pandas import read_csv  # to read csv parameter files
from numpy import nan  # to fill nan values
import param  # type: ignore


class samir_parameters_LC:
    """
    This class allows to store all the SAMIR parameters for one land cover class
    """
    
    def __init__(self, csvLine, defaultClass, mode_init = 1):

        # List of parameters that will be optimised (and hence that are not read in the param csv file)
        self.optimList = []
        
        if defaultClass:
            #print(csvLine)
            for v in csvLine.values():
                if v == '':
                    raise ValueError("All fields must be filled for the default value line")
        
        self.name = csvLine['ClassName']
        self.number = int(csvLine['ClassNumber'])
        
        # Parameters for the NDVI - Fraction Cover relation
        if (csvLine['FminFC'] != "optim"):
            self.ndviFCminFC = param.Number(float(csvLine['FminFC']), bounds=(0., 1.)).default
        else:
            self.optimList.append("FminFC")
            
        if (csvLine['FmaxFC'] != "optim"):
            self.ndviFCmaxFC = param.Number(float(csvLine['FmaxFC']), bounds=(0., 1.)).default
        else:
            self.optimList.append("FmaxFC")

        if (csvLine['Fslope'] != "optim"):
            self.ndviFCslope = param.Number(float(csvLine['Fslope']), bounds=(0., 10)).default
        else:
            self.optimList.append("Fslope")
            
        if (csvLine['Foffset'] != "optim"):
            self.ndviFCoffset = param.Number(float(csvLine['Foffset']), bounds=(-1, 1)).default        
        else:
            self.optimList.append("Foffset")
            
        if (csvLine['Plateau'] != "optim"):
            self.ndviPlateau = param.Number(int(float(csvLine['Plateau'])), bounds=(0, 365)).default        
        else:
            self.optimList.append("Plateau")
            
        # Parameters for the NDVI -Kcb relation
        if (csvLine['KminKcb'] != "optim"):
            self.ndviKcbminKcb = param.Number(float(csvLine['KminKcb']), bounds=(0, 0.5)).default
        else:
            self.optimList.append("KminKcb")
            
        if (csvLine['KmaxKcb'] != "optim"):
            self.ndviKcbmaxKcb =  param.Number(float(csvLine['KmaxKcb']), bounds=(0.5, 2)).default
        else:
            self.optimList.append("KmaxKcb")
             
        if (csvLine['Kslope'] != "optim"):
            self.ndviKcbslope = param.Number(float(csvLine['Kslope'])).default
        else:
            self.optimList.append("Kslope")
 
        if (csvLine['Koffset'] != "optim"):
            self.ndviKcboffset = param.Number(float(csvLine['Koffset'])).default
        else:
            self.optimList.append("Koffset")
 
        # Soil parameters 
        if (csvLine['Zsoil'] != "optim"):
            self.Zsoil = param.Number(float(csvLine['Zsoil']), bounds=(100, 10000), doc = "Soil depth (in mm)").default
        else:
            self.optimList.append("Zsoil")
            
        if (csvLine['Ze'] != "optim"):          
            self.Ze = param.Number(float(csvLine['Ze']), bounds=(1, self.Zsoil),  doc = "Evaporative layer depth (in mm)").default
        else:
            self.optimList.append("Ze")
            
        if mode_init == 1 or mode_init == 3:
            if (csvLine['Init_RU'] != "optim"):          
                self.Init_RU = param.Number(float(csvLine['Init_RU']), doc = "Filling rate of the available water").default
            else:
                self.optimList.append("Init_RU")
        else :
            self.Dei = param.Number(float(csvLine['Init_Dei']), bounds=(0, None), doc = "Initial Depletion of the evaporative layer (irrigation + precipitation) (in mm)").default
            self.Dep = param.Number(float(csvLine['Init_Dep']), bounds=(0, None), doc = "Initial Depletion of the evaporative layer (precipitation only) (in mm)").default
            self.Dr = param.Number(float(csvLine['Init_Dr']), bounds=(0, None), doc = "Initial Depletion of the root layer (in mm)").default
            self.Dd = param.Number(float(csvLine['Init_Dd']), bounds=(0, None), doc = "Initial Depletion of the deep layer (in mm)").default
            
            
        if (csvLine['DiffE'] != "optim"):          
            self.DiffE = param.Number(float(csvLine['DiffE']), bounds=(0, 1000), doc = "Diffusion coefficient between evaporative and root layers (unitless)").default
        else:
            self.optimList.append("DiffE")
                
        if (csvLine['DiffR'] != "optim"):          
            self.DiffR = param.Number(float(csvLine['DiffR']), bounds=(0, 1000), doc = "Diffusion coefficient between root and deep layers (unitless)").default
        else:
            self.optimList.append("DiffR")

        if (csvLine['REW'] != "optim"):          
            self.REW = param.Number(float(csvLine['REW']), bounds=(-1000, 1000), doc = "Readily Evaporable Water (in mm)").default
        else:
            self.optimList.append("REW")
 
        if (csvLine['m'] != "optim"):          
            self.m = param.Number(float(csvLine['m']), bounds=(0, 1), doc = "").default ## si utilise, REW  minimum doit etre à 0 ?
        else:
            self.optimList.append("m")
 
        # Crop parameters 
        if (csvLine['minZr'] != "optim"):
            if (csvLine['Ze'] != "optim") & (csvLine['Zsoil'] != "optim"):
                self.minZr = param.Number(float(csvLine['minZr']), bounds=(self.Ze, self.Zsoil), doc = "Minimum root depth (mm)").default
            else:
                self.minZr = param.Number(float(csvLine['minZr']), bounds=(1, 10000), doc = "Minimum root depth (mm)").default     
        else:
            self.optimList.append("minZr")
            
        if (csvLine['maxZr'] != "optim"):
            if (csvLine['Zsoil'] != "optim"):
                self.maxZr = param.Number(float(csvLine['maxZr']), bounds=(0, self.Zsoil-1), doc = "Maximum root depth (mm)").default
            else:
                self.maxZr = param.Number(float(csvLine['maxZr']), bounds=(0, 10000-1), doc = "Maximum root depth (mm)").default                
        else:
            self.optimList.append("maxZr")
                
        if (csvLine['p'] != "optim"):          
            self.p = param.Number(float(csvLine['p']), bounds=(0, 1), doc = "Fraction of readily available water").default
        else:
            self.optimList.append("p")

        # Irrigation parameters
        self.irrigFW = param.Number(float(csvLine['FW']), bounds=(0, 100), doc = "% of soil wetted by irrigation").default
        
        self.Irrig_auto = param.Integer(int(float(csvLine['Irrig_auto'])), doc = "1 if the automatic irrigation mode is activated").default
        
        self.Irrig_man = param.Integer(int(float(csvLine['Irrig_man'])), doc = "1 if the manual irrigation mode is activated").default
        
        if (csvLine['Lame_max'] != "optim"):          
            self.Lame_max = param.Number(float(csvLine['Lame_max']), doc = "Maximum of irrigation height for each irrigation event (in mm)").default
        else:
            self.optimList.append("Lame_max")

        if (csvLine['minDays'] != "optim"):              
            self.irrigMinDays = param.Integer(int(float(csvLine['minDays'])), bounds=(0, None), doc = "Minimum number of days between two irrigation events").default
        else:
            self.optimList.append("minDays")
            
        if (csvLine['Kcbmin_start'] != "optim"):              
            self.Kcbmin_start = param.Number(float(csvLine['Kcbmin_start']), bounds=(0, 1), doc = "Minimum Kcb value above which irrigation may start").default
        else:
            self.optimList.append("Kcbmin_start")

        if (csvLine['Kcbmax_stop'] != "optim"):              
            self.Kcbmax_stop = param.Number(float(csvLine['Kcbmax_stop']), bounds=(0, 1), doc = "Fraction of peak Kcb value below which irrigation stops").default
        else:
            self.optimList.append("Kcbmax_stop")

        if (csvLine['Kcmax'] != "optim"):              
            self.Kcmax = param.Number(float(csvLine['Kcmax']), bounds=(0, None), doc = "pas d'info").default
        else:
            self.optimList.append("Kcmax")

        if (csvLine['Fc_stop'] != "optim"):              
            self.Fc_stop = param.Number(float(csvLine['Fc_stop']), bounds=(0, None), doc = "pas d'info").default
        else:
            self.optimList.append("Fc_stop")

        if (csvLine['Start_date_Irr'] != "optim"):              
            self.Start_date_Irr = param.Number(int(float(csvLine['Start_date_Irr'])), bounds=(0, None), doc = "pas d'info").default
        else:
            self.optimList.append("Start_date_Irr")

        if (csvLine["p_trigger"] != "optim"):              
            self.p_trigger = param.Number(float(csvLine['p_trigger']), bounds=(-1, 1), doc = "Fraction of water storage capacity below which irrigation is triggered").default
        else:
            self.optimList.append("Fc_stop")

    def setParam(self, paramName, value):

        # Soil parameters
        if paramName == "REW":
            self.REW = value
        elif paramName == "Init_RU":
            self.Init_RU = value     
        elif paramName == "minZr":
            self.minZr = value
        elif paramName == "maxZr":
            self.maxZr = value
        elif paramName == "Ze":
            self.Ze = value
        elif paramName == "Zsoil":
            self.Zsoil = value
        elif paramName == "DiffR":
            self.DiffR = value
        elif paramName == "DiffE":
            self.DiffE = value
            
        # Irrigation parameters
        elif paramName == "Lame_max":
            self.Lame_max = value
        elif paramName == "minDays":
            self.irrigMinDays = value

        # Vegetation parameters
        elif paramName == "FminFC":
            self.ndviFCminFC = value
        elif paramName == "FmaxFC":
            self.ndviFCmaxFC = value
        elif paramName == "Fc_stop":
            self.Fc_stop = value
        elif paramName == "Kcmax":
            self.Kcmax = value
        elif paramName == "Kcbmin_start" :
            self.Kcbmin_start = value
        elif paramName == "Kcbmax_stop" :
            self.Kcbmax_stop = value
        elif paramName == "Plateau" :
            self.ndviPlateau = value
        elif paramName == "Fslope" :
            self.ndviFCslope = value
        elif paramName == "Foffset" :
            self.ndviFCoffset = value
        elif paramName == "KmaxKcb" :
            self.ndviKcbmaxKcb = value
        elif paramName == "KminKcb" :
            self.ndviKcbminKcb = value   
        elif paramName == "Kslope" :
            self.ndviKcbslope = value
        elif paramName == "Koffset" :
            self.ndviKcboffset = value
        elif paramName == "m" :
            self.m = value
        elif paramName == "p" :
            self.p = value
        elif paramName == "p_trigger":
            self.p_trigger = value


class samir_parameters:
    """
    Load all parameters for multiples classes in one object.
    """

    def __init__(self, paramFile, mode_init = 1):

        self.classes = {}

        # Read csv file with Pandas
        csvFile = read_csv(paramFile, header = None)
        
        # Index file for correct conversion to dictionnary
        csvFile.index = csvFile.iloc[:,0]
        csvFile.replace(nan, '', inplace = True)
        defaultClass = True
        
        # Loop on columns
        for column in csvFile.columns[1:]:
            
            # Convert pandas column to dictionnary
            line = csvFile[column].to_dict()
            
            #TODO : @VR+@CO Intoduire ici une verification des valeurs
            #!! notamment si min=max alors error_rel=0
            #!! Ajouter la possibilité de configurer plusieurs land cover
            
            if defaultClass:
                defaultLine = line.copy()
            elif line['ClassName'] in ['error_rel','error_abs','min','max']:
                self.classes[line['ClassName']] ={}
                for k in line.keys():
                    if k != 'ClassName':
                        if line[k] == '':
                            line[k] = 0
                        self.classes[line['ClassName']][k] = float(line[k])
                continue
            else:
                for k in line.keys():
                    if line[k] == '':
                        line[k] = defaultLine[k]
            self.classes[line['ClassName']] = samir_parameters_LC(line, defaultClass, mode_init)