@@ -119,35 +119,90 @@ 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
129- Parameters
130- ----------
131- key : str
132- val : str, None
137+ Args:
138+ key: identifier / argument name to be used for the call of the model executable.
139+ val: value for the given key; None for no value and for key == 'override' a dictionary can be used which
140+ indicates variables to override
133141 """
142+
143+ def override2str (
144+ okey : str ,
145+ oval : str | bool | numbers .Number ,
146+ ) -> str :
147+ """
148+ Convert a value for 'override' to a string taking into account differences between Modelica and Python.
149+ """
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+
159+ if isinstance (oval , str ):
160+ oval_str = oval .strip ()
161+ elif isinstance (oval , bool ):
162+ oval_str = 'true' if oval else 'false'
163+ elif isinstance (oval , numbers .Number ):
164+ oval_str = str (oval )
165+ else :
166+ raise ModelicaSystemError (f"Invalid value for override key { okey } : { type (oval )} " )
167+
168+ return f"{ okey } ={ oval_str } "
169+
134170 if not isinstance (key , str ):
135171 raise ModelicaSystemError (f"Invalid argument key: { repr (key )} (type: { type (key )} )" )
136172 key = key .strip ()
137- if val is None :
173+
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
192+
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 ()))
200+ elif val is None :
138201 argval = None
139202 elif isinstance (val , str ):
140203 argval = val .strip ()
141204 elif isinstance (val , numbers .Number ):
142205 argval = str (val )
143- elif key == 'override' and isinstance (val , dict ):
144- for okey in val :
145- if not isinstance (okey , str ) or not isinstance (val [okey ], (str , numbers .Number )):
146- raise ModelicaSystemError ("Invalid argument for 'override': "
147- f"{ repr (okey )} = { repr (val [okey ])} " )
148- self ._arg_override [okey ] = val [okey ]
149-
150- argval = ',' .join ([f"{ okey } ={ str (self ._arg_override [okey ])} " for okey in self ._arg_override ])
151206 else :
152207 raise ModelicaSystemError (f"Invalid argument value for { repr (key )} : { repr (val )} (type: { type (val )} )" )
153208
@@ -156,7 +211,7 @@ def arg_set(self, key: str, val: Optional[str | dict] = None) -> None:
156211 f"(was: { repr (self ._args [key ])} )" )
157212 self ._args [key ] = argval
158213
159- def arg_get (self , key : str ) -> Optional [str | dict ]:
214+ def arg_get (self , key : str ) -> Optional [str | dict [ str , str | bool | numbers . Number ] ]:
160215 """
161216 Return the value for the given key
162217 """
@@ -165,13 +220,12 @@ def arg_get(self, key: str) -> Optional[str | dict]:
165220
166221 return None
167222
168- 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 :
169227 """
170228 Define arguments for the model executable.
171-
172- Parameters
173- ----------
174- args : dict[str, Optional[str | dict[str, str]]]
175229 """
176230 for arg in args :
177231 self .arg_set (key = arg , val = args [arg ])
@@ -197,7 +251,7 @@ def get_cmd(self) -> list:
197251 path_exe = self .get_exe ()
198252
199253 cmdl = [path_exe .as_posix ()]
200- for key in self ._args :
254+ for key in sorted ( self ._args ) :
201255 if self ._args [key ] is None :
202256 cmdl .append (f"-{ key } " )
203257 else :
@@ -255,7 +309,7 @@ def run(self) -> int:
255309 return returncode
256310
257311 @staticmethod
258- 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 ]]:
259313 """
260314 Parse a simflag definition; this is deprecated!
261315
@@ -264,7 +318,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
264318 warnings .warn ("The argument 'simflags' is depreciated and will be removed in future versions; "
265319 "please use 'simargs' instead" , DeprecationWarning , stacklevel = 2 )
266320
267- simargs : dict [str , Optional [str | dict [str , str ] ]] = {}
321+ simargs : dict [str , Optional [str | dict [str , Any ] | numbers . Number ]] = {}
268322
269323 args = [s for s in simflags .split (' ' ) if s ]
270324 for arg in args :
@@ -921,7 +975,7 @@ def simulate_cmd(
921975 self ,
922976 result_file : pathlib .Path ,
923977 simflags : Optional [str ] = None ,
924- simargs : Optional [dict [str , Optional [str | dict [str , str ] ]]] = None ,
978+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]] = None ,
925979 timeout : Optional [float ] = None ,
926980 ) -> ModelicaSystemCmd :
927981 """
@@ -994,7 +1048,7 @@ def simulate(
9941048 self ,
9951049 resultfile : Optional [str ] = None ,
9961050 simflags : Optional [str ] = None ,
997- simargs : Optional [dict [str , Optional [str | dict [str , str ] ]]] = None ,
1051+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]] = None ,
9981052 timeout : Optional [float ] = None ,
9991053 ) -> None :
10001054 """Simulate the model according to simulation options.
@@ -1516,9 +1570,13 @@ def optimize(self) -> dict[str, Any]:
15161570
15171571 return optimizeResult
15181572
1519- def linearize (self , lintime : Optional [float ] = None , simflags : Optional [str ] = None ,
1520- simargs : Optional [dict [str , Optional [str | dict [str , str ]]]] = None ,
1521- timeout : Optional [float ] = None ) -> LinearizationResult :
1573+ def linearize (
1574+ self ,
1575+ lintime : Optional [float ] = None ,
1576+ simflags : Optional [str ] = None ,
1577+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers .Number ]]] = None ,
1578+ timeout : Optional [float ] = None ,
1579+ ) -> LinearizationResult :
15221580 """Linearize the model according to linearization options.
15231581
15241582 See setLinearizationOptions.
0 commit comments