From 4b33537e8b5a40aa5e5ba9ae758e2e30c4877aa2 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Mon, 25 Aug 2025 16:01:42 +0200 Subject: [PATCH 01/13] Update chore libraries --- environment.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/environment.yml b/environment.yml index c6961a9..e90a172 100644 --- a/environment.yml +++ b/environment.yml @@ -6,11 +6,11 @@ dependencies: - openpyxl=3.1.5 - pandas=2.2.3 - pandas-stubs=2.3.0.250703 - - pip=24.0 + - pip=25.0.1 - pyomo=6.9.2 - pytest=8.4.0 - python=3.12.2 - - rich=13.7.1 - - rich-argparse=1.6.0 + - rich=13.9.4 + - rich-argparse=1.7.0 - pip: - pulp==2.9.0 From b017a36e52621da94001a26eee830d057e122c93 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Mon, 25 Aug 2025 16:02:12 +0200 Subject: [PATCH 02/13] Implement Wind Technical Details reader --- ExcelReader.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ExcelReader.py b/ExcelReader.py index fa209b8..a67649c 100644 --- a/ExcelReader.py +++ b/ExcelReader.py @@ -293,6 +293,19 @@ def get_dPower_WeightsRP(excel_file_path: str, keep_excluded_entries: bool = Fal return dPower_WeightsRP +def get_dPower_Wind_TechnicalDetails(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: + """ + Read the dPower_Wind_TechnicalDetails data from the Excel file. + :param excel_file_path: Path to the Excel file + :param keep_excluded_entries: Do not exclude any entries which are marked to be excluded in the Excel file + :param fail_on_wrong_version: If True, raise an error if the version of the Excel file does not match the expected version + :return: dPower_Wind_TechnicalDetails + """ + dPower_Wind_TechnicalDetails = __read_non_pivoted_file(excel_file_path, "v0.1.0", ["g"], True, keep_excluded_entries, fail_on_wrong_version) + + return dPower_Wind_TechnicalDetails + + def compare_Excels(source_path: str, target_path: str, dont_check_formatting: bool = False) -> bool: """ Compare two Excel files for differences in formatting and values. From 40e946760f08abc294d141db6fd509ff15bfcffe Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Mon, 25 Aug 2025 16:03:15 +0200 Subject: [PATCH 03/13] Implement Wind Technical Details writer --- ExcelWriter.py | 10 ++++++ TableDefinitions.xml | 84 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/ExcelWriter.py b/ExcelWriter.py index 1c8f020..c52af35 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -321,6 +321,15 @@ def write_dPower_WeightsRP(self, dPower_WeightsRP: pd.DataFrame, folder_path: st """ self._write_Excel_from_definition(dPower_WeightsRP, folder_path, "Power_WeightsRP") + def write_dPower_Wind_TechnicalDetails(self, dPower_Wind_TechnicalDetails: pd.DataFrame, folder_path: str) -> None: + """ + Write the dPower_Wind_TechnicalDetails DataFrame to an Excel file in LEGO format. + :param dPower_Wind_TechnicalDetails: DataFrame containing the dPower_Wind_TechnicalDetails data. + :param folder_path: Path to the folder where the Excel file will be saved. + :return: None + """ + self._write_Excel_from_definition(dPower_Wind_TechnicalDetails, folder_path, "Power_Wind_TechnicalDetails") + def model_to_excel(model: pyomo.core.Model, target_path: str) -> None: """ @@ -388,6 +397,7 @@ def model_to_excel(model: pyomo.core.Model, target_path: str) -> None: ("Power_VRESProfiles", f"{args.caseStudyFolder}Power_VRESProfiles.xlsx", ExcelReader.get_dPower_VRESProfiles, ew.write_VRESProfiles), ("Power_WeightsK", f"{args.caseStudyFolder}Power_WeightsK.xlsx", ExcelReader.get_dPower_WeightsK, ew.write_dPower_WeightsK), ("Power_WeightsRP", f"{args.caseStudyFolder}Power_WeightsRP.xlsx", ExcelReader.get_dPower_WeightsRP, ew.write_dPower_WeightsRP), + ("Power_Wind_TechnicalDetails", f"{args.caseStudyFolder}Power_Wind_TechnicalDetails.xlsx", ExcelReader.get_dPower_Wind_TechnicalDetails, ew.write_dPower_Wind_TechnicalDetails) ] for excel_definition_id, file_path, read, write in combinations: diff --git a/TableDefinitions.xml b/TableDefinitions.xml index 487d494..b9b5933 100644 --- a/TableDefinitions.xml +++ b/TableDefinitions.xml @@ -247,6 +247,41 @@ + + v0.1.0 + Power - Wind Technical Details + 60.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + v0.1.3 Power - Weights of representative hours @@ -395,6 +430,13 @@ 15.0 general + + Hub Height + Heigth of the turbine + [m] + 15.0 + general + Node Name of node @@ -647,6 +689,13 @@ 17.0 rightFloat1 + + Power Factor + Power Factor for the given Year + [0, n] + 15.0 + rightInt + Pmax Maximum active power transfer @@ -773,6 +822,27 @@ 15.0 general + + Vmax + Cut-out wind speed + [m/s] + 15.0 + general + + + Vmin + Cut-in wind speed + [m/s] + 15.0 + general + + + Vrated + Rated wind speed + [m/s] + 15.0 + general + Commission Year Year where it is commissioned (1.1.xxxx) @@ -787,6 +857,20 @@ 19.5 rightInt + + Year + Year + [0, n] + 18.0 + general + + + Yearly Production + Yearly production for calibration + [MW/h] + 18.0 + general + Zone Zone in which node lies From 075512ec7f86b1d983d0227d5280ea8314061a91 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Mon, 25 Aug 2025 16:06:24 +0200 Subject: [PATCH 04/13] Add example Wind Technical Details data --- data/example/Power_Wind_TechnicalDetails.xlsx | Bin 0 -> 9128 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/example/Power_Wind_TechnicalDetails.xlsx diff --git a/data/example/Power_Wind_TechnicalDetails.xlsx b/data/example/Power_Wind_TechnicalDetails.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..dc66a5e8b73162729025da33b8a2d3ddd94dbd79 GIT binary patch literal 9128 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 literal 0 HcmV?d00001 From fe635b43799b954309efa74803a1c40aaf55aa4e Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 13:49:10 +0200 Subject: [PATCH 05/13] Update chore libraries --- environment.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/environment.yml b/environment.yml index e90a172..b62965a 100644 --- a/environment.yml +++ b/environment.yml @@ -4,13 +4,13 @@ channels: - conda-forge dependencies: - openpyxl=3.1.5 - - pandas=2.2.3 + - pandas=2.3.1 - pandas-stubs=2.3.0.250703 - - pip=25.0.1 - - pyomo=6.9.2 - - pytest=8.4.0 + - pip=25.1 + - pyomo=6.9.3 + - pytest=8.4.1 - python=3.12.2 - rich=13.9.4 - - rich-argparse=1.7.0 + - rich-argparse=1.7.1 - pip: - pulp==2.9.0 From ddb304edf967507971a38d22cc94749ecdd5af26 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 13:50:05 +0200 Subject: [PATCH 06/13] Rename excel reader functions --- CaseStudy.py | 24 ++++++++++++------------ ExcelReader.py | 30 +++++++++++++++--------------- ExcelWriter.py | 30 +++++++++++++++--------------- tests/test_ExcelReaderWriter.py | 28 ++++++++++++++-------------- 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/CaseStudy.py b/CaseStudy.py index ae0aa4f..2b6d460 100644 --- a/CaseStudy.py +++ b/CaseStudy.py @@ -45,7 +45,7 @@ def __init__(self, self.dGlobal_Scenarios = dGlobal_Scenarios else: self.global_scenarios_file = global_scenarios_file - self.dGlobal_Scenarios = ExcelReader.get_dGlobal_Scenarios(self.data_folder + self.global_scenarios_file) + self.dGlobal_Scenarios = ExcelReader.get_Global_Scenarios(self.data_folder + self.global_scenarios_file) if dPower_Parameters is not None: self.dPower_Parameters = dPower_Parameters @@ -57,37 +57,37 @@ def __init__(self, self.dPower_BusInfo = dPower_BusInfo else: self.power_businfo_file = power_businfo_file - self.dPower_BusInfo = ExcelReader.get_dPower_BusInfo(self.data_folder + self.power_businfo_file) + self.dPower_BusInfo = ExcelReader.get_Power_BusInfo(self.data_folder + self.power_businfo_file) if dPower_Network is not None: self.dPower_Network = dPower_Network else: self.power_network_file = power_network_file - self.dPower_Network = ExcelReader.get_dPower_Network(self.data_folder + self.power_network_file) + self.dPower_Network = ExcelReader.get_Power_Network(self.data_folder + self.power_network_file) if dPower_Demand is not None: self.dPower_Demand = dPower_Demand else: self.power_demand_file = power_demand_file - self.dPower_Demand = ExcelReader.get_dPower_Demand(self.data_folder + self.power_demand_file) + self.dPower_Demand = ExcelReader.get_Power_Demand(self.data_folder + self.power_demand_file) if dPower_WeightsRP is not None: self.dPower_WeightsRP = dPower_WeightsRP else: self.power_weightsrp_file = power_weightsrp_file - self.dPower_WeightsRP = ExcelReader.get_dPower_WeightsRP(self.data_folder + self.power_weightsrp_file) + self.dPower_WeightsRP = ExcelReader.get_Power_WeightsRP(self.data_folder + self.power_weightsrp_file) if dPower_WeightsK is not None: self.dPower_WeightsK = dPower_WeightsK else: self.power_weightsk_file = power_weightsk_file - self.dPower_WeightsK = ExcelReader.get_dPower_WeightsK(self.data_folder + self.power_weightsk_file) + self.dPower_WeightsK = ExcelReader.get_Power_WeightsK(self.data_folder + self.power_weightsk_file) if dPower_Hindex is not None: self.dPower_Hindex = dPower_Hindex else: self.power_hindex_file = power_hindex_file - self.dPower_Hindex = ExcelReader.get_dPower_Hindex(self.data_folder + self.power_hindex_file) + self.dPower_Hindex = ExcelReader.get_Power_Hindex(self.data_folder + self.power_hindex_file) self.rpTransitionMatrixAbsolute, self.rpTransitionMatrixRelativeTo, self.rpTransitionMatrixRelativeFrom = self.get_rpTransitionMatrices() @@ -96,34 +96,34 @@ def __init__(self, self.dPower_ThermalGen = dPower_ThermalGen else: self.power_thermalgen_file = power_thermalgen_file - self.dPower_ThermalGen = ExcelReader.get_dPower_ThermalGen(self.data_folder + self.power_thermalgen_file) + self.dPower_ThermalGen = ExcelReader.get_Power_ThermalGen(self.data_folder + self.power_thermalgen_file) if self.dPower_Parameters["pEnableVRES"]: if dPower_VRES is not None: self.dPower_VRES = dPower_VRES else: self.power_vres_file = power_vres_file - self.dPower_VRES = ExcelReader.get_dPower_VRES(self.data_folder + self.power_vres_file) + self.dPower_VRES = ExcelReader.get_Power_VRES(self.data_folder + self.power_vres_file) if dPower_VRESProfiles is not None: self.dPower_VRESProfiles = dPower_VRESProfiles elif os.path.isfile(self.data_folder + power_vresprofiles_file): self.power_vresprofiles_file = power_vresprofiles_file - self.dPower_VRESProfiles = ExcelReader.get_dPower_VRESProfiles(self.data_folder + self.power_vresprofiles_file) + self.dPower_VRESProfiles = ExcelReader.get_Power_VRESProfiles(self.data_folder + self.power_vresprofiles_file) if self.dPower_Parameters["pEnableStorage"]: if dPower_Storage is not None: self.dPower_Storage = dPower_Storage else: self.power_storage_file = power_storage_file - self.dPower_Storage = ExcelReader.get_dPower_Storage(self.data_folder + self.power_storage_file) + self.dPower_Storage = ExcelReader.get_Power_Storage(self.data_folder + self.power_storage_file) if self.dPower_Parameters["pEnableVRES"] or self.dPower_Parameters["pEnableStorage"]: if dPower_Inflows is not None: self.dPower_Inflows = dPower_Inflows elif os.path.isfile(self.data_folder + power_inflows_file): self.power_inflows_file = power_inflows_file - self.dPower_Inflows = ExcelReader.get_dPower_Inflows(self.data_folder + self.power_inflows_file) + self.dPower_Inflows = ExcelReader.get_Power_Inflows(self.data_folder + self.power_inflows_file) if self.dPower_Parameters["pEnablePowerImportExport"]: if dPower_ImpExpHubs is not None: diff --git a/ExcelReader.py b/ExcelReader.py index a67649c..a44af0d 100644 --- a/ExcelReader.py +++ b/ExcelReader.py @@ -82,7 +82,7 @@ def __read_pivoted_file(excel_file_path: str, version_specifier: str, indices: l return df -def get_dData_Packages(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Data_Packages(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dData_Packages data from the Excel file. :param excel_file_path: Path to the Excel file @@ -98,7 +98,7 @@ def get_dData_Packages(excel_file_path: str, keep_excluded_entries: bool = False return dData_Packages -def get_dData_Sources(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Data_Sources(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dData_Sources data from the Excel file. :param excel_file_path: Path to the Excel file @@ -114,7 +114,7 @@ def get_dData_Sources(excel_file_path: str, keep_excluded_entries: bool = False, return dData_Sources -def get_dGlobal_Scenarios(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Global_Scenarios(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dGlobal_Scenarios data from the Excel file. :param excel_file_path: Path to the Excel file @@ -132,7 +132,7 @@ def get_dGlobal_Scenarios(excel_file_path: str, keep_excluded_entries: bool = Fa return dGlobal_Scenarios -def get_dPower_BusInfo(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_BusInfo(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_BusInfo data from the Excel file. :param excel_file_path: Path to the Excel file @@ -145,7 +145,7 @@ def get_dPower_BusInfo(excel_file_path: str, keep_excluded_entries: bool = False return dPower_BusInfo -def get_dPower_Demand(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_Demand(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_Demand data from the Excel file. :param excel_file_path: Path to the Excel file @@ -161,7 +161,7 @@ def get_dPower_Demand(excel_file_path: str, keep_excluded_entries: bool = False, return dPower_Demand -def get_dPower_Hindex(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_Hindex(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_Hindex data from the Excel file. :param excel_file_path: Path to the Excel file @@ -177,7 +177,7 @@ def get_dPower_Hindex(excel_file_path: str, keep_excluded_entries: bool = False, return dPower_Hindex -def get_dPower_Inflows(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_Inflows(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_Inflows data from the Excel file. :param excel_file_path: Path to the Excel file @@ -193,7 +193,7 @@ def get_dPower_Inflows(excel_file_path: str, keep_excluded_entries: bool = False return dPower_Inflows -def get_dPower_Network(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_Network(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_Network data from the Excel file. :param excel_file_path: Path to the Excel file @@ -206,7 +206,7 @@ def get_dPower_Network(excel_file_path: str, keep_excluded_entries: bool = False return dPower_Network -def get_dPower_Storage(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_Storage(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_Storage data from the Excel file. :param excel_file_path: Path to the Excel file @@ -219,7 +219,7 @@ def get_dPower_Storage(excel_file_path: str, keep_excluded_entries: bool = False return dPower_Storage -def get_dPower_ThermalGen(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_ThermalGen(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_ThermalGen data from the Excel file. :param excel_file_path: Path to the Excel file @@ -232,7 +232,7 @@ def get_dPower_ThermalGen(excel_file_path: str, keep_excluded_entries: bool = Fa return dPower_ThermalGen -def get_dPower_VRES(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_VRES(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_VRES data from the Excel file. :param excel_file_path: Path to the Excel file @@ -245,7 +245,7 @@ def get_dPower_VRES(excel_file_path: str, keep_excluded_entries: bool = False, f return dPower_VRES -def get_dPower_VRESProfiles(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_VRESProfiles(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_VRESProfiles data from the Excel file. :param excel_file_path: Path to the Excel file @@ -261,7 +261,7 @@ def get_dPower_VRESProfiles(excel_file_path: str, keep_excluded_entries: bool = return dPower_VRESProfiles -def get_dPower_WeightsK(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_WeightsK(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_WeightsK data from the Excel file. :param excel_file_path: Path to the Excel file @@ -277,7 +277,7 @@ def get_dPower_WeightsK(excel_file_path: str, keep_excluded_entries: bool = Fals return dPower_WeightsK -def get_dPower_WeightsRP(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_WeightsRP(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_WeightsRP data from the Excel file. :param excel_file_path: Path to the Excel file @@ -293,7 +293,7 @@ def get_dPower_WeightsRP(excel_file_path: str, keep_excluded_entries: bool = Fal return dPower_WeightsRP -def get_dPower_Wind_TechnicalDetails(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: +def get_Power_Wind_TechnicalDetails(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: """ Read the dPower_Wind_TechnicalDetails data from the Excel file. :param excel_file_path: Path to the Excel file diff --git a/ExcelWriter.py b/ExcelWriter.py index c52af35..3750cf4 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -383,21 +383,21 @@ def model_to_excel(model: pyomo.core.Model, target_path: str) -> None: printer.separator() combinations = [ - ("Data_Packages", f"{args.caseStudyFolder}Data_Packages.xlsx", ExcelReader.get_dData_Packages, ew.write_dData_Packages), - ("Data_Sources", f"{args.caseStudyFolder}Data_Sources.xlsx", ExcelReader.get_dData_Sources, ew.write_dData_Sources), - ("Global_Scenarios", f"{args.caseStudyFolder}Global_Scenarios.xlsx", ExcelReader.get_dGlobal_Scenarios, ew.write_dGlobal_Scenarios), - ("Power_BusInfo", f"{args.caseStudyFolder}Power_BusInfo.xlsx", ExcelReader.get_dPower_BusInfo, ew.write_dPower_BusInfo), - ("Power_Demand", f"{args.caseStudyFolder}Power_Demand.xlsx", ExcelReader.get_dPower_Demand, ew.write_dPower_Demand), - ("Power_Hindex", f"{args.caseStudyFolder}Power_Hindex.xlsx", ExcelReader.get_dPower_Hindex, ew.write_dPower_Hindex), - ("Power_Inflows", f"{args.caseStudyFolder}Power_Inflows.xlsx", ExcelReader.get_dPower_Inflows, ew.write_dPower_Inflows), - ("Power_Network", f"{args.caseStudyFolder}Power_Network.xlsx", ExcelReader.get_dPower_Network, ew.write_dPower_Network), - ("Power_Storage", f"{args.caseStudyFolder}Power_Storage.xlsx", ExcelReader.get_dPower_Storage, ew.write_dPower_Storage), - ("Power_ThermalGen", f"{args.caseStudyFolder}Power_ThermalGen.xlsx", ExcelReader.get_dPower_ThermalGen, ew.write_dPower_ThermalGen), - ("Power_VRES", f"{args.caseStudyFolder}Power_VRES.xlsx", ExcelReader.get_dPower_VRES, ew.write_VRES), - ("Power_VRESProfiles", f"{args.caseStudyFolder}Power_VRESProfiles.xlsx", ExcelReader.get_dPower_VRESProfiles, ew.write_VRESProfiles), - ("Power_WeightsK", f"{args.caseStudyFolder}Power_WeightsK.xlsx", ExcelReader.get_dPower_WeightsK, ew.write_dPower_WeightsK), - ("Power_WeightsRP", f"{args.caseStudyFolder}Power_WeightsRP.xlsx", ExcelReader.get_dPower_WeightsRP, ew.write_dPower_WeightsRP), - ("Power_Wind_TechnicalDetails", f"{args.caseStudyFolder}Power_Wind_TechnicalDetails.xlsx", ExcelReader.get_dPower_Wind_TechnicalDetails, ew.write_dPower_Wind_TechnicalDetails) + ("Data_Packages", f"{args.caseStudyFolder}Data_Packages.xlsx", ExcelReader.get_Data_Packages, ew.write_dData_Packages), + ("Data_Sources", f"{args.caseStudyFolder}Data_Sources.xlsx", ExcelReader.get_Data_Sources, ew.write_dData_Sources), + ("Global_Scenarios", f"{args.caseStudyFolder}Global_Scenarios.xlsx", ExcelReader.get_Global_Scenarios, ew.write_dGlobal_Scenarios), + ("Power_BusInfo", f"{args.caseStudyFolder}Power_BusInfo.xlsx", ExcelReader.get_Power_BusInfo, ew.write_dPower_BusInfo), + ("Power_Demand", f"{args.caseStudyFolder}Power_Demand.xlsx", ExcelReader.get_Power_Demand, ew.write_dPower_Demand), + ("Power_Hindex", f"{args.caseStudyFolder}Power_Hindex.xlsx", ExcelReader.get_Power_Hindex, ew.write_dPower_Hindex), + ("Power_Inflows", f"{args.caseStudyFolder}Power_Inflows.xlsx", ExcelReader.get_Power_Inflows, ew.write_dPower_Inflows), + ("Power_Network", f"{args.caseStudyFolder}Power_Network.xlsx", ExcelReader.get_Power_Network, ew.write_dPower_Network), + ("Power_Storage", f"{args.caseStudyFolder}Power_Storage.xlsx", ExcelReader.get_Power_Storage, ew.write_dPower_Storage), + ("Power_ThermalGen", f"{args.caseStudyFolder}Power_ThermalGen.xlsx", ExcelReader.get_Power_ThermalGen, ew.write_dPower_ThermalGen), + ("Power_VRES", f"{args.caseStudyFolder}Power_VRES.xlsx", ExcelReader.get_Power_VRES, ew.write_VRES), + ("Power_VRESProfiles", f"{args.caseStudyFolder}Power_VRESProfiles.xlsx", ExcelReader.get_Power_VRESProfiles, ew.write_VRESProfiles), + ("Power_WeightsK", f"{args.caseStudyFolder}Power_WeightsK.xlsx", ExcelReader.get_Power_WeightsK, ew.write_dPower_WeightsK), + ("Power_WeightsRP", f"{args.caseStudyFolder}Power_WeightsRP.xlsx", ExcelReader.get_Power_WeightsRP, ew.write_dPower_WeightsRP), + ("Power_Wind_TechnicalDetails", f"{args.caseStudyFolder}Power_Wind_TechnicalDetails.xlsx", ExcelReader.get_Power_Wind_TechnicalDetails, ew.write_dPower_Wind_TechnicalDetails) ] for excel_definition_id, file_path, read, write in combinations: diff --git a/tests/test_ExcelReaderWriter.py b/tests/test_ExcelReaderWriter.py index cc0d7ac..a21bad7 100644 --- a/tests/test_ExcelReaderWriter.py +++ b/tests/test_ExcelReaderWriter.py @@ -9,20 +9,20 @@ case_study_folder = "data/example/" ew = ExcelWriter() combinations = [ - ("Data_Packages", f"{case_study_folder}Data_Packages.xlsx", ExcelReader.get_dData_Packages, ew.write_dData_Packages), - ("Data_Sources", f"{case_study_folder}Data_Sources.xlsx", ExcelReader.get_dData_Sources, ew.write_dData_Sources), - ("Global_Scenarios", f"{case_study_folder}Global_Scenarios.xlsx", ExcelReader.get_dGlobal_Scenarios, ew.write_dGlobal_Scenarios), - ("Power_BusInfo", f"{case_study_folder}Power_BusInfo.xlsx", ExcelReader.get_dPower_BusInfo, ew.write_dPower_BusInfo), - ("Power_Demand", f"{case_study_folder}Power_Demand.xlsx", ExcelReader.get_dPower_Demand, ew.write_dPower_Demand), - ("Power_Hindex", f"{case_study_folder}Power_Hindex.xlsx", ExcelReader.get_dPower_Hindex, ew.write_dPower_Hindex), - ("Power_Inflows", f"{case_study_folder}Power_Inflows.xlsx", ExcelReader.get_dPower_Inflows, ew.write_dPower_Inflows), - ("Power_Network", f"{case_study_folder}Power_Network.xlsx", ExcelReader.get_dPower_Network, ew.write_dPower_Network), - ("Power_Storage", f"{case_study_folder}Power_Storage.xlsx", ExcelReader.get_dPower_Storage, ew.write_dPower_Storage), - ("Power_ThermalGen", f"{case_study_folder}Power_ThermalGen.xlsx", ExcelReader.get_dPower_ThermalGen, ew.write_dPower_ThermalGen), - ("Power_VRES", f"{case_study_folder}Power_VRES.xlsx", ExcelReader.get_dPower_VRES, ew.write_VRES), - ("Power_VRESProfiles", f"{case_study_folder}Power_VRESProfiles.xlsx", ExcelReader.get_dPower_VRESProfiles, ew.write_VRESProfiles), - ("Power_WeightsK", f"{case_study_folder}Power_WeightsK.xlsx", ExcelReader.get_dPower_WeightsK, ew.write_dPower_WeightsK), - ("Power_WeightsRP", f"{case_study_folder}Power_WeightsRP.xlsx", ExcelReader.get_dPower_WeightsRP, ew.write_dPower_WeightsRP), + ("Data_Packages", f"{case_study_folder}Data_Packages.xlsx", ExcelReader.get_Data_Packages, ew.write_dData_Packages), + ("Data_Sources", f"{case_study_folder}Data_Sources.xlsx", ExcelReader.get_Data_Sources, ew.write_dData_Sources), + ("Global_Scenarios", f"{case_study_folder}Global_Scenarios.xlsx", ExcelReader.get_Global_Scenarios, ew.write_dGlobal_Scenarios), + ("Power_BusInfo", f"{case_study_folder}Power_BusInfo.xlsx", ExcelReader.get_Power_BusInfo, ew.write_dPower_BusInfo), + ("Power_Demand", f"{case_study_folder}Power_Demand.xlsx", ExcelReader.get_Power_Demand, ew.write_dPower_Demand), + ("Power_Hindex", f"{case_study_folder}Power_Hindex.xlsx", ExcelReader.get_Power_Hindex, ew.write_dPower_Hindex), + ("Power_Inflows", f"{case_study_folder}Power_Inflows.xlsx", ExcelReader.get_Power_Inflows, ew.write_dPower_Inflows), + ("Power_Network", f"{case_study_folder}Power_Network.xlsx", ExcelReader.get_Power_Network, ew.write_dPower_Network), + ("Power_Storage", f"{case_study_folder}Power_Storage.xlsx", ExcelReader.get_Power_Storage, ew.write_dPower_Storage), + ("Power_ThermalGen", f"{case_study_folder}Power_ThermalGen.xlsx", ExcelReader.get_Power_ThermalGen, ew.write_dPower_ThermalGen), + ("Power_VRES", f"{case_study_folder}Power_VRES.xlsx", ExcelReader.get_Power_VRES, ew.write_VRES), + ("Power_VRESProfiles", f"{case_study_folder}Power_VRESProfiles.xlsx", ExcelReader.get_Power_VRESProfiles, ew.write_VRESProfiles), + ("Power_WeightsK", f"{case_study_folder}Power_WeightsK.xlsx", ExcelReader.get_Power_WeightsK, ew.write_dPower_WeightsK), + ("Power_WeightsRP", f"{case_study_folder}Power_WeightsRP.xlsx", ExcelReader.get_Power_WeightsRP, ew.write_dPower_WeightsRP), ] From 1442374aa2124d372d99fe3600c8c14afacffac8 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 13:52:06 +0200 Subject: [PATCH 07/13] Rename excel writer functions --- ExcelWriter.py | 52 ++++++++++++++++----------------- tests/test_ExcelReaderWriter.py | 24 +++++++-------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/ExcelWriter.py b/ExcelWriter.py index 3750cf4..5f80d8d 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -194,7 +194,7 @@ def _write_Excel_from_definition(self, data: pd.DataFrame, folder_path: str, exc wb.save(path) printer.information(f"Saved Excel file to '{path}' after {time.time() - start_time:.2f} seconds") - def write_dData_Packages(self, dData_Packages: pd.DataFrame, folder_path: str) -> None: + def write_Data_Packages(self, dData_Packages: pd.DataFrame, folder_path: str) -> None: """ Write the dData_Packages DataFrame to an Excel file in LEGO format. :param dData_Packages: DataFrame containing the dData_Packages data. @@ -203,7 +203,7 @@ def write_dData_Packages(self, dData_Packages: pd.DataFrame, folder_path: str) - """ self._write_Excel_from_definition(dData_Packages, folder_path, "Data_Packages") - def write_dData_Sources(self, dData_Sources: pd.DataFrame, folder_path: str) -> None: + def write_Data_Sources(self, dData_Sources: pd.DataFrame, folder_path: str) -> None: """ Write the dData_Sources DataFrame to an Excel file in LEGO format. :param dData_Sources: DataFrame containing the dData_Sources data. @@ -212,7 +212,7 @@ def write_dData_Sources(self, dData_Sources: pd.DataFrame, folder_path: str) -> """ self._write_Excel_from_definition(dData_Sources, folder_path, "Data_Sources") - def write_dPower_BusInfo(self, dPower_BusInfo: pd.DataFrame, folder_path: str) -> None: + def write_Power_BusInfo(self, dPower_BusInfo: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_BusInfo DataFrame to an Excel file in LEGO format. :param dPower_BusInfo: DataFrame containing the dPower_BusInfo data. @@ -221,7 +221,7 @@ def write_dPower_BusInfo(self, dPower_BusInfo: pd.DataFrame, folder_path: str) - """ self._write_Excel_from_definition(dPower_BusInfo, folder_path, "Power_BusInfo") - def write_dGlobal_Scenarios(self, dGlobal_Scenarios: pd.DataFrame, folder_path: str) -> None: + def write_Global_Scenarios(self, dGlobal_Scenarios: pd.DataFrame, folder_path: str) -> None: """ Write the dGlobal_Scenarios DataFrame to an Excel file in LEGO format. :param dGlobal_Scenarios: DataFrame containing the dGlobal_Scenarios data. @@ -230,7 +230,7 @@ def write_dGlobal_Scenarios(self, dGlobal_Scenarios: pd.DataFrame, folder_path: """ self._write_Excel_from_definition(dGlobal_Scenarios, folder_path, "Global_Scenarios") - def write_dPower_Demand(self, dPower_Demand: pd.DataFrame, folder_path: str) -> None: + def write_Power_Demand(self, dPower_Demand: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_Demand DataFrame to an Excel file in LEGO format. :param dPower_Demand: DataFrame containing the dPower_Demand data. @@ -240,7 +240,7 @@ def write_dPower_Demand(self, dPower_Demand: pd.DataFrame, folder_path: str) -> self._write_Excel_from_definition(dPower_Demand, folder_path, "Power_Demand") - def write_dPower_Hindex(self, dPower_Hindex: pd.DataFrame, folder_path: str) -> None: + def write_Power_Hindex(self, dPower_Hindex: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_Hindex DataFrame to an Excel file in LEGO format. :param dPower_Hindex: DataFrame containing the dPower_Hindex data. @@ -249,7 +249,7 @@ def write_dPower_Hindex(self, dPower_Hindex: pd.DataFrame, folder_path: str) -> """ self._write_Excel_from_definition(dPower_Hindex, folder_path, "Power_Hindex") - def write_dPower_Inflows(self, dPower_Inflows: pd.DataFrame, folder_path: str) -> None: + def write_Power_Inflows(self, dPower_Inflows: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_Inflows DataFrame to an Excel file in LEGO format. :param dPower_Inflows: DataFrame containing the dPower_Inflows data. @@ -258,7 +258,7 @@ def write_dPower_Inflows(self, dPower_Inflows: pd.DataFrame, folder_path: str) - """ self._write_Excel_from_definition(dPower_Inflows, folder_path, "Power_Inflows") - def write_dPower_Network(self, dPower_Network: pd.DataFrame, folder_path: str) -> None: + def write_Power_Network(self, dPower_Network: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_Network DataFrame to an Excel file in LEGO format. :param dPower_Network: DataFrame containing the dPower_Network data. @@ -267,7 +267,7 @@ def write_dPower_Network(self, dPower_Network: pd.DataFrame, folder_path: str) - """ self._write_Excel_from_definition(dPower_Network, folder_path, "Power_Network") - def write_dPower_Storage(self, dPower_Storage: pd.DataFrame, folder_path: str) -> None: + def write_Power_Storage(self, dPower_Storage: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_Storage DataFrame to an Excel file in LEGO format. :param dPower_Storage: DataFrame containing the dPower_Storage data. @@ -276,7 +276,7 @@ def write_dPower_Storage(self, dPower_Storage: pd.DataFrame, folder_path: str) - """ self._write_Excel_from_definition(dPower_Storage, folder_path, "Power_Storage") - def write_dPower_ThermalGen(self, dPower_ThermalGen: pd.DataFrame, folder_path: str) -> None: + def write_Power_ThermalGen(self, dPower_ThermalGen: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_ThermalGen DataFrame to an Excel file in LEGO format. :param dPower_ThermalGen: DataFrame containing the dPower_ThermalGen data. @@ -303,7 +303,7 @@ def write_VRESProfiles(self, dPower_VRESProfiles: pd.DataFrame, folder_path: str """ self._write_Excel_from_definition(dPower_VRESProfiles, folder_path, "Power_VRESProfiles") - def write_dPower_WeightsK(self, dPower_WeightsK: pd.DataFrame, folder_path: str) -> None: + def write_Power_WeightsK(self, dPower_WeightsK: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_WeightsK DataFrame to an Excel file in LEGO format. :param dPower_WeightsK: DataFrame containing the dPower_WeightsK data. @@ -312,7 +312,7 @@ def write_dPower_WeightsK(self, dPower_WeightsK: pd.DataFrame, folder_path: str) """ self._write_Excel_from_definition(dPower_WeightsK, folder_path, "Power_WeightsK") - def write_dPower_WeightsRP(self, dPower_WeightsRP: pd.DataFrame, folder_path: str) -> None: + def write_Power_WeightsRP(self, dPower_WeightsRP: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_WeightsRP DataFrame to an Excel file in LEGO format. :param dPower_WeightsRP: DataFrame containing the dPower_WeightsRP data. @@ -321,7 +321,7 @@ def write_dPower_WeightsRP(self, dPower_WeightsRP: pd.DataFrame, folder_path: st """ self._write_Excel_from_definition(dPower_WeightsRP, folder_path, "Power_WeightsRP") - def write_dPower_Wind_TechnicalDetails(self, dPower_Wind_TechnicalDetails: pd.DataFrame, folder_path: str) -> None: + def write_Power_Wind_TechnicalDetails(self, dPower_Wind_TechnicalDetails: pd.DataFrame, folder_path: str) -> None: """ Write the dPower_Wind_TechnicalDetails DataFrame to an Excel file in LEGO format. :param dPower_Wind_TechnicalDetails: DataFrame containing the dPower_Wind_TechnicalDetails data. @@ -383,21 +383,21 @@ def model_to_excel(model: pyomo.core.Model, target_path: str) -> None: printer.separator() combinations = [ - ("Data_Packages", f"{args.caseStudyFolder}Data_Packages.xlsx", ExcelReader.get_Data_Packages, ew.write_dData_Packages), - ("Data_Sources", f"{args.caseStudyFolder}Data_Sources.xlsx", ExcelReader.get_Data_Sources, ew.write_dData_Sources), - ("Global_Scenarios", f"{args.caseStudyFolder}Global_Scenarios.xlsx", ExcelReader.get_Global_Scenarios, ew.write_dGlobal_Scenarios), - ("Power_BusInfo", f"{args.caseStudyFolder}Power_BusInfo.xlsx", ExcelReader.get_Power_BusInfo, ew.write_dPower_BusInfo), - ("Power_Demand", f"{args.caseStudyFolder}Power_Demand.xlsx", ExcelReader.get_Power_Demand, ew.write_dPower_Demand), - ("Power_Hindex", f"{args.caseStudyFolder}Power_Hindex.xlsx", ExcelReader.get_Power_Hindex, ew.write_dPower_Hindex), - ("Power_Inflows", f"{args.caseStudyFolder}Power_Inflows.xlsx", ExcelReader.get_Power_Inflows, ew.write_dPower_Inflows), - ("Power_Network", f"{args.caseStudyFolder}Power_Network.xlsx", ExcelReader.get_Power_Network, ew.write_dPower_Network), - ("Power_Storage", f"{args.caseStudyFolder}Power_Storage.xlsx", ExcelReader.get_Power_Storage, ew.write_dPower_Storage), - ("Power_ThermalGen", f"{args.caseStudyFolder}Power_ThermalGen.xlsx", ExcelReader.get_Power_ThermalGen, ew.write_dPower_ThermalGen), + ("Data_Packages", f"{args.caseStudyFolder}Data_Packages.xlsx", ExcelReader.get_Data_Packages, ew.write_Data_Packages), + ("Data_Sources", f"{args.caseStudyFolder}Data_Sources.xlsx", ExcelReader.get_Data_Sources, ew.write_Data_Sources), + ("Global_Scenarios", f"{args.caseStudyFolder}Global_Scenarios.xlsx", ExcelReader.get_Global_Scenarios, ew.write_Global_Scenarios), + ("Power_BusInfo", f"{args.caseStudyFolder}Power_BusInfo.xlsx", ExcelReader.get_Power_BusInfo, ew.write_Power_BusInfo), + ("Power_Demand", f"{args.caseStudyFolder}Power_Demand.xlsx", ExcelReader.get_Power_Demand, ew.write_Power_Demand), + ("Power_Hindex", f"{args.caseStudyFolder}Power_Hindex.xlsx", ExcelReader.get_Power_Hindex, ew.write_Power_Hindex), + ("Power_Inflows", f"{args.caseStudyFolder}Power_Inflows.xlsx", ExcelReader.get_Power_Inflows, ew.write_Power_Inflows), + ("Power_Network", f"{args.caseStudyFolder}Power_Network.xlsx", ExcelReader.get_Power_Network, ew.write_Power_Network), + ("Power_Storage", f"{args.caseStudyFolder}Power_Storage.xlsx", ExcelReader.get_Power_Storage, ew.write_Power_Storage), + ("Power_ThermalGen", f"{args.caseStudyFolder}Power_ThermalGen.xlsx", ExcelReader.get_Power_ThermalGen, ew.write_Power_ThermalGen), ("Power_VRES", f"{args.caseStudyFolder}Power_VRES.xlsx", ExcelReader.get_Power_VRES, ew.write_VRES), ("Power_VRESProfiles", f"{args.caseStudyFolder}Power_VRESProfiles.xlsx", ExcelReader.get_Power_VRESProfiles, ew.write_VRESProfiles), - ("Power_WeightsK", f"{args.caseStudyFolder}Power_WeightsK.xlsx", ExcelReader.get_Power_WeightsK, ew.write_dPower_WeightsK), - ("Power_WeightsRP", f"{args.caseStudyFolder}Power_WeightsRP.xlsx", ExcelReader.get_Power_WeightsRP, ew.write_dPower_WeightsRP), - ("Power_Wind_TechnicalDetails", f"{args.caseStudyFolder}Power_Wind_TechnicalDetails.xlsx", ExcelReader.get_Power_Wind_TechnicalDetails, ew.write_dPower_Wind_TechnicalDetails) + ("Power_WeightsK", f"{args.caseStudyFolder}Power_WeightsK.xlsx", ExcelReader.get_Power_WeightsK, ew.write_Power_WeightsK), + ("Power_WeightsRP", f"{args.caseStudyFolder}Power_WeightsRP.xlsx", ExcelReader.get_Power_WeightsRP, ew.write_Power_WeightsRP), + ("Power_Wind_TechnicalDetails", f"{args.caseStudyFolder}Power_Wind_TechnicalDetails.xlsx", ExcelReader.get_Power_Wind_TechnicalDetails, ew.write_Power_Wind_TechnicalDetails) ] for excel_definition_id, file_path, read, write in combinations: diff --git a/tests/test_ExcelReaderWriter.py b/tests/test_ExcelReaderWriter.py index a21bad7..c85bb56 100644 --- a/tests/test_ExcelReaderWriter.py +++ b/tests/test_ExcelReaderWriter.py @@ -9,20 +9,20 @@ case_study_folder = "data/example/" ew = ExcelWriter() combinations = [ - ("Data_Packages", f"{case_study_folder}Data_Packages.xlsx", ExcelReader.get_Data_Packages, ew.write_dData_Packages), - ("Data_Sources", f"{case_study_folder}Data_Sources.xlsx", ExcelReader.get_Data_Sources, ew.write_dData_Sources), - ("Global_Scenarios", f"{case_study_folder}Global_Scenarios.xlsx", ExcelReader.get_Global_Scenarios, ew.write_dGlobal_Scenarios), - ("Power_BusInfo", f"{case_study_folder}Power_BusInfo.xlsx", ExcelReader.get_Power_BusInfo, ew.write_dPower_BusInfo), - ("Power_Demand", f"{case_study_folder}Power_Demand.xlsx", ExcelReader.get_Power_Demand, ew.write_dPower_Demand), - ("Power_Hindex", f"{case_study_folder}Power_Hindex.xlsx", ExcelReader.get_Power_Hindex, ew.write_dPower_Hindex), - ("Power_Inflows", f"{case_study_folder}Power_Inflows.xlsx", ExcelReader.get_Power_Inflows, ew.write_dPower_Inflows), - ("Power_Network", f"{case_study_folder}Power_Network.xlsx", ExcelReader.get_Power_Network, ew.write_dPower_Network), - ("Power_Storage", f"{case_study_folder}Power_Storage.xlsx", ExcelReader.get_Power_Storage, ew.write_dPower_Storage), - ("Power_ThermalGen", f"{case_study_folder}Power_ThermalGen.xlsx", ExcelReader.get_Power_ThermalGen, ew.write_dPower_ThermalGen), + ("Data_Packages", f"{case_study_folder}Data_Packages.xlsx", ExcelReader.get_Data_Packages, ew.write_Data_Packages), + ("Data_Sources", f"{case_study_folder}Data_Sources.xlsx", ExcelReader.get_Data_Sources, ew.write_Data_Sources), + ("Global_Scenarios", f"{case_study_folder}Global_Scenarios.xlsx", ExcelReader.get_Global_Scenarios, ew.write_Global_Scenarios), + ("Power_BusInfo", f"{case_study_folder}Power_BusInfo.xlsx", ExcelReader.get_Power_BusInfo, ew.write_Power_BusInfo), + ("Power_Demand", f"{case_study_folder}Power_Demand.xlsx", ExcelReader.get_Power_Demand, ew.write_Power_Demand), + ("Power_Hindex", f"{case_study_folder}Power_Hindex.xlsx", ExcelReader.get_Power_Hindex, ew.write_Power_Hindex), + ("Power_Inflows", f"{case_study_folder}Power_Inflows.xlsx", ExcelReader.get_Power_Inflows, ew.write_Power_Inflows), + ("Power_Network", f"{case_study_folder}Power_Network.xlsx", ExcelReader.get_Power_Network, ew.write_Power_Network), + ("Power_Storage", f"{case_study_folder}Power_Storage.xlsx", ExcelReader.get_Power_Storage, ew.write_Power_Storage), + ("Power_ThermalGen", f"{case_study_folder}Power_ThermalGen.xlsx", ExcelReader.get_Power_ThermalGen, ew.write_Power_ThermalGen), ("Power_VRES", f"{case_study_folder}Power_VRES.xlsx", ExcelReader.get_Power_VRES, ew.write_VRES), ("Power_VRESProfiles", f"{case_study_folder}Power_VRESProfiles.xlsx", ExcelReader.get_Power_VRESProfiles, ew.write_VRESProfiles), - ("Power_WeightsK", f"{case_study_folder}Power_WeightsK.xlsx", ExcelReader.get_Power_WeightsK, ew.write_dPower_WeightsK), - ("Power_WeightsRP", f"{case_study_folder}Power_WeightsRP.xlsx", ExcelReader.get_Power_WeightsRP, ew.write_dPower_WeightsRP), + ("Power_WeightsK", f"{case_study_folder}Power_WeightsK.xlsx", ExcelReader.get_Power_WeightsK, ew.write_Power_WeightsK), + ("Power_WeightsRP", f"{case_study_folder}Power_WeightsRP.xlsx", ExcelReader.get_Power_WeightsRP, ew.write_Power_WeightsRP), ] From ef7db251f02ef25294f60fe001abb848987d5d39 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 13:53:44 +0200 Subject: [PATCH 08/13] Update power factor description in table definitions --- TableDefinitions.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TableDefinitions.xml b/TableDefinitions.xml index b9b5933..f85caf8 100644 --- a/TableDefinitions.xml +++ b/TableDefinitions.xml @@ -691,9 +691,9 @@ Power Factor - Power Factor for the given Year + Power Factor for this unit, correcting for maintenance, terrain specifics, etc. [0, n] - 15.0 + 25.0 rightInt From ef6af13ad3cb3966eea68a58a854f50789435771 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 13:56:44 +0200 Subject: [PATCH 09/13] Update year description in table definitions --- TableDefinitions.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/TableDefinitions.xml b/TableDefinitions.xml index f85caf8..208ca7c 100644 --- a/TableDefinitions.xml +++ b/TableDefinitions.xml @@ -843,6 +843,13 @@ 15.0 general + + Year + Year of the Yearly Production that is given + [0, n] + 19.5 + general + Commission Year Year where it is commissioned (1.1.xxxx) @@ -857,13 +864,6 @@ 19.5 rightInt - - Year - Year - [0, n] - 18.0 - general - Yearly Production Yearly production for calibration From e8525d6ffa54066a519a6f1692f3a175bd97e831 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 13:57:57 +0200 Subject: [PATCH 10/13] Update yearly production description in table definitions --- TableDefinitions.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TableDefinitions.xml b/TableDefinitions.xml index 208ca7c..81ee5ed 100644 --- a/TableDefinitions.xml +++ b/TableDefinitions.xml @@ -866,9 +866,9 @@ Yearly Production - Yearly production for calibration + Yearly production for calibration (i.e., used to calculate Power Factor if it's not specified yet) [MW/h] - 18.0 + 28.0 general From 40cd183e8ab957b764ef5b7fd6703bcf3325e978 Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 15:50:43 +0200 Subject: [PATCH 11/13] Generalize table definitions path in excel writer --- ExcelWriter.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ExcelWriter.py b/ExcelWriter.py index 5f80d8d..9b82a21 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -20,12 +20,18 @@ class ExcelWriter: - def __init__(self, excel_definitions_path: str = "TableDefinitions.xml"): + def __init__(self, excel_definitions_path: str = None): """ Initialize the ExcelWriter with the XML root element. :param excel_definitions_path: Path to the TableDefinitions.xml file. """ + if excel_definitions_path is None: + from pathlib import Path + excel_definitions_path = str(Path(__file__).parent / "TableDefinitions.xml") + + if not Path(excel_definitions_path).exists(): + raise FileNotFoundError(f"TableDefinitions.xml not found at {excel_definitions_path}") self.excel_definitions_path = excel_definitions_path self.xml_tree = ET.parse(excel_definitions_path) From 181b6b56e270fc78ac832052a35ed8ad1acb3c0d Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 15:55:31 +0200 Subject: [PATCH 12/13] Update test to support new Wind Technical Details format --- tests/test_ExcelReaderWriter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_ExcelReaderWriter.py b/tests/test_ExcelReaderWriter.py index c85bb56..396c084 100644 --- a/tests/test_ExcelReaderWriter.py +++ b/tests/test_ExcelReaderWriter.py @@ -23,6 +23,7 @@ ("Power_VRESProfiles", f"{case_study_folder}Power_VRESProfiles.xlsx", ExcelReader.get_Power_VRESProfiles, ew.write_VRESProfiles), ("Power_WeightsK", f"{case_study_folder}Power_WeightsK.xlsx", ExcelReader.get_Power_WeightsK, ew.write_Power_WeightsK), ("Power_WeightsRP", f"{case_study_folder}Power_WeightsRP.xlsx", ExcelReader.get_Power_WeightsRP, ew.write_Power_WeightsRP), + ("Power_Wind_TechnicalDetails", f"{case_study_folder}Power_Wind_TechnicalDetails.xlsx", ExcelReader.get_Power_Wind_TechnicalDetails, ew.write_Power_Wind_TechnicalDetails) ] From dbac8ffcd7bb4f2f703b9d8dae938d81c337d70d Mon Sep 17 00:00:00 2001 From: Marco Anarmo Date: Tue, 26 Aug 2025 15:57:06 +0200 Subject: [PATCH 13/13] Upload formatted Wind Technical Details file --- data/example/Power_Wind_TechnicalDetails.xlsx | Bin 9128 -> 9202 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/example/Power_Wind_TechnicalDetails.xlsx b/data/example/Power_Wind_TechnicalDetails.xlsx index dc66a5e8b73162729025da33b8a2d3ddd94dbd79..af3736a5a71f0586f203d15c355d1ab5251cb1e0 100644 GIT binary patch delta 3679 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 ZADbYEIK2x_5Duor4hV|S`0@X4{SSrJew+XR