3232 CONDITIONS OF OSMC-PL.
3333"""
3434
35+ import ast
3536import csv
3637from dataclasses import dataclass
3738import importlib
@@ -119,10 +120,18 @@ def __init__(self, runpath: pathlib.Path, modelname: str, timeout: Optional[floa
119120 self ._runpath = pathlib .Path (runpath ).resolve ().absolute ()
120121 self ._model_name = modelname
121122 self ._timeout = timeout
123+
124+ # dictionaries of command line arguments for the model executable
122125 self ._args : dict [str , str | None ] = {}
126+ # 'override' argument needs special handling, as it is a dict on its own saved as dict elements following the
127+ # structure: 'key' => 'key=value'
123128 self ._arg_override : dict [str , str ] = {}
124129
125- def arg_set (self , key : str , val : Optional [str | dict ] = None ) -> None :
130+ def arg_set (
131+ self ,
132+ key : str ,
133+ val : Optional [str | dict [str , Any ] | numbers .Number ] = None ,
134+ ) -> None :
126135 """
127136 Set one argument for the executable model.
128137
@@ -132,12 +141,24 @@ def arg_set(self, key: str, val: Optional[str | dict] = None) -> None:
132141 indicates variables to override
133142 """
134143
135- def override2str (okey : str , oval : Any ) -> str :
144+ def override2str (
145+ okey : str ,
146+ oval : str | bool | numbers .Number ,
147+ ) -> str :
136148 """
137149 Convert a value for 'override' to a string taking into account differences between Modelica and Python.
138150 """
151+ # check oval for any string representations of numbers (or bool) and convert these to Python representations
152+ if isinstance (oval , str ):
153+ try :
154+ oval_evaluated = ast .literal_eval (oval )
155+ if isinstance (oval_evaluated , (numbers .Number , bool )):
156+ oval = oval_evaluated
157+ except (ValueError , SyntaxError ):
158+ pass
159+
139160 if isinstance (oval , str ):
140- oval_str = f" \" { oval .strip ()} \" "
161+ oval_str = oval .strip ()
141162 elif isinstance (oval , bool ):
142163 oval_str = 'true' if oval else 'false'
143164 elif isinstance (oval , numbers .Number ):
@@ -151,14 +172,32 @@ def override2str(okey: str, oval: Any) -> str:
151172 raise ModelicaSystemError (f"Invalid argument key: { repr (key )} (type: { type (key )} )" )
152173 key = key .strip ()
153174
154- if key == 'override' and isinstance (val , dict ):
155- for okey in val :
156- if not isinstance (okey , str ) or not isinstance (val [okey ], (str , bool , numbers .Number )):
157- raise ModelicaSystemError ("Invalid argument for 'override': "
158- f"{ repr (okey )} = { repr (val [okey ])} " )
159- self ._arg_override [okey ] = val [okey ]
175+ if isinstance (val , dict ):
176+ if key != 'override' :
177+ raise ModelicaSystemError ("Dictionary input only possible for key 'override'!" )
178+
179+ for okey , oval in val .items ():
180+ if not isinstance (okey , str ):
181+ raise ModelicaSystemError ("Invalid key for argument 'override': "
182+ f"{ repr (okey )} (type: { type (okey )} )" )
183+
184+ if not isinstance (oval , (str , bool , numbers .Number , type (None ))):
185+ raise ModelicaSystemError (f"Invalid input for 'override'.{ repr (okey )} : "
186+ f"{ repr (oval )} (type: { type (oval )} )" )
187+
188+ if okey in self ._arg_override :
189+ if oval is None :
190+ logger .info (f"Remove model executable override argument: { repr (self ._arg_override [okey ])} " )
191+ del self ._arg_override [okey ]
192+ continue
160193
161- argval = ',' .join ([override2str (okey = okey , oval = oval ) for okey , oval in self ._arg_override .items ()])
194+ logger .info (f"Update model executable override argument: { repr (okey )} = { repr (oval )} "
195+ f"(was: { repr (self ._arg_override [okey ])} )" )
196+
197+ if oval is not None :
198+ self ._arg_override [okey ] = override2str (okey = okey , oval = oval )
199+
200+ argval = ',' .join (sorted (self ._arg_override .values ()))
162201 elif val is None :
163202 argval = None
164203 elif isinstance (val , str ):
@@ -173,7 +212,7 @@ def override2str(okey: str, oval: Any) -> str:
173212 f"(was: { repr (self ._args [key ])} )" )
174213 self ._args [key ] = argval
175214
176- def arg_get (self , key : str ) -> Optional [str | dict ]:
215+ def arg_get (self , key : str ) -> Optional [str | dict [ str , str | bool | numbers . Number ] ]:
177216 """
178217 Return the value for the given key
179218 """
@@ -182,7 +221,10 @@ def arg_get(self, key: str) -> Optional[str | dict]:
182221
183222 return None
184223
185- def args_set (self , args : dict [str , Optional [str | dict [str , str ]]]) -> None :
224+ def args_set (
225+ self ,
226+ args : dict [str , Optional [str | dict [str , Any ] | numbers .Number ]],
227+ ) -> None :
186228 """
187229 Define arguments for the model executable.
188230 """
@@ -210,7 +252,7 @@ def get_cmd(self) -> list:
210252 path_exe = self .get_exe ()
211253
212254 cmdl = [path_exe .as_posix ()]
213- for key in self ._args :
255+ for key in sorted ( self ._args ) :
214256 if self ._args [key ] is None :
215257 cmdl .append (f"-{ key } " )
216258 else :
@@ -268,7 +310,7 @@ def run(self) -> int:
268310 return returncode
269311
270312 @staticmethod
271- def parse_simflags (simflags : str ) -> dict [str , Optional [str | dict [str , str ] ]]:
313+ def parse_simflags (simflags : str ) -> dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]:
272314 """
273315 Parse a simflag definition; this is deprecated!
274316
@@ -277,7 +319,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, str]]]:
277319 warnings .warn ("The argument 'simflags' is depreciated and will be removed in future versions; "
278320 "please use 'simargs' instead" , DeprecationWarning , stacklevel = 2 )
279321
280- simargs : dict [str , Optional [str | dict [str , str ] ]] = {}
322+ simargs : dict [str , Optional [str | dict [str , Any ] | numbers . Number ]] = {}
281323
282324 args = [s for s in simflags .split (' ' ) if s ]
283325 for arg in args :
@@ -931,7 +973,7 @@ def simulate_cmd(
931973 self ,
932974 result_file : pathlib .Path ,
933975 simflags : Optional [str ] = None ,
934- simargs : Optional [dict [str , Optional [str | dict [str , str ] ]]] = None ,
976+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]] = None ,
935977 timeout : Optional [float ] = None ,
936978 ) -> ModelicaSystemCmd :
937979 """
@@ -1006,7 +1048,7 @@ def simulate(
10061048 self ,
10071049 resultfile : Optional [str ] = None ,
10081050 simflags : Optional [str ] = None ,
1009- simargs : Optional [dict [str , Optional [str | dict [str , str ] ]]] = None ,
1051+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers . Number ]]] = None ,
10101052 timeout : Optional [float ] = None ,
10111053 ) -> None :
10121054 """Simulate the model according to simulation options.
@@ -1427,9 +1469,13 @@ def optimize(self) -> dict[str, Any]:
14271469
14281470 return optimizeResult
14291471
1430- def linearize (self , lintime : Optional [float ] = None , simflags : Optional [str ] = None ,
1431- simargs : Optional [dict [str , Optional [str | dict [str , str ]]]] = None ,
1432- timeout : Optional [float ] = None ) -> LinearizationResult :
1472+ def linearize (
1473+ self ,
1474+ lintime : Optional [float ] = None ,
1475+ simflags : Optional [str ] = None ,
1476+ simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers .Number ]]] = None ,
1477+ timeout : Optional [float ] = None ,
1478+ ) -> LinearizationResult :
14331479 """Linearize the model according to linearization options.
14341480
14351481 See setLinearizationOptions.
0 commit comments