3939import numbers
4040import numpy as np
4141import os
42- import pandas as pd
4342import queue
4443import textwrap
4544import threading
@@ -1838,18 +1837,19 @@ def run_doe():
18381837 resdir = mypath / 'DoE'
18391838 resdir.mkdir(exist_ok=True)
18401839
1841- mod_doe = OMPython.ModelicaSystemDoE(
1840+ doe_mod = OMPython.ModelicaSystemDoE(
18421841 fileName=model.as_posix(),
18431842 modelName="M",
18441843 parameters=param,
18451844 resultpath=resdir,
18461845 simargs={"override": {'stopTime': 1.0}},
18471846 )
1848- mod_doe.prepare()
1849- df_doe = mod_doe.get_doe()
1850- mod_doe.simulate()
1851- var_list = mod_doe.get_solutions()
1852- sol_dict = mod_doe.get_solutions(var_list=var_list)
1847+ doe_mod.prepare()
1848+ doe_dict = doe_mod.get_doe()
1849+ doe_mod.simulate()
1850+ doe_sol = doe_mod.get_solutions()
1851+
1852+ # ... work with doe_df and doe_sol ...
18531853
18541854
18551855 if __name__ == "__main__":
@@ -1915,7 +1915,7 @@ def __init__(
19151915 else :
19161916 self ._parameters = {}
19171917
1918- self ._sim_df : Optional [pd . DataFrame ] = None
1918+ self ._sim_dict : Optional [dict [ str , dict [ str , Any ]] ] = None
19191919 self ._sim_task_query : queue .Queue = queue .Queue ()
19201920
19211921 def prepare (self ) -> int :
@@ -1940,7 +1940,7 @@ def prepare(self) -> int:
19401940 param_structure_combinations = list (itertools .product (* param_structure .values ()))
19411941 param_simple_combinations = list (itertools .product (* param_simple .values ()))
19421942
1943- df_entries : list [ pd . DataFrame ] = []
1943+ self . _sim_dict = {}
19441944 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
19451945 mod_structure = ModelicaSystem (
19461946 fileName = self ._fileName ,
@@ -1986,21 +1986,18 @@ def prepare(self) -> int:
19861986 df_data = (
19871987 {
19881988 'ID structure' : idx_pc_structure ,
1989- 'ID simple' : idx_pc_simple ,
1990- self .DF_COLUMNS_RESULT_FILENAME : resfilename ,
1991- 'structural parameters ID' : idx_pc_structure ,
19921989 }
19931990 | sim_param_structure
19941991 | {
1995- 'non-structural parameters ID ' : idx_pc_simple ,
1992+ 'ID non-structure ' : idx_pc_simple ,
19961993 }
19971994 | sim_param_simple
19981995 | {
19991996 self .DF_COLUMNS_RESULT_AVAILABLE : False ,
20001997 }
20011998 )
20021999
2003- df_entries . append ( pd . DataFrame ( data = df_data , index = [ 0 ]))
2000+ self . _sim_dict [ resfilename ] = df_data
20042001
20052002 mscmd = mod_structure .simulate_cmd (
20062003 resultfile = resultfile .absolute ().resolve (),
@@ -2012,17 +2009,26 @@ def prepare(self) -> int:
20122009
20132010 self ._sim_task_query .put (mscmd )
20142011
2015- self ._sim_df = pd .concat (df_entries , ignore_index = True )
2016-
2017- logger .info (f"Prepared { self ._sim_df .shape [0 ]} simulation definitions for the defined DoE." )
2012+ logger .info (f"Prepared { self ._sim_task_query .qsize ()} simulation definitions for the defined DoE." )
20182013
2019- return self ._sim_df . shape [ 0 ]
2014+ return self ._sim_task_query . qsize ()
20202015
2021- def get_doe (self ) -> Optional [pd . DataFrame ]:
2016+ def get_doe (self ) -> Optional [dict [ str , dict [ str , Any ]] ]:
20222017 """
2023- Get the defined Doe as a poandas dataframe.
2018+ Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
2019+ settings including structural and non-structural parameters.
2020+
2021+ The following code snippet can be used to convert the data to a pandas dataframe:
2022+
2023+ ```
2024+ import pandas as pd
2025+
2026+ doe_dict = doe_mod.get_doe()
2027+ doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
2028+ ```
2029+
20242030 """
2025- return self ._sim_df
2031+ return self ._sim_dict
20262032
20272033 def simulate (
20282034 self ,
@@ -2035,9 +2041,9 @@ def simulate(
20352041 """
20362042
20372043 sim_query_total = self ._sim_task_query .qsize ()
2038- if not isinstance (self ._sim_df , pd . DataFrame ) :
2044+ if not isinstance (self ._sim_dict , dict ) or len ( self . _sim_dict ) == 0 :
20392045 raise ModelicaSystemError ("Missing Doe Summary!" )
2040- sim_df_total = self ._sim_df . shape [ 0 ]
2046+ sim_dict_total = len ( self ._sim_dict )
20412047
20422048 def worker (worker_id , task_queue ):
20432049 while True :
@@ -2084,55 +2090,78 @@ def worker(worker_id, task_queue):
20842090 for thread in threads :
20852091 thread .join ()
20862092
2087- for row in self . _sim_df . to_dict ( 'records' ):
2088- resultfilename = row [ self .DF_COLUMNS_RESULT_FILENAME ]
2093+ sim_dict_done = 0
2094+ for resultfilename in self ._sim_dict :
20892095 resultfile = self ._resultpath / resultfilename
20902096
2091- if resultfile .exists ():
2092- mask = self ._sim_df [self .DF_COLUMNS_RESULT_FILENAME ] == resultfilename
2093- self ._sim_df .loc [mask , self .DF_COLUMNS_RESULT_AVAILABLE ] = True
2097+ # include check for an empty (=> 0B) result file which indicates a crash of the model executable
2098+ # see: https://github.com/OpenModelica/OMPython/issues/261
2099+ # https://github.com/OpenModelica/OpenModelica/issues/13829
2100+ if resultfile .is_file () and resultfile .stat ().st_size > 0 :
2101+ self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] = True
2102+ sim_dict_done += 1
20942103
2095- sim_df_done = self ._sim_df [self .DF_COLUMNS_RESULT_AVAILABLE ].sum ()
2096- logger .info (f"All workers finished ({ sim_df_done } of { sim_df_total } simulations with a result file)." )
2104+ logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
20972105
2098- return sim_df_total == sim_df_done
2106+ return sim_dict_total == sim_dict_done
20992107
21002108 def get_solutions (
21012109 self ,
21022110 var_list : Optional [list ] = None ,
2103- ) -> Optional [tuple [str ] | dict [str , pd . DataFrame | str ]]:
2111+ ) -> Optional [tuple [str ] | dict [str , dict [ str , np . ndarray ] ]]:
21042112 """
21052113 Get all solutions of the DoE run. The following return values are possible:
21062114
2107- * None, if there no simulation was run
2108-
21092115 * A list of variables if val_list == None
21102116
21112117 * The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
2118+
2119+ The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
2120+
2121+ ```
2122+ import pandas as pd
2123+
2124+ doe_sol = doe_mod.get_solutions()
2125+ for key in doe_sol:
2126+ data = doe_sol[key]['data']
2127+ if data:
2128+ doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
2129+ else:
2130+ doe_sol[key]['df'] = None
2131+ ```
2132+
21122133 """
2113- if self ._sim_df is None :
2134+ if not isinstance ( self ._sim_dict , dict ) :
21142135 return None
21152136
2116- if self ._sim_df . shape [ 0 ] == 0 or self . _sim_df [ self . DF_COLUMNS_RESULT_AVAILABLE ]. sum ( ) == 0 :
2137+ if len ( self ._sim_dict ) == 0 :
21172138 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
21182139
2119- if var_list is None :
2120- resultfilename = self ._sim_df [ self . DF_COLUMNS_RESULT_FILENAME ]. values [ 0 ]
2140+ sol_dict : dict [ str , dict [ str , Any ]] = {}
2141+ for resultfilename in self ._sim_dict :
21212142 resultfile = self ._resultpath / resultfilename
2122- return self ._mod .getSolutions (resultfile = resultfile )
21232143
2124- sol_dict : dict [str , pd .DataFrame | str ] = {}
2125- for row in self ._sim_df .to_dict ('records' ):
2126- resultfilename = row [self .DF_COLUMNS_RESULT_FILENAME ]
2127- resultfile = self ._resultpath / resultfilename
2144+ sol_dict [resultfilename ] = {}
2145+
2146+ if self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] != True :
2147+ sol_dict [resultfilename ]['msg' ] = 'No result file available!'
2148+ sol_dict [resultfilename ]['data' ] = {}
2149+ continue
2150+
2151+ if var_list is None :
2152+ var_list_row = list (self ._mod .getSolutions (resultfile = resultfile ))
2153+ else :
2154+ var_list_row = var_list
21282155
21292156 try :
2130- sol = self ._mod .getSolutions (varList = var_list , resultfile = resultfile )
2131- sol_data = {var : sol [idx ] for idx , var in var_list }
2132- sol_df = pd . DataFrame ( sol_data )
2133- sol_dict [resultfilename ] = sol_df
2157+ sol = self ._mod .getSolutions (varList = var_list_row , resultfile = resultfile )
2158+ sol_data = {var : sol [idx ] for idx , var in enumerate ( var_list_row ) }
2159+ sol_dict [ resultfilename ][ 'msg' ] = 'Simulation available'
2160+ sol_dict [resultfilename ][ 'data' ] = sol_data
21342161 except ModelicaSystemError as ex :
2135- logger .warning (f"No solution for { resultfilename } : { ex } " )
2136- sol_dict [resultfilename ] = str (ex )
2162+ msg = f"Error reading solution for { resultfilename } : { ex } "
2163+ logger .warning (msg )
2164+ sol_dict [resultfilename ]['msg' ] = msg
2165+ sol_dict [resultfilename ]['data' ] = {}
21372166
21382167 return sol_dict
0 commit comments