Skip to content

Commit 0ee0aab

Browse files
committed
??? MSDOE
1 parent 4bba64a commit 0ee0aab

1 file changed

Lines changed: 113 additions & 72 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 113 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,11 +1705,11 @@ def run_doe():
17051705
simargs={"override": {'stopTime': 1.0}},
17061706
)
17071707
doe_mod.prepare()
1708-
doe_dict = doe_mod.get_doe()
1708+
doe_def = doe_mod.get_doe_definition()
17091709
doe_mod.simulate()
1710-
doe_sol = doe_mod.get_solutions()
1710+
doe_sol = doe_mod.get_doe_solutions()
17111711
1712-
# ... work with doe_df and doe_sol ...
1712+
# ... work with doe_def and doe_sol ...
17131713
17141714
17151715
if __name__ == "__main__":
@@ -1723,64 +1723,64 @@ def run_doe():
17231723

17241724
def __init__(
17251725
self,
1726-
fileName: Optional[str | os.PathLike | pathlib.Path] = None,
1726+
fileName: Optional[str | os.PathLike | pathlib.Path | OMCPath] = None,
17271727
modelName: Optional[str] = None,
17281728
lmodel: Optional[list[str | tuple[str, str]]] = None,
17291729
commandLineOptions: Optional[str] = None,
17301730
variableFilter: Optional[str] = None,
1731-
customBuildDirectory: Optional[str | os.PathLike | pathlib.Path] = None,
1731+
customBuildDirectory: Optional[str | os.PathLike | pathlib.Path | OMCPath] = None,
17321732
omhome: Optional[str] = None,
17331733
omc_process: Optional[OMCProcessLocal] = None,
17341734

1735-
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
1735+
simargs: Optional[dict[str, Optional[str | dict[str, str] | numbers.Number]]] = None,
17361736
timeout: Optional[int] = None,
17371737

17381738
resultpath: Optional[pathlib.Path] = None,
17391739
parameters: Optional[dict[str, list[str] | list[int] | list[float]]] = None,
1740+
# simulation_options
1741+
# ... other options to be set? ...
17401742
) -> None:
17411743
"""
17421744
Initialisation of ModelicaSystemDoE. The parameters are based on: ModelicaSystem.__init__() and
17431745
ModelicaSystem.simulate(). Additionally, the path to store the result files is needed (= resultpath) as well as
17441746
a list of parameters to vary for the Doe (= parameters). All possible combinations are considered.
17451747
"""
17461748
self._lmodel = lmodel
1747-
self._modelName = modelName
1748-
self._fileName = fileName
1749+
self._model_name = modelName
1750+
self._file_name = fileName
17491751

1750-
self._CommandLineOptions = commandLineOptions
1751-
self._variableFilter = variableFilter
1752-
self._customBuildDirectory = customBuildDirectory
1752+
self._command_line_options = commandLineOptions
1753+
self._variable_filter = variableFilter
1754+
self._custom_build_directory = customBuildDirectory
17531755
self._omhome = omhome
17541756
self._omc_process = omc_process
17551757

17561758
# reference for the model; not used for any simulations but to evaluate parameters, etc.
17571759
self._mod = ModelicaSystem(
1758-
fileName=self._fileName,
1759-
modelName=self._modelName,
1760+
fileName=self._file_name,
1761+
modelName=self._model_name,
17601762
lmodel=self._lmodel,
1761-
commandLineOptions=self._CommandLineOptions,
1762-
variableFilter=self._variableFilter,
1763-
customBuildDirectory=self._customBuildDirectory,
1763+
commandLineOptions=self._command_line_options,
1764+
variableFilter=self._variable_filter,
1765+
customBuildDirectory=self._custom_build_directory,
17641766
omhome=self._omhome,
17651767
omc_process=self._omc_process,
17661768
)
17671769

17681770
self._simargs = simargs
17691771
self._timeout = timeout
17701772

1771-
# TODO: Update her & all path handling of this class
1772-
if isinstance(resultpath, pathlib.Path):
1773-
self._resultpath = resultpath
1774-
else:
1775-
self._resultpath = pathlib.Path('.')
1773+
if resultpath is None: # isinstance(resultpath, (OMCPath, pathlib.path, str, os.PathLike)):
1774+
raise ModelicaSystemError("Argument resultpath must be set to a valid path where the results are stored")
1775+
self._resultpath = self._mod._getconn.omcpath(resultpath)
17761776

17771777
if isinstance(parameters, dict):
17781778
self._parameters = parameters
17791779
else:
17801780
self._parameters = {}
17811781

