From e7a08656cdf507c97d2ec22a5f235ed37364b69c Mon Sep 17 00:00:00 2001 From: Karelly Stephan <172272351+KaStephan@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:15:48 +0200 Subject: [PATCH 1/5] Add bool and Excel writer now for real i promise --- CaseStudy.py | 4 ++-- ExcelWriter.py | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/CaseStudy.py b/CaseStudy.py index 8162dde..2c894f0 100644 --- a/CaseStudy.py +++ b/CaseStudy.py @@ -146,7 +146,7 @@ def get_dGlobal_Parameters(self): dGlobal_Parameters = pd.read_excel(self.example_folder + self.global_parameters_file, skiprows=[0, 1]) dGlobal_Parameters = dGlobal_Parameters.drop(dGlobal_Parameters.columns[0], axis=1) dGlobal_Parameters = dGlobal_Parameters.set_index('Sectors') - + self.yesNo_to_bool(dGlobal_Parameters,['pEnableRMIP']) # 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) dGlobal_Parameters = dict({(parameter_name, parameter_value["Value"]) for parameter_name, parameter_value in dGlobal_Parameters.iterrows()}) # Transform into dictionary @@ -159,7 +159,7 @@ def get_dPower_Parameters(self): dPower_Parameters = dPower_Parameters.dropna(how="all") dPower_Parameters = dPower_Parameters.set_index('General') - self.yesNo_to_bool(dPower_Parameters, ['pEnableChDisPower', 'pFixStInterResToIniReserve', 'pEnableSoftLineLoadLimits', 'pEnableThermalGen', 'pEnableRoR', 'pEnableVRES', 'pEnableStorage', 'pEnablePowerImportExport']) + self.yesNo_to_bool(dPower_Parameters, ['pEnableChDisPower', 'pFixStInterResToIniReserve', 'pEnableSoftLineLoadLimits', 'pEnableThermalGen', 'pEnableRoR', 'pEnableVRES', 'pEnableStorage', 'pEnablePowerImportExport','pEnableSOCP']) # Transform to make it easier to access values dPower_Parameters = dPower_Parameters.drop(dPower_Parameters.columns[1:], axis=1) # Drop all columns but "Value" (rest is just for information in the Excel) diff --git a/ExcelWriter.py b/ExcelWriter.py index 2984293..4ce59be 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -1,11 +1,14 @@ import os from copy import copy +import pyomo.core import numpy as np import pandas as pd from openpyxl import load_workbook from openpyxl.utils.dataframe import dataframe_to_rows - +import openpyxl +from tools.printer import Printer +printer = Printer.getInstance() from InOutModule import ExcelReader package_directory_ExcelWriter = os.path.dirname(os.path.abspath(__file__)) @@ -28,6 +31,35 @@ def __copyCellStyle(origin, target): target.alignment = copy(origin.alignment) +def model_to_excel(model: pyomo.core.Model, target_path: str) -> None: + """ + Write all variables of the given Pyomo model to an Excel file. + + :param model: The Pyomo model to be written to Excel. + :param target_path: Path to the target Excel file. + :return: None + """ + printer.information(f"Writing model to '{target_path}'") + wb = openpyxl.Workbook() + ws = wb.active + + for i, var in enumerate(model.component_objects(pyomo.core.Var, active=True)): + if i == 0: # Use the automatically existing sheet for the first variable + ws.title = str(var) + else: # Create a sheet for each (other) variable + ws = wb.create_sheet(title=str(var)) + + # Prepare the data from the model and prepare the header + data = [(j, v.value if not v.stale else None) for j, v in var.items()] + col_number = len(data[0][0]) if not isinstance(data[0][0], str) else 1 + ws.append([f"index_{j}" for j in range(col_number)] + [str(var)]) + + # Write data to the sheet + for j, v in data: + ws.append(([j_index for j_index in j] if not isinstance(j, str) else [j]) + [v]) + + wb.save(target_path) + def write_VRESProfiles(data: pd.DataFrame, file_path: str): templateName = "Power_VRESProfiles" From 85596364a19af2c5364e9d15baab7daf198b5b32 Mon Sep 17 00:00:00 2001 From: Karelly Stephan <172272351+KaStephan@users.noreply.github.com> Date: Fri, 27 Jun 2025 13:41:23 +0200 Subject: [PATCH 2/5] Update gitgnore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c4d5a07..dcbcd2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Python files __pycache__/ - +data/ # Example output examples/output From 88db272254fd9bb89a3a347c2e68153e163cdada Mon Sep 17 00:00:00 2001 From: "Felix C. A. Auer" <10127354+FelixCAAuer@users.noreply.github.com> Date: Thu, 10 Jul 2025 16:29:05 +0200 Subject: [PATCH 3/5] Fix formatting and imports (minor) Remove data/ from .gitignore again --- .gitignore | 2 +- CaseStudy.py | 2 +- ExcelWriter.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0868abb..21b87b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Python files __pycache__/ -data/ + # Example output data/example/output diff --git a/CaseStudy.py b/CaseStudy.py index 4d6174b..f5d15e8 100644 --- a/CaseStudy.py +++ b/CaseStudy.py @@ -289,7 +289,7 @@ def get_dPower_Parameters(self): dPower_Parameters = dPower_Parameters.dropna(how="all") dPower_Parameters = dPower_Parameters.set_index('General') - self.yesNo_to_bool(dPower_Parameters, ['pEnableChDisPower', 'pFixStInterResToIniReserve', 'pEnableSoftLineLoadLimits', 'pEnableThermalGen', 'pEnableRoR', 'pEnableVRES', 'pEnableStorage', 'pEnablePowerImportExport','pEnableSOCP']) + self.yesNo_to_bool(dPower_Parameters, ['pEnableChDisPower', 'pFixStInterResToIniReserve', 'pEnableSoftLineLoadLimits', 'pEnableThermalGen', 'pEnableRoR', 'pEnableVRES', 'pEnableStorage', 'pEnablePowerImportExport', 'pEnableSOCP']) # Transform to make it easier to access values dPower_Parameters = dPower_Parameters.drop(dPower_Parameters.columns[1:], axis=1) # Drop all columns but "Value" (rest is just for information in the Excel) diff --git a/ExcelWriter.py b/ExcelWriter.py index 4896261..50d95c5 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -3,7 +3,6 @@ import xml.etree.ElementTree as ET from copy import copy, deepcopy -import pyomo.core import numpy as np import openpyxl import pandas as pd From fe104c063c4afe9d3d30811aada0b51593c02b4b Mon Sep 17 00:00:00 2001 From: "Felix C. A. Auer" <10127354+FelixCAAuer@users.noreply.github.com> Date: Thu, 10 Jul 2025 16:37:50 +0200 Subject: [PATCH 4/5] Add pandas-stubs to environment.yml Fix ExcelWriter default path --- ExcelWriter.py | 2 +- environment.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ExcelWriter.py b/ExcelWriter.py index 50d95c5..319fbe0 100644 --- a/ExcelWriter.py +++ b/ExcelWriter.py @@ -339,7 +339,7 @@ def model_to_excel(model: pyomo.core.Model, target_path: str) -> None: from rich_argparse import RichHelpFormatter parser = argparse.ArgumentParser(description="Re-write all files in given folder and compare against source", formatter_class=RichHelpFormatter) - parser.add_argument("caseStudyFolder", type=str, default="examples/", help="Path to folder containing data for LEGO model.", nargs="?") + parser.add_argument("caseStudyFolder", type=str, default="data/example/", help="Path to folder containing data for LEGO model.", nargs="?") parser.add_argument("excelDefinitionsPath", type=str, help="Path to the Excel definitions XML file. Uses default if none is supplied.", nargs="?") parser.add_argument("--dontCheckFormatting", action="store_true", help="Do not check formatting of the Excel files. Only check if they are equal.") args = parser.parse_args() diff --git a/environment.yml b/environment.yml index 73f27c9..c6961a9 100644 --- a/environment.yml +++ b/environment.yml @@ -5,6 +5,7 @@ channels: dependencies: - openpyxl=3.1.5 - pandas=2.2.3 + - pandas-stubs=2.3.0.250703 - pip=24.0 - pyomo=6.9.2 - pytest=8.4.0 From 8e7f1810b79b956057428c45cdc8af83ceb1dff9 Mon Sep 17 00:00:00 2001 From: "Felix C. A. Auer" <10127354+FelixCAAuer@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:36:27 +0200 Subject: [PATCH 5/5] Move conversion from MVar to kVar for QMin & QMax to CaseStudy --- CaseStudy.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CaseStudy.py b/CaseStudy.py index f5d15e8..3d57f26 100644 --- a/CaseStudy.py +++ b/CaseStudy.py @@ -152,6 +152,7 @@ def __init__(self, self.power_scaling_factor = self.dGlobal_Parameters["pPowerScalingFactor"] self.cost_scaling_factor = self.dGlobal_Parameters["pCostScalingFactor"] + self.reactive_power_scaling_factor = 1e-3 # MVar to kVar conversion factor self.angle_to_rad_scaling_factor = np.pi / 180 if not do_not_scale_units: @@ -239,10 +240,16 @@ def scale_dPower_ThermalGen(self): self.dPower_ThermalGen['MinUpTime'] = self.dPower_ThermalGen['MinUpTime'].astype('int64') self.dPower_ThermalGen['MinDownTime'] = self.dPower_ThermalGen['MinDownTime'].astype('int64') + self.dPower_ThermalGen['Qmin'] = self.dPower_ThermalGen['Qmin'].fillna(0) * self.reactive_power_scaling_factor + self.dPower_ThermalGen['Qmax'] = self.dPower_ThermalGen['Qmax'].fillna(0) * self.reactive_power_scaling_factor + def scale_dPower_RoR(self): self.dPower_RoR['InvestCostEUR'] = self.dPower_RoR['MaxProd'] * self.power_scaling_factor * (self.dPower_RoR['InvestCostPerMW'] + self.dPower_RoR['InvestCostPerMWh'] * self.dPower_RoR['Ene2PowRatio']) * (self.cost_scaling_factor / self.power_scaling_factor) self.dPower_RoR['MaxProd'] *= self.power_scaling_factor + self.dPower_RoR['Qmin'] = self.dPower_RoR['Qmin'].fillna(0) * self.reactive_power_scaling_factor + self.dPower_RoR['Qmax'] = self.dPower_RoR['Qmax'].fillna(0) * self.reactive_power_scaling_factor + def scale_dPower_Inflows(self): self.dPower_Inflows["Inflow"] *= self.power_scaling_factor @@ -254,6 +261,9 @@ def scale_dPower_VRES(self): self.dPower_VRES['MaxProd'] *= self.power_scaling_factor self.dPower_VRES['OMVarCost'] *= (self.cost_scaling_factor / self.power_scaling_factor) + self.dPower_VRES['Qmin'] = self.dPower_VRES['Qmin'].fillna(0) * self.reactive_power_scaling_factor + self.dPower_VRES['Qmax'] = self.dPower_VRES['Qmax'].fillna(0) * self.reactive_power_scaling_factor + def scale_dPower_Storage(self): self.dPower_Storage['IniReserve'] = self.dPower_Storage['IniReserve'].fillna(0) self.dPower_Storage['MinReserve'] = self.dPower_Storage['MinReserve'].fillna(0) @@ -263,6 +273,9 @@ def scale_dPower_Storage(self): self.dPower_Storage['MaxProd'] *= self.power_scaling_factor self.dPower_Storage['MaxCons'] *= self.power_scaling_factor + self.dPower_Storage['Qmin'] = self.dPower_Storage['Qmin'].fillna(0) * self.reactive_power_scaling_factor + self.dPower_Storage['Qmax'] = self.dPower_Storage['Qmax'].fillna(0) * self.reactive_power_scaling_factor + def scale_dPower_ImpExpHubs(self): self.dPower_ImpExpHubs["Pmax Import"] *= self.power_scaling_factor self.dPower_ImpExpHubs["Pmax Export"] *= self.power_scaling_factor