Skip to content

Commit 4c651b9

Browse files
committed
[ModelicaSystemCmd] update handling of (override) args
* sort args for a defined output * update type hints
1 parent 8a15ddc commit 4c651b9

1 file changed

Lines changed: 66 additions & 21 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,18 @@ def __init__(
127127
self._runpath = runpath
128128
self._model_name = modelname
129129
self._timeout = timeout
130+
131+
# dictionaries of command line arguments for the model executable
130132
self._args: dict[str, str | None] = {}
133+
# 'override' argument needs special handling, as it is a dict on its own saved as dict elements following the
134+
# structure: 'key' => 'key=value'
131135
self._arg_override: dict[str, str] = {}
132136

133-
def arg_set(self, key: str, val: Optional[str | dict[str, Any]] = None) -> None:
137+
def arg_set(
138+
self,
139+
key: str,
140+
val: Optional[str | dict[str, Any] | numbers.Number] = None,
141+
) -> None:
134142
"""
135143
Set one argument for the executable model.
136144
@@ -140,12 +148,24 @@ def arg_set(self, key: str, val: Optional[str | dict[str, Any]] = None) -> None:
140148
indicates variables to override
141149
"""
142150

143-
def override2str(okey: str, oval: Any) -> str:
151+
def override2str(
152+
okey: str,
153+
oval: str | bool | numbers.Number,
154+
) -> str:
144155
"""
145156
Convert a value for 'override' to a string taking into account differences between Modelica and Python.
146157
"""
158+
# check oval for any string representations of numbers (or bool) and convert these to Python representations
159+
if isinstance(oval, str):
160+
try:
161+
oval_evaluated = ast.literal_eval(oval)
162+
if isinstance(oval_evaluated, (numbers.Number, bool)):
163+
oval = oval_evaluated
164+
except (ValueError, SyntaxError):
165+
pass
166+
147167
if isinstance(oval, str):
148-
oval_str = f"\"{oval.strip()}\"" # TODO: use shlex.quote()?
168+
oval_str = oval.strip()
149169
elif isinstance(oval, bool):
150170
oval_str = 'true' if oval else 'false'
151171
elif isinstance(oval, numbers.Number):
@@ -159,14 +179,32 @@ def override2str(okey: str, oval: Any) -> str:
159179
raise ModelicaSystemError(f"Invalid argument key: {repr(key)} (type: {type(key)})")
160180
key = key.strip()
161181

162-
if key == 'override' and isinstance(val, dict):
163-
for okey in val:
164-
if not isinstance(okey, str) or not isinstance(val[okey], (str, bool, numbers.Number)):
165-
raise ModelicaSystemError("Invalid argument for 'override': "
166-
f"{repr(okey)} = {repr(val[okey])}")
167-
self._arg_override[okey] = val[okey]
182+
if isinstance(val, dict):
183+
if key != 'override':
184+
raise ModelicaSystemError("Dictionary input only possible for key 'override'!")
185+
186+
for okey, oval in val.items():
187+
if not isinstance(okey, str):
188+
raise ModelicaSystemError("Invalid key for argument 'override': "
189+
f"{repr(okey)} (type: {type(okey)})")
190+
191+
if not isinstance(oval, (str, bool, numbers.Number, type(None))):
192+
raise ModelicaSystemError(f"Invalid input for 'override'.{repr(okey)}: "
193+
f"{repr(oval)} (type: {type(oval)})")
168194

169-
argval = ','.join([override2str(okey=okey, oval=oval) for okey, oval in self._arg_override.items()])
195+
if okey in self._arg_override:
196+
if oval is None:
197+
logger.info(f"Remove model executable override argument: {repr(self._arg_override[okey])}")
198+
del self._arg_override[okey]
199+
continue
200+
201+
logger.info(f"Update model executable override argument: {repr(okey)} = {repr(oval)} "
202+
f"(was: {repr(self._arg_override[okey])})")
203+
204+
if oval is not None:
205+
self._arg_override[okey] = override2str(okey=okey, oval=oval)
206+
207+
argval = ','.join(sorted(self._arg_override.values()))
170208
elif val is None:
171209
argval = None
172210
elif isinstance(val, str):
@@ -181,7 +219,7 @@ def override2str(okey: str, oval: Any) -> str:
181219
f"(was: {repr(self._args[key])})")
182220
self._args[key] = argval
183221

