3939import numbers
4040import numpy as np
4141import os
42- import pandas as pd
4342import queue
4443import textwrap
4544import threading
@@ -1801,18 +1800,19 @@ def run_doe():
18011800 resdir = mypath / 'DoE'
18021801 resdir.mkdir(exist_ok=True)
18031802
1804- mod_doe = OMPython.ModelicaSystemDoE(
1803+ doe_mod = OMPython.ModelicaSystemDoE(
18051804 fileName=model.as_posix(),
18061805 modelName="M",
18071806 parameters=param,
18081807 resultpath=resdir,
18091808 simargs={"override": {'stopTime': 1.0}},
18101809 )
1811- mod_doe.prepare()
1812- df_doe = mod_doe.get_doe()
1813- mod_doe.simulate()
1814- var_list = mod_doe.get_solutions()
1815- sol_dict = mod_doe.get_solutions(var_list=var_list)
1810+ doe_mod.prepare()
1811+ doe_dict = doe_mod.get_doe()
1812+ doe_mod.simulate()
1813+ doe_sol = doe_mod.get_solutions()
1814+
1815+ # ... work with doe_df and doe_sol ...
18161816
18171817
18181818 if __name__ == "__main__":
@@ -1878,7 +1878,7 @@ def __init__(
18781878 else :
18791879 self ._parameters = {}
18801880
1881- self ._sim_df : Optional [pd . DataFrame ] = None
1881+ self ._sim_dict : Optional [dict [ str , dict [ str , Any ]] ] = None
18821882 self ._sim_task_query : queue .Queue = queue .Queue ()
18831883
18841884 def prepare (self ) -> int :
@@ -1903,7 +1903,7 @@ def prepare(self) -> int:
19031903 param_structure_combinations = list (itertools .product (* param_structure .values ()))
19041904 param_simple_combinations = list (itertools .product (* param_simple .values ()))
19051905
1906- df_entries : list [ pd . DataFrame ] = []
1906+ self . _sim_dict = {}
19071907 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
19081908 mod_structure = ModelicaSystem (
19091909 fileName = self ._fileName ,
@@ -1949,21 +1949,18 @@ def prepare(self) -> int:
19491949 df_data = (
19501950 {
19511951 'ID structure' : idx_pc_structure ,
1952- 'ID simple' : idx_pc_simple ,
1953- self .DF_COLUMNS_RESULT_FILENAME : resfilename ,
1954- 'structural parameters ID' : idx_pc_structure ,
19551952 }
19561953 | sim_param_structure
19571954 | {
1958- 'non-structural parameters ID ' : idx_pc_simple ,
1955+ 'ID non-structure ' : idx_pc_simple ,
19591956 }
19601957 | sim_param_simple
19611958 | {
19621959 self .DF_COLUMNS_RESULT_AVAILABLE : False ,
19631960 }
19641961 )
19651962
1966- df_entries . append ( pd . DataFrame ( data = df_data , index = [ 0 ]))
1963+ self . _sim_dict [ resfilename ] = df_data
19671964
19681965 mscmd = mod_structure .simulate_cmd (
19691966 resultfile = resultfile .absolute ().resolve (),
@@ -1975,17 +1972,26 @@ def prepare(self) -> int:
19751972
19761973 self ._sim_task_query .put (mscmd )
19771974
1978- self ._sim_df = pd .concat (df_entries , ignore_index = True )
1979-
1980- logger .info (f"Prepared { self ._sim_df .shape [0 ]} simulation definitions for the defined DoE." )
1975+ logger .info (f"Prepared { self ._sim_task_query .qsize ()} simulation definitions for the defined DoE." )
19811976
1982- return self ._sim_df . shape [ 0 ]
1977+ return self ._sim_task_query . qsize ()
19831978
1984- def get_doe (self ) -> Optional [pd . DataFrame ]:
1979+ def get_doe (self ) -> Optional [dict [ str , dict [ str , Any ]] ]:
19851980 """
1986- Get the defined Doe as a poandas dataframe.
1981+ Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
1982+ settings including structural and non-structural parameters.
1983+
1984+ The following code snippet can be used to convert the data to a pandas dataframe:
1985+
1986+ ```
1987+ import pandas as pd
1988+
1989+ doe_dict = doe_mod.get_doe()
1990+ doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
1991+ ```
1992+
19871993 """
1988- return self ._sim_df
1994+ return self ._sim_dict
19891995
19901996 def simulate (
19911997 self ,
@@ -1998,9 +2004,9 @@ def simulate(
19982004 """
19992005
20002006 sim_query_total = self ._sim_task_query .qsize ()
2001- if not isinstance (self ._sim_df , pd . DataFrame ) :
2007+ if not isinstance (self ._sim_dict , dict ) or len ( self . _sim_dict ) == 0 :
20022008 raise ModelicaSystemError ("Missing Doe Summary!" )
2003- sim_df_total = self ._sim_df . shape [ 0 ]
2009+ sim_dict_total = len ( self ._sim_dict )
20042010
20052011 def worker (worker_id , task_queue ):
20062012 while True :
@@ -2047,55 +2053,78 @@ def worker(worker_id, task_queue):
20472053 for thread in threads :
20482054 thread .join ()
20492055
2050- for row in self . _sim_df . to_dict ( 'records' ):
2051- resultfilename = row [ self .DF_COLUMNS_RESULT_FILENAME ]
2056+ sim_dict_done = 0
2057+ for resultfilename in self ._sim_dict :
20522058 resultfile = self ._resultpath / resultfilename
20532059
2054- if resultfile .exists ():
2055- mask = self ._sim_df [self .DF_COLUMNS_RESULT_FILENAME ] == resultfilename
2056- self ._sim_df .loc [mask , self .DF_COLUMNS_RESULT_AVAILABLE ] = True
2060+ # include check for an empty (=> 0B) result file which indicates a crash of the model executable
2061+ # see: https://github.com/OpenModelica/OMPython/issues/261
2062+ # https://github.com/OpenModelica/OpenModelica/issues/13829
2063+ if resultfile .is_file () and resultfile .stat ().st_size > 0 :
2064+ self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] = True
2065+ sim_dict_done += 1
20572066
2058- sim_df_done = self ._sim_df [self .DF_COLUMNS_RESULT_AVAILABLE ].sum ()
2059- logger .info (f"All workers finished ({ sim_df_done } of { sim_df_total } simulations with a result file)." )
2067+ logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
20602068
2061- return sim_df_total == sim_df_done
2069+ return sim_dict_total == sim_dict_done
20622070
20632071 def get_solutions (
20642072 self ,
20652073 var_list : Optional [list ] = None ,
2066- ) -> Optional [tuple [str ] | dict [str , pd . DataFrame | str ]]:
2074+ ) -> Optional [tuple [str ] | dict [str , dict [ str , np . ndarray ] ]]:
20672075 """
20682076 Get all solutions of the DoE run. The following return values are possible:
20692077
2070- * None, if there no simulation was run
2071-
20722078 * A list of variables if val_list == None
20732079
20742080 * The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
2081+
2082+ The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
2083+
2084+ ```
2085+ import pandas as pd
2086+
2087+ doe_sol = doe_mod.get_solutions()
2088+ for key in doe_sol:
2089+ data = doe_sol[key]['data']
2090+ if data:
2091+ doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
2092+ else:
2093+ doe_sol[key]['df'] = None
2094+ ```
2095+
20752096 """
2076- if self ._sim_df is None :
2097+ if not isinstance ( self ._sim_dict , dict ) :
20772098 return None
20782099
2079- if self ._sim_df . shape [ 0 ] == 0 or self . _sim_df [ self . DF_COLUMNS_RESULT_AVAILABLE ]. sum ( ) == 0 :
2100+ if len ( self ._sim_dict ) == 0 :
20802101 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
20812102
2082- if var_list is None :
2083- resultfilename = self ._sim_df [ self . DF_COLUMNS_RESULT_FILENAME ]. values [ 0 ]
2103+ sol_dict : dict [ str , dict [ str , Any ]] = {}
2104+ for resultfilename in self ._sim_dict :
20842105 resultfile = self ._resultpath / resultfilename
2085- return self ._mod .getSolutions (resultfile = resultfile )
20862106
2087- sol_dict : dict [str , pd .DataFrame | str ] = {}
2088- for row in self ._sim_df .to_dict ('records' ):
2089- resultfilename = row [self .DF_COLUMNS_RESULT_FILENAME ]
2090- resultfile = self ._resultpath / resultfilename
2107+ sol_dict [resultfilename ] = {}
2108+
2109+ if self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] != True :
2110+ sol_dict [resultfilename ]['msg' ] = 'No result file available!'
2111+ sol_dict [resultfilename ]['data' ] = {}
2112+ continue
2113+
2114+ if var_list is None :
2115+ var_list_row = list (self ._mod .getSolutions (resultfile = resultfile ))
2116+ else :
2117+ var_list_row = var_list
20912118
20922119 try :
2093- sol = self ._mod .getSolutions (varList = var_list , resultfile = resultfile )
2094- sol_data = {var : sol [idx ] for idx , var in var_list }
2095- sol_df = pd . DataFrame ( sol_data )
2096- sol_dict [resultfilename ] = sol_df
2120+ sol = self ._mod .getSolutions (varList = var_list_row , resultfile = resultfile )
2121+ sol_data = {var : sol [idx ] for idx , var in enumerate ( var_list_row ) }
2122+ sol_dict [ resultfilename ][ 'msg' ] = 'Simulation available'
2123+ sol_dict [resultfilename ][ 'data' ] = sol_data
20972124 except ModelicaSystemError as ex :
2098- logger .warning (f"No solution for { resultfilename } : { ex } " )
2099- sol_dict [resultfilename ] = str (ex )
2125+ msg = f"Error reading solution for { resultfilename } : { ex } "
2126+ logger .warning (msg )
2127+ sol_dict [resultfilename ]['msg' ] = msg
2128+ sol_dict [resultfilename ]['data' ] = {}
21002129
21012130 return sol_dict
0 commit comments