diff --git a/CaseStudy.py b/CaseStudy.py index 8f1bb5f..25e0697 100644 --- a/CaseStudy.py +++ b/CaseStudy.py @@ -19,14 +19,18 @@ class CaseStudy: # Lists of dataframes based on their dependencies - every table should only be present in one of these lists - rpk_dependent_dataframes: list[str] = ["dPower_Demand", + rpk_dependent_dataframes: list[str] = ["dHeat_P2H_Conversion_Factors" + "dHeat_Demand", + "dPower_Demand", "dPower_Hindex", "dPower_ImportExport", "dPower_Inflows", "dPower_VRESProfiles"] rp_only_dependent_dataframes: list[str] = ["dPower_WeightsRP"] k_only_dependent_dataframes: list[str] = ["dPower_WeightsK"] - non_time_dependent_dataframes: list[str] = ["dPower_BusInfo", + non_time_dependent_dataframes: list[str] = ["dHeat_Nodes", + "dHeat_P2H_Technologies", + "dPower_BusInfo", "dPower_Network", "dPower_Storage", "dPower_ThermalGen", @@ -48,19 +52,23 @@ def __init__(self, n_jobs: int = 4, global_parameters_file: str = "Global_Parameters.xlsx", dGlobal_Parameters: pd.DataFrame = None, global_scenarios_file: str = "Global_Scenarios.xlsx", dGlobal_Scenarios: pd.DataFrame = None, - power_parameters_file: str = "Power_Parameters.xlsx", dPower_Parameters: pd.DataFrame = None, + heat_demand_file: str = "Heat_Demand.xlsx", dHeat_Demand: pd.DataFrame = None, + heat_nodes_file: str = "Heat_Nodes.xlsx", dHeat_Nodes: pd.DataFrame = None, + heat_p2h_conversion_factors_file: str = "Heat_P2H_Conversion_Factors.xlsx", dHeat_P2H_Conversion_Factors: pd.DataFrame = None, + heat_p2h_technologies_file: str = "Heat_P2H_Technologies.xlsx", dHeat_P2H_Technologies: pd.DataFrame = None, power_businfo_file: str = "Power_BusInfo.xlsx", dPower_BusInfo: pd.DataFrame = None, + power_demand_file: str = "Power_Demand.xlsx", dPower_Demand: pd.DataFrame = None, + power_hindex_file: str = "Power_Hindex.xlsx", dPower_Hindex: pd.DataFrame = None, + power_importexport_file: str = "Power_ImportExport.xlsx", dPower_ImportExport: pd.DataFrame = None, + power_inflows_file: str = "Power_Inflows.xlsx", dPower_Inflows: pd.DataFrame = None, power_network_file: str = "Power_Network.xlsx", dPower_Network: pd.DataFrame = None, + power_parameters_file: str = "Power_Parameters.xlsx", dPower_Parameters: pd.DataFrame = None, + power_storage_file: str = "Power_Storage.xlsx", dPower_Storage: pd.DataFrame = None, power_thermalgen_file: str = "Power_ThermalGen.xlsx", dPower_ThermalGen: pd.DataFrame = None, power_vres_file: str = "Power_VRES.xlsx", dPower_VRES: pd.DataFrame = None, - power_demand_file: str = "Power_Demand.xlsx", dPower_Demand: pd.DataFrame = None, - power_inflows_file: str = "Power_Inflows.xlsx", dPower_Inflows: pd.DataFrame = None, power_vresprofiles_file: str = "Power_VRESProfiles.xlsx", dPower_VRESProfiles: pd.DataFrame = None, - power_storage_file: str = "Power_Storage.xlsx", dPower_Storage: pd.DataFrame = None, - power_weightsrp_file: str = "Power_WeightsRP.xlsx", dPower_WeightsRP: pd.DataFrame = None, power_weightsk_file: str = "Power_WeightsK.xlsx", dPower_WeightsK: pd.DataFrame = None, - power_hindex_file: str = "Power_Hindex.xlsx", dPower_Hindex: pd.DataFrame = None, - power_importexport_file: str = "Power_ImportExport.xlsx", dPower_ImportExport: pd.DataFrame = None, + power_weightsrp_file: str = "Power_WeightsRP.xlsx", dPower_WeightsRP: pd.DataFrame = None, clip_method: str = "none", clip_value: float = 0): self.data_folder = str(data_folder) if str(data_folder).endswith("/") else str(data_folder) + "/" self.do_not_scale_units = do_not_scale_units @@ -131,6 +139,34 @@ def __init__(self, self.dPower_WeightsK = dPower_WeightsK # Add conditional tasks (dependent on dPower_Parameters) + if self.dGlobal_Parameters["pEnableHeat"]: + self.heat_demand_file = heat_demand_file + if dHeat_Demand is None: + tasks.append(("dHeat_Demand", ExcelReader.get_Heat_Demand, (self.data_folder + self.heat_demand_file,))) + else: + self.dHeat_Demand = dHeat_Demand + + if self.dGlobal_Parameters["pEnableHeat"]: + self.heat_p2h_conversion_factors_file = heat_p2h_conversion_factors_file + if dHeat_P2H_Conversion_Factors is None: + tasks.append(("dHeat_P2H_Conversion_Factors", ExcelReader.get_Heat_P2H_Conversion_Factors, (self.data_folder + self.heat_p2h_conversion_factors_file,))) + else: + self.dHeat_P2H_Conversion_Factors = dHeat_P2H_Conversion_Factors + + if self.dGlobal_Parameters["pEnableHeat"]: + self.heat_nodes_file = heat_nodes_file + if dHeat_Nodes is None: + tasks.append(("dHeat_Nodes", ExcelReader.get_Heat_Nodes, (self.data_folder + self.heat_nodes_file,))) + else: + self.dHeat_Nodes = dHeat_Nodes + + if self.dGlobal_Parameters["pEnableHeat"]: + self.heat_p2h_technologies_file = heat_p2h_technologies_file + if dHeat_P2H_Technologies is None: + tasks.append(("dHeat_P2H_Technologies", ExcelReader.get_Heat_P2H_Technologies, (self.data_folder + self.heat_p2h_technologies_file,))) + else: + self.dHeat_P2H_Technologies = dHeat_P2H_Technologies + if self.dPower_Parameters["pEnableThermalGen"]: self.power_thermalgen_file = power_thermalgen_file if dPower_ThermalGen is None: @@ -305,6 +341,9 @@ def scale_CaseStudy(self): if self.dPower_Parameters["pEnablePowerImportExport"]: self.scale_dPower_ImportExport() + if self.dGlobal_Parameters["pEnableHeat"]: + self.scale_dHeat_Demand() + def remove_scaling(self): self.power_scaling_factor = 1 / self.power_scaling_factor self.cost_scaling_factor = 1 / self.cost_scaling_factor @@ -316,6 +355,13 @@ def remove_scaling(self): self.cost_scaling_factor = 1 / self.cost_scaling_factor self.angle_to_rad_scaling_factor = 1 / self.angle_to_rad_scaling_factor + def scale_dHeat_Demand(self): + self.dHeat_Demand["value"] *= self.power_scaling_factor + + def scale_dHeat_P2H_Technologies(self): + self.dHeat_P2H_Technologies["InstCap"] *= self.power_scaling_factor + self.dHeat_P2H_Technologies["ImpStoCap"] *= self.power_scaling_factor + def scale_dPower_Parameters(self): self.dPower_Parameters["pSBase"] *= self.power_scaling_factor self.dPower_Parameters["pENSCost"] *= self.cost_scaling_factor / self.power_scaling_factor @@ -410,7 +456,7 @@ def scale_dPower_ImportExport(self): def get_dGlobal_Parameters(self): file_path = self.data_folder + self.global_parameters_file - version_spec = "v0.1.0" + version_spec = "v0.1.1" fail_on_wrong_version = False try: @@ -430,7 +476,7 @@ def get_dGlobal_Parameters(self): dGlobal_Parameters = dGlobal_Parameters.drop(dGlobal_Parameters.columns[0], axis=1) dGlobal_Parameters = dGlobal_Parameters.set_index('Solver Options') - self.yesNo_to_bool(dGlobal_Parameters, ['pEnableRMIP']) + self.yesNo_to_bool(dGlobal_Parameters, ['pEnableRMIP', 'pEnableHeat']) # Transform to make it easier to access values dGlobal_Parameters = dGlobal_Parameters.drop(dGlobal_Parameters.columns[1:], axis=1) # Drop all columns but "Value" (rest is just for information in the Excel) diff --git a/ExcelReader.py b/ExcelReader.py index 6ae4ab9..409d5b4 100644 --- a/ExcelReader.py +++ b/ExcelReader.py @@ -145,6 +145,64 @@ def get_Global_Scenarios(excel_file_path: str, keep_excluded_entries: bool = Fal return dGlobal_Scenarios +def get_Heat_Demand(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: + """ + Read the dHeat_Demand data from the Excel file. + :param excel_file_path: Path to the Excel file + :param keep_excluded_entries: Unused but kept for compatibility with other functions + :param fail_on_wrong_version: If True, raise an error if the version of the Excel file does not match the expected version + :return: dHeat_Demand + """ + dHeat_Demand = __read_pivoted_file(excel_file_path, "v0.0.1", ['rp', 'k', 'hn', 'dt'], 'k', ['rp', 'hn', 'dt', 'dataPackage', 'dataSource', 'id'], False, False, fail_on_wrong_version) + + if keep_excluded_entries: + printer.warning("'keep_excluded_entries' is set for 'get_Heat_Demand', although nothing is excluded anyway - please check if this is intended.") + + return dHeat_Demand + + +def get_Heat_Nodes(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: + """ + Read the dHeat_Node_Links 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: dHeat_Node_Links + """ + dHeat_Node_Links = __read_non_pivoted_file(excel_file_path, "v0.0.1", ["i", "hn"], True, keep_excluded_entries, fail_on_wrong_version) + + return dHeat_Node_Links + + +def get_Heat_P2H_Conversion_Factors(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: + """ + Read the dHeat_P2H_Conversion_Factors data from the Excel file. + :param excel_file_path: Path to the Excel file + :param keep_excluded_entries: Unused but kept for compatibility with other functions + :param fail_on_wrong_version: If True, raise an error if the version of the Excel file does not match the expected version + :return: dHeat_P2H_Conversion_Factors + """ + dHeat_P2H_Conversion_Factors = __read_pivoted_file(excel_file_path, "v0.0.1", ['rp', 'k', 'hn', 'dt', 'htec'], 'k', ['rp', 'hn', 'dt', 'htec', 'dataPackage', 'dataSource', 'id'], False, False, fail_on_wrong_version) + + if keep_excluded_entries: + printer.warning("'keep_excluded_entries' is set for 'dHeat_P2H_Conversion_Factors', although nothing is excluded anyway - please check if this is intended.") + + return dHeat_P2H_Conversion_Factors + + +def get_Heat_P2H_Technologies(excel_file_path: str, keep_excluded_entries: bool = False, fail_on_wrong_version: bool = False) -> pd.DataFrame: + """ + Read the dHeat_P2H_Technologies 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: dHeat_P2H_Technologies + """ + dHeat_P2H_Technologies = __read_non_pivoted_file(excel_file_path, "v0.0.1", ["hn", "dt", "htec"], False, keep_excluded_entries, fail_on_wrong_version) + + return dHeat_P2H_Technologies + + 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. diff --git a/ExcelWriter.py b/ExcelWriter.py index f1ee83e..7abff1d 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -298,6 +298,46 @@ def write_Global_Scenarios(self, dGlobal_Scenarios: pd.DataFrame, folder_path: s """ self._write_Excel_from_definition(dGlobal_Scenarios, folder_path, "Global_Scenarios") + def write_Heat_Demand(self, dHeat_Demand: pd.DataFrame, folder_path: str) -> None: + """ + Write the dHeat_Demand DataFrame to an Excel file in LEGO format. + :param dHeat_Demand: DataFrame containing the dHeat_Demand data. + :param folder_path: Path to the folder where the Excel file will be saved. + :return: None + """ + + self._write_Excel_from_definition(dHeat_Demand, folder_path, "Heat_Demand") + + def write_Heat_Nodes(self, dHeat_Nodes: pd.DataFrame, folder_path: str) -> None: + """ + Write the dHeat_Nodes DataFrame to an Excel file in LEGO format. + :param dHeat_Nodes: DataFrame containing the dHeat_Demand data. + :param folder_path: Path to the folder where the Excel file will be saved. + :return: None + """ + + self._write_Excel_from_definition(dHeat_Nodes, folder_path, "Heat_Nodes") + + def write_Heat_P2H_Technologies(self, dHeat_P2H_Technologies: pd.DataFrame, folder_path: str) -> None: + """ + Write the dHeat_P2H_Technologies DataFrame to an Excel file in LEGO format. + :param dHeat_P2H_Technologies: DataFrame containing the dHeat_Demand data. + :param folder_path: Path to the folder where the Excel file will be saved. + :return: None + """ + + self._write_Excel_from_definition(dHeat_P2H_Technologies, folder_path, "Heat_P2H_Technologies") + + def write_Heat_P2H_Conversion_Factors(self, dHeat_P2H_Conversion_Factors: pd.DataFrame, folder_path: str) -> None: + """ + Write the dHeat_P2H_Conversion_Factors DataFrame to an Excel file in LEGO format. + :param dHeat_P2H_Conversion_Factors: DataFrame containing the dHeat_Demand data. + :param folder_path: Path to the folder where the Excel file will be saved. + :return: None + """ + + self._write_Excel_from_definition(dHeat_P2H_Conversion_Factors, folder_path, "Heat_P2H_Conversion_Factors") + 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. @@ -534,6 +574,10 @@ def model_to_excel(model: pyomo.core.Model, target_path: str) -> None: ("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), + ("Heat_Demand", f"{args.caseStudyFolder}Heat_Demand.xlsx", ExcelReader.get_Heat_Demand, ew.write_Heat_Demand), + ("Heat_Nodes", f"{args.caseStudyFolder}Heat_Nodes.xlsx", ExcelReader.get_Heat_Nodes, ew.write_Heat_Nodes), + ("Heat_P2H_Technologies", f"{args.caseStudyFolder}Heat_P2H_Technologies.xlsx", ExcelReader.get_Heat_P2H_Technologies, ew.write_Heat_P2H_Technologies), + ("Heat_P2H_Conversion_Factors", f"{args.caseStudyFolder}Heat_P2H_Conversion_Factors.xlsx", ExcelReader.get_Heat_P2H_Conversion_Factors, ew.write_Heat_P2H_Conversion_Factors), ("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_Demand_KInRows", f"{args.caseStudyFolder}Power_Demand_KInRows.xlsx", ExcelReader.get_Power_Demand_KInRows, ew.write_Power_Demand_KInRows), diff --git a/TableDefinitions.xml b/TableDefinitions.xml index 18771ac..e3cea04 100644 --- a/TableDefinitions.xml +++ b/TableDefinitions.xml @@ -39,6 +39,70 @@ + + v0.0.1 + Heat - Demand + 30.0 + + + + + + + + + + + + + v0.0.1 + Heat - Node Links + 45.0 + + + + + + + + + + + + + v0.0.1 + Heat - P2H Conversion Factors + 30.0 + + + + + + + + + + + + + + v0.0.1 + Heat - P2H Technologies + 60.0 + + + + + + + + + + + + + + v0.1.2 Power - Bus information @@ -426,6 +490,13 @@ 21.0 rightFloat2 + + Heat Demand Type + Type of Heat Demand + [residential, industry, ...] + 22.0 + general + Efficiency Efficiency calculated as 1/input for 1 MWh, i.e. 0.5 (or 50%) if 2 MWh fuel are required for 1 MWh electricity @@ -489,6 +560,20 @@ 15.0 general + + Heat Node + Name of heat node + [hn] + 15.0 + general + + + Heat Conversion Technology + Type of Conversion Technology + [ASHP, WSHP, electric, …] + 27.0 + general + Hub Height Heigth of the turbine @@ -510,6 +595,13 @@ 19.00 dbKey + + Implicit Thermal Storage Capacity + Implicit thermal storage capacity + [MWh_thermal] + 30.0 + rightFloat2 + InertiaConst Inertia constant H of the unit @@ -524,6 +616,13 @@ 15.0 rightFloat2 + + Installed Capacity + Installed Capacity termal + [MW_thermal] + 17.0 + rightFloat2 + InvestCost Annualized investment cost per MW @@ -853,6 +952,13 @@ 41.0 general + + Self Discharge Thermal + Self-discharge rate of thermal storage unit + [%, 0-1] + 20.0 + rightFloat2 + Self Discharge Self discharge of the storage unit @@ -881,6 +987,13 @@ 15.0 general + + Heating technology share + Share of conversion technology at hn for dt + [%, 0-1] + 24.0 + rightFloat2 + Vmax Cut-out wind speed @@ -991,6 +1104,14 @@ 10.57 rightFloat2 + + + k + Conversion Efficiency at this representative time step k + [-] + 10.57 + rightFloat2 + i @@ -1007,6 +1128,22 @@ 10.57 rightInt + + + k + Heat Demand at this representative time step k + [MW] + 10.57 + rightInt + + + + k + Heat Conversion Efficiency at this representative time step k + [-] + 10.57 + rightFloat2 + g diff --git a/data/example/Global_Parameters.xlsx b/data/example/Global_Parameters.xlsx new file mode 100644 index 0000000..0ff5a7d Binary files /dev/null and b/data/example/Global_Parameters.xlsx differ diff --git a/data/example/Heat_Demand.xlsx b/data/example/Heat_Demand.xlsx new file mode 100644 index 0000000..a9d8e90 Binary files /dev/null and b/data/example/Heat_Demand.xlsx differ diff --git a/data/example/Heat_Nodes.xlsx b/data/example/Heat_Nodes.xlsx new file mode 100644 index 0000000..d2906c4 Binary files /dev/null and b/data/example/Heat_Nodes.xlsx differ diff --git a/data/example/Heat_P2H_Conversion_Factors.xlsx b/data/example/Heat_P2H_Conversion_Factors.xlsx new file mode 100644 index 0000000..4a53dac Binary files /dev/null and b/data/example/Heat_P2H_Conversion_Factors.xlsx differ diff --git a/data/example/Heat_P2H_Technologies.xlsx b/data/example/Heat_P2H_Technologies.xlsx new file mode 100644 index 0000000..7af9875 Binary files /dev/null and b/data/example/Heat_P2H_Technologies.xlsx differ