Skip to content

Commit 309be78

Browse files
committed
(B002) split ModelicaSystemDoE
[ModelicaSystem] split ModelicaSystemDoE into ModelicaDoEABC and ModelicaDoE [ModelicaSystem] rename ModelicaSystemDoE => ModelicaDoEOMC * add compatibility variable for ModelicaSystemDoE [test_ModelicaDoEOMC] rename from ModelicaSystemDoE and update [ModelicaSystem] update ModelicaDoEABC to use ModelicaSystemABC [ModelicaSystem] define doe_get_solutions() as separate method
1 parent 46eb63d commit 309be78

File tree

3 files changed

+176
-85
lines changed

3 files changed

+176
-85
lines changed

OMPython/ModelicaSystem.py

Lines changed: 159 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import re
1616
import textwrap
1717
import threading
18-
from typing import Any, cast, Optional
18+
from typing import Any, cast, Optional, Tuple
1919
import warnings
2020
import xml.etree.ElementTree as ET
2121

@@ -2108,9 +2108,9 @@ class ModelicaSystem(ModelicaSystemOMC):
21082108
"""
21092109

21102110

2111-
class ModelicaSystemDoE:
2111+
class ModelicaDoEABC(metaclass=abc.ABCMeta):
21122112
"""
2113-
Class to run DoEs based on a (Open)Modelica model using ModelicaSystem
2113+
Base class to run DoEs based on a (Open)Modelica model using ModelicaSystem
21142114
21152115
Example
21162116
-------
@@ -2183,7 +2183,7 @@ def run_doe():
21832183
def __init__(
21842184
self,
21852185
# ModelicaSystem definition to use
2186-
mod: ModelicaSystemOMC,
2186+
mod: ModelicaSystemABC,
21872187
# simulation specific input
21882188
# TODO: add more settings (simulation options, input options, ...)
21892189
simargs: Optional[dict[str, Optional[str | dict[str, str] | numbers.Number]]] = None,
@@ -2196,7 +2196,7 @@ def __init__(
21962196
ModelicaSystem.simulate(). Additionally, the path to store the result files is needed (= resultpath) as well as
21972197
a list of parameters to vary for the Doe (= parameters). All possible combinations are considered.
21982198
"""
2199-
if not isinstance(mod, ModelicaSystemOMC):
2199+
if not isinstance(mod, ModelicaSystemABC):
22002200
raise ModelicaSystemError("Missing definition of ModelicaSystem!")
22012201

22022202
self._mod = mod
@@ -2252,30 +2252,11 @@ def prepare(self) -> int:
22522252
param_non_structural_combinations = list(itertools.product(*param_non_structure.values()))
22532253

22542254
for idx_pc_structure, pc_structure in enumerate(param_structure_combinations):
2255-
2256-
build_dir = self._resultpath / f"DOE_{idx_pc_structure:09d}"
2257-
build_dir.mkdir()
2258-
self._mod.setWorkDirectory(work_directory=build_dir)
2259-
2260-
sim_param_structure = {}
2261-
for idx_structure, pk_structure in enumerate(param_structure.keys()):
2262-
sim_param_structure[pk_structure] = pc_structure[idx_structure]
2263-
2264-
pk_value = pc_structure[idx_structure]
2265-
if isinstance(pk_value, str):
2266-
pk_value_str = self.get_session().escape_str(pk_value)
2267-
expr = f"setParameterValue({self._model_name}, {pk_structure}, \"{pk_value_str}\")"
2268-
elif isinstance(pk_value, bool):
2269-
pk_value_bool_str = "true" if pk_value else "false"
2270-
expr = f"setParameterValue({self._model_name}, {pk_structure}, {pk_value_bool_str});"
2271-
else:
2272-
expr = f"setParameterValue({self._model_name}, {pk_structure}, {pk_value})"
2273-
res = self._mod.sendExpression(expr=expr)
2274-
if not res:
2275-
raise ModelicaSystemError(f"Cannot set structural parameter {self._model_name}.{pk_structure} "
2276-
f"to {pk_value} using {repr(expr)}")
2277-
2278-
self._mod.buildModel()
2255+
sim_param_structure = self._prepare_structure_parameters(
2256+
idx_pc_structure=idx_pc_structure,
2257+
pc_structure=pc_structure,
2258+
param_structure=param_structure,
2259+
)
22792260

22802261
for idx_non_structural, pk_non_structural in enumerate(param_non_structural_combinations):
22812262
sim_param_non_structural = {}
@@ -2320,6 +2301,17 @@ def prepare(self) -> int:
23202301

23212302
return len(doe_sim)
23222303

2304+
@abc.abstractmethod
2305+
def _prepare_structure_parameters(
2306+
self,
2307+
idx_pc_structure: int,
2308+
pc_structure: Tuple,
2309+
param_structure: dict[str, list[str] | list[int] | list[float]],
2310+
) -> dict[str, str | int | float]:
2311+
"""
2312+
Handle structural parameters. This should be implemented by the derived class
2313+
"""
2314+
23232315
def get_doe_definition(self) -> Optional[dict[str, dict[str, Any]]]:
23242316
"""
23252317
Get the defined DoE as a dict, where each key is the result filename and the value is a dict of simulation
@@ -2431,65 +2423,157 @@ def worker(worker_id, task_queue):
24312423

24322424
return doe_def_total == doe_def_done
24332425

2426+
2427+
class ModelicaDoEOMC(ModelicaDoEABC):
2428+
"""
2429+
Class to run DoEs based on a (Open)Modelica model using ModelicaSystemOMC
2430+
2431+
The example is the same as defined for ModelicaDoEABC
2432+
"""
2433+
2434+
def __init__(
2435+
self,
2436+
# ModelicaSystem definition to use
2437+
mod: ModelicaSystemOMC,
2438+
# simulation specific input
2439+
# TODO: add more settings (simulation options, input options, ...)
2440+
simargs: Optional[dict[str, Optional[str | dict[str, str] | numbers.Number]]] = None,
2441+
# DoE specific inputs
2442+
resultpath: Optional[str | os.PathLike] = None,
2443+
parameters: Optional[dict[str, list[str] | list[int] | list[float]]] = None,
2444+
) -> None:
2445+
2446+
if not isinstance(mod, ModelicaSystemOMC):
2447+
raise ModelicaSystemError(f"Invalid definition for mod: {type(mod)} - expect ModelicaSystemOMC!")
2448+
2449+
super().__init__(
2450+
mod=mod,
2451+
simargs=simargs,
2452+
resultpath=resultpath,
2453+
parameters=parameters,
2454+
)
2455+
2456+
def _prepare_structure_parameters(
2457+
self,
2458+
idx_pc_structure: int,
2459+
pc_structure: Tuple,
2460+
param_structure: dict[str, list[str] | list[int] | list[float]],
2461+
) -> dict[str, str | int | float]:
2462+
build_dir = self._resultpath / f"DOE_{idx_pc_structure:09d}"
2463+
build_dir.mkdir()
2464+
self._mod.setWorkDirectory(work_directory=build_dir)
2465+
2466+
# need to repeat this check to make the linters happy
2467+
if not isinstance(self._mod, ModelicaSystemOMC):
2468+
raise ModelicaSystemError(f"Invalid definition for mod: {type(self._mod)} - expect ModelicaSystemOMC!")
2469+
2470+
sim_param_structure = {}
2471+
for idx_structure, pk_structure in enumerate(param_structure.keys()):
2472+
sim_param_structure[pk_structure] = pc_structure[idx_structure]
2473+
2474+
pk_value = pc_structure[idx_structure]
2475+
if isinstance(pk_value, str):
2476+
pk_value_str = self.get_session().escape_str(pk_value)
2477+
expr = f"setParameterValue({self._model_name}, {pk_structure}, \"{pk_value_str}\")"
2478+
elif isinstance(pk_value, bool):
2479+
pk_value_bool_str = "true" if pk_value else "false"
2480+
expr = f"setParameterValue({self._model_name}, {pk_structure}, {pk_value_bool_str});"
2481+
else:
2482+
expr = f"setParameterValue({self._model_name}, {pk_structure}, {pk_value})"
2483+
res = self._mod.sendExpression(expr=expr)
2484+
if not res:
2485+
raise ModelicaSystemError(f"Cannot set structural parameter {self._model_name}.{pk_structure} "
2486+
f"to {pk_value} using {repr(expr)}")
2487+
2488+
self._mod.buildModel()
2489+
2490+
return sim_param_structure
2491+
24342492
def get_doe_solutions(
24352493
self,
24362494
var_list: Optional[list] = None,
24372495
) -> Optional[tuple[str] | dict[str, dict[str, np.ndarray]]]:
24382496
"""
2439-
Get all solutions of the DoE run. The following return values are possible:
2497+
Wrapper for doe_get_solutions()
2498+
"""
2499+
if not isinstance(self._mod, ModelicaSystemOMC):
2500+
raise ModelicaSystemError(f"Invalid definition for mod: {type(self._mod)} - expect ModelicaSystemOMC!")
24402501

2441-
* A list of variables if val_list == None
2502+
return doe_get_solutions(
2503+
msomc=self._mod,
2504+
resultpath=self._resultpath,
2505+
doe_def=self.get_doe_definition(),
2506+
var_list=var_list,
2507+
)
24422508

2443-
* The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
24442509

2445-
The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
2510+
def doe_get_solutions(
2511+
msomc: ModelicaSystemOMC,
2512+
resultpath: OMCPath,
2513+
doe_def: Optional[dict] = None,
2514+
var_list: Optional[list] = None,
2515+
) -> Optional[tuple[str] | dict[str, dict[str, np.ndarray]]]:
2516+
"""
2517+
Get all solutions of the DoE run. The following return values are possible:
24462518
2447-
```
2448-
import pandas as pd
2519+
* A list of variables if val_list == None
24492520
2450-
doe_sol = doe_mod.get_doe_solutions()
2451-
for key in doe_sol:
2452-
data = doe_sol[key]['data']
2453-
if data:
2454-
doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
2455-
else:
2456-
doe_sol[key]['df'] = None
2457-
```
2521+
* The Solutions as dict[str, pd.DataFrame] if a value list (== val_list) is defined.
24582522
2459-
"""
2460-
if not isinstance(self._doe_def, dict):
2461-
return None
2523+
The following code snippet can be used to convert the solution data for each run to a pandas dataframe:
24622524
2463-
if len(self._doe_def) == 0:
2464-
raise ModelicaSystemError("No result files available - all simulations did fail?")
2525+
```
2526+
import pandas as pd
24652527
2466-
sol_dict: dict[str, dict[str, Any]] = {}
2467-
for resultfilename in self._doe_def:
2468-
resultfile = self._resultpath / resultfilename
2528+
doe_sol = doe_mod.get_doe_solutions()
2529+
for key in doe_sol:
2530+
data = doe_sol[key]['data']
2531+
if data:
2532+
doe_sol[key]['df'] = pd.DataFrame.from_dict(data=data)
2533+
else:
2534+
doe_sol[key]['df'] = None
2535+
```
24692536
2470-
sol_dict[resultfilename] = {}
2537+
"""
2538+
if not isinstance(doe_def, dict):
2539+
return None
24712540

2472-
if not self._doe_def[resultfilename][self.DICT_RESULT_AVAILABLE]:
2473-
msg = f"No result file available for {resultfilename}"
2474-
logger.warning(msg)
2475-
sol_dict[resultfilename]['msg'] = msg
2476-
sol_dict[resultfilename]['data'] = {}
2477-
continue
2541+
if len(doe_def) == 0:
2542+
raise ModelicaSystemError("No result files available - all simulations did fail?")
24782543

2479-
if var_list is None:
2480-
var_list_row = list(self._mod.getSolutions(resultfile=resultfile))
2481-
else:
2482-
var_list_row = var_list
2483-
2484-
try:
2485-
sol = self._mod.getSolutions(varList=var_list_row, resultfile=resultfile)
2486-
sol_data = {var: sol[idx] for idx, var in enumerate(var_list_row)}
2487-
sol_dict[resultfilename]['msg'] = 'Simulation available'
2488-
sol_dict[resultfilename]['data'] = sol_data
2489-
except ModelicaSystemError as ex:
2490-
msg = f"Error reading solution for {resultfilename}: {ex}"
2491-
logger.warning(msg)
2492-
sol_dict[resultfilename]['msg'] = msg
2493-
sol_dict[resultfilename]['data'] = {}
2494-
2495-
return sol_dict
2544+
sol_dict: dict[str, dict[str, Any]] = {}
2545+
for resultfilename in doe_def:
2546+
resultfile = resultpath / resultfilename
2547+
2548+
sol_dict[resultfilename] = {}
2549+
2550+
if not doe_def[resultfilename][ModelicaDoEABC.DICT_RESULT_AVAILABLE]:
2551+
msg = f"No result file available for {resultfilename}"
2552+
logger.warning(msg)
2553+
sol_dict[resultfilename]['msg'] = msg
2554+
sol_dict[resultfilename]['data'] = {}
2555+
continue
2556+
2557+
if var_list is None:
2558+
var_list_row = list(msomc.getSolutions(resultfile=resultfile))
2559+
else:
2560+
var_list_row = var_list
2561+
2562+
try:
2563+
sol = msomc.getSolutions(varList=var_list_row, resultfile=resultfile)
2564+
sol_data = {var: sol[idx] for idx, var in enumerate(var_list_row)}
2565+
sol_dict[resultfilename]['msg'] = 'Simulation available'
2566+
sol_dict[resultfilename]['data'] = sol_data
2567+
except ModelicaSystemError as ex:
2568+
msg = f"Error reading solution for {resultfilename}: {ex}"
2569+
logger.warning(msg)
2570+
sol_dict[resultfilename]['msg'] = msg
2571+
sol_dict[resultfilename]['data'] = {}
2572+
2573+
return sol_dict
2574+
2575+
2576+
class ModelicaSystemDoE(ModelicaDoEOMC):
2577+
"""
2578+
Compatibility class.
2579+
"""

OMPython/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
ModelicaSystemOMC,
1818
ModelExecutionCmd,
1919
ModelicaSystemDoE,
20+
ModelicaDoEOMC,
2021
ModelicaSystemError,
22+
23+
doe_get_solutions,
2124
)
2225
from OMPython.OMCSession import (
2326
OMCPath,
@@ -47,11 +50,15 @@
4750
'ModelicaSystemOMC',
4851
'ModelExecutionCmd',
4952
'ModelicaSystemDoE',
53+
'ModelicaDoEOMC',
5054
'ModelicaSystemError',
5155

5256
'OMCPath',
5357

5458
'OMCSession',
59+
60+
'doe_get_solutions',
61+
5562
'OMCSessionCmd',
5663
'OMCSessionDocker',
5764
'OMCSessionDockerContainer',
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def param_doe() -> dict[str, list]:
5151
return param
5252

5353

54-
def test_ModelicaSystemDoE_local(tmp_path, model_doe, param_doe):
54+
def test_ModelicaDoEOMC_local(tmp_path, model_doe, param_doe):
5555
tmpdir = tmp_path / 'DoE'
5656
tmpdir.mkdir(exist_ok=True)
5757

@@ -61,19 +61,19 @@ def test_ModelicaSystemDoE_local(tmp_path, model_doe, param_doe):
6161
model_name="M",
6262
)
6363

64-
doe_mod = OMPython.ModelicaSystemDoE(
64+
doe_mod = OMPython.ModelicaDoEOMC(
6565
mod=mod,
6666
parameters=param_doe,
6767
resultpath=tmpdir,
6868
simargs={"override": {'stopTime': '1.0'}},
6969
)
7070

71-
_run_ModelicaSystemDoe(doe_mod=doe_mod)
71+
_run_ModelicaDoEOMC(doe_mod=doe_mod)
7272

7373

7474
@skip_on_windows
7575
@skip_python_older_312
76-
def test_ModelicaSystemDoE_docker(tmp_path, model_doe, param_doe):
76+
def test_ModelicaDoEOMC_docker(tmp_path, model_doe, param_doe):
7777
omcs = OMPython.OMCSessionDocker(docker="openmodelica/openmodelica:v1.25.0-minimal")
7878
omversion = omcs.sendExpression("getVersion()")
7979
assert isinstance(omversion, str) and omversion.startswith("OpenModelica")
@@ -86,18 +86,18 @@ def test_ModelicaSystemDoE_docker(tmp_path, model_doe, param_doe):
8686
model_name="M",
8787
)
8888

89-
doe_mod = OMPython.ModelicaSystemDoE(
89+
doe_mod = OMPython.ModelicaDoEOMC(
9090
mod=mod,
9191
parameters=param_doe,
9292
simargs={"override": {'stopTime': '1.0'}},
9393
)
9494

95-
_run_ModelicaSystemDoe(doe_mod=doe_mod)
95+
_run_ModelicaDoEOMC(doe_mod=doe_mod)
9696

9797

9898
@pytest.mark.skip(reason="Not able to run WSL on github")
9999
@skip_python_older_312
100-
def test_ModelicaSystemDoE_WSL(tmp_path, model_doe, param_doe):
100+
def test_ModelicaDoEOMC_WSL(tmp_path, model_doe, param_doe):
101101
omcs = OMPython.OMCSessionWSL()
102102
omversion = omcs.sendExpression("getVersion()")
103103
assert isinstance(omversion, str) and omversion.startswith("OpenModelica")
@@ -110,16 +110,16 @@ def test_ModelicaSystemDoE_WSL(tmp_path, model_doe, param_doe):
110110
model_name="M",
111111
)
112112

113-
doe_mod = OMPython.ModelicaSystemDoE(
113+
doe_mod = OMPython.ModelicaDoEOMC(
114114
mod=mod,
115115
parameters=param_doe,
116116
simargs={"override": {'stopTime': '1.0'}},
117117
)
118118

119-
_run_ModelicaSystemDoe(doe_mod=doe_mod)
119+
_run_ModelicaDoEOMC(doe_mod=doe_mod)
120120

121121

122-
def _run_ModelicaSystemDoe(doe_mod):
122+
def _run_ModelicaDoEOMC(doe_mod):
123123
doe_count = doe_mod.prepare()
124124
assert doe_count == 16
125125

0 commit comments

Comments
 (0)