@@ -118,35 +118,90 @@ def __init__(self, runpath: pathlib.Path, modelname: str, timeout: Optional[floa
118118 self ._runpath = pathlib .Path (runpath ).resolve ().absolute ()
119119 self ._model_name = modelname
120120 self ._timeout = timeout
121+
122+ # dictionaries of command line arguments for the model executable
121123 self ._args : dict [str , str | None ] = {}
124+ # 'override' argument needs special handling, as it is a dict on its own saved as dict elements following the
125+ # structure: 'key' => 'key=value'
122126 self ._arg_override : dict [str , str ] = {}
123127
124- def arg_set (self , key : str , val : Optional [str | dict ] = None ) -> None :
128+ def arg_set (
129+ self ,
130+ key : str ,
131+ val : Optional [str | dict [str , Any ] | numbers .Number ] = None ,
132+ ) -> None :
125133 """
126134 Set one argument for the executable model.
127135
128- Parameters
129- ----------
130- key : str
131- val : str, None
136+ Args:
137+ key: identifier / argument name to be used for the call of the model executable.
138+ val: value for the given key; None for no value and for key == 'override' a dictionary can be used which
139+ indicates variables to override
132140 """
141+
142+ def override2str (
143+ okey : str ,
144+ oval : str | bool | numbers .Number ,
145+ ) -> str :
146+ """
147+ Convert a value for 'override' to a string taking into account differences between Modelica and Python.
148+ """
149+ # check oval for any string representations of numbers (or bool) and convert these to Python representations
150+ if isinstance (oval , str ):
151+ try :
152+ oval_evaluated = ast .literal_eval (oval )
153+ if isinstance (oval_evaluated , (numbers .Number , bool )):
154+ oval = oval_evaluated
155+ except (ValueError , SyntaxError ):
156+ pass
157+
158+ if isinstance (oval , str ):
159+ oval_str = oval .strip ()
160+ elif isinstance (oval , bool ):
161+ oval_str = 'true' if oval else 'false'
162+ elif isinstance (oval , numbers .Number ):
163+ oval_str = str (oval )
164+ else :
165+ raise ModelicaSystemError (f"Invalid value for override key { okey } : { type (oval )} " )
166+
167+ return f"{ okey } ={ oval_str } "
168+
133169 if not isinstance (key , str ):
134170 raise ModelicaSystemError (f"Invalid argument key: { repr (key )} (type: { type (key )} )" )
135171 key = key .strip ()
136- if val is None :
172+
173+ if isinstance (val , dict ):
174+ if key != 'override' :
175+ raise ModelicaSystemError ("Dictionary input only possible for key 'override'!" )
176+
177+ for okey , oval in val .items ():
178+ if not isinstance (okey , str ):
179+ raise ModelicaSystemError ("Invalid key for argument 'override': "
180+ f"{ repr (okey )} (type: { type (okey )} )" )
181+
182+ if not isinstance (oval , (str , bool , numbers .Number , type (None ))):
183+ raise ModelicaSystemError (f"Invalid input for 'override'.{ repr (okey )} : "
184+ f"{ repr (oval )} (type: { type (oval )} )" )
185+
186+ if okey in self ._arg_override :
187+ if oval is None :
188+ logger .info (f"Remove model executable override argument: { repr (self ._arg_override [okey ])} " )
189+ del self ._arg_override [okey ]
190+ continue
191+
192+ logger .info (f"Update model executable override argument: { repr (okey )} = { repr (oval )} "
193+ f"(was: { repr (self ._arg_override [okey ])} )" )
194+
195+ if oval is not None :
196+ self ._arg_override [okey ] = override2str (okey = okey , oval = oval )
197+
198+ argval = ',' .join (sorted (self ._arg_override .values ()))
199+ elif val is None :
137200 argval = None
138201 elif isinstance (val , str ):
139202 argval = val .strip ()
140203 elif isinstance (val , numbers .Number ):
141204 argval = str (val )
142- elif key == 'override' and isinstance (val , dict ):
143- for okey in val :
144- if not isinstance (okey , str ) or not isinstance (val [okey ], (str , numbers .Number )):
145- raise ModelicaSystemError ("Invalid argument for 'override': "
146- f"{ repr (okey )} = { repr (val [okey ])} " )
147- self ._arg_override [okey ] = val [okey ]
148-
149- argval = ',' .join ([f"{ okey } ={ str (self ._arg_override [okey ])} " for okey in self ._arg_override ])
150205 else :
151206 raise ModelicaSystemError (f"Invalid argument value for { repr (key )} : { repr (val )} (type: { type (val )} )" )
152207
@@ -155,7 +210,7 @@ def arg_set(self, key: str, val: Optional[str | dict] = None) -> None:
155210 f"(was: { repr (self ._args [key ])} )" )
156211 self ._args [key ] = argval
157212
158- def arg_get (self , key : str ) -> Optional [str | dict ]:
213+ def arg_get (self , key : str ) -> Optional [str | dict [ str , str | bool | numbers . Number ] ]:
159214 """
160215 Return the value for the given key
161216 """
@@ -164,13 +219,12 @@ def arg_get(self, key: str) -> Optional[str | dict]:
164219
165220 return None
166221
167- def args_set (self , args : dict [str , Optional [str | dict [str , str ]]]) -> None :
222+ def args_set (
223+ self ,
224+ args : dict [str , Optional [str | dict [str , Any ] | numbers .Number ]],
225+ ) -> None :
168226 """
169227 Define arguments for the model executable.
170-
171- Parameters
172- ----------
173- args : dict[str, Optional[str | dict[str, str]]]
174228 """
175229 for arg in args :
176230 self .arg_set (key = arg , val = args [arg ])
@@ -196,7 +250,7 @@ def get_cmd(self) -> list:
196250 path_exe = self .get_exe ()
197251
198252 cmdl = [path_exe .as_posix ()]
199- for key in self ._args :
253+ for key in sorted ( self ._args ) :
200254 if self ._args [key ] is None :
201255 cmdl .append (f"-{ key } " )
202256 else :
@@ -254,7 +308,7 @@ def run(self) -> int:
254308 return returncode
255309
256310 @staticmethod
257- def parse_simflags (simflags : str ) -> dict [str , Optional [str | dict [str , str ] ]]:
311+ def parse_simflags (simflags : str ) -> dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]:
258312 """
259313 Parse a simflag definition; this is deprecated!
260314
@@ -263,7 +317,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
263317 warnings .warn ("The argument 'simflags' is depreciated and will be removed in future versions; "
264318 "please use 'simargs' instead" , DeprecationWarning , stacklevel = 2 )
265319
266- simargs : dict [str , Optional [str | dict [str , str ] ]] = {}
320+ simargs : dict [str , Optional [str | dict [str , Any ] | numbers . Number ]] = {}
267321
268322 args = [s for s in simflags .split (' ' ) if s ]
269323 for arg in args :
@@ -940,7 +994,7 @@ def simulate_cmd(
940994 self ,
941995 result_file : pathlib .Path ,
942996 simflags : Optional [str ] = None ,
943- simargs : Optional [dict [str , Optional [str | dict [str , str ] ]]] = None ,
997+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]] = None ,
944998 timeout : Optional [float ] = None ,
945999 ) -> ModelicaSystemCmd :
9461000 """
@@ -1018,7 +1072,7 @@ def simulate(
10181072 self ,
10191073 resultfile : Optional [str ] = None ,
10201074 simflags : Optional [str ] = None ,
1021- simargs : Optional [dict [str , Optional [str | dict [str , str ] ]]] = None ,
1075+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]] = None ,
10221076 timeout : Optional [float ] = None ,
10231077 ) -> None :
10241078 """Simulate the model according to simulation options.
@@ -1541,9 +1595,13 @@ def optimize(self) -> dict[str, Any]:
15411595
15421596 return optimizeResult
15431597
1544- def linearize (self , lintime : Optional [float ] = None , simflags : Optional [str ] = None ,
1545- simargs : Optional [dict [str , Optional [str | dict [str , str ]]]] = None ,
1546- timeout : Optional [float ] = None ) -> LinearizationResult :
1598+ def linearize (
1599+ self ,
1600+ lintime : Optional [float ] = None ,
1601+ simflags : Optional [str ] = None ,
1602+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers .Number ]]] = None ,
1603+ timeout : Optional [float ] = None ,
1604+ ) -> LinearizationResult :
15471605 """Linearize the model according to linearization options.
15481606
15491607 See setLinearizationOptions.
0 commit comments