1782-
self._sim_dict: Optional[dict[str, dict[str, Any]]] = None
1783-
self._sim_task_query: queue.Queue = queue.Queue()
1782+
self._doe_def: Optional[dict[str, dict[str, Any]]] = None
1783+
self._doe_cmd: Optional[dict[str, OMCSessionRunData]] = None
17841784

17851785
def prepare(self) -> int:
17861786
"""
@@ -1790,6 +1790,8 @@ def prepare(self) -> int:
17901790
The return value is the number of simulation defined.
17911791
"""
17921792

1793+
doe_sim = {}
1794+
17931795
param_structure = {}
17941796
param_simple = {}
17951797
for param_name in self._parameters.keys():
@@ -1804,15 +1806,18 @@ def prepare(self) -> int:
18041806
param_structure_combinations = list(itertools.product(*param_structure.values()))
18051807
param_simple_combinations = list(itertools.product(*param_simple.values()))
18061808

1807-
self._sim_dict = {}
1809+
self._doe_def = {}
18081810
for idx_pc_structure, pc_structure in enumerate(param_structure_combinations):
1811+
mod_structure_dir = self._mod.getWorkDirectory() / f"DOE_{idx_pc_structure:09d}"
1812+
mod_structure_dir.mkdir()
1813+
18091814
mod_structure = ModelicaSystem(
1810-
fileName=self._fileName,
1811-
modelName=self._modelName,
1815+
fileName=self._file_name,
1816+
modelName=self._model_name,
18121817
lmodel=self._lmodel,
1813-
commandLineOptions=self._CommandLineOptions,
1814-
variableFilter=self._variableFilter,
1815-
customBuildDirectory=self._customBuildDirectory,
1818+
commandLineOptions=self._command_line_options,
1819+
variableFilter=self._variable_filter,
1820+
customBuildDirectory=mod_structure_dir,
18161821
omhome=self._omhome,
18171822
omc_process=self._omc_process,
18181823
build=False,
@@ -1824,20 +1829,21 @@ def prepare(self) -> int:
18241829

18251830
pk_value = pc_structure[idx_structure]
18261831
if isinstance(pk_value, str):
1827-
expression = f"setParameterValue({self._modelName}, {pk_structure}, \"{pk_value}\")"
1832+
expression = f"setParameterValue({self._model_name}, {pk_structure}, \"{pk_value}\")"
18281833
elif isinstance(pk_value, bool):
18291834
pk_value_bool_str = "true" if pk_value else "false"
1830-
expression = f"setParameterValue({self._modelName}, {pk_structure}, {pk_value_bool_str});"
1835+
expression = f"setParameterValue({self._model_name}, {pk_structure}, {pk_value_bool_str});"
18311836
else:
1832-
expression = f"setParameterValue({self._modelName}, {pk_structure}, {pk_value})"
1837+
expression = f"setParameterValue({self._model_name}, {pk_structure}, {pk_value})"
18331838
res = mod_structure.sendExpression(expression)
18341839
if not res:
1835-
raise ModelicaSystemError(f"Cannot set structural parameter {self._modelName}.{pk_structure} "
1840+
raise ModelicaSystemError(f"Cannot set structural parameter {self._model_name}.{pk_structure} "
18361841
f"to {pk_value} using {repr(expression)}")
18371842

1838-
mod_structure.buildModel(variableFilter=self._variableFilter)
1843+
mod_structure.buildModel(variableFilter=self._variable_filter)
18391844

18401845
for idx_pc_simple, pc_simple in enumerate(param_simple_combinations):
1846+
# TODO: check mypy error message / type hints
18411847
sim_param_simple = {}
18421848
for idx_simple, pk_simple in enumerate(param_simple.keys()):
18431849
sim_param_simple[pk_simple] = pc_simple[idx_simple]
@@ -1862,23 +1868,41 @@ def prepare(self) -> int:
18621868
}
18631869
)
18641870

1865-
self._sim_dict[resfilename] = df_data
1871+
self._doe_def[resfilename] = df_data
1872+
1873+
mod_structure.setParameters(sim_param_simple)
18661874

