3939import numbers
4040import numpy as np
4141import os
42- import pandas as pd
4342import pathlib
4443import platform
4544import queue
@@ -1781,18 +1780,19 @@ def run_doe():
17811780 resdir = mypath / 'DoE'
17821781 resdir.mkdir(exist_ok=True)
17831782
1784- mod_doe = OMPython.ModelicaSystemDoE(
1783+ doe_mod = OMPython.ModelicaSystemDoE(
17851784 fileName=model.as_posix(),
17861785 modelName="M",
17871786 parameters=param,
17881787 resultpath=resdir,
17891788 simargs={"override": {'stopTime': 1.0}},
17901789 )
1791- mod_doe.prepare()
1792- df_doe = mod_doe.get_doe()
1793- mod_doe.simulate()
1794- var_list = mod_doe.get_solutions()
1795- sol_dict = mod_doe.get_solutions(var_list=var_list)
1790+ doe_mod.prepare()
1791+ doe_dict = doe_mod.get_doe()
1792+ doe_mod.simulate()
1793+ doe_sol = doe_mod.get_solutions()
1794+
1795+ # ... work with doe_df and doe_sol ...
17961796
17971797
17981798 if __name__ == "__main__":
@@ -1858,7 +1858,7 @@ def __init__(
18581858 else :
18591859 self ._parameters = {}
18601860
1861- self ._sim_df : Optional [pd . DataFrame ] = None
1861+ self ._sim_dict : Optional [dict [ str , dict [ str , Any ]] ] = None
18621862 self ._sim_task_query : queue .Queue = queue .Queue ()
18631863
18641864 def prepare (self ) -> int :
@@ -1883,7 +1883,7 @@ def prepare(self) -> int:
18831883 param_structure_combinations = list (itertools .product (* param_structure .values ()))
18841884 param_simple_combinations = list (itertools .product (* param_simple .values ()))
18851885
1886- df_entries : list [ pd . DataFrame ] = []
1886+ self . _sim_dict = {}
18871887 for idx_pc_structure , pc_structure in enumerate (param_structure_combinations ):
18881888 mod_structure = ModelicaSystem (
18891889 fileName = self ._fileName ,
@@ -1929,21 +1929,18 @@ def prepare(self) -> int:
19291929 df_data = (
19301930 {
19311931 'ID structure' : idx_pc_structure ,
1932- 'ID simple' : idx_pc_simple ,
1933- self .DF_COLUMNS_RESULT_FILENAME : resfilename ,
1934- 'structural parameters ID' : idx_pc_structure ,
19351932 }
19361933 | sim_param_structure
19371934 | {
1938- 'non-structural parameters ID ' : idx_pc_simple ,
1935+ 'ID non-structure ' : idx_pc_simple ,
19391936 }
19401937 | sim_param_simple
19411938 | {
19421939 self .DF_COLUMNS_RESULT_AVAILABLE : False ,
19431940 }
19441941 )
19451942
1946- df_entries . append ( pd . DataFrame ( data = df_data , index = [ 0 ]))
1943+ self . _sim_dict [ resfilename ] = df_data
19471944
19481945 mscmd = mod_structure .simulate_cmd (
19491946 resultfile = resultfile .absolute ().resolve (),
@@ -1955,17 +1952,26 @@ def prepare(self) -> int:
19551952
19561953 self ._sim_task_query .put (mscmd )
19571954
1958- self ._sim_df = pd .concat (df_entries , ignore_index = True )
1959-
1960- logger .info (f"Prepared { self ._sim_df .shape [0 ]} simulation definitions for the defined DoE." )
1955+ logger .info (f"Prepared { self ._sim_task_query .qsize ()} simulation definitions for the defined DoE." )
19611956
1962- return self ._sim_df . shape [ 0 ]
1957+ return self ._sim_task_query . qsize ()
19631958
1964- def get_doe (self ) -> Optional [pd . DataFrame ]:
1959+ def get_doe (self ) -> Optional [dict [ str , dict [ str , Any ]] ]:
19651960 """
1966- Get the defined Doe as a poandas dataframe.
1961+ Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
1962+ settings including structural and non-structural parameters.
1963+
1964+ The following code snippet can be used to convert the data to a pandas dataframe:
1965+
1966+ ```
1967+ import pandas as pd
1968+
1969+ doe_dict = doe_mod.get_doe()
1970+ doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
1971+ ```
1972+
19671973 """
1968- return self ._sim_df
1974+ return self ._sim_dict
19691975
19701976 def simulate (
19711977 self ,
@@ -1978,9 +1984,9 @@ def simulate(
19781984 """
19791985
19801986 sim_query_total = self ._sim_task_query .qsize ()
1981- if not isinstance (self ._sim_df , pd . DataFrame ) :
1987+ if not isinstance (self ._sim_dict , dict ) or len ( self . _sim_dict ) == 0 :
19821988 raise ModelicaSystemError ("Missing Doe Summary!" )
1983- sim_df_total = self ._sim_df . shape [ 0 ]
1989+ sim_dict_total = len ( self ._sim_dict )
19841990
19851991 def worker (worker_id , task_queue ):
19861992 while True :
@@ -2027,55 +2033,78 @@ def worker(worker_id, task_queue):
20272033 for thread in threads :
20282034 thread .join ()
20292035
2030- for row in self . _sim_df . to_dict ( 'records' ):
2031- resultfilename = row [ self .DF_COLUMNS_RESULT_FILENAME ]
2036+ sim_dict_done = 0
2037+ for resultfilename in self ._sim_dict :
20322038 resultfile = self ._resultpath / resultfilename
20332039
2034- if resultfile .exists ():
2035- mask = self ._sim_df [self .DF_COLUMNS_RESULT_FILENAME ] == resultfilename
2036- self ._sim_df .loc [mask , self .DF_COLUMNS_RESULT_AVAILABLE ] = True
2040+ # include check for an empty (=> 0B) result file which indicates a crash of the model executable
2041+ # see: https://github.com/OpenModelica/OMPython/issues/261
2042+ # https://github.com/OpenModelica/OpenModelica/issues/13829
2043+ if resultfile .is_file () and resultfile .stat ().st_size > 0 :
2044+ self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] = True
2045+ sim_dict_done += 1
20372046
2038- sim_df_done = self ._sim_df [self .DF_COLUMNS_RESULT_AVAILABLE ].sum ()
2039- logger .info (f"All workers finished ({ sim_df_done } of { sim_df_total } simulations with a result file)." )
2047+ logger .info (f"All workers finished ({ sim_dict_done } of { sim_dict_total } simulations with a result file)." )
20402048
2041- return sim_df_total == sim_df_done
2049+ return sim_dict_total == sim_dict_done
20422050
20432051 def get_solutions (
20442052 self ,
20452053 var_list : Optional [list ] = None ,
2046- ) -> Optional [tuple [str ] | dict [str , pd . DataFrame | str ]]:
2054+ ) -> Optional [tuple [str ] | dict [str , dict [ str , np . ndarray ] ]]:
20472055 """
20482056 Get all solutions of the DoE run. The following return values are possible:
20492057
2050- * None, if there no simulation was run
2051-
20522058 * A list of variables if val_list == None
20532059
20542060 * The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
2061+
2062+ The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
2063+
2064+ ```
2065+ import pandas as pd
2066+
2067+ doe_sol = doe_mod.get_solutions()
2068+ for key in doe_sol:
2069+ data = doe_sol[key]['data']
2070+ if data:
2071+ doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
2072+ else:
2073+ doe_sol[key]['df'] = None
2074+ ```
2075+
20552076 """
2056- if self ._sim_df is None :
2077+ if not isinstance ( self ._sim_dict , dict ) :
20572078 return None
20582079
2059- if self ._sim_df . shape [ 0 ] == 0 or self . _sim_df [ self . DF_COLUMNS_RESULT_AVAILABLE ]. sum ( ) == 0 :
2080+ if len ( self ._sim_dict ) == 0 :
20602081 raise ModelicaSystemError ("No result files available - all simulations did fail?" )
20612082
2062- if var_list is None :
2063- resultfilename = self ._sim_df [ self . DF_COLUMNS_RESULT_FILENAME ]. values [ 0 ]
2083+ sol_dict : dict [ str , dict [ str , Any ]] = {}
2084+ for resultfilename in self ._sim_dict :
20642085 resultfile = self ._resultpath / resultfilename
2065- return self ._mod .getSolutions (resultfile = resultfile )
20662086
2067- sol_dict : dict [str , pd .DataFrame | str ] = {}
2068- for row in self ._sim_df .to_dict ('records' ):
2069- resultfilename = row [self .DF_COLUMNS_RESULT_FILENAME ]
2070- resultfile = self ._resultpath / resultfilename
2087+ sol_dict [resultfilename ] = {}
2088+
2089+ if self ._sim_dict [resultfilename ][self .DF_COLUMNS_RESULTS_AVAILABLE ] != True :
2090+ sol_dict [resultfilename ]['msg' ] = 'No result file available!'
2091+ sol_dict [resultfilename ]['data' ] = {}
2092+ continue
2093+
2094+ if var_list is None :
2095+ var_list_row = list (self ._mod .getSolutions (resultfile = resultfile ))
2096+ else :
2097+ var_list_row = var_list
20712098
20722099 try :
2073- sol = self ._mod .getSolutions (varList = var_list , resultfile = resultfile )
2074- sol_data = {var : sol [idx ] for idx , var in var_list }
2075- sol_df = pd . DataFrame ( sol_data )
2076- sol_dict [resultfilename ] = sol_df
2100+ sol = self ._mod .getSolutions (varList = var_list_row , resultfile = resultfile )
2101+ sol_data = {var : sol [idx ] for idx , var in enumerate ( var_list_row ) }
2102+ sol_dict [ resultfilename ][ 'msg' ] = 'Simulation available'
2103+ sol_dict [resultfilename ][ 'data' ] = sol_data
20772104 except ModelicaSystemError as ex :
2078- logger .warning (f"No solution for { resultfilename } : { ex } " )
2079- sol_dict [resultfilename ] = str (ex )
2105+ msg = f"Error reading solution for { resultfilename } : { ex } "
2106+ logger .warning (msg )
2107+ sol_dict [resultfilename ]['msg' ] = msg
2108+ sol_dict [resultfilename ]['data' ] = {}
20802109
20812110 return sol_dict
0 commit comments