Skip to content

Remove deprecated code in ModelicaSystem set*() functions #347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 50 additions & 78 deletions OMPython/ModelicaSystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ def getSolutions(self, varList: Optional[str | list[str]] = None, resultfile: Op

@staticmethod
def _prepare_input_data(
raw_input: str | list[str] | dict[str, Any],
input_kwargs: dict[str, Any],
) -> dict[str, str]:
"""
Convert raw input to a structured dictionary {'key1': 'value1', 'key2': 'value2'}.
Expand All @@ -1133,38 +1133,20 @@ def prepare_str(str_in: str) -> dict[str, str]:

input_data: dict[str, str] = {}

if isinstance(raw_input, str):
warnings.warn(message="The definition of values to set should use a dictionary, "
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
category=DeprecationWarning,
stacklevel=3)
return prepare_str(raw_input)

if isinstance(raw_input, list):
warnings.warn(message="The definition of values to set should use a dictionary, "
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
category=DeprecationWarning,
stacklevel=3)

for item in raw_input:
input_data |= prepare_str(item)

return input_data

if isinstance(raw_input, dict):
for key, val in raw_input.items():
# convert all values to strings to align it on one type: dict[str, str]
# spaces have to be removed as setInput() could take list of tuples as input and spaces would
str_val = str(val).replace(' ', '')
if len(input_kwargs):
for key, val in input_kwargs.items():
# ensure all values are strings to align it on one type: dict[str, str]
if not isinstance(val, str):
# spaces have to be removed as setInput() could take list of tuples as input and spaces would
# result in an error on recreating the input data
str_val = str(val).replace(' ', '')
else:
str_val = val
if ' ' in key or ' ' in str_val:
raise ModelicaSystemError(f"Spaces not allowed in key/value pairs: {repr(key)} = {repr(val)}!")
input_data[key] = str_val

return input_data

raise ModelicaSystemError(f"Invalid type of input: {type(raw_input)}")
return input_data

def _set_method_helper(
self,
Expand Down Expand Up @@ -1196,8 +1178,7 @@ def _set_method_helper(

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

if datatype == "parameter" and not self.isParameterChangeable(key):
raise ModelicaSystemError(f"It is not possible to set the parameter {repr(key)}. It seems to be "
Expand Down Expand Up @@ -1225,17 +1206,15 @@ def isParameterChangeable(

def setContinuous(
self,
cvals: str | list[str] | dict[str, Any],
**kwargs: dict[str, Any],
) -> bool:
"""
This method is used to set continuous values. It can be called:
with a sequence of continuous name and assigning corresponding values as arguments as show in the example below:
usage
>>> setContinuous("Name=value") # depreciated
>>> setContinuous(["Name1=value1","Name2=value2"]) # depreciated
>>> setContinuous(cvals={"Name1": "value1", "Name2": "value2"})
This method is used to set continuous values.
>>> setContinuous(Name1="value1", Name2="value2")
>>> param = {"Name1": "value1", "Name2": "value2"}
>>> setContinuous(**param)
"""
inputdata = self._prepare_input_data(raw_input=cvals)
inputdata = self._prepare_input_data(input_kwargs=kwargs)

return self._set_method_helper(
inputdata=inputdata,
Expand All @@ -1245,17 +1224,15 @@ def setContinuous(

def setParameters(
self,
pvals: str | list[str] | dict[str, Any],
**kwargs: dict[str, Any],
) -> bool:
"""
This method is used to set parameter values. It can be called:
with a sequence of parameter name and assigning corresponding value as arguments as show in the example below:
usage
>>> setParameters("Name=value") # depreciated
>>> setParameters(["Name1=value1","Name2=value2"]) # depreciated
>>> setParameters(pvals={"Name1": "value1", "Name2": "value2"})
This method is used to set parameter values.
>>> setParameters(Name1="value1", Name2="value2")
>>> param = {"Name1": "value1", "Name2": "value2"}
>>> setParameters(**param)
"""
inputdata = self._prepare_input_data(raw_input=pvals)
inputdata = self._prepare_input_data(input_kwargs=kwargs)

return self._set_method_helper(
inputdata=inputdata,
Expand All @@ -1265,17 +1242,15 @@ def setParameters(

def setSimulationOptions(
self,
simOptions: str | list[str] | dict[str, Any],
**kwargs: dict[str, Any],
) -> bool:
"""
This method is used to set simulation options. It can be called:
with a sequence of simulation options name and assigning corresponding values as arguments as show in the example below:
usage
>>> setSimulationOptions("Name=value") # depreciated
>>> setSimulationOptions(["Name1=value1","Name2=value2"]) # depreciated
>>> setSimulationOptions(simOptions={"Name1": "value1", "Name2": "value2"})
This method is used to set simulation options.
>>> setSimulationOptions(Name1="value1", Name2="value2")
>>> param = {"Name1": "value1", "Name2": "value2"}
>>> setSimulationOptions(**param)
"""
inputdata = self._prepare_input_data(raw_input=simOptions)
inputdata = self._prepare_input_data(input_kwargs=kwargs)

return self._set_method_helper(
inputdata=inputdata,
Expand All @@ -1285,17 +1260,15 @@ def setSimulationOptions(

def setLinearizationOptions(
self,
linearizationOptions: str | list[str] | dict[str, Any],
**kwargs: dict[str, Any],
) -> bool:
"""
This method is used to set linearization options. It can be called:
with a sequence of linearization options name and assigning corresponding value as arguments as show in the example below
usage
>>> setLinearizationOptions("Name=value") # depreciated
>>> setLinearizationOptions(["Name1=value1","Name2=value2"]) # depreciated
>>> setLinearizationOptions(linearizationOtions={"Name1": "value1", "Name2": "value2"})
This method is used to set linearization options.
>>> setLinearizationOptions(Name1="value1", Name2="value2")
>>> param = {"Name1": "value1", "Name2": "value2"}
>>> setLinearizationOptions(**param)
"""
inputdata = self._prepare_input_data(raw_input=linearizationOptions)
inputdata = self._prepare_input_data(input_kwargs=kwargs)

return self._set_method_helper(
inputdata=inputdata,
Expand All @@ -1305,17 +1278,17 @@ def setLinearizationOptions(

def setOptimizationOptions(
self,
optimizationOptions: str | list[str] | dict[str, Any],
**kwargs: dict[str, Any],
) -> bool:
"""
This method is used to set optimization options. It can be called:
with a sequence of optimization options name and assigning corresponding values as arguments as show in the example below:
usage
>>> setOptimizationOptions("Name=value") # depreciated
>>> setOptimizationOptions(["Name1=value1","Name2=value2"]) # depreciated
>>> setOptimizationOptions(optimizationOptions={"Name1": "value1", "Name2": "value2"})
>>> setOptimizationOptions(Name1="value1", Name2="value2")
>>> param = {"Name1": "value1", "Name2": "value2"}
>>> setOptimizationOptions(**param)
"""
inputdata = self._prepare_input_data(raw_input=optimizationOptions)
inputdata = self._prepare_input_data(input_kwargs=kwargs)

return self._set_method_helper(
inputdata=inputdata,
Expand All @@ -1325,19 +1298,18 @@ def setOptimizationOptions(

def setInputs(
self,
name: str | list[str] | dict[str, Any],
**kwargs: dict[str, Any],
) -> bool:
"""
This method is used to set input values. It can be called with a sequence of input name and assigning
corresponding values as arguments as show in the example below. Compared to other set*() methods this is a
special case as value could be a list of tuples - these are converted to a string in _prepare_input_data()
and restored here via ast.literal_eval().

