4040import numbers
4141import numpy as np
4242import os
43- import pandas as pd
4443import pathlib
4544import platform
4645import queue
@@ -1684,18 +1683,19 @@ def run_doe():
16841683 resdir = mypath / 'DoE'
16851684 resdir.mkdir(exist_ok=True)
16861685
1687- mod_doe = OMPython.ModelicaSystemDoE(
1686+ doe_mod = OMPython.ModelicaSystemDoE(
16881687 fileName=model.as_posix(),
16891688 modelName="M",
16901689 parameters=param,
16911690 resultpath=resdir,
16921691 simargs={"override": {'stopTime': 1.0}},
16931692 )
1694- mod_doe.prepare()
1695- df_doe = mod_doe.get_doe()
1696- mod_doe.simulate()
1697- var_list = mod_doe.get_solutions()
1698- sol_dict = mod_doe.get_solutions(var_list=var_list)
1693+ doe_mod.prepare()
1694+ doe_dict = doe_mod.get_doe()
1695+ doe_mod.simulate()
1696+ doe_sol = doe_mod.get_solutions()
1697+
1698+ # ... work with doe_df and doe_sol ...
16991699
17001700
17011701 if __name__ == "__main__":
@@ -1761,7 +1761,7 @@ def __init__(
17611761 else :
17621762 self ._parameters = {}
17631763
1764- self ._sim_df : Optional [pd . DataFrame ] = None
1764+ self ._sim_dict : Optional [dict [ str , dict [ str , Any ]] ] = None
17651765 self ._sim_task_query : queue .Queue = queue .Queue ()
17661766
17671767 def prepare (self ) -> int :
@@ -1786,7 +1786,7 @@ def prepare(self) -> int:
17861786 param_structure_combinations = list (itertools .product (* param_structure .values ()))
17871787 param_simple_combinations = list (itertools .product (* param_simple .values ()))
17881788
1789- df_entries : list [ pd . DataFrame ] = []
1789+ self . _sim_dict = {}
17901790 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
17911791 mod_structure = ModelicaSystem (
17921792 fileName = self ._fileName ,
@@ -1832,21 +1832,18 @@ def prepare(self) -> int:
18321832 df_data = (
18331833 {
18341834 'ID structure' : idx_pc_structure ,
1835- 'ID simple' : idx_pc_simple ,
1836- self .DF_COLUMNS_RESULT_FILENAME : resfilename ,
1837- 'structural parameters ID' : idx_pc_structure ,
18381835 }
18391836 | sim_param_structure
18401837 | {
1841- 'non-structural parameters ID ' : idx_pc_simple ,
1838+ 'ID non-structure ' : idx_pc_simple ,
18421839 }
18431840 | sim_param_simple
18441841 | {
18451842 self .DF_COLUMNS_RESULT_AVAILABLE : False ,
18461843 }
18471844 )
18481845
1849- df_entries . append ( pd . DataFrame ( data = df_data , index = [ 0 ]))
1846+ self . _sim_dict [ resfilename ] = df_data
18501847
18511848 mscmd = mod_structure .simulate_cmd (
18521849 resultfile = resultfile .absolute ().resolve (),
@@ -1858,17 +1855,26 @@ def prepare(self) -> int:
18581855
18591856 self ._sim_task_query .put (mscmd )
18601857
1861- self ._sim_df = pd .concat (df_entries , ignore_index = True )
1862-
1863- logger .info (f"Prepared { self ._sim_df .shape [0 ]} simulation definitions for the defined DoE." )
1858+ logger .info (f"Prepared { self ._sim_task_query .qsize ()} simulation definitions for the defined DoE." )
18641859
1865- return self ._sim_df . shape [ 0 ]
1860+ return self ._sim_task_query . qsize ()
18661861
1867- def get_doe (self ) -> Optional [pd . DataFrame ]:
1862+ def get_doe (self ) -> Optional [dict [ str , dict [ str , Any ]] ]:
18681863 """
1869- Get the defined Doe as a poandas dataframe.
1864+ Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
1865+ settings including structural and non-structural parameters.
1866+
1867+ The following code snippet can be used to convert the data to a pandas dataframe:
1868+
1869+ ```
1870+ import pandas as pd
1871+
1872+ doe_dict = doe_mod.get_doe()
1873+ doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
1874+ ```
1875+
18701876 """
1871- return self ._sim_df
1877+ return self ._sim_dict
18721878
18731879 def simulate (
18741880 self ,
@@ -1881,9 +1887,9 @@ def simulate(
18811887 """
18821888
18831889 sim_query_total = self ._sim_task_query .qsize ()
1884- if not isinstance (self ._sim_df , pd . DataFrame ) :
1890+ if not isinstance (self ._sim_dict , dict ) or len ( self . _sim_dict ) == 0 :
18851891 raise ModelicaSystemError ("Missing Doe Summary!" )
1886- sim_df_total = self ._sim_df . shape [ 0 ]
1892+ sim_dict_total = len ( self ._sim_dict )
18871893
18881894 def worker (worker_id , task_queue ):
18891895 while True :
@@ -1930,55 +1936,78 @@ def worker(worker_id, task_queue):
19301936 for thread in threads :
19311937 thread .join ()
19321938
1933- for row in self . _sim_df . to_dict ( 'records' ):
1934- resultfilename = row [ self .DF_COLUMNS_RESULT_FILENAME ]
1939+ sim_dict_done = 0
1940+ for resultfilename in self ._sim_dict :
19351941 resultfile = self ._resultpath / resultfilename
19361942
1937- if resultfile .exists ():
1938- mask = self ._sim_df [self .DF_COLUMNS_RESULT_FILENAME ] == resultfilename
1939- self ._sim_df .loc [mask , self .DF_COLUMNS_RESULT_AVAILABLE ] = True
1943+ # include check for an empty (=> 0B) result file which indicates a crash of the model executable
1944+ # see: https://github.com/OpenModelica/OMPython/issues/261
1945+ # https://github.com/OpenModelica/OpenModelica/issues/13829
1946+ if resultfile .is_file () and resultfile .stat ().st_size > 0 :
1947+ self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] = True
1948+ sim_dict_done += 1
19401949
1941- sim_df_done = self ._sim_df [self .DF_COLUMNS_RESULT_AVAILABLE ].sum ()
1942- logger .info (f"All workers finished ({ sim_df_done } of { sim_df_total } simulations with a result file)." )
1950+ logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
19431951
1944- return sim_df_total == sim_df_done
1952+ return sim_dict_total == sim_dict_done
19451953
19461954 def get_solutions (
19471955 self ,
19481956 var_list : Optional [list ] = None ,
1949- ) -> Optional [tuple [str ] | dict [str , pd . DataFrame | str ]]:
1957+ ) -> Optional [tuple [str ] | dict [str , dict [ str , np . ndarray ] ]]:
19501958 """
19511959 Get all solutions of the DoE run. The following return values are possible:
19521960
1953- * None, if there no simulation was run
1954-
19551961 * A list of variables if val_list == None
19561962
19571963 * The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
1964+
1965+ The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
1966+
1967+ ```
1968+ import pandas as pd
1969+
1970+ doe_sol = doe_mod.get_solutions()
1971+ for key in doe_sol:
1972+ data = doe_sol[key]['data']
1973+ if data:
1974+ doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
1975+ else:
1976+ doe_sol[key]['df'] = None
1977+ ```
1978+
19581979 """
1959- if self ._sim_df is None :
1980+ if not isinstance ( self ._sim_dict , dict ) :
19601981 return None
19611982
1962- if self ._sim_df . shape [ 0 ] == 0 or self . _sim_df [ self . DF_COLUMNS_RESULT_AVAILABLE ]. sum ( ) == 0 :
1983+ if len ( self ._sim_dict ) == 0 :
19631984 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
19641985
1965- if var_list is None :
1966- resultfilename = self ._sim_df [ self . DF_COLUMNS_RESULT_FILENAME ]. values [ 0 ]
1986+ sol_dict : dict [ str , dict [ str , Any ]] = {}
1987+ for resultfilename in self ._sim_dict :
19671988 resultfile = self ._resultpath / resultfilename
1968- return self ._mod .getSolutions (resultfile = resultfile )
19691989
1970- sol_dict : dict [str , pd .DataFrame | str ] = {}
1971- for row in self ._sim_df .to_dict ('records' ):
1972- resultfilename = row [self .DF_COLUMNS_RESULT_FILENAME ]
1973- resultfile = self ._resultpath / resultfilename
1990+ sol_dict [resultfilename ] = {}
1991+
1992+ if self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] != True :
1993+ sol_dict [resultfilename ]['msg' ] = 'No result file available!'
1994+ sol_dict [resultfilename ]['data' ] = {}
1995+ continue
1996+
1997+ if var_list is None :
1998+ var_list_row = list (self ._mod .getSolutions (resultfile = resultfile ))
1999+ else :
2000+ var_list_row = var_list
19742001
19752002 try :
1976- sol = self ._mod .getSolutions (varList = var_list , resultfile = resultfile )
1977- sol_data = {var : sol [idx ] for idx , var in var_list }
1978- sol_df = pd . DataFrame ( sol_data )
1979- sol_dict [resultfilename ] = sol_df
2003+ sol = self ._mod .getSolutions (varList = var_list_row , resultfile = resultfile )
2004+ sol_data = {var : sol [idx ] for idx , var in enumerate ( var_list_row ) }
2005+ sol_dict [ resultfilename ][ 'msg' ] = 'Simulation available'
2006+ sol_dict [resultfilename ][ 'data' ] = sol_data
19802007 except ModelicaSystemError as ex :
1981- logger .warning (f"No solution for { resultfilename } : { ex } " )
1982- sol_dict [resultfilename ] = str (ex )
2008+ msg = f"Error reading solution for { resultfilename } : { ex } "
2009+ logger .warning (msg )
2010+ sol_dict [resultfilename ]['msg' ] = msg
2011+ sol_dict [resultfilename ]['data' ] = {}
19832012
19842013 return sol_dict
0 commit comments