Skip to content

Commit b216ee0

Browse files
committed
Merge branch 'ModelicaSystem_improve_set_functions' into small_updates
2 parents 3702395 + 171c0f5 commit b216ee0

4 files changed

Lines changed: 112 additions & 73 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,8 @@ def getSolutions(self, varList: Optional[str | list[str]] = None, resultfile: Op
11761176

11771177
@staticmethod
11781178
def _prepare_input_data(
1179-
raw_input: str | list[str] | dict[str, Any],
1179+
input_args: Any,
1180+
input_kwargs: dict[str, Any],
11801181
) -> dict[str, str]:
11811182
"""
11821183
Convert raw input to a structured dictionary {'key1': 'value1', 'key2': 'value2'}.
@@ -1194,38 +1195,42 @@ def prepare_str(str_in: str) -> dict[str, str]:
11941195

11951196
input_data: dict[str, str] = {}
11961197

1197-
if isinstance(raw_input, str):
1198-
warnings.warn(message="The definition of values to set should use a dictionary, "
1199-
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1200-
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1201-
category=DeprecationWarning,
1202-
stacklevel=3)
1203-
return prepare_str(raw_input)
1204-
1205-
if isinstance(raw_input, list):
1206-
warnings.warn(message="The definition of values to set should use a dictionary, "
1207-
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1208-
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1209-
category=DeprecationWarning,
1210-
stacklevel=3)
1211-
1212-
for item in raw_input:
1213-
input_data |= prepare_str(item)
1214-
1215-
return input_data
1216-
1217-
if isinstance(raw_input, dict):
1218-
for key, val in raw_input.items():
1219-
# convert all values to strings to align it on one type: dict[str, str]
1220-
# spaces have to be removed as setInput() could take list of tuples as input and spaces would
1221-
str_val = str(val).replace(' ', '')
1198+
for input_arg in input_args:
1199+
if isinstance(input_arg, str):
1200+
warnings.warn(message="The definition of values to set should use a dictionary, "
1201+
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1202+
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1203+
category=DeprecationWarning,
1204+
stacklevel=3)
1205+
input_data = input_data | prepare_str(input_arg)
1206+
elif isinstance(input_arg, list):
1207+
warnings.warn(message="The definition of values to set should use a dictionary, "
1208+
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1209+
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1210+
category=DeprecationWarning,
1211+
stacklevel=3)
1212+
1213+
for item in input_arg:
1214+
if not isinstance(item, str):
1215+
raise ModelicaSystemError(f"Invalid input data type for set*() function: {type(item)}!")
1216+
input_data = input_data | prepare_str(item)
1217+
else:
1218+
raise ModelicaSystemError(f"Invalid input data type for set*() function: {type(input_arg)}!")
1219+
1220+
if len(input_kwargs):
1221+
for key, val in input_kwargs.items():
1222+
# ensure all values are strings to align it on one type: dict[str, str]
1223+
if not isinstance(val, str):
1224+
# spaces have to be removed as setInput() could take list of tuples as input and spaces would
1225+
# result in an error on recreating the input data
1226+
str_val = str(val).replace(' ', '')
1227+
else:
1228+
str_val = val
12221229
if ' ' in key or ' ' in str_val:
12231230
raise ModelicaSystemError(f"Spaces not allowed in key/value pairs: {repr(key)} = {repr(val)}!")
12241231
input_data[key] = str_val
12251232

1226-
return input_data
1227-
1228-
raise ModelicaSystemError(f"Invalid type of input: {type(raw_input)}")
1233+
return input_data
12291234

12301235
def _set_method_helper(
12311236
self,
@@ -1257,8 +1262,7 @@ def _set_method_helper(
12571262

12581263
for key, val in inputdata.items():
12591264
if key not in classdata:
1260-
raise ModelicaSystemError("Unhandled case in setMethodHelper.apply_single() - "
1261-
f"{repr(key)} is not a {repr(datatype)} variable")
1265+
raise ModelicaSystemError(f"Invalid variable for type {repr(datatype)}: {repr(key)}")
12621266

12631267
if datatype == "parameter" and not self.isParameterChangeable(key):
12641268
raise ModelicaSystemError(f"It is not possible to set the parameter {repr(key)}. It seems to be "
@@ -1286,17 +1290,21 @@ def isParameterChangeable(
12861290

12871291
def setContinuous(
12881292
self,
1289-
cvals: str | list[str] | dict[str, Any],
1293+
*args: Any,
1294+
**kwargs: dict[str, Any],
12901295
) -> bool:
12911296
"""
12921297
This method is used to set continuous values. It can be called:
12931298
with a sequence of continuous name and assigning corresponding values as arguments as show in the example below:
12941299
usage
12951300
>>> setContinuous("Name=value") # depreciated
12961301
>>> setContinuous(["Name1=value1","Name2=value2"]) # depreciated
1297-
>>> setContinuous(cvals={"Name1": "value1", "Name2": "value2"})
1302+
1303+
>>> setContinuous(Name1="value1", Name2="value2")
1304+
>>> param = {"Name1": "value1", "Name2": "value2"}
1305+
>>> setContinuous(**param)
12981306
"""
1299-
inputdata = self._prepare_input_data(raw_input=cvals)
1307+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13001308

13011309
return self._set_method_helper(
13021310
inputdata=inputdata,
@@ -1306,17 +1314,21 @@ def setContinuous(
13061314

13071315
def setParameters(
13081316
self,
1309-
pvals: str | list[str] | dict[str, Any],
1317+
*args: Any,
1318+
**kwargs: dict[str, Any],
13101319
) -> bool:
13111320
"""
13121321
This method is used to set parameter values. It can be called:
13131322
with a sequence of parameter name and assigning corresponding value as arguments as show in the example below:
13141323
usage
13151324
>>> setParameters("Name=value") # depreciated
13161325
>>> setParameters(["Name1=value1","Name2=value2"]) # depreciated
1317-
>>> setParameters(pvals={"Name1": "value1", "Name2": "value2"})
1326+
1327+
>>> setParameters(Name1="value1", Name2="value2")
1328+
>>> param = {"Name1": "value1", "Name2": "value2"}
1329+
>>> setParameters(**param)
13181330
"""
1319-
inputdata = self._prepare_input_data(raw_input=pvals)
1331+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13201332

13211333
return self._set_method_helper(
13221334
inputdata=inputdata,
@@ -1326,17 +1338,21 @@ def setParameters(
13261338

13271339
def setSimulationOptions(
13281340
self,
1329-
simOptions: str | list[str] | dict[str, Any],
1341+
*args: Any,
1342+
**kwargs: dict[str, Any],
13301343
) -> bool:
13311344
"""
13321345
This method is used to set simulation options. It can be called:
13331346
with a sequence of simulation options name and assigning corresponding values as arguments as show in the example below:
13341347
usage
13351348
>>> setSimulationOptions("Name=value") # depreciated
13361349
>>> setSimulationOptions(["Name1=value1","Name2=value2"]) # depreciated
1337-
>>> setSimulationOptions(simOptions={"Name1": "value1", "Name2": "value2"})
1350+
1351+
>>> setSimulationOptions(Name1="value1", Name2="value2")
1352+
>>> param = {"Name1": "value1", "Name2": "value2"}
1353+
>>> setSimulationOptions(**param)
13381354
"""
1339-
inputdata = self._prepare_input_data(raw_input=simOptions)
1355+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13401356

13411357
return self._set_method_helper(
13421358
inputdata=inputdata,
@@ -1346,17 +1362,21 @@ def setSimulationOptions(
13461362

13471363
def setLinearizationOptions(
13481364
self,
1349-
linearizationOptions: str | list[str] | dict[str, Any],
1365+
*args: Any,
1366+
**kwargs: dict[str, Any],
13501367
) -> bool:
13511368
"""
13521369
This method is used to set linearization options. It can be called:
13531370
with a sequence of linearization options name and assigning corresponding value as arguments as show in the example below
13541371
usage
13551372
>>> setLinearizationOptions("Name=value") # depreciated
13561373
>>> setLinearizationOptions(["Name1=value1","Name2=value2"]) # depreciated
1357-
>>> setLinearizationOptions(linearizationOtions={"Name1": "value1", "Name2": "value2"})
1374+
1375+
>>> setLinearizationOptions(Name1="value1", Name2="value2")
1376+
>>> param = {"Name1": "value1", "Name2": "value2"}
1377+
>>> setLinearizationOptions(**param)
13581378
"""
1359-
inputdata = self._prepare_input_data(raw_input=linearizationOptions)
1379+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13601380

13611381
return self._set_method_helper(
13621382
inputdata=inputdata,
@@ -1366,17 +1386,21 @@ def setLinearizationOptions(
13661386

13671387
def setOptimizationOptions(
13681388
self,
1369-
optimizationOptions: str | list[str] | dict[str, Any],
1389+
*args: Any,
1390+
**kwargs: dict[str, Any],
13701391
) -> bool:
13711392
"""
13721393
This method is used to set optimization options. It can be called:
13731394
with a sequence of optimization options name and assigning corresponding values as arguments as show in the example below:
13741395
usage
13751396
>>> setOptimizationOptions("Name=value") # depreciated
13761397
>>> setOptimizationOptions(["Name1=value1","Name2=value2"]) # depreciated
1377-
>>> setOptimizationOptions(optimizationOptions={"Name1": "value1", "Name2": "value2"})
1398+
1399+
>>> setOptimizationOptions(Name1="value1", Name2="value2")
1400+
>>> param = {"Name1": "value1", "Name2": "value2"}
1401+
>>> setOptimizationOptions(**param)
13781402
"""
1379-
inputdata = self._prepare_input_data(raw_input=optimizationOptions)
1403+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13801404

13811405
return self._set_method_helper(
13821406
inputdata=inputdata,
@@ -1386,7 +1410,8 @@ def setOptimizationOptions(
13861410

13871411
def setInputs(
13881412
self,
1389-
name: str | list[str] | dict[str, Any],
1413+
*args: Any,
1414+
**kwargs: dict[str, Any],
13901415
) -> bool:
13911416
"""
13921417
This method is used to set input values. It can be called with a sequence of input name and assigning
@@ -1396,9 +1421,12 @@ def setInputs(
13961421
13971422
>>> setInputs("Name=value") # depreciated
13981423
>>> setInputs(["Name1=value1","Name2=value2"]) # depreciated
1399-
>>> setInputs(name={"Name1": "value1", "Name2": "value2"})
1424+
1425+
>>> setInputs(Name1="value1", Name2="value2")
1426+
>>> param = {"Name1": "value1", "Name2": "value2"}
1427+
>>> setInputs(**param)
14001428
"""
1401-
inputdata = self._prepare_input_data(raw_input=name)
1429+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
14021430

14031431
for key, val in inputdata.items():
14041432
if key not in self._inputs:

tests/test_ModelicaSystem.py

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ def test_setParameters():
3434
model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/"
3535
mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall")
3636

37-
# method 1
38-
mod.setParameters(pvals={"e": 1.234})
39-
mod.setParameters(pvals={"g": 321.0})
37+
# method 1 (test depreciated variants)
38+
mod.setParameters("e=1.234")
39+
mod.setParameters(["g=321.0"])
4040
assert mod.getParameters("e") == ["1.234"]
4141
assert mod.getParameters("g") == ["321.0"]
4242
assert mod.getParameters() == {
@@ -46,8 +46,9 @@ def test_setParameters():
4646
with pytest.raises(KeyError):
4747
mod.getParameters("thisParameterDoesNotExist")
4848

49-
# method 2
50-
mod.setParameters(pvals={"e": 21.3, "g": 0.12})
49+
# method 2 (new style)
50+
pvals = {"e": 21.3, "g": 0.12}
51+
mod.setParameters(**pvals)
5152
assert mod.getParameters() == {
5253
"e": "21.3",
5354
"g": "0.12",
@@ -64,8 +65,8 @@ def test_setSimulationOptions():
6465
mod = OMPython.ModelicaSystem(fileName=model_path + "BouncingBall.mo", modelName="BouncingBall")
6566

6667
# method 1
67-
mod.setSimulationOptions(simOptions={"stopTime": 1.234})
68-
mod.setSimulationOptions(simOptions={"tolerance": 1.1e-08})
68+
mod.setSimulationOptions(stopTime=1.234)
69+
mod.setSimulationOptions(tolerance=1.1e-08)
6970
assert mod.getSimulationOptions("stopTime") == ["1.234"]
7071
assert mod.getSimulationOptions("tolerance") == ["1.1e-08"]
7172
assert mod.getSimulationOptions(["tolerance", "stopTime"]) == ["1.1e-08", "1.234"]
@@ -77,7 +78,7 @@ def test_setSimulationOptions():
7778
mod.getSimulationOptions("thisOptionDoesNotExist")
7879

7980
# method 2
80-
mod.setSimulationOptions(simOptions={"stopTime": 2.1, "tolerance": "1.2e-08"})
81+
mod.setSimulationOptions(stopTime=2.1, tolerance=1.2e-08)
8182
d = mod.getSimulationOptions()
8283
assert d["stopTime"] == "2.1"
8384
assert d["tolerance"] == "1.2e-08"
@@ -119,7 +120,9 @@ def test_getSolutions(model_firstorder):
119120
a = -1
120121
tau = -1 / a
121122
stopTime = 5*tau
122-
mod.setSimulationOptions(simOptions={"stopTime": stopTime, "stepSize": 0.1, "tolerance": 1e-8})
123+
124+
simOptions = {"stopTime": stopTime, "stepSize": 0.1, "tolerance": 1e-8}
125+
mod.setSimulationOptions(**simOptions)
123126
mod.simulate()
124127

125128
x = mod.getSolutions("x")
@@ -298,7 +301,7 @@ def test_getters(tmp_path):
298301
x0 = 1.0
299302
x_analytical = -b/a + (x0 + b/a) * np.exp(a * stopTime)
300303
dx_analytical = (x0 + b/a) * a * np.exp(a * stopTime)
301-
mod.setSimulationOptions(simOptions={"stopTime": stopTime})
304+
mod.setSimulationOptions(stopTime=stopTime)
302305
mod.simulate()
303306

304307
# getOutputs after simulate()
@@ -327,7 +330,7 @@ def test_getters(tmp_path):
327330
mod.getContinuous("a") # a is a parameter
328331

329332
with pytest.raises(OMPython.ModelicaSystemError):
330-
mod.setSimulationOptions(simOptions={"thisOptionDoesNotExist": 3})
333+
mod.setSimulationOptions(thisOptionDoesNotExist=3)
331334

332335

333336
def test_simulate_inputs(tmp_path):
@@ -345,7 +348,8 @@ def test_simulate_inputs(tmp_path):
345348
""")
346349
mod = OMPython.ModelicaSystem(fileName=model_file.as_posix(), modelName="M_input")
347350

348-
mod.setSimulationOptions(simOptions={"stopTime": 1.0})
351+
simOptions = {"stopTime": 1.0}
352+
mod.setSimulationOptions(**simOptions)
349353

350354
# integrate zero (no setInputs call) - it should default to None -> 0
351355
assert mod.getInputs() == {
@@ -357,7 +361,7 @@ def test_simulate_inputs(tmp_path):
357361
assert np.isclose(y[-1], 0.0)
358362

359363
# integrate a constant
360-
mod.setInputs(name={"u1": 2.5})
364+
mod.setInputs(u1=2.5)
361365
assert mod.getInputs() == {
362366
"u1": [
363367
(0.0, 2.5),
@@ -374,7 +378,8 @@ def test_simulate_inputs(tmp_path):
374378
assert np.isclose(y[-1], 2.5)
375379

376380
# now let's integrate the sum of two ramps
377-
mod.setInputs(name={"u1": [(0.0, 0.0), (0.5, 2), (1.0, 0)]})
381+
inputs = {"u1": [(0.0, 0.0), (0.5, 2), (1.0, 0)]}
382+
mod.setInputs(**inputs)
378383
assert mod.getInputs("u1") == [[
379384
(0.0, 0.0),
380385
(0.5, 2.0),
@@ -387,17 +392,20 @@ def test_simulate_inputs(tmp_path):
387392
# let's try some edge cases
388393
# unmatched startTime
389394
with pytest.raises(OMPython.ModelicaSystemError):
390-
mod.setInputs(name={"u1": [(-0.5, 0.0), (1.0, 1)]})
395+
mod.setInputs(u1=[(-0.5, 0.0), (1.0, 1)])
391396
mod.simulate()
392397
# unmatched stopTime
393398
with pytest.raises(OMPython.ModelicaSystemError):
394-
mod.setInputs(name={"u1": [(0.0, 0.0), (0.5, 1)]})
399+
mod.setInputs(u1=[(0.0, 0.0), (0.5, 1)])
395400
mod.simulate()
396401

397402
# Let's use both inputs, but each one with different number of
398403
# samples. This has an effect when generating the csv file.
399-
mod.setInputs(name={"u1": [(0.0, 0), (1.0, 1)],
400-
"u2": [(0.0, 0), (0.25, 0.5), (0.5, 1.0), (1.0, 0)]})
404+
inputs = {
405+
"u1": [(0.0, 0), (1.0, 1)],
406+
"u2": [(0.0, 0), (0.25, 0.5), (0.5, 1.0), (1.0, 0)],
407+
}
408+
mod.setInputs(**inputs)
401409
csv_file = mod._createCSVData()
402410
assert pathlib.Path(csv_file).read_text() == """time,u1,u2,end
403411
0.0,0.0,0.0,0

tests/test_linearization.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ def test_getters(tmp_path):
6262
assert "startTime" in d
6363
assert "stopTime" in d
6464
assert mod.getLinearizationOptions(["stopTime", "startTime"]) == [d["stopTime"], d["startTime"]]
65-
mod.setLinearizationOptions(linearizationOptions={"stopTime": 0.02})
65+
mod.setLinearizationOptions(stopTime=0.02)
6666
assert mod.getLinearizationOptions("stopTime") == ["0.02"]
6767

68-
mod.setInputs(name={"u1": 10, "u2": 0})
68+
mod.setInputs(u1=10, u2=0)
6969
[A, B, C, D] = mod.linearize()
7070
g = float(mod.getParameters("g")[0])
7171
l = float(mod.getParameters("l")[0])

tests/test_optimization.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ def test_optimization_example(tmp_path):
3535

3636
mod = OMPython.ModelicaSystem(fileName=model_file.as_posix(), modelName="BangBang2021")
3737

38-
mod.setOptimizationOptions(optimizationOptions={"numberOfIntervals": 16,
39-
"stopTime": 1,
40-
"stepSize": 0.001,
41-
"tolerance": 1e-8})
38+
optimizationOptions = {
39+
"numberOfIntervals": 16,
40+
"stopTime": 1,
41+
"stepSize": 0.001,
42+
"tolerance": 1e-8,
43+
}
44+
mod.setOptimizationOptions(**optimizationOptions)
4245

4346
# test the getter
4447
assert mod.getOptimizationOptions()["stopTime"] == "1"

0 commit comments

Comments
 (0)