Skip to content
Snippets Groups Projects
params_samir_class.py 9.43 KiB
Newer Older
  • Learn to ignore specific revisions
  • #! /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
    
    import numpy as np  # vectorised math
    
        Load all parameters for multiples classes in one object. 
        
    
        1. .self.table: ``pd.DataFrame``
    
            ``pandas DataFrame`` with all paramters (rows) for all classes (columns)
     
    
            It also contains :
            
            - a ``scale_factor`` column (first column) that allows to convert all parameters to integer values for reduced memory usage
            
            - a ``Default`` column (second column) that contains default values to fill in missing values
            
    
            Example of the parameter self.table:
    
                ClassName       scale_factor   Default   no_sim  straw_cereal  oilseed        soy  sunflower      corn
                ClassNumber                1     0.000     1.000        2.000     3.000     4.000      5.000     6.000
                NDVIsol                    1     0.150     0.150        0.150     0.150     0.150      0.150     0.150
                NDVImax                    1     0.850     0.850        0.850     0.850     0.850      0.850     0.850
                FCmax                   1000     1.000     0.000        1.000     1.000     1.000      1.000     1.000
                Fslope                  1000     1.400     0.000        1.180     1.220     1.400      1.400     1.400
                Foffset                 1000    -0.075     0.000       -0.165    -0.170    -0.075     -0.075    -0.075
    
                Kcbmax                  1000     1.100     0.000        1.100     0.700     1.100      1.100     1.100
    
                Kslope                  1000     1.200     0.000        1.470     1.350     1.200      1.200     1.200
                Koffset                 1000    -0.240     0.000       -0.170    -0.160    -0.240     -0.240    -0.240
    
                Kcmax,                   1000    1.150     1.150        1.150     1.150     1.150      1.150     1.150 
    
                Zsoil                      1  2000.000  2000.000     2000.000  2000.000  2000.000   2000.000  2000.000
                Ze                         1   300.000   300.000      125.000   125.000   300.000    300.000   300.000
                Init_RU                 1000     0.870     0.870        0.500     0.500     0.870      0.870     0.870
                DiffE                      1     1.000     0.000        0.000     0.000     1.000      1.000     1.000
                DiffR                      1     5.000     0.000        0.000     0.000     5.000      5.000     5.000
                REW                        1     0.000     0.000        9.000     9.000     0.000      0.000     0.000
                minZr                      1   150.000     0.000      125.000   125.000   150.000    150.000   150.000
                maxZr                      1   600.000     0.000     1250.000  1450.000   600.000    600.000   600.000
                p                       1000     0.550     0.000        0.550     0.650     0.550      0.550     0.550
                FW                      1000     1.000     0.000        1.000     1.000     1.000      1.000     1.000
                Irrig_auto                 1     1.000     0.000        1.000     1.000     1.000      1.000     1.000
                Irrig_man                  1     0.000     0.000        0.000     0.000     0.000      0.000     0.000
    
                Lame_max                   1     50.00     0.000        50.00     50.00     50.00      50.00     50.00
                Lame_min                   1     0.000     0.000        0.000     0.000     0.000      0.000     0.000
                Kcb_min_start_irrig        1     0.100     0.000        0.100     0.100     0.100      0.100     0.100
                frac_Kcb_stop_irrig        1     0.500     0.000        0.500     0.500     0.500      0.500     0.500
                frac_TAW                   1     0.650     0.000        0.650     0.650     0.650      0.650     0.650
    
        1. test_samir_parameter(min_max_param_file: ``str``):
        
            Test the values of the SAMIR parameters to check if they
            are in the correct range (defined the the csv param_range
            file) and automatically calculate the Fcover and Kcb slope
            and offset (from the NDVIsol and NDVImax values) if their
            value is equal to -9999.
    
            Arguments
            =========
    
            1. min_max_param_file: ``str``
                samir parameter maximum and minimum values file
    
            Returns
            =======
    
            1. table: ``pd.DataFrame``
                updated samir parameter dataframe
        
    
        
        def __init__(self, paramFile: str) -> None:
            """    
    
            Create pandas self.table from the csv parameter file.
    
            Arguments
            =========
            
            1. paramFile: ``str``
    
                path to csv parameter file
    
            
            # Index file for correct conversion to dictionnary
            csvFile.index = csvFile.iloc[:,0]
    
            csvFile.drop(columns = ['ClassName'], inplace = True)
    
            # Replace missing parameters by their default values
            csvFile[csvFile.columns[1:]] = csvFile[csvFile.columns[1:]].astype('float32')
            for col in csvFile.columns[2:]:
                mask = csvFile[col].isna()
                csvFile.loc[mask, col] = csvFile.loc[mask, 'Default']
    
            # Store dataframe in attribute table
            self.table = csvFile
    
            
        
        # Define method to test parameters
        def test_samir_parameter(self, min_max_param_file: str):
            """
            Test the values of the SAMIR parameters to check if they
            are in the correct range (defined the the csv param_range
            file) and automatically calculate the Fcover and Kcb slope
            and offset (from the NDVIsol and NDVImax values) if their
            value is equal to -9999.
    
            Arguments
            =========
    
            1. min_max_param_file: ``str``
                samir parameter maximum and minimum values file
    
            Returns
            =======
    
            1. table: ``pd.DataFrame``
                updated samir parameter dataframe
            """
            
            # Automatically calculate the FC and Kcb slopes and offsets if necessary
            for class_name in self.table.columns[1:]:
                if self.table.at['Fslope', class_name] == -9999:
                    
                    # Equation: Fslope = FCmax / (NDVImax - NDVIsol)
    
                    self.table.at['Fslope', class_name] = np.round(self.table.at['FCmax', class_name] / (self.table.at['NDVImax', class_name] - self.table.at['NDVIsol', class_name]), decimals = 3)
    
                    
                if self.table.at['Foffset', class_name] == -9999:
                    
                    # Equation: Foffset = - NDVIsol * Fslope
    
                    self.table.at['Foffset', class_name] = - np.round(self.table.at['NDVIsol', class_name] * self.table.at['Fslope', class_name], decimals = 3)
    
                
                if self.table.at['Kslope', class_name] == -9999:
                    
                    # Equation: Kslope = Kcbmax / (NDVImax - NDVIsol)
    
                    self.table.at['Kslope', class_name] = np.round(self.table.at['Kcbmax', class_name] / (self.table.at['NDVImax', class_name] - self.table.at['NDVIsol', class_name]), decimals = 3)
    
                    
                if self.table.at['Koffset', class_name] == -9999:
                    
                    # Equation: Koffset = - NDVIsol * Kslope
    
                    self.table.at['Koffset', class_name] = - np.round(self.table.at['NDVIsol', class_name] * self.table.at['Kslope', class_name], decimals = 3)
    
                
                if self.table.at['maxZr', class_name] > self.table.at['Zsoil', class_name]:
                    
                    print(f'\nmaxZr is higher than Zsoil for class {class_name}')
                        
                    # Set boolean to true to exit script
                    out_of_range_param = True
                
                if self.table.at['minZr', class_name] > self.table.at['maxZr', class_name]:
                    
                    print(f'\minZr is higher than maxZr for class {class_name}')
                        
                    # Set boolean to true to exit script
                    out_of_range_param = True
    
            
            # Test values of the parameters
            min_max_table = read_csv(min_max_param_file, index_col = 0)
    
            # Boolean set to true if a parameter is out of range
            out_of_range_param = False
    
            # Loop through parameter values for all the classes
            for parameter in min_max_table.index:
                for class_name in self.table.columns[1:]:
                    
                    # Test if parameter is out of range
                    if self.table.at[parameter, class_name] > min_max_table.at[parameter, 'max_value'] or self.table.at[parameter, class_name] < min_max_table.at[parameter, 'min_value']:
                        
                        # If parameter is out of range, print which parameter and for which class
                        print(f'\nParameter {parameter} is out of range for class {class_name}')
                        
                        # Set boolean to true to exit script
                        out_of_range_param = True
            
            # Return 0 if param is out of range
            if out_of_range_param:
                
                self.table = 0
            
            else:
                # Remove unecessary rows
                self.table.drop(['NDVIsol', 'NDVImax'], inplace = True)