4141import numbers
4242import numpy as np
4343import os
44- import pandas as pd
4544import pathlib
4645import platform
4746import queue
@@ -1561,18 +1560,19 @@ def run_doe():
15611560 resdir = mypath / 'DoE'
15621561 resdir.mkdir(exist_ok=True)
15631562
1564- mod_doe = OMPython.ModelicaSystemDoE(
1563+ doe_mod = OMPython.ModelicaSystemDoE(
15651564 fileName=model.as_posix(),
15661565 modelName="M",
15671566 parameters=param,
15681567 resultpath=resdir,
15691568 simargs={"override": {'stopTime': 1.0}},
15701569 )
1571- mod_doe.prepare()
1572- df_doe = mod_doe.get_doe()
1573- mod_doe.simulate()
1574- var_list = mod_doe.get_solutions()
1575- sol_dict = mod_doe.get_solutions(var_list=var_list)
1570+ doe_mod.prepare()
1571+ doe_dict = doe_mod.get_doe()
1572+ doe_mod.simulate()
1573+ doe_sol = doe_mod.get_solutions()
1574+
1575+ # ... work with doe_df and doe_sol ...
15761576
15771577
15781578 if __name__ == "__main__":
@@ -1638,7 +1638,7 @@ def __init__(
16381638 else :
16391639 self ._parameters = {}
16401640
1641- self ._sim_df : Optional [pd . DataFrame ] = None
1641+ self ._sim_dict : Optional [dict [ str , dict [ str , Any ]] ] = None
16421642 self ._sim_task_query : queue .Queue = queue .Queue ()
16431643
16441644 def prepare (self ) -> int :
@@ -1663,7 +1663,7 @@ def prepare(self) -> int:
16631663 param_structure_combinations = list (itertools .product (* param_structure .values ()))
16641664 param_simple_combinations = list (itertools .product (* param_simple .values ()))
16651665
1666- df_entries : list [ pd . DataFrame ] = []
1666+ self . _sim_dict = {}
16671667 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
16681668 mod_structure = ModelicaSystem (
16691669 fileName = self ._fileName ,
@@ -1709,21 +1709,18 @@ def prepare(self) -> int:
17091709 df_data = (
17101710 {
17111711 'ID structure' : idx_pc_structure ,
1712- 'ID simple' : idx_pc_simple ,
1713- self .DF_COLUMNS_RESULT_FILENAME : resfilename ,
1714- 'structural parameters ID' : idx_pc_structure ,
17151712 }
17161713 | sim_param_structure
17171714 | {
1718- 'non-structural parameters ID ' : idx_pc_simple ,
1715+ 'ID non-structure ' : idx_pc_simple ,
17191716 }
17201717 | sim_param_simple
17211718 | {
17221719 self .DF_COLUMNS_RESULT_AVAILABLE : False ,
17231720 }
17241721 )
17251722
1726- df_entries . append ( pd . DataFrame ( data = df_data , index = [ 0 ]))
1723+ self . _sim_dict [ resfilename ] = df_data
17271724
17281725 mscmd = mod_structure .simulate_cmd (
17291726 resultfile = resultfile .absolute ().resolve (),
@@ -1735,17 +1732,26 @@ def prepare(self) -> int:
17351732
17361733 self ._sim_task_query .put (mscmd )
17371734
1738- self ._sim_df = pd .concat (df_entries , ignore_index = True )
1739-
1740- logger .info (f"Prepared { self ._sim_df .shape [0 ]} simulation definitions for the defined DoE." )
1735+ logger .info (f"Prepared { self ._sim_task_query .qsize ()} simulation definitions for the defined DoE." )
17411736
1742- return self ._sim_df . shape [ 0 ]
1737+ return self ._sim_task_query . qsize ()
17431738
1744- def get_doe (self ) -> Optional [pd . DataFrame ]:
1739+ def get_doe (self ) -> Optional [dict [ str , dict [ str , Any ]] ]:
17451740 """
1746- Get the defined Doe as a poandas dataframe.
1741+ Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
1742+ settings including structural and non-structural parameters.
1743+
1744+ The following code snippet can be used to convert the data to a pandas dataframe:
1745+
1746+ ```
1747+ import pandas as pd
1748+
1749+ doe_dict = doe_mod.get_doe()
1750+ doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
1751+ ```
1752+
17471753 """
1748- return self ._sim_df
1754+ return self ._sim_dict
17491755
17501756 def simulate (
17511757 self ,
@@ -1758,9 +1764,9 @@ def simulate(
17581764 """
17591765
17601766 sim_query_total = self ._sim_task_query .qsize ()
1761- if not isinstance (self ._sim_df , pd . DataFrame ) :
1767+ if not isinstance (self ._sim_dict , dict ) or len ( self . _sim_dict ) == 0 :
17621768 raise ModelicaSystemError ("Missing Doe Summary!" )
1763- sim_df_total = self ._sim_df . shape [ 0 ]
1769+ sim_dict_total = len ( self ._sim_dict )
17641770
17651771 def worker (worker_id , task_queue ):
17661772 while True :
@@ -1807,55 +1813,78 @@ def worker(worker_id, task_queue):
18071813 for thread in threads :
18081814 thread .join ()
18091815
1810- for row in self . _sim_df . to_dict ( 'records' ):
1811- resultfilename = row [ self .DF_COLUMNS_RESULT_FILENAME ]
1816+ sim_dict_done = 0
1817+ for resultfilename in self ._sim_dict :
18121818 resultfile = self ._resultpath / resultfilename
18131819
1814- if resultfile .exists ():
1815- mask = self ._sim_df [self .DF_COLUMNS_RESULT_FILENAME ] == resultfilename
1816- self ._sim_df .loc [mask , self .DF_COLUMNS_RESULT_AVAILABLE ] = True
1820+ # include check for an empty (=> 0B) result file which indicates a crash of the model executable
1821+ # see: https://github.com/OpenModelica/OMPython/issues/261
1822+ # https://github.com/OpenModelica/OpenModelica/issues/13829
1823+ if resultfile .is_file () and resultfile .stat ().st_size > 0 :
1824+ self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] = True
1825+ sim_dict_done += 1
18171826
1818- sim_df_done = self ._sim_df [self .DF_COLUMNS_RESULT_AVAILABLE ].sum ()
1819- logger .info (f"All workers finished ({ sim_df_done } of { sim_df_total } simulations with a result file)." )
1827+ logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
18201828
1821- return sim_df_total == sim_df_done
1829+ return sim_dict_total == sim_dict_done
18221830
18231831 def get_solutions (
18241832 self ,
18251833 var_list : Optional [list ] = None ,
1826- ) -> Optional [tuple [str ] | dict [str , pd . DataFrame | str ]]:
1834+ ) -> Optional [tuple [str ] | dict [str , dict [ str , np . ndarray ] ]]:
18271835 """
18281836 Get all solutions of the DoE run. The following return values are possible:
18291837
1830- * None, if there no simulation was run
1831-
18321838 * A list of variables if val_list == None
18331839
18341840 * The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
1841+
1842+ The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
1843+
1844+ ```
1845+ import pandas as pd
1846+
1847+ doe_sol = doe_mod.get_solutions()
1848+ for key in doe_sol:
1849+ data = doe_sol[key]['data']
1850+ if data:
1851+ doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
1852+ else:
1853+ doe_sol[key]['df'] = None
1854+ ```
1855+
18351856 """
1836- if self ._sim_df is None :
1857+ if not isinstance ( self ._sim_dict , dict ) :
18371858 return None
18381859
1839- if self ._sim_df . shape [ 0 ] == 0 or self . _sim_df [ self . DF_COLUMNS_RESULT_AVAILABLE ]. sum ( ) == 0 :
1860+ if len ( self ._sim_dict ) == 0 :
18401861 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
18411862
1842- if var_list is None :
1843- resultfilename = self ._sim_df [ self . DF_COLUMNS_RESULT_FILENAME ]. values [ 0 ]
1863+ sol_dict : dict [ str , dict [ str , Any ]] = {}
1864+ for resultfilename in self ._sim_dict :
18441865 resultfile = self ._resultpath / resultfilename
1845- return self ._mod .getSolutions (resultfile = resultfile )
18461866
1847- sol_dict : dict [str , pd .DataFrame | str ] = {}
1848- for row in self ._sim_df .to_dict ('records' ):
1849- resultfilename = row [self .DF_COLUMNS_RESULT_FILENAME ]
1850- resultfile = self ._resultpath / resultfilename
1867+ sol_dict [resultfilename ] = {}
1868+
1869+ if self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] != True :
1870+ sol_dict [resultfilename ]['msg' ] = 'No result file available!'
1871+ sol_dict [resultfilename ]['data' ] = {}
1872+ continue
1873+
1874+ if var_list is None :
1875+ var_list_row = list (self ._mod .getSolutions (resultfile = resultfile ))
1876+ else :
1877+ var_list_row = var_list
18511878
18521879 try :
1853- sol = self ._mod .getSolutions (varList = var_list , resultfile = resultfile )
1854- sol_data = {var : sol [idx ] for idx , var in var_list }
1855- sol_df = pd . DataFrame ( sol_data )
1856- sol_dict [resultfilename ] = sol_df
1880+ sol = self ._mod .getSolutions (varList = var_list_row , resultfile = resultfile )
1881+ sol_data = {var : sol [idx ] for idx , var in enumerate ( var_list_row ) }
1882+ sol_dict [ resultfilename ][ 'msg' ] = 'Simulation available'
1883+ sol_dict [resultfilename ][ 'data' ] = sol_data
18571884 except ModelicaSystemError as ex :
1858- logger .warning (f"No solution for { resultfilename } : { ex } " )
1859- sol_dict [resultfilename ] = str (ex )
1885+ msg = f"Error reading solution for { resultfilename } : { ex } "
1886+ logger .warning (msg )
1887+ sol_dict [resultfilename ]['msg' ] = msg
1888+ sol_dict [resultfilename ]['data' ] = {}
18601889
18611890 return sol_dict
0 commit comments