184-
def arg_get(self, key: str) -> Optional[str | dict]:
222+
def arg_get(self, key: str) -> Optional[str | dict[str, str | bool | numbers.Number]]:
185223
"""
186224
Return the value for the given key
187225
"""
@@ -190,7 +228,10 @@ def arg_get(self, key: str) -> Optional[str | dict]:
190228

191229
return None
192230

193-
def args_set(self, args: dict[str, Optional[str | dict[str, Any]]]) -> None:
231+
def args_set(
232+
self,
233+
args: dict[str, Optional[str | dict[str, Any] | numbers.Number]],
234+
) -> None:
194235
"""
195236
Define arguments for the model executable.
196237
"""
@@ -203,7 +244,7 @@ def get_cmd_args(self) -> list[str]:
203244
"""
204245

205246
cmdl = []
206-
for key in self._args:
247+
for key in sorted(self._args):
207248
if self._args[key] is None:
208249
cmdl.append(f"-{key}")
209250
else:
@@ -233,7 +274,7 @@ def definition(self) -> OMCSessionRunData:
233274
return omc_run_data_updated
234275

235276
@staticmethod
236-
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
277+
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | numbers.Number]]:
237278
"""
238279
Parse a simflag definition; this is deprecated!
239280
@@ -242,7 +283,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
242283
warnings.warn("The argument 'simflags' is depreciated and will be removed in future versions; "
243284
"please use 'simargs' instead", DeprecationWarning, stacklevel=2)
244285

245-
simargs: dict[str, Optional[str | dict[str, str]]] = {}
286+
simargs: dict[str, Optional[str | dict[str, Any] | numbers.Number]] = {}
246287

247288
args = [s for s in simflags.split(' ') if s]
248289
for arg in args:
@@ -935,7 +976,7 @@ def simulate_cmd(
935976
self,
936977
result_file: OMCPath,
937978
simflags: Optional[str] = None,
938-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
979+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
939980
timeout: Optional[float] = None,
940981
) -> ModelicaSystemCmd:
941982
"""
@@ -1014,7 +1055,7 @@ def simulate(
10141055
self,
10151056
resultfile: Optional[str] = None,
10161057
simflags: Optional[str] = None,
1017-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1058+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
10181059
timeout: Optional[float] = None,
10191060
) -> None:
10201061
"""Simulate the model according to simulation options.
@@ -1547,9 +1588,13 @@ def optimize(self) -> dict[str, Any]:
15471588

15481589
return optimizeResult
15491590

1550-
def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = None,
1551-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1552-
timeout: Optional[float] = None) -> LinearizationResult:
1591+
def linearize(
1592+
self,
1593+
lintime: Optional[float] = None,
1594+
simflags: Optional[str] = None,
1595+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
1596+
timeout: Optional[float] = None,
1597+
) -> LinearizationResult:
15531598
"""Linearize the model according to linearization options.
15541599
15551600
See setLinearizationOptions.
@@ -1759,7 +1804,7 @@ def __init__(
17591804
omc_process: Optional[OMCProcess] = None,
17601805
# simulation specific input
17611806
# TODO: add more settings (simulation options, input options, ...)
1762-
simargs: Optional[dict[str, Optional[str | dict[str, Any]]]] = None,
1807+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
17631808
timeout: Optional[int] = None,
17641809
# DoE specific inputs
17651810
resultpath: Optional[str | os.PathLike] = None,

0 commit comments

Comments
 (0)