diff --git a/.gitignore b/.gitignore index 1d4a5843..9aa75b92 100644 --- a/.gitignore +++ b/.gitignore @@ -93,3 +93,6 @@ ENV/ *.o *.dll + +# VSCode Settings +.vscode/* diff --git a/src/OMSens/analysis/indiv_sens.py b/src/OMSens/analysis/indiv_sens.py index e12f0229..9029b602 100644 --- a/src/OMSens/analysis/indiv_sens.py +++ b/src/OMSens/analysis/indiv_sens.py @@ -4,19 +4,23 @@ import os # for os.path import re # regular expressions import unicodedata # slugifying file names +from typing import Any import pandas # dataframes +import numpy as np # arrays import filesystem.files_aux as files_aux import plotting.plot_heatmap as heatmap_f import modelica_interface.build_model as build_model +from modelica_interface.compiled_model import CompiledModelicaModel +from running.simulation_run_info import SimulationResults, PerturbedParameterInfo import running.simulation_run_info as simu_run_info logger = logging.getLogger("--ParameterSensAnalysis--") # this modules logger class ParametersIsolatedPerturbator(): - def __init__(self, model_name, model_file_path, start_time, stop_time, parameters, perc_perturb, build_folder_path): + def __init__(self, model_name:str, model_file_path:str, start_time:float, stop_time:float, parameters: list[str], perc_perturb: int, build_folder_path: str): # Save args self.model_name = model_name self.model_file_path = model_file_path @@ -33,7 +37,7 @@ def __init__(self, model_name, model_file_path, start_time, stop_time, parameter # Calculate the values per param self.values_per_param = perturbedValuePerParam(self.params_defaults, self.parameters, self.perc_perturb) - def runSimulations(self,dest_folder_path): + def runSimulations(self,dest_folder_path:str): # Make folder for runs runs_folder_name = "runs" runs_folder_path = os.path.join(dest_folder_path, runs_folder_name) @@ -74,29 +78,24 @@ def runSimulations(self,dest_folder_path): runs_per_parameter) return isolated_perturbations_results - # Auxs - def defaultValuesForParamsToPerturb(self, compiled_model): + def defaultValuesForParamsToPerturb(self, compiled_model: CompiledModelicaModel): # Using the compiled model, ask for the default value of each one - params_defaults = {} - for p in self.parameters: - p_def_val = compiled_model.defaultParameterValue(p) - params_defaults[p] = p_def_val - return params_defaults + return {p: compiled_model.defaultParameterValue(p) for p in self.parameters} class IsolatedPerturbationsResults(): - def __init__(self,model_name, std_run, runs_per_parameter): + def __init__(self,model_name:str, std_run: SimulationResults, runs_per_parameter:dict[str, "OneParameterPerturbedResults"]): self.model_name = model_name self.std_run = std_run self.runs_per_parameter = runs_per_parameter class OneParameterPerturbedResults(): - def __init__(self, simu_results, pert_param_info): + def __init__(self, simu_results: SimulationResults, pert_param_info: PerturbedParameterInfo): self.simu_results = simu_results self.pert_param_info = pert_param_info -def perturbedValuePerParam(params_defaults, parameters, perc_perturb): +def perturbedValuePerParam(params_defaults: dict[str, float], parameters: list[str], perc_perturb: int): value_per_param = {} for param_name in parameters: # Disaggregate param info @@ -106,8 +105,8 @@ def perturbedValuePerParam(params_defaults, parameters, perc_perturb): return value_per_param -def completeIndividualSensAnalysis(isolated_perturbations_results, target_vars, percentage_perturbed, specific_year, - rms_first_year, rms_last_year, output_folder_analyses_path): +def completeIndividualSensAnalysis(isolated_perturbations_results: IsolatedPerturbationsResults, target_vars: list[str], percentage_perturbed: int, specific_year: float, + rms_first_year: float, rms_last_year: float, output_folder_analyses_path:str): # Create perturbed runs info list using the dict output form the mos script # TODO: adapt this function when we stop using tuples inside the analyzer in favor of using proper objects to represent the info perturbed_csvs_path_and_info_pairs = perturbationAsTuplesFromDict(isolated_perturbations_results) @@ -154,7 +153,7 @@ def completeIndividualSensAnalysis(isolated_perturbations_results, target_vars, return analysis_results -def perturbationAsTuplesFromDict(isolated_perturbations_results): +def perturbationAsTuplesFromDict(isolated_perturbations_results: IsolatedPerturbationsResults): runs_per_parameter = isolated_perturbations_results.runs_per_parameter perturbed_csvs_path_and_info_pairs = [] for param_name in runs_per_parameter: @@ -170,8 +169,8 @@ def perturbationAsTuplesFromDict(isolated_perturbations_results): return perturbed_csvs_path_and_info_pairs -def generateSensMatricesPerMethod(rms_first_year, rms_last_year, - sens_to_params_per_var): +def generateSensMatricesPerMethod(rms_first_year: float, rms_last_year: float, + sens_to_params_per_var: dict[str,dict]): methods_records_dict = generateMatrixRecordsForEachSensitivityMethod(rms_first_year, rms_last_year, sens_to_params_per_var) df_rel_matrix_trans, df_rms_matrix_trans = methodsDataframesFromRecordsDict(methods_records_dict) @@ -182,7 +181,7 @@ def generateSensMatricesPerMethod(rms_first_year, rms_last_year, } return sens_matrices_dfs_dict -def makeFolderForMethodsHeatmapFiles(output_folder_analyses_path): +def makeFolderForMethodsHeatmapFiles(output_folder_analyses_path: str): # Create folder for matrices per method sens_matrices_folder_name = "heatmaps" sens_matrices_folder_path = os.path.join(output_folder_analyses_path, sens_matrices_folder_name) @@ -190,7 +189,7 @@ def makeFolderForMethodsHeatmapFiles(output_folder_analyses_path): return sens_matrices_folder_path -def generateMatrixRecordsForEachSensitivityMethod(rms_first_year, rms_last_year, sens_to_params_per_var): +def generateMatrixRecordsForEachSensitivityMethod(rms_first_year: float, rms_last_year: float, sens_to_params_per_var: dict[str, dict]): # Initialize the dict that will have the records (rows of the matrix) for each method methods_records_dict = {"Rel": [], "RMS": []} # Generate the records (row of the matrix) from the sensitivities of the params vs vars @@ -203,7 +202,7 @@ def generateMatrixRecordsForEachSensitivityMethod(rms_first_year, rms_last_year, return methods_records_dict -def methodsRecordsForVariable(rms_first_year, rms_last_year, sens_to_params_per_var, var_name): +def methodsRecordsForVariable(rms_first_year: float, rms_last_year: float, sens_to_params_per_var: dict[str, dict[str, Any]], var_name: str): # Get the sens info associated with this variable params_sens_to_var = sens_to_params_per_var[var_name] # Initialize the records for this variable. Each record corresponds to a variable and has information of its @@ -217,8 +216,8 @@ def methodsRecordsForVariable(rms_first_year, rms_last_year, sens_to_params_per_ return rel_method_record, rms_method_record -def addParamsMethodsValsToVarsRecords(param_name, params_sens_to_var, rel_method_record, rms_first_year, rms_last_year, - rms_method_record): +def addParamsMethodsValsToVarsRecords(param_name: str, params_sens_to_var: dict[str, dict[str, Any]], rel_method_record: dict[str,str], rms_first_year: float, rms_last_year: float, + rms_method_record: dict[str,str]): var_vs_param_sens = params_sens_to_var[param_name] # Get relative method info rel_method_val = var_vs_param_sens["(new-std)/std"] @@ -230,7 +229,7 @@ def addParamsMethodsValsToVarsRecords(param_name, params_sens_to_var, rel_method rms_method_record[param_name] = rms_method_val -def methodsDataframesFromRecordsDict(methods_records_dict): +def methodsDataframesFromRecordsDict(methods_records_dict: dict[str, list[dict[str, Any]]]): # Generate dataframes from the matrices df_rel_matrix = pandas.DataFrame.from_records(methods_records_dict["Rel"], index="parameter/variable") df_rms_matrix = pandas.DataFrame.from_records(methods_records_dict["RMS"], index="parameter/variable") @@ -239,8 +238,8 @@ def methodsDataframesFromRecordsDict(methods_records_dict): df_rms_matrix_trans = df_rms_matrix.transpose() return df_rel_matrix_trans, df_rms_matrix_trans -def sensitivitiesInformationPathsPerVariable(output_folder_analyses_path, percentage_perturbed, rms_first_year, - rms_last_year, sens_to_params_per_var, specific_year, target_vars): +def sensitivitiesInformationPathsPerVariable(output_folder_analyses_path: str, percentage_perturbed: int, rms_first_year: float, + rms_last_year: float, sens_to_params_per_var: dict[str, dict[str, Any]], specific_year: float, target_vars: list[str]): # Create folder for complete sensitivity info per var vars_sens_info_folder_name = "vars_sens_info" vars_sens_info_folder_path = os.path.join(output_folder_analyses_path, vars_sens_info_folder_name) @@ -253,8 +252,8 @@ def sensitivitiesInformationPathsPerVariable(output_folder_analyses_path, percen return vars_sens_infos_paths -def analysisPerParamPerturbedForEachVar(isolated_perturbations_results, percentage_perturbed, rms_first_year, - rms_last_year, specific_year, target_vars): +def analysisPerParamPerturbedForEachVar(isolated_perturbations_results: IsolatedPerturbationsResults, percentage_perturbed: int, rms_first_year: float, + rms_last_year: float, specific_year: float, target_vars: list[str]): # Initialize dict with rows for each variable. Each row will correspond to the values of said variable for a # respective run from each respective parameter perturbed sens_to_params_per_var = {var_name: {} for var_name in target_vars} @@ -270,8 +269,8 @@ def analysisPerParamPerturbedForEachVar(isolated_perturbations_results, percenta return sens_to_params_per_var -def analyzeParamResultsForEachVar(df_std_run, pert_run_info, percentage_perturbed, rms_first_year, - rms_last_year, specific_year, target_vars, sens_to_params_per_var): +def analyzeParamResultsForEachVar(df_std_run: pandas.DataFrame, pert_run_info: OneParameterPerturbedResults, percentage_perturbed: int, rms_first_year: float, + rms_last_year: float, specific_year: float, target_vars: list[str], sens_to_params_per_var: dict[str, dict[str, Any]]): # Read perturbed parameter csv param_csv_path = pert_run_info.simu_results.output_path df_param_perturbed = pandas.read_csv(param_csv_path, index_col=0) @@ -286,9 +285,9 @@ def analyzeParamResultsForEachVar(df_std_run, pert_run_info, percentage_perturbe rms_last_year, specific_year, target_var, sens_to_params_per_var) -def analyzeVarFromPerturbedParamResults(df_param_perturbed, df_std_run, param_csv_path, param_default, param_name, - param_new_value, percentage_perturbed, rms_first_year, rms_last_year, - specific_year, target_var, sens_to_params_per_var): +def analyzeVarFromPerturbedParamResults(df_param_perturbed: pandas.DataFrame, df_std_run: pandas.DataFrame, param_csv_path: str, param_default: float, param_name: str, + param_new_value:float, percentage_perturbed: int, rms_first_year: float, rms_last_year: float, + specific_year: float, target_var: str, sens_to_params_per_var: dict[str, dict[str, Any]]): var_analysis_dict = varAnalysisForPerturbedParam(df_std_run, df_param_perturbed, target_var, specific_year, rms_first_year, rms_last_year) sens_file_row_dict = rowDictFromParamVarSensAnal(param_name, param_default, param_new_value, @@ -300,8 +299,8 @@ def analyzeVarFromPerturbedParamResults(df_param_perturbed, df_std_run, param_cs var_sens_per_param[param_name] = sens_file_row_dict -def writeRunInfosAndReturnThePaths(output_folder_analyses_path, percentage_perturbed, rms_first_year, rms_last_year, - specific_year, target_vars, sens_to_params_per_var): +def writeRunInfosAndReturnThePaths(output_folder_analyses_path: str, percentage_perturbed: int, rms_first_year: float, rms_last_year: float, + specific_year: float, target_vars: list[str], sens_to_params_per_var: dict[str, dict[str, Any]]): run_infos_paths = {} # Set the columns order of the sensitivity analysis csv columns_order = varSensAnalysisInfodefaultColsOrder(percentage_perturbed, specific_year, rms_first_year, @@ -317,7 +316,7 @@ def writeRunInfosAndReturnThePaths(output_folder_analyses_path, percentage_pertu return run_infos_paths -def slugify(value): +def slugify(value: str): """ Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. @@ -328,28 +327,58 @@ def slugify(value): return value -def rootMeanSquareForVar(df_std_run, df_param_perturbed, rms_first_year, rms_last_year, target_var): - # Get the columns from year to year indicated for std run and perturbed param run - col_subyrs_std = df_std_run[target_var].loc[rms_first_year:rms_last_year] - col_subyrs_perturbed = df_param_perturbed[target_var].loc[rms_first_year:rms_last_year] - # Assert that both columns have the same number of rows - raiseErrorIfDifferentLengthsInDFs(df_std_run, df_param_perturbed) - # Calculate root mean square from both columns - diff = col_subyrs_std - col_subyrs_perturbed +def rootMeanSquareForVar(df_std_run: pandas.DataFrame, df_param_perturbed: pandas.DataFrame, rms_first_year: float, rms_last_year: float, target_var: str): + """ + Computes the RMS using Numpy Arrays. + """ + t_std = df_std_run.index.to_numpy() + y_std = df_std_run[target_var].to_numpy() + + t_pert = df_param_perturbed.index.to_numpy() + y_pert = df_param_perturbed[target_var].to_numpy() + + # time axes need to be sorted, otherwise np.interp will fail silently + # given that this should always be the case an assertion makes sense here. + # the implementation can handle very large arrays, it shouldn't be a problem here + assert np.all(t_std[:-1] <= t_std[1:]) and np.all(t_pert[:-1] <= t_pert[1:]), "Time-Axes are not monotonous" + + # resample perturbed timeseries to unperturbed time-axis so they share the same X-Axis, in case of different samplings + y_pert_resampled = np.interp(t_std, t_pert, y_pert) + + # create boolean mask to only consider times inside first_year and last_year + mask = (t_std >= rms_first_year) & (t_std <= rms_last_year) + + diff = y_std[mask] - y_pert_resampled[mask] diff_squared = diff ** 2 - mean_diff_squared = diff_squared.mean() + + mean_diff_squared = np.mean(diff_squared) rms = math.sqrt(mean_diff_squared) + return rms -def varAnalysisForPerturbedParam(df_std_run, df_param_perturbed, target_var, specific_year, rms_first_year, - rms_last_year): - # Get values for variable from standard run and perturbed run outputs and an specific year - var_std_value_for_year = df_std_run[target_var][specific_year] - var_new_value_for_year = df_param_perturbed[target_var][specific_year] +def varAnalysisForPerturbedParam(df_std_run: pandas.DataFrame, df_param_perturbed: pandas.DataFrame, target_var: str, specific_year: float, rms_first_year: float, + rms_last_year: float): + # Get nearest values for variable from standard run and perturbed run outputs and an specific year + target_idx = pandas.Index([specific_year]) + idx_pos_std = int(df_std_run.index.get_indexer(target_idx, method='nearest')[0]) + var_std_value_for_year = df_std_run[target_var].iloc[idx_pos_std] + + idx_pos_pert = int(df_param_perturbed.index.get_indexer(target_idx, method='nearest')[0]) + var_new_value_for_year = df_param_perturbed[target_var].iloc[idx_pos_pert] + # Calculate sensitivity methods for an specific year - std_div_new = var_std_value_for_year / var_new_value_for_year - perturbation_proportion = (var_new_value_for_year - var_std_value_for_year) / var_std_value_for_year + if var_new_value_for_year == 0: + std_div_new = 0 + else: + std_div_new = var_std_value_for_year / var_new_value_for_year + + if var_std_value_for_year == 0: + perturbation_proportion = 0 # avoid ZeroDivisionError + else: + perturbation_proportion = (var_new_value_for_year - var_std_value_for_year) / var_std_value_for_year + perturbation_proportion_abs = abs(perturbation_proportion) + # Calculate sensitivity methods for the whole run rootMeanSquare = rootMeanSquareForVar(df_std_run, df_param_perturbed, rms_first_year, rms_last_year, target_var) @@ -364,7 +393,7 @@ def varAnalysisForPerturbedParam(df_std_run, df_param_perturbed, target_var, spe return var_analysis_dict -def varSensAnalysisInfodefaultColsOrder(percentage_perturbed, specific_year, rms_first_year, rms_last_year): +def varSensAnalysisInfodefaultColsOrder(percentage_perturbed: int, specific_year: float, rms_first_year: float, rms_last_year: float): columns_order = [ "parameter", "parameter_default", @@ -380,7 +409,7 @@ def varSensAnalysisInfodefaultColsOrder(percentage_perturbed, specific_year, rms return columns_order -def dataFrameWithSensAnalysisForVar(sens_to_params_per_var, target_var, columns_order): +def dataFrameWithSensAnalysisForVar(sens_to_params_per_var: dict[str, dict[str, Any]], target_var: str, columns_order: list[str]): # Get sensitivities corresponding to this variable var_sens_per_param = sens_to_params_per_var[target_var] # Make records from the sens to be used in the dataframe @@ -391,15 +420,15 @@ def dataFrameWithSensAnalysisForVar(sens_to_params_per_var, target_var, columns_ df_run_info = df_run_info.sort_values(by="ABS((new-std)/std)", ascending=False) return df_run_info -def dfPathFromFolderPathAndVarName(output_folder_analyses_path, target_var): +def dfPathFromFolderPathAndVarName(output_folder_analyses_path: str, target_var: str): var_name_slugified = slugify(target_var) var_sens_csv_file_name = "sens_{0}.csv".format(var_name_slugified) output_analysis_path = os.path.join(output_folder_analyses_path, var_sens_csv_file_name) return output_analysis_path -def rowDictFromParamVarSensAnal(param_name, param_default, param_new_value, percentage_perturbed, specific_year, - rms_first_year, rms_last_year, var_analysis_dict, param_csv_path): +def rowDictFromParamVarSensAnal(param_name: str, param_default: float, param_new_value:float, percentage_perturbed: int, specific_year: float, + rms_first_year: float, rms_last_year: float, var_analysis_dict: dict[str, Any], param_csv_path: str): sens_file_row_dict = { "parameter": param_name, "parameter_default": param_default, @@ -415,7 +444,7 @@ def rowDictFromParamVarSensAnal(param_name, param_default, param_new_value, perc return sens_file_row_dict -def raiseErrorIfDifferentLengthsInDFs(df_1, df_2): +def raiseErrorIfDifferentLengthsInDFs(df_1: pandas.DataFrame, df_2: pandas.DataFrame): # Get both dfs shapes nrows_1, ncols_1 = df_1.shape nrows_2, ncols_2 = df_2.shape diff --git a/src/OMSens/filesystem/files_aux.py b/src/OMSens/filesystem/files_aux.py index a02b6507..9fbed2d7 100644 --- a/src/OMSens/filesystem/files_aux.py +++ b/src/OMSens/filesystem/files_aux.py @@ -21,11 +21,11 @@ def makeOutputPath(folder_name="modelica_output"): return timestamp_dir -def makeFolderWithPath(dest_path): +def makeFolderWithPath(dest_path:str): if not os.path.exists(dest_path): os.makedirs(dest_path) -def makeDirFromCurrentTimestamp(dest_path): +def makeDirFromCurrentTimestamp(dest_path:str): logger.debug("Making timestamp dir") if not os.path.exists(dest_path): os.makedirs(dest_path) @@ -43,12 +43,18 @@ def destPath(folder_name): def projectRoot(): - currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + frame = inspect.currentframe() + if frame is None: + raise SystemError("Project root could not be determined") + currentdir = os.path.dirname(os.path.abspath(inspect.getfile(frame))) project_root = parentDir(currentdir) return project_root def tmpPath(): - currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + frame = inspect.currentframe() + if frame is None: + raise SystemError("Project root could not be determined") + currentdir = os.path.dirname(os.path.abspath(inspect.getfile(frame))) parentdir = parentDir(currentdir) return os.path.join(parentdir, "tmp") # return os.path.join(currentdir,"tmp") diff --git a/src/OMSens/individual_sens_calculator.py b/src/OMSens/individual_sens_calculator.py index b779d08b..09ff0ad2 100644 --- a/src/OMSens/individual_sens_calculator.py +++ b/src/OMSens/individual_sens_calculator.py @@ -27,7 +27,7 @@ def main(): return 0 -def perturbateAndAnalyzeFromJsonToPath(json_file_path, dest_folder_path): +def perturbateAndAnalyzeFromJsonToPath(json_file_path: str, dest_folder_path: str): # Read JSON with open(json_file_path, 'r') as fp: full_json = json.load(fp) @@ -45,8 +45,8 @@ def perturbateAndAnalyzeFromJsonToPath(json_file_path, dest_folder_path): perturbateAndAnalyze(**perturbateAndAnalyze_kwargs) -def perturbateAndAnalyze( model_name, model_file_path, start_time, stop_time, parameters_to_perturb, percentage, - target_vars, dest_folder_path ): +def perturbateAndAnalyze( model_name: str, model_file_path: str, start_time: float, stop_time: float, parameters_to_perturb: list[str], percentage: int, + target_vars: list[str], dest_folder_path: str ): # Create simulations folder perturbations_folder_name = "simulation" perturbations_folder_path = os.path.join(dest_folder_path, perturbations_folder_name) @@ -92,7 +92,7 @@ def perturbateAndAnalyze( model_name, model_file_path, start_time, stop_time, pa logger.info("Finished. The file {0} has all the analysis files paths.".format(paths_json_file_path)) -def listOfParametersPerturbationInfo(param_names, param_vals, percentage): +def listOfParametersPerturbationInfo(param_names: list[str], param_vals: list[float], percentage: int): parameters_to_perturbate_tuples = [] # Iterate parameters name and default info for p_name, p_val in zip(param_names, param_vals): @@ -104,7 +104,7 @@ def listOfParametersPerturbationInfo(param_names, param_vals, percentage): return parameters_to_perturbate_tuples -def finalDestFolderPath(dest_folder_path_arg): +def finalDestFolderPath(dest_folder_path_arg:str): # Make dest folder path in this projects root if none indicated in command line if not dest_folder_path_arg: dest_folder_path = files_aux.makeOutputPath("indiv_sens_analysis") @@ -114,7 +114,7 @@ def finalDestFolderPath(dest_folder_path_arg): return dest_folder_path -def csvPathAndParameterNameForFolderAndParametersInfo(dest_folder_path, parameters_info): +def csvPathAndParameterNameForFolderAndParametersInfo(dest_folder_path: str, parameters_info): perturbed_csvs_path_and_info_pairs = [] for param_info in parameters_info: param_name = param_info[0] diff --git a/src/OMSens/misc/csv_output_to_csv_matrix_converter.py b/src/OMSens/misc/csv_output_to_csv_matrix_converter.py index 7c9cd1b5..169a2128 100644 --- a/src/OMSens/misc/csv_output_to_csv_matrix_converter.py +++ b/src/OMSens/misc/csv_output_to_csv_matrix_converter.py @@ -52,7 +52,7 @@ def W3TheoSensToMatrixRowsListFromHeadersAndYearRow(header_row, year_row): # Check if its a sensitivity param/variable value complex_var_name = header_row_list[i] # Check if its a sens param/variable value - w3TheoSens_regex = "\$Sensitivities\..*" + w3TheoSens_regex = r"\$Sensitivities\..*" if re.match(w3TheoSens_regex, complex_var_name): param_name, var_name = extractParamNameAndVarNameFromComplexVarName(complex_var_name) # Add parameter influence to variable to the param_influences_dict dict diff --git a/src/OMSens/modelica_interface/build_model.py b/src/OMSens/modelica_interface/build_model.py index 632bcdee..2f8433c6 100644 --- a/src/OMSens/modelica_interface/build_model.py +++ b/src/OMSens/modelica_interface/build_model.py @@ -7,7 +7,7 @@ from modelica_interface.compiled_model import CompiledModelicaModel -class ModelicaModelBuilder(): +class ModelicaModelBuilder: mos_script_skeleton = \ ( # This shouldn't be the responsibility of the builder, but for now we leave it here @@ -19,7 +19,7 @@ class ModelicaModelBuilder(): """buildModel({model_name}, startTime={startTime},stopTime={stopTime},outputFormat="csv", numberOfIntervals={numberOfIntervals}); getErrorString();""" ) - def __init__(self, model_name, start_time, stop_time, model_file_path, number_of_intervals=300): + def __init__(self, model_name:str, start_time:float, stop_time:float, model_file_path:str, number_of_intervals:int=300): # Attrs from args self.model_name = model_name self.start_time = start_time @@ -29,7 +29,7 @@ def __init__(self, model_name, start_time, stop_time, model_file_path, number_of # Hardcoded attrs self.mos_script_file_name = "builder.mos" - def buildToFolderPath(self,dest_folder_path): + def buildToFolderPath(self,dest_folder_path:str): # Write .mos script to folder mos_script_path = os.path.join(dest_folder_path, self.mos_script_file_name) self.writeMOSScriptToPath(mos_script_path) @@ -55,7 +55,7 @@ def mosScriptString(self): stopTime = self.stop_time) return mos_script_str - def writeMOSScriptToPath(self,file_path): + def writeMOSScriptToPath(self,file_path:str): # This shouldn't be the responsibility of the builder, but for now we leave it here mos_script_str = self.mosScriptString() files_aux.writeStrToFile(mos_script_str,file_path) diff --git a/src/OMSens/modelica_interface/compiled_model.py b/src/OMSens/modelica_interface/compiled_model.py index 109be4bd..c17cda09 100644 --- a/src/OMSens/modelica_interface/compiled_model.py +++ b/src/OMSens/modelica_interface/compiled_model.py @@ -5,6 +5,7 @@ import xml.etree.ElementTree as ElementTree import pandas import copy +from typing import Any # Mine import filesystem.files_aux as files_aux @@ -24,20 +25,20 @@ def __init__(self,model_name, binary_file_path): self.default_xml_tree = copy.deepcopy(xml_tree) # Getters - def parameterValue(self,param_name): + def parameterValue(self,param_name: str): # Get XML element for param and its value from the (changed) XML xml_tree = self.xml_tree param_val_casted = parameterValueInModelicaXML(param_name, xml_tree) return param_val_casted - def defaultParameterValue(self,param_name): + def defaultParameterValue(self,param_name: str): # Get XML element for param and its value from the original XML xml_tree = self.default_xml_tree param_val_casted = parameterValueInModelicaXML(param_name, xml_tree) return param_val_casted # Setters - def setParameterStartValue(self,param_name,param_val): + def setParameterStartValue(self,param_name:str,param_val:float): # Cast value as string param_val_str = str(param_val) # Get XML element for param and its value @@ -58,7 +59,7 @@ def simulate(self, dest_csv_path, params_vals_dict=None, optional_flags=""): simu_results = simu_run_info.SimulationResults(dest_csv_path, self.model_name, self.binary_file_path, output) return simu_results - def quickSimulate(self, var_name, params_vals_dict=None): + def quickSimulate(self, var_name: str, params_vals_dict=None): # Set flags output_flag = "-output {0}".format(var_name) minimal_output_flag = "-lv=-LOG_SUCCESS" diff --git a/src/OMSens/modelica_interface/run_omc.py b/src/OMSens/modelica_interface/run_omc.py index 90c74428..fc05a8c7 100644 --- a/src/OMSens/modelica_interface/run_omc.py +++ b/src/OMSens/modelica_interface/run_omc.py @@ -8,12 +8,15 @@ import filesystem.files_aux def interpreterForCurrentPlatform(): - if platform.system() == "Linux": + system = platform.system() + if system == "Linux": interpreter = gral_settings._interpreter_linux - elif platform.system() == "Windows": + elif system == "Windows": interpreter = gral_settings._interpreter_windows else: logger.error("This script was tested only on Windows and Linux. The omc interpreter for another platform has not been set") + # This should really crash, otherwise interpreter could be uninitialized, setting this to None for now + interpreter = None return interpreter def runMosScript(script_path): diff --git a/src/OMSens/mos_writer/formulas.py b/src/OMSens/mos_writer/formulas.py index fd04a2ab..c4517eb4 100644 --- a/src/OMSens/mos_writer/formulas.py +++ b/src/OMSens/mos_writer/formulas.py @@ -9,7 +9,7 @@ class SweepingFormulas(ABC): # This abstract class forces all of its subclasses to implement the "initialize" method. We need the formulas to be represented like this because # we want to maximize userfriendliness and for that, the parameter info has to be set programatically (default value, for example) @abstractmethod - def initialize(extra_info): + def initialize(self, extra_info) -> str: pass diff --git a/src/OMSens/mos_writer/mos_script_factory.py b/src/OMSens/mos_writer/mos_script_factory.py index e44a2dc1..d55c690f 100644 --- a/src/OMSens/mos_writer/mos_script_factory.py +++ b/src/OMSens/mos_writer/mos_script_factory.py @@ -1,5 +1,6 @@ import inspect from abc import ABC +from typing import Any # Mine: import mos_writer.sweeping_mos_writer @@ -7,6 +8,8 @@ # Abstract class that implements most of the functionaltity. # The only responsibility of the subclasses is to set if uniparam or multiparam mos_writer class MosScriptFactory(ABC): + _sweeping_mos_writer: Any + # Init def __init__(self, *args, **kwargs): self._settings_dict = kwargs.pop("settings_dict") @@ -17,7 +20,7 @@ def requiredSettings(self): def allSeteableSettings(self): # Returns the mandatory and the optional parameters - argsspecmsf = inspect.getargspec(self._sweeping_mos_writer.createMos) + argsspecmsf = inspect.getfullargspec(self._sweeping_mos_writer.createMos) if argsspecmsf.defaults: # If there are args with default values mandatory = argsspecmsf.args[0:len(argsspecmsf.args) - len(argsspecmsf.defaults)] diff --git a/src/OMSens/mos_writer/sweeping_mos_writer.py b/src/OMSens/mos_writer/sweeping_mos_writer.py index bcc31e40..443452d1 100644 --- a/src/OMSens/mos_writer/sweeping_mos_writer.py +++ b/src/OMSens/mos_writer/sweeping_mos_writer.py @@ -170,6 +170,3 @@ def strForEndFor(): all_params_are_valid := all_params_are_valid and my_param_is_valid; end for; print(String(all_params_are_valid));""" - -if __name__ == "__main__": - main() diff --git a/src/OMSens/plotting/plot_lines.py b/src/OMSens/plotting/plot_lines.py index 63a7ccf4..9b8e98f1 100644 --- a/src/OMSens/plotting/plot_lines.py +++ b/src/OMSens/plotting/plot_lines.py @@ -78,7 +78,7 @@ def tryTimestampOrNumberForList(orig_index): def setupPlot(setup_specs): plt.style.use('fivethirtyeight') - plt.gca().set_position([0.10, 0.15, 0.80, 0.77]) + plt.gca().set_position((0.10, 0.15, 0.80, 0.77)) plt.xlabel(setup_specs.x_label) plt.title(setup_specs.title + "\n" + setup_specs.subtitle, fontsize=14, y=1.08) plt.ylabel(setup_specs.y_label) diff --git a/src/OMSens/running/simulation_run_info.py b/src/OMSens/running/simulation_run_info.py index c6986555..315b5a9c 100644 --- a/src/OMSens/running/simulation_run_info.py +++ b/src/OMSens/running/simulation_run_info.py @@ -1,5 +1,7 @@ +from typing import Any + class SimulationResults(): - def __init__(self, output_path, model_name, executable_path, std_output): + def __init__(self, output_path: Any, model_name: str, executable_path:str, std_output:str): self.output_path = output_path self.model_name = model_name self.executable = executable_path