Skip to content
Snippets Groups Projects
params_samir_class.py 9.43 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
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)