@@ -1705,11 +1705,11 @@ def run_doe():
17051705 simargs={"override": {'stopTime': 1.0}},
17061706 )
17071707 doe_mod.prepare()
1708- doe_dict = doe_mod.get_doe ()
1708+ doe_def = doe_mod.get_doe_definition ()
17091709 doe_mod.simulate()
1710- doe_sol = doe_mod.get_solutions ()
1710+ doe_sol = doe_mod.get_doe_solutions ()
17111711
1712- # ... work with doe_df and doe_sol ...
1712+ # ... work with doe_def and doe_sol ...
17131713
17141714
17151715 if __name__ == "__main__":
@@ -1723,64 +1723,64 @@ def run_doe():
17231723
17241724 def __init__ (
17251725 self ,
1726- fileName : Optional [str | os .PathLike | pathlib .Path ] = None ,
1726+ fileName : Optional [str | os .PathLike | pathlib .Path | OMCPath ] = None ,
17271727 modelName : Optional [str ] = None ,
17281728 lmodel : Optional [list [str | tuple [str , str ]]] = None ,
17291729 commandLineOptions : Optional [str ] = None ,
17301730 variableFilter : Optional [str ] = None ,
1731- customBuildDirectory : Optional [str | os .PathLike | pathlib .Path ] = None ,
1731+ customBuildDirectory : Optional [str | os .PathLike | pathlib .Path | OMCPath ] = None ,
17321732 omhome : Optional [str ] = None ,
17331733 omc_process : Optional [OMCProcessLocal ] = None ,
17341734
1735- simargs : Optional [dict [str , Optional [str | dict [str , str ]]]] = None ,
1735+ simargs : Optional [dict [str , Optional [str | dict [str , str ] | numbers . Number ]]] = None ,
17361736 timeout : Optional [int ] = None ,
17371737
17381738 resultpath : Optional [pathlib .Path ] = None ,
17391739 parameters : Optional [dict [str , list [str ] | list [int ] | list [float ]]] = None ,
1740+ # simulation_options
1741+ # ... other options to be set? ...
17401742 ) -> None :
17411743 """
17421744 Initialisation of ModelicaSystemDoE. The parameters are based on: ModelicaSystem.__init__() and
17431745 ModelicaSystem.simulate(). Additionally, the path to store the result files is needed (= resultpath) as well as
17441746 a list of parameters to vary for the Doe (= parameters). All possible combinations are considered.
17451747 """
17461748 self ._lmodel = lmodel
1747- self ._modelName = modelName
1748- self ._fileName = fileName
1749+ self ._model_name = modelName
1750+ self ._file_name = fileName
17491751
1750- self ._CommandLineOptions = commandLineOptions
1751- self ._variableFilter = variableFilter
1752- self ._customBuildDirectory = customBuildDirectory
1752+ self ._command_line_options = commandLineOptions
1753+ self ._variable_filter = variableFilter
1754+ self ._custom_build_directory = customBuildDirectory
17531755 self ._omhome = omhome
17541756 self ._omc_process = omc_process
17551757
17561758 # reference for the model; not used for any simulations but to evaluate parameters, etc.
17571759 self ._mod = ModelicaSystem (
1758- fileName = self ._fileName ,
1759- modelName = self ._modelName ,
1760+ fileName = self ._file_name ,
1761+ modelName = self ._model_name ,
17601762 lmodel = self ._lmodel ,
1761- commandLineOptions = self ._CommandLineOptions ,
1762- variableFilter = self ._variableFilter ,
1763- customBuildDirectory = self ._customBuildDirectory ,
1763+ commandLineOptions = self ._command_line_options ,
1764+ variableFilter = self ._variable_filter ,
1765+ customBuildDirectory = self ._custom_build_directory ,
17641766 omhome = self ._omhome ,
17651767 omc_process = self ._omc_process ,
17661768 )
17671769
17681770 self ._simargs = simargs
17691771 self ._timeout = timeout
17701772
1771- # TODO: Update her & all path handling of this class
1772- if isinstance (resultpath , pathlib .Path ):
1773- self ._resultpath = resultpath
1774- else :
1775- self ._resultpath = pathlib .Path ('.' )
1773+ if resultpath is None : # isinstance(resultpath, (OMCPath, pathlib.path, str, os.PathLike)):
1774+ raise ModelicaSystemError ("Argument resultpath must be set to a valid path where the results are stored" )
1775+ self ._resultpath = self ._mod ._getconn .omcpath (resultpath )
17761776
17771777 if isinstance (parameters , dict ):
17781778 self ._parameters = parameters
17791779 else :
17801780 self ._parameters = {}
17811781
1782- self ._sim_dict : Optional [dict [str , dict [str , Any ]]] = None
1783- self ._sim_task_query : queue . Queue = queue . Queue ()
1782+ self ._doe_def : Optional [dict [str , dict [str , Any ]]] = None
1783+ self ._doe_cmd : Optional [ dict [ str , OMCSessionRunData ]] = None
17841784
17851785 def prepare (self ) -> int :
17861786 """
@@ -1790,6 +1790,8 @@ def prepare(self) -> int:
17901790 The return value is the number of simulation defined.
17911791 """
17921792
1793+ doe_sim = {}
1794+
17931795 param_structure = {}
17941796 param_simple = {}
17951797 for param_name in self ._parameters .keys ():
@@ -1804,15 +1806,18 @@ def prepare(self) -> int:
18041806 param_structure_combinations = list (itertools .product (* param_structure .values ()))
18051807 param_simple_combinations = list (itertools .product (* param_simple .values ()))
18061808
1807- self ._sim_dict = {}
1809+ self ._doe_def = {}
18081810 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
1811+ mod_structure_dir = self ._mod .getWorkDirectory () / f"DOE_{ idx_pc_structure :09d} "
1812+ mod_structure_dir .mkdir ()
1813+
18091814 mod_structure = ModelicaSystem (
1810- fileName = self ._fileName ,
1811- modelName = self ._modelName ,
1815+ fileName = self ._file_name ,
1816+ modelName = self ._model_name ,
18121817 lmodel = self ._lmodel ,
1813- commandLineOptions = self ._CommandLineOptions ,
1814- variableFilter = self ._variableFilter ,
1815- customBuildDirectory = self . _customBuildDirectory ,
1818+ commandLineOptions = self ._command_line_options ,
1819+ variableFilter = self ._variable_filter ,
1820+ customBuildDirectory = mod_structure_dir ,
18161821 omhome = self ._omhome ,
18171822 omc_process = self ._omc_process ,
18181823 build = False ,
@@ -1824,20 +1829,21 @@ def prepare(self) -> int:
18241829
18251830 pk_value = pc_structure [idx_structure ]
18261831 if isinstance (pk_value , str ):
1827- expression = f"setParameterValue({ self ._modelName } , { pk_structure } , \" { pk_value } \" )"
1832+ expression = f"setParameterValue({ self ._model_name } , { pk_structure } , \" { pk_value } \" )"
18281833 elif isinstance (pk_value , bool ):
18291834 pk_value_bool_str = "true" if pk_value else "false"
1830- expression = f"setParameterValue({ self ._modelName } , { pk_structure } , { pk_value_bool_str } );"
1835+ expression = f"setParameterValue({ self ._model_name } , { pk_structure } , { pk_value_bool_str } );"
18311836 else :
1832- expression = f"setParameterValue({ self ._modelName } , { pk_structure } , { pk_value } )"
1837+ expression = f"setParameterValue({ self ._model_name } , { pk_structure } , { pk_value } )"
18331838 res = mod_structure .sendExpression (expression )
18341839 if not res :
1835- raise ModelicaSystemError (f"Cannot set structural parameter { self ._modelName } .{ pk_structure } "
1840+ raise ModelicaSystemError (f"Cannot set structural parameter { self ._model_name } .{ pk_structure } "
18361841 f"to { pk_value } using { repr (expression )} " )
18371842
1838- mod_structure .buildModel (variableFilter = self ._variableFilter )
1843+ mod_structure .buildModel (variableFilter = self ._variable_filter )
18391844
18401845 for idx_pc_simple , pc_simple in enumerate (param_simple_combinations ):
1846+ # TODO: check mypy error message / type hints
18411847 sim_param_simple = {}
18421848 for idx_simple , pk_simple in enumerate (param_simple .keys ()):
18431849 sim_param_simple [pk_simple ] = pc_simple [idx_simple ]
@@ -1862,23 +1868,41 @@ def prepare(self) -> int:
18621868 }
18631869 )
18641870
1865- self ._sim_dict [resfilename ] = df_data
1871+ self ._doe_def [resfilename ] = df_data
1872+
1873+ mod_structure .setParameters (sim_param_simple )
18661874
18671875 mscmd = mod_structure .simulate_cmd (
1868- result_file = resultfile . absolute (). resolve () ,
1876+ result_file = resultfile ,
18691877 timeout = self ._timeout ,
18701878 )
18711879 if self ._simargs is not None :
18721880 mscmd .args_set (args = self ._simargs )
1873- mscmd .args_set (args = {"override" : sim_param_simple })
1881+ # mscmd.args_set(args={"override": sim_param_simple})
1882+
1883+ cmd_definition = mscmd .definition ()
1884+
1885+ # TODO: fix path to executable; copy it using OMCPath.copy() => no clone; only one OMC!
1886+ # exe = exe.parent / (DOE name)- exe.name
1887+
1888+ doe_sim [resultfile .name ] = cmd_definition
18741889
1875- self . _sim_task_query . put ( mscmd )
1890+ del mscmd
18761891
1877- logger .info (f"Prepared { self . _sim_task_query . qsize () } simulation definitions for the defined DoE." )
1892+ logger .info (f'Status OMC: { mod_structure . _getconn . sendExpression ( "getVersion()" ) } ' )
18781893
1879- return self ._sim_task_query .qsize ()
1894+ # cleanup; ensure that there is not too much RAM consumption
1895+ logger .warning ("DEL" )
1896+ del mod_structure ._getconn
1897+ del mod_structure
18801898
1881- def get_doe (self ) -> Optional [dict [str , dict [str , Any ]]]:
1899+ logger .info (f"Prepared { len (doe_sim )} simulation definitions for the defined DoE." )
1900+
1901+ self ._doe_cmd = doe_sim
1902+
1903+ return len (doe_sim )
1904+
1905+ def get_doe_definition (self ) -> Optional [dict [str , dict [str , Any ]]]:
18821906 """
18831907 Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
18841908 settings including structural and non-structural parameters.
@@ -1888,12 +1912,18 @@ def get_doe(self) -> Optional[dict[str, dict[str, Any]]]:
18881912 ```
18891913 import pandas as pd
18901914
1891- doe_dict = doe_mod.get_doe ()
1915+ doe_dict = doe_mod.get_doe_definition ()
18921916 doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
18931917 ```
18941918
18951919 """
1896- return self ._sim_dict
1920+ return self ._doe_def
1921+
1922+ def get_doe_command (self ) -> Optional [dict [str , OMCSessionRunData ]]:
1923+ """
1924+ Get the definitions of simulations commands to run for this DoE.
1925+ """
1926+ return self ._doe_cmd
18971927
18981928 def simulate (
18991929 self ,
@@ -1905,71 +1935,82 @@ def simulate(
19051935 Returns True if all simulations were done successfully, else False.
19061936 """
19071937
1908- sim_query_total = self ._sim_task_query .qsize ()
1909- if not isinstance (self ._sim_dict , dict ) or len (self ._sim_dict ) == 0 :
1938+ doe_cmd_total = len (self ._doe_cmd )
1939+ doe_def_total = len (self ._doe_def )
1940+
1941+ if doe_cmd_total != doe_def_total :
1942+ raise ModelicaSystemError (f"Mismatch between number simulation commands ({ doe_cmd_total } ) "
1943+ f"and simulation definitions ({ doe_def_total } )." )
1944+
1945+ doe_task_query : queue .Queue = queue .Queue ()
1946+ for doe_cmd in self ._doe_cmd .values ():
1947+ doe_task_query .put (doe_cmd )
1948+
1949+ if not isinstance (self ._doe_def , dict ) or len (self ._doe_def ) == 0 :
19101950 raise ModelicaSystemError ("Missing Doe Summary!" )
1911- sim_dict_total = len (self ._sim_dict )
19121951
19131952 def worker (worker_id , task_queue ):
19141953 while True :
19151954 try :
19161955 # Get the next task from the queue
1917- mscmd = task_queue .get (block = False )
1956+ cmd_definition = task_queue .get (block = False )
19181957 except queue .Empty :
1919- logger .info (f"[Worker { worker_id } ] No more simulations to run." )
1958+ logger .warning (f"[Worker { worker_id } ] No more simulations to run." )
19201959 break
19211960
1922- if mscmd is None :
1961+ if cmd_definition is None :
19231962 raise ModelicaSystemError ("Missing simulation definition!" )
19241963
1925- resultfile = mscmd . arg_get ( key = 'r' )
1926- resultpath = pathlib . Path (resultfile )
1964+ resultfile = cmd_definition . cmd_result_path
1965+ resultpath = self . _mod . _getconn . omcpath (resultfile )
19271966
1928- logger .info (f"[Worker { worker_id } ] Performing task: { resultpath .name } " )
1967+ logger .warning (f"[Worker { worker_id } ] Performing task: { resultpath .name } " )
19291968
19301969 try :
1931- mscmd .run ()
1970+ returncode = self ._mod ._getconn .run_model_executable (cmd_run_data = cmd_definition )
1971+ logger .warning (f"[Worker { worker_id } ] Simulation { resultpath .name } "
1972+ f"finished with return code: { returncode } " )
19321973 except ModelicaSystemError as ex :
19331974 logger .warning (f"Simulation error for { resultpath .name } : { ex } " )
19341975
19351976 # Mark the task as done
19361977 task_queue .task_done ()
19371978
1938- sim_query_done = sim_query_total - self . _sim_task_query .qsize ()
1939- logger .info (f"[Worker { worker_id } ] Task completed: { resultpath .name } "
1940- f"({ sim_query_total - sim_query_done } /{ sim_query_total } = "
1941- f"{ (sim_query_total - sim_query_done ) / sim_query_total * 100 :.2f} % of tasks left)" )
1979+ sim_query_done = doe_cmd_total - doe_task_query .qsize ()
1980+ logger .warning (f"[Worker { worker_id } ] Task completed: { resultpath .name } "
1981+ f"({ doe_cmd_total - sim_query_done } /{ doe_cmd_total } = "
1982+ f"{ (doe_cmd_total - sim_query_done ) / doe_cmd_total * 100 :.2f} % of tasks left)" )
19421983
1943- logger .info (f"Start simulations for DoE with { sim_query_total } simulations "
1944- f"using { num_workers } workers ..." )
1984+ logger .warning (f"Start simulations for DoE with { doe_cmd_total } simulations "
1985+ f"using { num_workers } workers ..." )
19451986
19461987 # Create and start worker threads
19471988 threads = []
19481989 for i in range (num_workers ):
1949- thread = threading .Thread (target = worker , args = (i , self . _sim_task_query ))
1990+ thread = threading .Thread (target = worker , args = (i , doe_task_query ))
19501991 thread .start ()
19511992 threads .append (thread )
19521993
19531994 # Wait for all threads to complete
19541995 for thread in threads :
19551996 thread .join ()
19561997
1957- sim_dict_done = 0
1958- for resultfilename in self ._sim_dict :
1998+ doe_def_done = 0
1999+ for resultfilename in self ._doe_def :
19592000 resultfile = self ._resultpath / resultfilename
19602001
19612002 # include check for an empty (=> 0B) result file which indicates a crash of the model executable
19622003 # see: https://github.com/OpenModelica/OMPython/issues/261
19632004 # https://github.com/OpenModelica/OpenModelica/issues/13829
1964- if resultfile .is_file () and resultfile .stat (). st_size > 0 :
1965- self ._sim_dict [resultfilename ][self .DICT_RESULT_AVAILABLE ] = True
1966- sim_dict_done += 1
2005+ if resultfile .is_file () and resultfile .size () > 0 :
2006+ self ._doe_def [resultfilename ][self .DICT_RESULT_AVAILABLE ] = True
2007+ doe_def_done += 1
19672008
1968- logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
2009+ logger .warning (f"All workers finished ({ doe_def_done } of { doe_def_total } simulations with a result file)." )
19692010
1970- return sim_dict_total == sim_dict_done
2011+ return doe_def_total == doe_def_done
19712012
1972- def get_solutions (
2013+ def get_doe_solutions (
19732014 self ,
19742015 var_list : Optional [list ] = None ,
19752016 ) -> Optional [tuple [str ] | dict [str , dict [str , np .ndarray ]]]:
@@ -1995,19 +2036,19 @@ def get_solutions(
19952036 ```
19962037
19972038 """
1998- if not isinstance (self ._sim_dict , dict ):
2039+ if not isinstance (self ._doe_def , dict ):
19992040 return None
20002041
2001- if len (self ._sim_dict ) == 0 :
2042+ if len (self ._doe_def ) == 0 :
20022043 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
20032044
20042045 sol_dict : dict [str , dict [str , Any ]] = {}
2005- for resultfilename in self ._sim_dict :
2046+ for resultfilename in self ._doe_def :
20062047 resultfile = self ._resultpath / resultfilename
20072048
20082049 sol_dict [resultfilename ] = {}
20092050
2010- if not self ._sim_dict [resultfilename ][self .DICT_RESULT_AVAILABLE ]:
2051+ if not self ._doe_def [resultfilename ][self .DICT_RESULT_AVAILABLE ]:
20112052 sol_dict [resultfilename ]['msg' ] = 'No result file available!'
20122053 sol_dict [resultfilename ]['data' ] = {}
20132054 continue
0 commit comments