Skip to content

Commit c173613

Browse files
committed
Merge branch 'ModelicaSystemCmd_arg_set' into small_updates
2 parents 8ba87f3 + ac6d126 commit c173613

2 files changed

Lines changed: 98 additions & 29 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -119,35 +119,90 @@ def __init__(self, runpath: pathlib.Path, modelname: str, timeout: Optional[floa
119119
self._runpath = pathlib.Path(runpath).resolve().absolute()
120120
self._model_name = modelname
121121
self._timeout = timeout
122+
123+
# dictionaries of command line arguments for the model executable
122124
self._args: dict[str, str | None] = {}
125+
# 'override' argument needs special handling, as it is a dict on its own saved as dict elements following the
126+
# structure: 'key' => 'key=value'
123127
self._arg_override: dict[str, str] = {}
124128

125-
def arg_set(self, key: str, val: Optional[str | dict] = None) -> None:
129+
def arg_set(
130+
self,
131+
key: str,
132+
val: Optional[str | dict[str, Any] | numbers.Number] = None,
133+
) -> None:
126134
"""
127135
Set one argument for the executable model.
128136
129-
Parameters
130-
----------
131-
key : str
132-
val : str, None
137+
Args:
138+
key: identifier / argument name to be used for the call of the model executable.
139+
val: value for the given key; None for no value and for key == 'override' a dictionary can be used which
140+
indicates variables to override
133141
"""
142+
143+
def override2str(
144+
okey: str,
145+
oval: str | bool | numbers.Number,
146+
) -> str:
147+
"""
148+
Convert a value for 'override' to a string taking into account differences between Modelica and Python.
149+
"""
150+
# check oval for any string representations of numbers (or bool) and convert these to Python representations
151+
if isinstance(oval, str):
152+
try:
153+
oval_evaluated = ast.literal_eval(oval)
154+
if isinstance(oval_evaluated, (numbers.Number, bool)):
155+
oval = oval_evaluated
156+
except (ValueError, SyntaxError):
157+
pass
158+
159+
if isinstance(oval, str):
160+
oval_str = oval.strip()
161+
elif isinstance(oval, bool):
162+
oval_str = 'true' if oval else 'false'
163+
elif isinstance(oval, numbers.Number):
164+
oval_str = str(oval)
165+
else:
166+
raise ModelicaSystemError(f"Invalid value for override key {okey}: {type(oval)}")
167+
168+
return f"{okey}={oval_str}"
169+
134170
if not isinstance(key, str):
135171
raise ModelicaSystemError(f"Invalid argument key: {repr(key)} (type: {type(key)})")
136172
key = key.strip()
137-
if val is None:
173+
174+
if isinstance(val, dict):
175+
if key != 'override':
176+
raise ModelicaSystemError("Dictionary input only possible for key 'override'!")
177+
178+
for okey, oval in val.items():
179+
if not isinstance(okey, str):
180+
raise ModelicaSystemError("Invalid key for argument 'override': "
181+
f"{repr(okey)} (type: {type(okey)})")
182+
183+
if not isinstance(oval, (str, bool, numbers.Number, type(None))):
184+
raise ModelicaSystemError(f"Invalid input for 'override'.{repr(okey)}: "
185+
f"{repr(oval)} (type: {type(oval)})")
186+
187+
if okey in self._arg_override:
188+
if oval is None:
189+
logger.info(f"Remove model executable override argument: {repr(self._arg_override[okey])}")
190+
del self._arg_override[okey]
191+
continue
192+
193+
logger.info(f"Update model executable override argument: {repr(okey)} = {repr(oval)} "
194+
f"(was: {repr(self._arg_override[okey])})")
195+
196+
if oval is not None:
197+
self._arg_override[okey] = override2str(okey=okey, oval=oval)
198+
199+
argval = ','.join(sorted(self._arg_override.values()))
200+
elif val is None:
138201
argval = None
139202
elif isinstance(val, str):
140203
argval = val.strip()
141204
elif isinstance(val, numbers.Number):
142205
argval = str(val)
143-
elif key == 'override' and isinstance(val, dict):
144-
for okey in val:
145-
if not isinstance(okey, str) or not isinstance(val[okey], (str, numbers.Number)):
146-
raise ModelicaSystemError("Invalid argument for 'override': "
147-
f"{repr(okey)} = {repr(val[okey])}")
148-
self._arg_override[okey] = val[okey]
149-
150-
argval = ','.join([f"{okey}={str(self._arg_override[okey])}" for okey in self._arg_override])
151206
else:
152207
raise ModelicaSystemError(f"Invalid argument value for {repr(key)}: {repr(val)} (type: {type(val)})")
153208

@@ -156,7 +211,7 @@ def arg_set(self, key: str, val: Optional[str | dict] = None) -> None:
156211
f"(was: {repr(self._args[key])})")
157212
self._args[key] = argval
158213

159-
def arg_get(self, key: str) -> Optional[str | dict]:
214+
def arg_get(self, key: str) -> Optional[str | dict[str, str | bool | numbers.Number]]:
160215
"""
161216
Return the value for the given key
162217
"""
@@ -165,13 +220,12 @@ def arg_get(self, key: str) -> Optional[str | dict]:
165220

166221
return None
167222

168-
def args_set(self, args: dict[str, Optional[str | dict[str, str]]]) -> None:
223+
def args_set(
224+
self,
225+
args: dict[str, Optional[str | dict[str, Any] | numbers.Number]],
226+
) -> None:
169227
"""
170228
Define arguments for the model executable.
171-
172-
Parameters
173-
----------
174-
args : dict[str, Optional[str | dict[str, str]]]
175229
"""
176230
for arg in args:
177231
self.arg_set(key=arg, val=args[arg])
@@ -197,7 +251,7 @@ def get_cmd(self) -> list:
197251
path_exe = self.get_exe()
198252

199253
cmdl = [path_exe.as_posix()]
200-
for key in self._args:
254+
for key in sorted(self._args):
201255
if self._args[key] is None:
202256
cmdl.append(f"-{key}")
203257
else:
@@ -255,7 +309,7 @@ def run(self) -> int:
255309
return returncode
256310

257311
@staticmethod
258-
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
312+
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | numbers.Number]]:
259313
"""
260314
Parse a simflag definition; this is deprecated!
261315
@@ -264,7 +318,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
264318
warnings.warn("The argument 'simflags' is depreciated and will be removed in future versions; "
265319
"please use 'simargs' instead", DeprecationWarning, stacklevel=2)
266320

267-
simargs: dict[str, Optional[str | dict[str, str]]] = {}
321+
simargs: dict[str, Optional[str | dict[str, Any] | numbers.Number]] = {}
268322

269323
args = [s for s in simflags.split(' ') if s]
270324
for arg in args:
@@ -921,7 +975,7 @@ def simulate_cmd(
921975
self,
922976
result_file: pathlib.Path,
923977
simflags: Optional[str] = None,
924-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
978+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
925979
timeout: Optional[float] = None,
926980
) -> ModelicaSystemCmd:
927981
"""
@@ -994,7 +1048,7 @@ def simulate(
9941048
self,
9951049
resultfile: Optional[str] = None,
9961050
simflags: Optional[str] = None,
997-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1051+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
9981052
timeout: Optional[float] = None,
9991053
) -> None:
10001054
"""Simulate the model according to simulation options.
@@ -1516,9 +1570,13 @@ def optimize(self) -> dict[str, Any]:
15161570

15171571
return optimizeResult
15181572

1519-
def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = None,
1520-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1521-
timeout: Optional[float] = None) -> LinearizationResult:
1573+
def linearize(
1574+
self,
1575+
lintime: Optional[float] = None,
1576+
simflags: Optional[str] = None,
1577+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
1578+
timeout: Optional[float] = None,
1579+
) -> LinearizationResult:
15221580
"""Linearize the model according to linearization options.
15231581
15241582
See setLinearizationOptions.

tests/test_ModelicaSystemCmd.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ def test_simflags(mscmd_firstorder):
3535
assert mscmd.get_cmd() == [
3636
mscmd.get_exe().as_posix(),
3737
'-noEventEmit',
38-
'-override=b=2,a=1,x=3',
3938
'-noRestart',
39+
'-override=a=1,b=2,x=3',
40+
]
41+
42+
mscmd.args_set({
43+
"override": {'b': None},
44+
})
45+
46+
assert mscmd.get_cmd() == [
47+
mscmd.get_exe().as_posix(),
48+
'-noEventEmit',
49+
'-noRestart',
50+
'-override=a=1,x=3',
4051
]

0 commit comments

Comments
 (0)