18671875
mscmd = mod_structure.simulate_cmd(
1868-
result_file=resultfile.absolute().resolve(),
1876+
result_file=resultfile,
18691877
timeout=self._timeout,
18701878
)
18711879
if self._simargs is not None:
18721880
mscmd.args_set(args=self._simargs)
1873-
mscmd.args_set(args={"override": sim_param_simple})
1881+
# mscmd.args_set(args={"override": sim_param_simple})
1882+
1883+
cmd_definition = mscmd.definition()
1884+
1885+
# TODO: fix path to executable; copy it using OMCPath.copy() => no clone; only one OMC!
1886+
# exe = exe.parent / (DOE name)- exe.name
1887+
1888+
doe_sim[resultfile.name] = cmd_definition
18741889

1875-
self._sim_task_query.put(mscmd)
1890+
del mscmd
18761891

1877-
logger.info(f"Prepared {self._sim_task_query.qsize()} simulation definitions for the defined DoE.")
1892+
logger.info(f'Status OMC: {mod_structure._getconn.sendExpression("getVersion()")}')
18781893

1879-
return self._sim_task_query.qsize()
1894+
# cleanup; ensure that there is not too much RAM consumption
1895+
logger.warning("DEL")
1896+
del mod_structure._getconn
1897+
del mod_structure
18801898

1881-
def get_doe(self) -> Optional[dict[str, dict[str, Any]]]:
1899+
logger.info(f"Prepared {len(doe_sim)} simulation definitions for the defined DoE.")
1900+
1901+
self._doe_cmd = doe_sim
1902+
1903+
return len(doe_sim)
1904+
1905+
def get_doe_definition(self) -> Optional[dict[str, dict[str, Any]]]:
18821906
"""
18831907
Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
18841908
settings including structural and non-structural parameters.
@@ -1888,12 +1912,18 @@ def get_doe(self) -> Optional[dict[str, dict[str, Any]]]:
18881912
```
18891913
import pandas as pd
18901914
1891-
doe_dict = doe_mod.get_doe()
1915+
doe_dict = doe_mod.get_doe_definition()
18921916
doe_df = pd.DataFrame.from_dict(data=doe_dict, orient='index')
18931917
```
18941918
18951919
"""
1896-
return self._sim_dict
1920+
return self._doe_def
1921+
1922+
def get_doe_command(self) -> Optional[dict[str, OMCSessionRunData]]:
1923+
"""
1924+
Get the definitions of simulations commands to run for this DoE.
1925+
"""
1926+
return self._doe_cmd
18971927

