Skip to content

Commit 98f374f

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

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
@@ -125,10 +125,18 @@ def __init__(
125125
self._runpath = runpath
126126
self._model_name = modelname
127127
self._timeout = timeout
128+
129+
# dictionaries of command line arguments for the model executable
128130
self._args: dict[str, str | None] = {}
131+
# 'override' argument needs special handling, as it is a dict on its own saved as dict elements following the
132+
# structure: 'key' => 'key=value'
129133
self._arg_override: dict[str, str] = {}
130134

131-
def arg_set(self, key: str, val: Optional[str | dict[str, Any]] = None) -> None:
135+
def arg_set(
136+
self,
137+
key: str,
138+
val: Optional[str | dict[str, Any] | numbers.Number] = None,
139+
) -> None:
132140
"""
133141
Set one argument for the executable model.
134142
@@ -138,12 +146,24 @@ def arg_set(self, key: str, val: Optional[str | dict[str, Any]] = None) -> None:
138146
indicates variables to override
139147
"""
140148

141-
def override2str(okey: str, oval: Any) -> str:
149+
def override2str(
150+
okey: str,
151+
oval: str | bool | numbers.Number,
152+
) -> str:
142153
"""
143154
Convert a value for 'override' to a string taking into account differences between Modelica and Python.
144155
"""
156+
# check oval for any string representations of numbers (or bool) and convert these to Python representations
157+
if isinstance(oval, str):
158+
try:
159+
oval_evaluated = ast.literal_eval(oval)
160+
if isinstance(oval_evaluated, (numbers.Number, bool)):
161+
oval = oval_evaluated
162+
except (ValueError, SyntaxError):
163+
pass
164+
145165
if isinstance(oval, str):
146-
oval_str = f"\"{oval.strip()}\"" # TODO: use shlex.quote()?
166+
oval_str = oval.strip()
147167
elif isinstance(oval, bool):
148168
oval_str = 'true' if oval else 'false'
149169
elif isinstance(oval, numbers.Number):
@@ -157,14 +177,32 @@ def override2str(okey: str, oval: Any) -> str:
157177
raise ModelicaSystemError(f"Invalid argument key: {repr(key)} (type: {type(key)})")
158178
key = key.strip()
159179

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

167-
argval = ','.join([override2str(okey=okey, oval=oval) for okey, oval in self._arg_override.items()])
193+
if okey in self._arg_override:
194+
if oval is None:
195+
logger.info(f"Remove model executable override argument: {repr(self._arg_override[okey])}")
196+
del self._arg_override[okey]
197+
continue
198+
199+
logger.info(f"Update model executable override argument: {repr(okey)} = {repr(oval)} "
200+
f"(was: {repr(self._arg_override[okey])})")
201+
202+
if oval is not None:
203+
self._arg_override[okey] = override2str(okey=okey, oval=oval)
204+
205+
argval = ','.join(sorted(self._arg_override.values()))
168206
elif val is None:
169207
argval = None
170208
elif isinstance(val, str):
@@ -179,7 +217,7 @@ def override2str(okey: str, oval: Any) -> str:
179217
f"(was: {repr(self._args[key])})")
180218
self._args[key] = argval
181219

182-
def arg_get(self, key: str) -> Optional[str | dict]:
220+
def arg_get(self, key: str) -> Optional[str | dict[str, str | bool | numbers.Number]]:
183221
"""
184222
Return the value for the given key
185223
"""
@@ -188,7 +226,10 @@ def arg_get(self, key: str) -> Optional[str | dict]:
188226

189227
return None
190228

191-
def args_set(self, args: dict[str, Optional[str | dict[str, Any]]]) -> None:
229+
def args_set(
230+
self,
231+
args: dict[str, Optional[str | dict[str, Any] | numbers.Number]],
232+
) -> None:
192233
"""
193234
Define arguments for the model executable.
194235
"""
@@ -201,7 +242,7 @@ def get_cmd_args(self) -> list[str]:
201242
"""
202243

203244
cmdl = []
204-
for key in self._args:
245+
for key in sorted(self._args):
205246
if self._args[key] is None:
206247
cmdl.append(f"-{key}")
207248
else:
@@ -231,7 +272,7 @@ def definition(self) -> OMCSessionRunData:
231272
return omc_run_data_updated
232273

233274
@staticmethod
234-
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
275+
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | numbers.Number]]:
235276
"""
236277
Parse a simflag definition; this is deprecated!
237278
@@ -240,7 +281,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
240281
warnings.warn("The argument 'simflags' is depreciated and will be removed in future versions; "
241282
"please use 'simargs' instead", DeprecationWarning, stacklevel=2)
242283

243-
simargs: dict[str, Optional[str | dict[str, str]]] = {}
284+
simargs: dict[str, Optional[str | dict[str, Any] | numbers.Number]] = {}
244285

245286
args = [s for s in simflags.split(' ') if s]
246287
for arg in args:
@@ -933,7 +974,7 @@ def simulate_cmd(
933974
self,
934975
result_file: OMCPath,
935976
simflags: Optional[str] = None,
936-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
977+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
937978
timeout: Optional[float] = None,
938979
) -> ModelicaSystemCmd:
939980
"""
@@ -1012,7 +1053,7 @@ def simulate(
10121053
self,
10131054
resultfile: Optional[str] = None,
10141055
simflags: Optional[str] = None,
1015-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1056+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
10161057
timeout: Optional[float] = None,
10171058
) -> None:
10181059
"""Simulate the model according to simulation options.
@@ -1545,9 +1586,13 @@ def optimize(self) -> dict[str, Any]:
15451586

15461587
return optimizeResult
15471588

1548-
def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = None,
1549-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1550-
timeout: Optional[float] = None) -> LinearizationResult:
1589+
def linearize(
1590+
self,
1591+
lintime: Optional[float] = None,
1592+
simflags: Optional[str] = None,
1593+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
1594+
timeout: Optional[float] = None,
1595+
) -> LinearizationResult:
15511596
"""Linearize the model according to linearization options.
15521597
15531598
See setLinearizationOptions.
@@ -1755,7 +1800,7 @@ def __init__(
17551800
omc_process: Optional[OMCProcess] = None,
17561801
# simulation specific input
17571802
# TODO: add more settings (simulation options, input options, ...)
1758-
simargs: Optional[dict[str, Optional[str | dict[str, Any]]]] = None,
1803+
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
17591804
timeout: Optional[int] = None,
17601805
# DoE specific inputs
17611806
resultpath: Optional[str | os.PathLike] = None,

0 commit comments

Comments
 (0)