@@ -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