4141import numbers
4242import numpy as np
4343import os
44- import pandas as pd
4544import pathlib
4645import platform
4746import queue
@@ -1676,18 +1675,19 @@ def run_doe():
16761675 resdir = mypath / 'DoE'
16771676 resdir.mkdir(exist_ok=True)
16781677
1679- mod_doe = OMPython.ModelicaSystemDoE(
1678+ doe_mod = OMPython.ModelicaSystemDoE(
16801679 fileName=model.as_posix(),
16811680 modelName="M",
16821681 parameters=param,
16831682 resultpath=resdir,
16841683 simargs={"override": {'stopTime': 1.0}},
16851684 )
1686- mod_doe.prepare()
1687- df_doe = mod_doe.get_doe()
1688- mod_doe.simulate()
1689- var_list = mod_doe.get_solutions()
1690- sol_dict = mod_doe.get_solutions(var_list=var_list)
1685+ doe_mod.prepare()
1686+ doe_dict = doe_mod.get_doe()
1687+ doe_mod.simulate()
1688+ doe_sol = doe_mod.get_solutions()
1689+
1690+ # ... work with doe_df and doe_sol ...
16911691
16921692
16931693 if __name__ == "__main__":
@@ -1753,7 +1753,7 @@ def __init__(
17531753 else :
17541754 self ._parameters = {}
17551755
1756- self ._sim_df : Optional [pd . DataFrame ] = None
1756+ self ._sim_dict : Optional [dict [ str , dict [ str , Any ]] ] = None
17571757 self ._sim_task_query : queue .Queue = queue .Queue ()
17581758
17591759 def prepare (self ) -> int :
@@ -1778,7 +1778,7 @@ def prepare(self) -> int:
17781778 param_structure_combinations = list (itertools .product (* param_structure .values ()))
17791779 param_simple_combinations = list (itertools .product (* param_simple .values ()))
17801780
1781- df_entries : list [ pd . DataFrame ] = []
1781+ self . _sim_dict = {}
17821782 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
17831783 mod_structure = ModelicaSystem (
17841784 fileName = self ._fileName ,
@@ -1824,21 +1824,18 @@ def prepare(self) -> int:
18241824 df_data = (
18251825 {
18261826 'ID structure' : idx_pc_structure ,
1827- 'ID simple' : idx_pc_simple ,
1828- self .DF_COLUMNS_RESULT_FILENAME : resfilename ,
1829- 'structural parameters ID' : idx_pc_structure ,
18301827 }
18311828 | sim_param_structure
18321829 | {
1833- 'non-structural parameters ID ' : idx_pc_simple ,
1830+ 'ID non-structure ' : idx_pc_simple ,
18341831 }
18351832 | sim_param_simple
18361833 | {
18371834 self .DF_COLUMNS_RESULT_AVAILABLE : False ,
18381835 }
18391836 )
18401837
1841- df_entries . append ( pd . DataFrame ( data = df_data , index = [ 0 ]))
1838+ self . _sim_dict [ resfilename ] = df_data
18421839
18431840 mscmd = mod_structure .simulate_cmd (
18441841 resultfile = resultfile .absolute ().resolve (),
@@ -1850,17 +1847,26 @@ def prepare(self) -> int:
18501847
18511848 self ._sim_task_query .put (mscmd )
18521849
1853- self ._sim_df = pd .concat (df_entries , ignore_index = True )
1854-
1855- logger .info (f"Prepared { self ._sim_df .shape [0 ]} simulation definitions for the defined DoE." )
1850+ logger .info (f"Prepared { self ._sim_task_query .qsize ()} simulation definitions for the defined DoE." )
18561851
1857- return self ._sim_df . shape [ 0 ]
1852+ return self ._sim_task_query . qsize ()
18581853
1859- def get_doe (self ) -> Optional [pd . DataFrame ]:
1854+ def get_doe (self ) -> Optional [dict [ str , dict [ str , Any ]] ]:
18601855 """
1861- Get the defined Doe as a poandas dataframe.
1856+ Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
1857+ settings including structural and non-structural parameters.
1858+
1859+ The following code snippet can be used to convert the data to a pandas dataframe:
1860+
1861+ ```
1862+ import pandas as pd
1863+
1864+ doe_dict = doe_mod.get_doe()
1865+ doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
1866+ ```
1867+
18621868 """
1863- return self ._sim_df
1869+ return self ._sim_dict
18641870
18651871 def simulate (
18661872 self ,
@@ -1873,9 +1879,9 @@ def simulate(
18731879 """
18741880
18751881 sim_query_total = self ._sim_task_query .qsize ()
1876- if not isinstance (self ._sim_df , pd . DataFrame ) :
1882+ if not isinstance (self ._sim_dict , dict ) or len ( self . _sim_dict ) == 0 :
18771883 raise ModelicaSystemError ("Missing Doe Summary!" )
1878- sim_df_total = self ._sim_df . shape [ 0 ]
1884+ sim_dict_total = len ( self ._sim_dict )
18791885
18801886 def worker (worker_id , task_queue ):
18811887 while True :
@@ -1922,55 +1928,78 @@ def worker(worker_id, task_queue):
19221928 for thread in threads :
19231929 thread .join ()
19241930
1925- for row in self . _sim_df . to_dict ( 'records' ):
1926- resultfilename = row [ self .DF_COLUMNS_RESULT_FILENAME ]
1931+ sim_dict_done = 0
1932+ for resultfilename in self ._sim_dict :
19271933 resultfile = self ._resultpath / resultfilename
19281934
1929- if resultfile .exists ():
1930- mask = self ._sim_df [self .DF_COLUMNS_RESULT_FILENAME ] == resultfilename
1931- self ._sim_df .loc [mask , self .DF_COLUMNS_RESULT_AVAILABLE ] = True
1935+ # include check for an empty (=> 0B) result file which indicates a crash of the model executable
1936+ # see: https://github.com/OpenModelica/OMPython/issues/261
1937+ # https://github.com/OpenModelica/OpenModelica/issues/13829
1938+ if resultfile .is_file () and resultfile .stat ().st_size > 0 :
1939+ self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] = True
1940+ sim_dict_done += 1
19321941
1933- sim_df_done = self ._sim_df [self .DF_COLUMNS_RESULT_AVAILABLE ].sum ()
1934- logger .info (f"All workers finished ({ sim_df_done } of { sim_df_total } simulations with a result file)." )
1942+ logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
19351943
1936- return sim_df_total == sim_df_done
1944+ return sim_dict_total == sim_dict_done
19371945
19381946 def get_solutions (
19391947 self ,
19401948 var_list : Optional [list ] = None ,
1941- ) -> Optional [tuple [str ] | dict [str , pd . DataFrame | str ]]:
1949+ ) -> Optional [tuple [str ] | dict [str , dict [ str , np . ndarray ] ]]:
19421950 """
19431951 Get all solutions of the DoE run. The following return values are possible:
19441952
1945- * None, if there no simulation was run
1946-
19471953 * A list of variables if val_list == None
19481954
19491955 * The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
1956+
1957+ The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
1958+
1959+ ```
1960+ import pandas as pd
1961+
1962+ doe_sol = doe_mod.get_solutions()
1963+ for key in doe_sol:
1964+ data = doe_sol[key]['data']
1965+ if data:
1966+ doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
1967+ else:
1968+ doe_sol[key]['df'] = None
1969+ ```
1970+
19501971 """
1951- if self ._sim_df is None :
1972+ if not isinstance ( self ._sim_dict , dict ) :
19521973 return None
19531974
1954- if self ._sim_df . shape [ 0 ] == 0 or self . _sim_df [ self . DF_COLUMNS_RESULT_AVAILABLE ]. sum ( ) == 0 :
1975+ if len ( self ._sim_dict ) == 0 :
19551976 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
19561977
1957- if var_list is None :
1958- resultfilename = self ._sim_df [ self . DF_COLUMNS_RESULT_FILENAME ]. values [ 0 ]
1978+ sol_dict : dict [ str , dict [ str , Any ]] = {}
1979+ for resultfilename in self ._sim_dict :
19591980 resultfile = self ._resultpath / resultfilename
1960- return self ._mod .getSolutions (resultfile = resultfile )
19611981
1962- sol_dict : dict [str , pd .DataFrame | str ] = {}
1963- for row in self ._sim_df .to_dict ('records' ):
1964- resultfilename = row [self .DF_COLUMNS_RESULT_FILENAME ]
1965- resultfile = self ._resultpath / resultfilename
1982+ sol_dict [resultfilename ] = {}
1983+
1984+ if self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] != True :
1985+ sol_dict [resultfilename ]['msg' ] = 'No result file available!'
1986+ sol_dict [resultfilename ]['data' ] = {}
1987+ continue
1988+
1989+ if var_list is None :
1990+ var_list_row = list (self ._mod .getSolutions (resultfile = resultfile ))
1991+ else :
1992+ var_list_row = var_list
19661993
19671994 try :
1968- sol = self ._mod .getSolutions (varList = var_list , resultfile = resultfile )
1969- sol_data = {var : sol [idx ] for idx , var in var_list }
1970- sol_df = pd . DataFrame ( sol_data )
1971- sol_dict [resultfilename ] = sol_df
1995+ sol = self ._mod .getSolutions (varList = var_list_row , resultfile = resultfile )
1996+ sol_data = {var : sol [idx ] for idx , var in enumerate ( var_list_row ) }
1997+ sol_dict [ resultfilename ][ 'msg' ] = 'Simulation available'
1998+ sol_dict [resultfilename ][ 'data' ] = sol_data
19721999 except ModelicaSystemError as ex :
1973- logger .warning (f"No solution for { resultfilename } : { ex } " )
1974- sol_dict [resultfilename ] = str (ex )
2000+ msg = f"Error reading solution for { resultfilename } : { ex } "
2001+ logger .warning (msg )
2002+ sol_dict [resultfilename ]['msg' ] = msg
2003+ sol_dict [resultfilename ]['data' ] = {}
19752004
19762005 return sol_dict
0 commit comments