Skip to content

Commit 9ed2ead

Browse files
authored
Merge pull request #26 from BuildingEnergySimulationTools/method-set-param-and-boundary-file
Method set param and boundary file
2 parents c5d0d7e + 580c38a commit 9ed2ead

File tree

4 files changed

+171
-119
lines changed

4 files changed

+171
-119
lines changed

modelitool/corrai_connector.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def function(self, x_dict):
9999
param[Parameter.NAME]: x_dict[param[Parameter.NAME]]
100100
for param in self.param_list
101101
}
102-
self.om_model._set_param_dict(temp_dict)
102+
self.om_model.set_param_dict(temp_dict)
103103
res = self.om_model.simulate()
104104

105105
function_results = {}

modelitool/simulate.py

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import tempfile
3+
import warnings
34
from pathlib import Path
45

56
import pandas as pd
@@ -15,7 +16,6 @@ def __init__(
1516
self,
1617
model_path: Path | str,
1718
simulation_options: dict[str, float | str | int] = None,
18-
x: pd.DataFrame = None,
1919
output_list: list[str] = None,
2020
simulation_path: Path = None,
2121
x_combitimetable_name: str = None,
@@ -33,9 +33,6 @@ def __init__(
3333
- simulation_options (dict[str, float | str | int], optional):
3434
Options for the simulation. May include values for "startTime",
3535
"stopTime", "stepSize", "tolerance", "solver", "outputFormat".
36-
- x (pd.DataFrame, optional): Input data for the simulation. Index shall
37-
be a DatetimeIndex or integers. Columns must match the combi time table
38-
used to specify boundary conditions in the Modelica System.
3936
- output_list (list[str], optional): List of output variables. Default
4037
will output all available variables.
4138
- simulation_path (Path, optional): Path to run the simulation and
@@ -57,7 +54,7 @@ def __init__(
5754
if not os.path.exists(self._simulation_path):
5855
os.mkdir(simulation_path)
5956

60-
self._x = x if x is not None else pd.DataFrame()
57+
self._x = pd.DataFrame()
6158
self.output_list = output_list
6259
self.omc = OMCSessionZMQ()
6360
self.omc.sendExpression(f'cd("{self._simulation_path.as_posix()}")')
@@ -84,14 +81,16 @@ def simulate(
8481
) -> pd.DataFrame:
8582
"""
8683
Runs the simulation with the provided parameters, simulation options and
87-
boundariy conditions.
84+
boundary conditions.
8885
- parameter_dict (dict, optional): Dictionary of parameters.
89-
- simulation_options (dict, optional): Will update simulation options if it
90-
had been given at the init phase. May include values for "startTime",
91-
"stopTime", "stepSize", "tolerance", "solver", "outputFormat".
86+
- simulation_options (dict, optional): May include values for "startTime",
87+
"stopTime", "stepSize", "tolerance", "solver", "outputFormat". Can
88+
also include 'x' with a DataFrame for boundary conditions.
9289
- x (pd.DataFrame, optional): Input data for the simulation. Index shall
93-
be a DatetimeIndex or integers. Columns must match the combi time table
94-
used to specify boundary conditions in the Modelica System.
90+
be a DatetimeIndex or integers. Columns must match the combitimetable
91+
used to specify boundary conditions in the Modelica System. If 'x' is
92+
provided both in simulation_options and as a direct parameter, the one
93+
provided as direct parameter will be used.
9594
- verbose (bool, optional): If True, prints simulation progress. Defaults to
9695
True.
9796
- simflags (str, optional): Additional simulation flags.
@@ -102,9 +101,17 @@ def simulate(
102101
"""
103102

104103
if parameter_dict is not None:
105-
self._set_param_dict(parameter_dict)
104+
self.set_param_dict(parameter_dict)
106105

107106
if simulation_options is not None:
107+
if x is not None and "x" in simulation_options:
108+
warnings.warn(
109+
"Boundary file 'x' specified both in simulation_options and as a "
110+
"direct parameter. The 'x' provided in simulate() will be used.",
111+
UserWarning,
112+
stacklevel=2,
113+
)
114+
108115
self._set_simulation_options(simulation_options)
109116

110117
if x is not None:
@@ -172,28 +179,32 @@ def get_parameters(self):
172179
return self.model.getParameters()
173180

174181
def _set_simulation_options(self, simulation_options):
175-
self.model.setSimulationOptions(
176-
[
177-
f'startTime={simulation_options["startTime"]}',
178-
f'stopTime={simulation_options["stopTime"]}',
179-
f'stepSize={simulation_options["stepSize"]}',
180-
f'tolerance={simulation_options["tolerance"]}',
181-
f'solver={simulation_options["solver"]}',
182-
f'outputFormat={simulation_options["outputFormat"]}',
183-
]
184-
)
182+
standard_options = {
183+
"startTime": simulation_options.get("startTime"),
184+
"stopTime": simulation_options.get("stopTime"),
185+
"stepSize": simulation_options.get("stepSize"),
186+
"tolerance": simulation_options.get("tolerance"),
187+
"solver": simulation_options.get("solver"),
188+
"outputFormat": simulation_options.get("outputFormat"),
189+
}
190+
191+
options = [f"{k}={v}" for k, v in standard_options.items() if v is not None]
192+
self.model.setSimulationOptions(options)
185193
self.simulation_options = simulation_options
186194

195+
if "x" in simulation_options:
196+
self._set_x(simulation_options["x"])
197+
187198
def _set_x(self, df: pd.DataFrame):
188199
"""Sets the input data for the simulation and updates the corresponding file."""
189200
if not self._x.equals(df):
190201
new_bounds_path = self._simulation_path / "boundaries.txt"
191202
df_to_combitimetable(df, new_bounds_path)
192203
full_path = (self._simulation_path / "boundaries.txt").resolve().as_posix()
193-
self._set_param_dict({f"{self.x_combitimetable_name}.fileName": full_path})
204+
self.set_param_dict({f"{self.x_combitimetable_name}.fileName": full_path})
194205
self._x = df
195206

196-
def _set_param_dict(self, param_dict):
207+
def set_param_dict(self, param_dict):
197208
self.model.setParameters([f"{item}={val}" for item, val in param_dict.items()])
198209

199210

tests/test_simulate.py

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def test_set_param_dict(self, simul):
4242
"y.k": 2.0,
4343
}
4444

45-
simul._set_param_dict(test_dict)
45+
simul.set_param_dict(test_dict)
4646

4747
for key in test_dict.keys():
4848
assert float(test_dict[key]) == float(simul.model.getParameters()[key])
@@ -100,19 +100,68 @@ def test_set_boundaries_df(self):
100100
"outputFormat": "mat",
101101
}
102102

103-
x = pd.DataFrame(
103+
x_options = pd.DataFrame(
104104
{"Boundaries.y[1]": [10, 20, 30], "Boundaries.y[2]": [3, 4, 5]},
105105
index=pd.date_range("2009-07-13 00:00:00", periods=3, freq="h"),
106106
)
107+
x_direct = pd.DataFrame(
108+
{"Boundaries.y[1]": [100, 200, 300], "Boundaries.y[2]": [30, 40, 50]},
109+
index=pd.date_range("2009-07-13 00:00:00", periods=3, freq="h"),
110+
)
111+
112+
simu = OMModel(
113+
model_path="TestLib.boundary_test",
114+
package_path=PACKAGE_DIR / "package.mo",
115+
lmodel=["Modelica"],
116+
)
117+
118+
simulation_options_with_x = simulation_options.copy()
119+
simulation_options_with_x["x"] = x_options
120+
res1 = simu.simulate(simulation_options=simulation_options_with_x)
121+
res1 = res1.loc[:, ["Boundaries.y[1]", "Boundaries.y[2]"]]
122+
np.testing.assert_allclose(x_options.to_numpy(), res1.to_numpy())
123+
assert np.all(
124+
[x_options.index[i] == res1.index[i] for i in range(len(x_options.index))]
125+
)
126+
assert np.all(
127+
[
128+
x_options.columns[i] == res1.columns[i]
129+
for i in range(len(x_options.columns))
130+
]
131+
)
107132

108133
simu = OMModel(
109134
model_path="TestLib.boundary_test",
110135
package_path=PACKAGE_DIR / "package.mo",
111136
lmodel=["Modelica"],
112137
)
138+
res2 = simu.simulate(simulation_options=simulation_options, x=x_direct)
139+
res2 = res2.loc[:, ["Boundaries.y[1]", "Boundaries.y[2]"]]
140+
np.testing.assert_allclose(x_direct.to_numpy(), res2.to_numpy())
141+
assert np.all(
142+
[x_direct.index[i] == res2.index[i] for i in range(len(x_direct.index))]
143+
)
144+
assert np.all(
145+
[
146+
x_direct.columns[i] == res2.columns[i]
147+
for i in range(len(x_direct.columns))
148+
]
149+
)
113150

114-
res = simu.simulate(simulation_options=simulation_options, x=x)
115-
res = res.loc[:, ["Boundaries.y[1]", "Boundaries.y[2]"]]
116-
assert np.all([x.index[i] == res.index[i] for i in range(len(x.index))])
117-
np.testing.assert_allclose(x.to_numpy(), res.to_numpy())
118-
assert np.all([x.columns[i] == res.columns[i] for i in range(len(x.columns))])
151+
simu = OMModel(
152+
model_path="TestLib.boundary_test",
153+
package_path=PACKAGE_DIR / "package.mo",
154+
lmodel=["Modelica"],
155+
)
156+
with pytest.warns(
157+
UserWarning,
158+
match="Boundary file 'x' specified both in simulation_options and as a "
159+
"direct parameter",
160+
):
161+
res3 = simu.simulate(
162+
simulation_options=simulation_options_with_x, x=x_direct
163+
)
164+
res3 = res3.loc[:, ["Boundaries.y[1]", "Boundaries.y[2]"]]
165+
np.testing.assert_allclose(x_direct.to_numpy(), res3.to_numpy())
166+
with pytest.raises(AssertionError):
167+
np.testing.assert_allclose(x_options.to_numpy(), res3.to_numpy())

0 commit comments

Comments
 (0)