Source code for enrichedfem.gains.compare

import pandas as pd
import numpy as np
import dataframe_image as dfi

from enrichedfem.gains.gains import GainsEnhancedFEM

[docs] class CompareGainsMethods: """Compare gains of enhanced FEM methods. This class compares the gains achieved by enhanced FEM methods (additive and multiplicative corrections) over standard FEM and PINNs. It reads error data from CSV files, computes gains, creates dataframes for errors and gains, and saves statistics about the gains. Args: gains_enhanced_fem (GainsEnhancedFEM): An instance of the `GainsEnhancedFEM` class, containing the error data. """ def __init__(self,gains_enhanced_fem:GainsEnhancedFEM): self.gef = gains_enhanced_fem self.params_str = self.create_params_str() self.__row_names = [str(i) + " : " + self.params_str[i] for i in range(self.gef.n_params)]
[docs] def create_params_str(self): """Create string representations of parameter sets. This method generates a list of strings, where each string represents a set of parameters used in the problem. The parameters are rounded to two decimal places and separated by commas within each string. Returns: list: A list of strings, each representing a parameter set. """ dim_params = len(self.gef.params[0]) params_str = [] for i in range(self.gef.n_params): param_str = "" for j in range(dim_params): param_str += f"{self.gef.params[i][j].round(2)}" if j < dim_params-1: param_str += "," params_str.append(param_str) return params_str
def __read_errors(self, degree, tab_M=None): """Read error data for different methods. This method reads error data from CSV files for FEM, PINNs, additive correction ("Corr"), and multiplicative correction ("Mult") methods for a given degree and a list of M values. It raises FileNotFoundError if any of the required files are not found. Args: degree (int): The degree of the finite element solution. tab_M (list, optional): A list of M values to consider for the "Mult" methods. Defaults to None. Returns: tuple: A tuple containing a dictionary of error arrays for each method and a list of mesh sizes (h). Raises: FileNotFoundError: If any of the required error files are not found. """ tab_nb_vert = self.gef.tab_nb_vert # Read date for all methods tab_errors = {} try: csv_file = self.gef.results_dir+f'FEM_errors_case{self.gef.testcase}_v{self.gef.version}_degree{degree}.csv' print(csv_file) _,tab_h,tab_errors["FEM"] = self.gef.read_csv(csv_file) except: tab_errors["FEM"] = None raise FileNotFoundError(f'FEM P{degree} not found') assert tab_errors["FEM"] is not None, "FEM errors not found" try: csv_file = self.gef.results_dir+f'PINNs_errors_case{self.gef.testcase}_v{self.gef.version}_degree{degree}.csv' _,_,tab_errors["PINNs"] = self.gef.read_csv(csv_file) except: tab_errors["PINNs"] = None raise FileNotFoundError(f'PINNs P{degree} not found') assert tab_errors["PINNs"] is not None, "PINNs errors not found" try: csv_file = self.gef.results_dir+f'Corr_errors_case{self.gef.testcase}_v{self.gef.version}_degree{degree}.csv' _,_,tab_errors["Corr"] = self.gef.read_csv(csv_file) except: tab_errors["Corr"] = None raise FileNotFoundError(f'Corr P{degree} not found') dict_Mult = {} dict_Mult_weak = {} if tab_M is not None: for M in tab_M: try: csv_file = self.gef.results_dir+f'Mult_errors_case{self.gef.testcase}_v{self.gef.version}_degree{degree}_M{M}.csv' _,_,tab_err_Mult = self.gef.read_csv(csv_file) dict_Mult[M] = tab_err_Mult except: print(f'Mult strong P{degree} M{M} not found') try: csv_file = self.gef.results_dir+f'Mult_errors_case{self.gef.testcase}_v{self.gef.version}_degree{degree}_M{M}_weak.csv' _,_,tab_err_Mult = self.gef.read_csv(csv_file) dict_Mult_weak[M] = tab_err_Mult except: print(f'Mult weak P{degree} M{M} not found') assert len(dict_Mult) in [0,len(tab_M)], "Number of Mult methods is not correct" assert len(dict_Mult_weak) in [0,len(tab_M)], "Number of Mult weak methods is not correct" tab_errors["Mult"] = dict_Mult tab_errors["Mult_weak"] = dict_Mult_weak return tab_errors,tab_h
[docs] def create_dferrors_deg_allM(self, degree, tab_M=None): """Create a DataFrame of errors for a given degree and all M values. This method reads error data for different methods (FEM, PINNs, Corr, Mult, Mult_weak) from CSV files, and constructs a pandas DataFrame where each row represents a parameter sample and each column represents a method, mesh size, and mesh size (h). The DataFrame is then saved to a CSV file. Args: degree (int): The degree of the finite element solution. tab_M (list, optional): A list of M values to consider for the "Mult" methods. Defaults to None. Returns: pandas.DataFrame: The DataFrame of errors. """ tab_nb_vert = self.gef.tab_nb_vert size = len(tab_nb_vert) tab_errors,tab_h = self.__read_errors(degree,tab_M=tab_M) # Create dataframe for errors col_names = [] for method in tab_errors.keys(): tab_errors_method = tab_errors[method] if tab_errors_method is not None and len(tab_errors_method) > 0: if "Mult" in method: for M in tab_M: for i in range(size): col_names += [(method+str(M),str(tab_nb_vert[i]),str(tab_h[i]))] else: for i in range(size): col_names += [(method,str(tab_nb_vert[i]),str(tab_h[i]))] mi = pd.MultiIndex.from_tuples(col_names, names=["method","n_vert","h"]) df_errors = pd.DataFrame(columns=mi,index=self.__row_names) for i in range(self.gef.n_params): col = 0 for j in range(size): df_errors.loc[self.__row_names[i],col_names[col+j]] = tab_errors["FEM"][i,j] col += size for j in range(size): df_errors.loc[self.__row_names[i],col_names[col+j]] = tab_errors["PINNs"][i,j] col += size if tab_errors["Corr"] is not None: for j in range(size): df_errors.loc[self.__row_names[i],col_names[col+j]] = tab_errors["Corr"][i,j] col += size if tab_M is not None: if len(tab_errors["Mult"]) > 0: for M in tab_M: for j in range(size): df_errors.loc[self.__row_names[i],col_names[col+j]] = tab_errors["Mult"][M][i,j] col += size if len(tab_errors["Mult_weak"]) > 0: for M in tab_M: for j in range(size): df_errors.loc[self.__row_names[i],col_names[col+j]] = tab_errors["Mult_weak"][M][i,j] col += size save_file = self.gef.results_dir+f'df_errors_case{self.gef.testcase}_v{self.gef.version}_degree{degree}.csv' df_errors.to_csv(save_file) return df_errors
def __compute_gains(self, df_errors): """Compute gains of enhanced methods over FEM and PINNs. This method computes the gains of additive ("Corr") and multiplicative ("Mult") correction methods over standard FEM and PINNs, based on the errors provided in the input DataFrame. Args: df_errors (pandas.DataFrame): A DataFrame containing the errors for each method, with "method" as a level in the columns MultiIndex. Returns: tuple: A tuple containing two dictionaries: `tab_gains_overFEM` with gains over FEM, and `tab_gains_overPINNs` with gains over PINNs. """ tab_gains_overFEM = {} tab_gains_overPINNs = {} for method in df_errors.columns.get_level_values("method").unique(): if method != "FEM": tab_gains_overFEM[method] = df_errors["FEM"] / df_errors[method] if method != "PINNs": tab_gains_overPINNs[method] = df_errors["PINNs"] / df_errors[method] return tab_gains_overFEM,tab_gains_overPINNs
[docs] def create_dataframes_deg_allM(self, degree, tab_M=None): """Create DataFrames of errors and gains for a given degree and all M values. This method creates two pandas DataFrames: one for errors and one for gains, for a given degree and all specified M values. The error DataFrame contains the L2 errors for each method (FEM, PINNs, Corr, Mult, Mult_weak), while the gains DataFrame contains the gains of enhanced methods over FEM and PINNs. Both DataFrames are saved to CSV files. Args: degree (int): The degree of the finite element solution. tab_M (list, optional): A list of M values to consider for the "Mult" methods. Defaults to None. Returns: pandas.DataFrame: The DataFrame of gains. """ # Create dataframe for errors df_errors = self.create_dferrors_deg_allM(degree,tab_M=tab_M) tab_methods = df_errors.columns.get_level_values("method").unique() tab_nb_vert = self.gef.tab_nb_vert size = len(tab_nb_vert) tab_h = df_errors.columns.get_level_values("h").unique().to_numpy() # Compute gains tab_gains_overFEM,tab_gains_overPINNs = self.__compute_gains(df_errors) # Create dataframe for gains col_names = [] for method in tab_methods: if method != "FEM": if method != "PINNs": col_names += [(f"PINNs/{method}",str(tab_nb_vert[i]),str(tab_h[i])) for i in range(size)] col_names += [(f"FEM/{method}",str(tab_nb_vert[i]),str(tab_h[i])) for i in range(size)] mi = pd.MultiIndex.from_tuples(col_names, names=["facteurs","n_vert","h"]) df_gains = pd.DataFrame(columns=mi,index=self.__row_names) # Fill dataframe for gains col = 0 for method in tab_methods: if method != "FEM": if method != "PINNs": for j in range(size): col_name = col_names[col+j] df_gains.loc[:,col_name] = tab_gains_overPINNs[method].to_numpy()[:,j] col += size for j in range(size): col_name = col_names[col+j] df_gains.loc[:,col_name] = tab_gains_overFEM[method].to_numpy()[:,j] col += size save_file = self.gef.results_dir+f'df_gains_case{self.gef.testcase}_v{self.gef.version}_degree{degree}.csv' df_gains.to_csv(save_file) return df_gains
[docs] def create_dataframes_alldeg_allM(self, tab_M=None): """Create DataFrames of errors and gains for all degrees and all M values. This method creates and saves DataFrames of errors and gains for all degrees specified in `self.gef.tab_degree` and all specified M values. It calls the `create_dataframes_deg_allM` method for each degree. Args: tab_M (list, optional): A list of M values to consider for the "Mult" methods. Defaults to None. Returns: None """ for degree in self.gef.tab_degree: self.create_dataframes_deg_allM(degree,tab_M=tab_M)
[docs] def save_stats_deg_allM(self, degree, tab_M=None): """Save statistics of gains for a given degree and all M values. This method computes and saves statistics (min, max, mean, std) of the gains of enhanced methods (Corr, Mult, Mult_weak) over FEM and PINNs for a given degree and all specified M values. The statistics are saved as a CSV file and a PNG image. Args: degree (int): The degree of the finite element solution. tab_M (list, optional): A list of M values to consider for the "Mult" methods. Defaults to None. Returns: pandas.DataFrame: The DataFrame containing the rounded statistics. """ df_errors = self.create_dferrors_deg_allM(degree,tab_M=tab_M) tab_gains_overFEM,tab_gains_overPINNs = self.__compute_gains(df_errors) tab_nb_vert = self.gef.tab_nb_vert size = len(tab_nb_vert) tab_enhmethods = list(tab_gains_overFEM.keys()) tab_enhmethods.remove("PINNs") def get_stats(tab_gains): df_min = tab_gains.min(axis=0).to_numpy() df_max = tab_gains.max(axis=0).to_numpy() df_mean = tab_gains.mean(axis=0).to_numpy() df_std = tab_gains.std(axis=0).to_numpy() return np.array([df_min,df_max,df_mean,df_std]).T stats_overPINNs = [] stats_overFEM = [] for method in tab_enhmethods: gains_overPINNs_meth = tab_gains_overPINNs[method] stats_overPINNs_meth = get_stats(gains_overPINNs_meth) stats_overPINNs.append(stats_overPINNs_meth) gains_overFEM_meth = tab_gains_overFEM[method] stats_overFEM_meth = get_stats(gains_overFEM_meth) stats_overFEM.append(stats_overFEM_meth) stats_overPINNs = np.array(stats_overPINNs).reshape(-1,4) stats_overFEM = np.array(stats_overFEM).reshape(-1,4) global_stats = np.concatenate([stats_overPINNs,stats_overFEM],axis=1) tab_methods = ["PINNs", "FEM"] type_stats = ["min","max","mean","std"] col_names = [] for method in tab_methods: for type_stat in type_stats: col_names.append((method,type_stat)) row_names = [] for method in tab_enhmethods: for j in range(size): row_names.append((method,str(tab_nb_vert[j]))) mi = pd.MultiIndex.from_tuples(col_names, names=["method","type"]) ri = pd.MultiIndex.from_tuples(row_names, names=["method","n_vert"]) df_stats = pd.DataFrame(global_stats,columns=mi,index=ri) result_file = self.gef.results_dir+f'Tab_stats_case{self.gef.testcase}_v{self.gef.version}_degree{degree}' df_stats.to_csv(result_file+'.csv') # df_stats_round = df_stats.round(2) # df_stats_round = df_stats_round.astype(int) # table_conversion = "chrome" table_conversion = "matplotlib" df_stats_round = df_stats.applymap(lambda x: f"{x:.2f}") print(df_stats_round) dfi.export(df_stats_round,result_file+".png",dpi=1000,table_conversion=table_conversion) return df_stats_round
[docs] def save_stats_alldeg_allM(self, tab_M=None): """Save statistics of gains for all degrees and all M values. This method computes and saves statistics (min, max, mean, std) of the gains of enhanced methods over FEM and PINNs for all degrees specified in `self.gef.tab_degree` and all specified M values. It calls the `save_stats_deg_allM` method for each degree. Args: tab_M (list, optional): A list of M values to consider for the "Mult" methods. Defaults to None. Returns: None """ for degree in self.gef.tab_degree: self.save_stats_deg_allM(degree,tab_M=tab_M)