Skip to content

Commit 6d5bfe7

Browse files
committed
[ModelicaSystemCmd] update handling of (override) args
* sort args for a defined output * update type hints
1 parent 69654e7 commit 6d5bfe7

1 file changed

Lines changed: 65 additions & 20 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,18 @@ 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
@@ -132,12 +140,24 @@ def arg_set(self, key: str, val: Optional[str | dict] = None) -> None:
132140
indicates variables to override
133141
"""
134142

135-
def override2str(okey: str, oval: Any) -> str:
143+
def override2str(
144+
okey: str,
145+
oval: str | bool | numbers.Number,
146+
) -> str:
136147
"""
137148
Convert a value for 'override' to a string taking into account differences between Modelica and Python.
138149
"""
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+
139159
if isinstance(oval, str):
140-
oval_str = f"\"{oval.strip()}\""
160+
oval_str = oval.strip()
141161
elif isinstance(oval, bool):
142162
oval_str = 'true' if oval else 'false'
143163
elif isinstance(oval, numbers.Number):
@@ -151,14 +171,32 @@ def override2str(okey: str, oval: Any) -> str:
151171
raise ModelicaSystemError(f"Invalid argument key: {repr(key)} (type: {type(key)})")
152172
key = key.strip()
153173

154-
if key == 'override' and isinstance(val, dict):
155-
for okey in val:
156-
if not isinstance(okey, str) or not isinstance(val[okey], (str, bool, numbers.Number)):
157-
raise ModelicaSystemError("Invalid argument for 'override': "
158-
f"{repr(okey)} = {repr(val[okey])}")
159-
self._arg_override[okey] = val[okey]
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
160192

161-
argval = ','.join([override2str(okey=okey, oval=oval) for okey, oval in self._arg_override.items()])
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()))
162200
elif val is None:
163201
argval = None
164202
elif isinstance(val, str):
@@ -173,7 +211,7 @@ def override2str(okey: str, oval: Any) -> str:
173211
f"(was: {repr(self._args[key])})")
174212
self._args[key] = argval
175213

176-
def arg_get(self, key: str) -> Optional[str | dict]:
214+
def arg_get(self, key: str) -> Optional[str | dict[str, str | bool | numbers.Number]]:
177215
"""
178216
Return the value for the given key
179217
"""
@@ -182,7 +220,10 @@ def arg_get(self, key: str) -> Optional[str | dict]:
182220

183221
return None
184222

185-
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:
186227
"""
187228
Define arguments for the model executable.
188229
"""
@@ -210,7 +251,7 @@ def get_cmd(self) -> list:
210251
path_exe = self.get_exe()
211252

212253
cmdl = [path_exe.as_posix()]
213-
for key in self._args:
254+
for key in sorted(self._args):
214255
if self._args[key] is None:
215256
cmdl.append(f"-{key}")
216257
else:
@@ -268,7 +309,7 @@ def run(self) -> int:
268309
return returncode
269310

270311
@staticmethod
271-
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]]:
272313
"""
273314
Parse a simflag definition; this is deprecated!
274315
@@ -277,7 +318,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
277318
warnings.warn("The argument 'simflags' is depreciated and will be removed in future versions; "
278319
"please use 'simargs' instead", DeprecationWarning, stacklevel=2)
279320

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

282323
args = [s for s in simflags.split(' ') if s]
283324
for arg in args:
@@ -930,7 +971,7 @@ def simulate_cmd(
930971
self,
931972
result_file: pathlib.Path,
932973
simflags: Optional[str] = None,
933-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
974+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
934975
timeout: Optional[float] = None,
935976
) -> ModelicaSystemCmd:
936977
"""
@@ -1003,7 +1044,7 @@ def simulate(
10031044
self,
10041045
resultfile: Optional[str] = None,
10051046
simflags: Optional[str] = None,
1006-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1047+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
10071048
timeout: Optional[float] = None,
10081049
) -> None:
10091050
"""Simulate the model according to simulation options.
@@ -1524,9 +1565,13 @@ def optimize(self) -> dict[str, Any]:
15241565

15251566
return optimizeResult
15261567

1527-
def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = None,
1528-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1529-
timeout: Optional[float] = None) -> LinearizationResult:
1568+
def linearize(
1569+
self,
1570+
lintime: Optional[float] = None,
1571+
simflags: Optional[str] = None,
1572+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
1573+
timeout: Optional[float] = None,
1574+
) -> LinearizationResult:
15301575
"""Linearize the model according to linearization options.
15311576
15321577
See setLinearizationOptions.

0 commit comments

Comments
 (0)