>>> setInputs("Name=value") # depreciated
>>> setInputs(["Name1=value1","Name2=value2"]) # depreciated
>>> setInputs(name={"Name1": "value1", "Name2": "value2"})
This method is used to set input values.

Compared to other set*() methods this is a special case as value could be a list of tuples - these are
converted to a string in _prepare_input_data() and restored here via ast.literal_eval().
>>> setInputs(Name1="value1", Name2="value2")
>>> param = {"Name1": "value1", "Name2": "value2"}
>>> setInputs(**param)
"""
inputdata = self._prepare_input_data(raw_input=name)
inputdata = self._prepare_input_data(input_kwargs=kwargs)

for key, val in inputdata.items():
if key not in self._inputs:
Expand Down
44 changes: 26 additions & 18 deletions tests/test_ModelicaSystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ def test_setParameters():
model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/"
mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall")

# method 1
mod.setParameters(pvals={"e": 1.234})
mod.setParameters(pvals={"g": 321.0})
# method 1 (test depreciated variants)
mod.setParameters(e=1.234)
mod.setParameters(g=321.0)
assert mod.getParameters("e") == ["1.234"]
assert mod.getParameters("g") == ["321.0"]
assert mod.getParameters() == {
Expand All @@ -46,8 +46,9 @@ def test_setParameters():
with pytest.raises(KeyError):
mod.getParameters("thisParameterDoesNotExist")

# method 2
mod.setParameters(pvals={"e": 21.3, "g": 0.12})
# method 2 (new style)
pvals = {"e": 21.3, "g": 0.12}
mod.setParameters(**pvals)
assert mod.getParameters() == {
"e": "21.3",
"g": "0.12",
Expand All @@ -64,8 +65,8 @@ def test_setSimulationOptions():
mod = OMPython.ModelicaSystem(fileName=model_path + "BouncingBall.mo", modelName="BouncingBall")

# method 1
mod.setSimulationOptions(simOptions={"stopTime": 1.234})
mod.setSimulationOptions(simOptions={"tolerance": 1.1e-08})
mod.setSimulationOptions(stopTime=1.234)
mod.setSimulationOptions(tolerance=1.1e-08)
assert mod.getSimulationOptions("stopTime") == ["1.234"]
assert mod.getSimulationOptions("tolerance") == ["1.1e-08"]
assert mod.getSimulationOptions(["tolerance", "stopTime"]) == ["1.1e-08", "1.234"]
Expand All @@ -77,7 +78,7 @@ def test_setSimulationOptions():
mod.getSimulationOptions("thisOptionDoesNotExist")

# method 2
mod.setSimulationOptions(simOptions={"stopTime": 2.1, "tolerance": "1.2e-08"})
mod.setSimulationOptions(stopTime=2.1, tolerance=1.2e-08)
d = mod.getSimulationOptions()
assert d["stopTime"] == "2.1"
assert d["tolerance"] == "1.2e-08"
Expand Down Expand Up @@ -119,7 +120,9 @@ def test_getSolutions(model_firstorder):
a = -1
tau = -1 / a
stopTime = 5*tau
mod.setSimulationOptions(simOptions={"stopTime": stopTime, "stepSize": 0.1, "tolerance": 1e-8})

simOptions = {"stopTime": stopTime, "stepSize": 0.1, "tolerance": 1e-8}
mod.setSimulationOptions(**simOptions)
mod.simulate()

x = mod.getSolutions("x")
Expand Down Expand Up @@ -298,7 +301,7 @@ def test_getters(tmp_path):
x0 = 1.0
x_analytical = -b/a + (x0 + b/a) * np.exp(a * stopTime)
dx_analytical = (x0 + b/a) * a * np.exp(a * stopTime)
mod.setSimulationOptions(simOptions={"stopTime": stopTime})
mod.setSimulationOptions(stopTime=stopTime)
mod.simulate()

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

with pytest.raises(OMPython.ModelicaSystemError):
mod.setSimulationOptions(simOptions={"thisOptionDoesNotExist": 3})
mod.setSimulationOptions(thisOptionDoesNotExist=3)


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

mod.setSimulationOptions(simOptions={"stopTime": 1.0})
simOptions = {"stopTime": 1.0}
mod.setSimulationOptions(**simOptions)

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

# integrate a constant
mod.setInputs(name={"u1": 2.5})
mod.setInputs(u1=2.5)
assert mod.getInputs() == {
"u1": [
(0.0, 2.5),
Expand All @@ -374,7 +378,8 @@ def test_simulate_inputs(tmp_path):
assert np.isclose(y[-1], 2.5)

# now let's integrate the sum of two ramps
mod.setInputs(name={"u1": [(0.0, 0.0), (0.5, 2), (1.0, 0)]})
inputs = {"u1": [(0.0, 0.0), (0.5, 2), (1.0, 0)]}
mod.setInputs(**inputs)
assert mod.getInputs("u1") == [[
(0.0, 0.0),
(0.5, 2.0),
Expand All @@ -387,17 +392,20 @@ def test_simulate_inputs(tmp_path):
# let's try some edge cases
# unmatched startTime
with pytest.raises(OMPython.ModelicaSystemError):
mod.setInputs(name={"u1": [(-0.5, 0.0), (1.0, 1)]})
mod.setInputs(u1=[(-0.5, 0.0), (1.0, 1)])
mod.simulate()
# unmatched stopTime
with pytest.raises(OMPython.ModelicaSystemError):
mod.setInputs(name={"u1": [(0.0, 0.0), (0.5, 1)]})
mod.setInputs(u1=[(0.0, 0.0), (0.5, 1)])
mod.simulate()

# Let's use both inputs, but each one with different number of
# samples. This has an effect when generating the csv file.
mod.setInputs(name={"u1": [(0.0, 0), (1.0, 1)],
"u2": [(0.0, 0), (0.25, 0.5), (0.5, 1.0), (1.0, 0)]})
inputs = {
"u1": [(0.0, 0), (1.0, 1)],
"u2": [(0.0, 0), (0.25, 0.5), (0.5, 1.0), (1.0, 0)],
}
mod.setInputs(**inputs)
csv_file = mod._createCSVData()
assert pathlib.Path(csv_file).read_text() == """time,u1,u2,end
0.0,0.0,0.0,0
Expand Down
4 changes: 2 additions & 2 deletions tests/test_linearization.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ def test_getters(tmp_path):
assert "startTime" in d
assert "stopTime" in d
assert mod.getLinearizationOptions(["stopTime", "startTime"]) == [d["stopTime"], d["startTime"]]
mod.setLinearizationOptions(linearizationOptions={"stopTime": 0.02})
mod.setLinearizationOptions(stopTime=0.02)
assert mod.getLinearizationOptions("stopTime") == ["0.02"]

mod.setInputs(name={"u1": 10, "u2": 0})
mod.setInputs(u1=10, u2=0)
[A, B, C, D] = mod.linearize()
g = float(mod.getParameters("g")[0])
l = float(mod.getParameters("l")[0])
Expand Down
11 changes: 7 additions & 4 deletions tests/test_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ def test_optimization_example(tmp_path):

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

mod.setOptimizationOptions(optimizationOptions={"numberOfIntervals": 16,
"stopTime": 1,
"stepSize": 0.001,
"tolerance": 1e-8})
optimizationOptions = {
"numberOfIntervals": 16,
"stopTime": 1,
"stepSize": 0.001,
"tolerance": 1e-8,
}
mod.setOptimizationOptions(**optimizationOptions)

# test the getter
assert mod.getOptimizationOptions()["stopTime"] == "1"
Expand Down
Loading