3939import numbers
4040import numpy as np
4141import os
42- import pandas as pd
4342import platform
4443import queue
4544import re
@@ -1822,18 +1821,19 @@ def run_doe():
18221821 resdir = mypath / 'DoE'
18231822 resdir.mkdir(exist_ok=True)
18241823
1825- mod_doe = OMPython.ModelicaSystemDoE(
1824+ doe_mod = OMPython.ModelicaSystemDoE(
18261825 fileName=model.as_posix(),
18271826 modelName="M",
18281827 parameters=param,
18291828 resultpath=resdir,
18301829 simargs={"override": {'stopTime': 1.0}},
18311830 )
1832- mod_doe.prepare()
1833- df_doe = mod_doe.get_doe()
1834- mod_doe.simulate()
1835- var_list = mod_doe.get_solutions()
1836- sol_dict = mod_doe.get_solutions(var_list=var_list)
1831+ doe_mod.prepare()
1832+ doe_dict = doe_mod.get_doe()
1833+ doe_mod.simulate()
1834+ doe_sol = doe_mod.get_solutions()
1835+
1836+ # ... work with doe_df and doe_sol ...
18371837
18381838
18391839 if __name__ == "__main__":
@@ -1899,7 +1899,7 @@ def __init__(
18991899 else :
19001900 self ._parameters = {}
19011901
1902- self ._sim_df : Optional [pd . DataFrame ] = None
1902+ self ._sim_dict : Optional [dict [ str , dict [ str , Any ]] ] = None
19031903 self ._sim_task_query : queue .Queue = queue .Queue ()
19041904
19051905 def prepare (self ) -> int :
@@ -1924,7 +1924,7 @@ def prepare(self) -> int:
19241924 param_structure_combinations = list (itertools .product (* param_structure .values ()))
19251925 param_simple_combinations = list (itertools .product (* param_simple .values ()))
19261926
1927- df_entries : list [ pd . DataFrame ] = []
1927+ self . _sim_dict = {}
19281928 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
19291929 mod_structure = ModelicaSystem (
19301930 fileName = self ._fileName ,
@@ -1970,21 +1970,18 @@ def prepare(self) -> int:
19701970 df_data = (
19711971 {
19721972 'ID structure' : idx_pc_structure ,
1973- 'ID simple' : idx_pc_simple ,
1974- self .DF_COLUMNS_RESULT_FILENAME : resfilename ,
1975- 'structural parameters ID' : idx_pc_structure ,
19761973 }
19771974 | sim_param_structure
19781975 | {
1979- 'non-structural parameters ID ' : idx_pc_simple ,
1976+ 'ID non-structure ' : idx_pc_simple ,
19801977 }
19811978 | sim_param_simple
19821979 | {
19831980 self .DF_COLUMNS_RESULT_AVAILABLE : False ,
19841981 }
19851982 )
19861983
1987- df_entries . append ( pd . DataFrame ( data = df_data , index = [ 0 ]))
1984+ self . _sim_dict [ resfilename ] = df_data
19881985
19891986 mscmd = mod_structure .simulate_cmd (
19901987 resultfile = resultfile .absolute ().resolve (),
@@ -1996,17 +1993,26 @@ def prepare(self) -> int:
19961993
19971994 self ._sim_task_query .put (mscmd )
19981995
1999- self ._sim_df = pd .concat (df_entries , ignore_index = True )
2000-
2001- logger .info (f"Prepared { self ._sim_df .shape [0 ]} simulation definitions for the defined DoE." )
1996+ logger .info (f"Prepared { self ._sim_task_query .qsize ()} simulation definitions for the defined DoE." )
20021997
2003- return self ._sim_df . shape [ 0 ]
1998+ return self ._sim_task_query . qsize ()
20041999
2005- def get_doe (self ) -> Optional [pd . DataFrame ]:
2000+ def get_doe (self ) -> Optional [dict [ str , dict [ str , Any ]] ]:
20062001 """
2007- Get the defined Doe as a poandas dataframe.
2002+ Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
2003+ settings including structural and non-structural parameters.
2004+
2005+ The following code snippet can be used to convert the data to a pandas dataframe:
2006+
2007+ ```
2008+ import pandas as pd
2009+
2010+ doe_dict = doe_mod.get_doe()
2011+ doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
2012+ ```
2013+
20082014 """
2009- return self ._sim_df
2015+ return self ._sim_dict
20102016
20112017 def simulate (
20122018 self ,
@@ -2019,9 +2025,9 @@ def simulate(
20192025 """
20202026
20212027 sim_query_total = self ._sim_task_query .qsize ()
2022- if not isinstance (self ._sim_df , pd . DataFrame ) :
2028+ if not isinstance (self ._sim_dict , dict ) or len ( self . _sim_dict ) == 0 :
20232029 raise ModelicaSystemError ("Missing Doe Summary!" )
2024- sim_df_total = self ._sim_df . shape [ 0 ]
2030+ sim_dict_total = len ( self ._sim_dict )
20252031
20262032 def worker (worker_id , task_queue ):
20272033 while True :
@@ -2068,55 +2074,78 @@ def worker(worker_id, task_queue):
20682074 for thread in threads :
20692075 thread .join ()
20702076
2071- for row in self . _sim_df . to_dict ( 'records' ):
2072- resultfilename = row [ self .DF_COLUMNS_RESULT_FILENAME ]
2077+ sim_dict_done = 0
2078+ for resultfilename in self ._sim_dict :
20732079 resultfile = self ._resultpath / resultfilename
20742080
2075- if resultfile .exists ():
2076- mask = self ._sim_df [self .DF_COLUMNS_RESULT_FILENAME ] == resultfilename
2077- self ._sim_df .loc [mask , self .DF_COLUMNS_RESULT_AVAILABLE ] = True
2081+ # include check for an empty (=> 0B) result file which indicates a crash of the model executable
2082+ # see: https://github.com/OpenModelica/OMPython/issues/261
2083+ # https://github.com/OpenModelica/OpenModelica/issues/13829
2084+ if resultfile .is_file () and resultfile .stat ().st_size > 0 :
2085+ self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] = True
2086+ sim_dict_done += 1
20782087
2079- sim_df_done = self ._sim_df [self .DF_COLUMNS_RESULT_AVAILABLE ].sum ()
2080- logger .info (f"All workers finished ({ sim_df_done } of { sim_df_total } simulations with a result file)." )
2088+ logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
20812089
2082- return sim_df_total == sim_df_done
2090+ return sim_dict_total == sim_dict_done
20832091
20842092 def get_solutions (
20852093 self ,
20862094 var_list : Optional [list ] = None ,
2087- ) -> Optional [tuple [str ] | dict [str , pd . DataFrame | str ]]:
2095+ ) -> Optional [tuple [str ] | dict [str , dict [ str , np . ndarray ] ]]:
20882096 """
20892097 Get all solutions of the DoE run. The following return values are possible:
20902098
2091- * None, if there no simulation was run
2092-
20932099 * A list of variables if val_list == None
20942100
20952101 * The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
2102+
2103+ The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
2104+
2105+ ```
2106+ import pandas as pd
2107+
2108+ doe_sol = doe_mod.get_solutions()
2109+ for key in doe_sol:
2110+ data = doe_sol[key]['data']
2111+ if data:
2112+ doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
2113+ else:
2114+ doe_sol[key]['df'] = None
2115+ ```
2116+
20962117 """
2097- if self ._sim_df is None :
2118+ if not isinstance ( self ._sim_dict , dict ) :
20982119 return None
20992120
2100- if self ._sim_df . shape [ 0 ] == 0 or self . _sim_df [ self . DF_COLUMNS_RESULT_AVAILABLE ]. sum ( ) == 0 :
2121+ if len ( self ._sim_dict ) == 0 :
21012122 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
21022123
2103- if var_list is None :
2104- resultfilename = self ._sim_df [ self . DF_COLUMNS_RESULT_FILENAME ]. values [ 0 ]
2124+ sol_dict : dict [ str , dict [ str , Any ]] = {}
2125+ for resultfilename in self ._sim_dict :
21052126 resultfile = self ._resultpath / resultfilename
2106- return self ._mod .getSolutions (resultfile = resultfile )
21072127
2108- sol_dict : dict [str , pd .DataFrame | str ] = {}
2109- for row in self ._sim_df .to_dict ('records' ):
2110- resultfilename = row [self .DF_COLUMNS_RESULT_FILENAME ]
2111- resultfile = self ._resultpath / resultfilename
2128+ sol_dict [resultfilename ] = {}
2129+
2130+ if self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] != True :
2131+ sol_dict [resultfilename ]['msg' ] = 'No result file available!'
2132+ sol_dict [resultfilename ]['data' ] = {}
2133+ continue
2134+
2135+ if var_list is None :
2136+ var_list_row = list (self ._mod .getSolutions (resultfile = resultfile ))
2137+ else :
2138+ var_list_row = var_list
21122139
21132140 try :
2114- sol = self ._mod .getSolutions (varList = var_list , resultfile = resultfile )
2115- sol_data = {var : sol [idx ] for idx , var in var_list }
2116- sol_df = pd . DataFrame ( sol_data )
2117- sol_dict [resultfilename ] = sol_df
2141+ sol = self ._mod .getSolutions (varList = var_list_row , resultfile = resultfile )
2142+ sol_data = {var : sol [idx ] for idx , var in enumerate ( var_list_row ) }
2143+ sol_dict [ resultfilename ][ 'msg' ] = 'Simulation available'
2144+ sol_dict [resultfilename ][ 'data' ] = sol_data
21182145 except ModelicaSystemError as ex :
2119- logger .warning (f"No solution for { resultfilename } : { ex } " )
2120- sol_dict [resultfilename ] = str (ex )
2146+ msg = f"Error reading solution for { resultfilename } : { ex } "
2147+ logger .warning (msg )
2148+ sol_dict [resultfilename ]['msg' ] = msg
2149+ sol_dict [resultfilename ]['data' ] = {}
21212150
21222151 return sol_dict
0 commit comments