18981928
def simulate(
18991929
self,
@@ -1905,71 +1935,82 @@ def simulate(
19051935
Returns True if all simulations were done successfully, else False.
19061936
"""
19071937

1908-
sim_query_total = self._sim_task_query.qsize()
1909-
if not isinstance(self._sim_dict, dict) or len(self._sim_dict) == 0:
1938+
doe_cmd_total = len(self._doe_cmd)
1939+
doe_def_total = len(self._doe_def)
1940+
1941+
if doe_cmd_total != doe_def_total:
1942+
raise ModelicaSystemError(f"Mismatch between number simulation commands ({doe_cmd_total}) "
1943+
f"and simulation definitions ({doe_def_total}).")
1944+
1945+
doe_task_query: queue.Queue = queue.Queue()
1946+
for doe_cmd in self._doe_cmd.values():
1947+
doe_task_query.put(doe_cmd)
1948+
1949+
if not isinstance(self._doe_def, dict) or len(self._doe_def) == 0:
19101950
raise ModelicaSystemError("Missing Doe Summary!")
1911-
sim_dict_total = len(self._sim_dict)
19121951

19131952
def worker(worker_id, task_queue):
19141953
while True:
19151954
try:
19161955
# Get the next task from the queue
1917-
mscmd = task_queue.get(block=False)
1956+
cmd_definition = task_queue.get(block=False)
19181957
except queue.Empty:
1919-
logger.info(f"[Worker {worker_id}] No more simulations to run.")
1958+
logger.warning(f"[Worker {worker_id}] No more simulations to run.")
19201959
break
19211960

1922-
if mscmd is None:
1961+
if cmd_definition is None:
19231962
raise ModelicaSystemError("Missing simulation definition!")
19241963

1925-
resultfile = mscmd.arg_get(key='r')
1926-
resultpath = pathlib.Path(resultfile)
1964+
resultfile = cmd_definition.cmd_result_path
1965+
resultpath = self._mod._getconn.omcpath(resultfile)
19271966

1928-
logger.info(f"[Worker {worker_id}] Performing task: {resultpath.name}")
1967+
logger.warning(f"[Worker {worker_id}] Performing task: {resultpath.name}")
19291968

19301969
try:
1931-
mscmd.run()
1970+
returncode = self._mod._getconn.run_model_executable(cmd_run_data=cmd_definition)
1971+
logger.warning(f"[Worker {worker_id}] Simulation {resultpath.name} "
1972+
f"finished with return code: {returncode}")
19321973
except ModelicaSystemError as ex:
19331974
logger.warning(f"Simulation error for {resultpath.name}: {ex}")
19341975

19351976
# Mark the task as done
19361977
task_queue.task_done()
19371978

1938-
sim_query_done = sim_query_total - self._sim_task_query.qsize()
1939-
logger.info(f"[Worker {worker_id}] Task completed: {resultpath.name} "
1940-
f"({sim_query_total - sim_query_done}/{sim_query_total} = "
1941-
f"{(sim_query_total - sim_query_done) / sim_query_total * 100:.2f}% of tasks left)")
1979+
sim_query_done = doe_cmd_total - doe_task_query.qsize()
1980+
logger.warning(f"[Worker {worker_id}] Task completed: {resultpath.name} "
1981+
f"({doe_cmd_total - sim_query_done}/{doe_cmd_total} = "
1982+
f"{(doe_cmd_total - sim_query_done) / doe_cmd_total * 100:.2f}% of tasks left)")
19421983

1943-
logger.info(f"Start simulations for DoE with {sim_query_total} simulations "
1944-
f"using {num_workers} workers ...")
1984+
logger.warning(f"Start simulations for DoE with {doe_cmd_total} simulations "
1985+
f"using {num_workers} workers ...")
19451986

19461987
# Create and start worker threads
19471988
threads = []
19481989
for i in range(num_workers):
1949-
thread = threading.Thread(target=worker, args=(i, self._sim_task_query))
1990+
thread = threading.Thread(target=worker, args=(i, doe_task_query))
19501991
thread.start()
19511992
threads.append(thread)
19521993

19531994
# Wait for all threads to complete
19541995
for thread in threads:
19551996
thread.join()
19561997

1957-
sim_dict_done = 0
1958-
for resultfilename in self._sim_dict:
1998+
doe_def_done = 0
1999+
for resultfilename in self._doe_def:
19592000
resultfile = self._resultpath / resultfilename
19602001

19612002
# include check for an empty (=> 0B) result file which indicates a crash of the model executable
19622003
# see: https://github.com/OpenModelica/OMPython/issues/261
19632004
# https://github.com/OpenModelica/OpenModelica/issues/13829
1964-
if resultfile.is_file() and resultfile.stat().st_size > 0:
1965-
self._sim_dict[resultfilename][self.DICT_RESULT_AVAILABLE] = True
1966-
sim_dict_done += 1
2005+
if resultfile.is_file() and resultfile.size() > 0:
2006+
self._doe_def[resultfilename][self.DICT_RESULT_AVAILABLE] = True
2007+
doe_def_done += 1
19672008

1968-
logger.info(f"All workers finished ({sim_dict_done} of {sim_dict_total} simulations with a result file).")
2009+
logger.warning(f"All workers finished ({doe_def_done} of {doe_def_total} simulations with a result file).")
19692010

1970-
return sim_dict_total == sim_dict_done
2011+
return doe_def_total == doe_def_done
19712012

1972-
def get_solutions(
2013+
def get_doe_solutions(
19732014
self,
19742015
var_list: Optional[list] = None,
19752016
) -> Optional[tuple[str] | dict[str, dict[str, np.ndarray]]]:
@@ -1995,19 +2036,19 @@ def get_solutions(
19952036
```
19962037
19972038
"""
1998-
if not isinstance(self._sim_dict, dict):
2039+
if not isinstance(self._doe_def, dict):
19992040
return None
20002041

2001-
if len(self._sim_dict) == 0:
2042+
if len(self._doe_def) == 0:
20022043
raise ModelicaSystemError("No result files available - all simulations did fail?")
20032044

20042045
sol_dict: dict[str, dict[str, Any]] = {}
2005-
for resultfilename in self._sim_dict:
2046+
for resultfilename in self._doe_def:
20062047
resultfile = self._resultpath / resultfilename
20072048

20082049
sol_dict[resultfilename] = {}
20092050

2010-
if not self._sim_dict[resultfilename][self.DICT_RESULT_AVAILABLE]:
2051+
if not self._doe_def[resultfilename][self.DICT_RESULT_AVAILABLE]:
20112052
sol_dict[resultfilename]['msg'] = 'No result file available!'
20122053
sol_dict[resultfilename]['data'] = {}
20132054
continue

0 commit comments

Comments
 (0)