Skip to content

Commit e0aae13

Browse files
Shift Simulation Class ownership and loading to Model Manager (#286)
* Moved Simulation Class ownership and loading to Model Manager * Config was changed to support multiple solvers by default, not just for Model Adaptivity --------- Co-authored-by: Ishaan Desai <ishaandesai@gmail.com>
1 parent 7565e30 commit e0aae13

19 files changed

Lines changed: 160 additions & 244 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## latest
44

5+
- Moved Simulation Class ownership and loading to Model Manager [#286](https://github.com/precice/micro-manager/pull/286)
56
- Added simulation container for more encapsulation [#284](https://github.com/precice/micro-manager/pull/284)
67
- Redesigned configuration loading for enhanced clarity and standardization [#264](https://github.com/precice/micro-manager/pull/264)
78
- Added RBF interpolation, currently used within adaptivity for output interpolation [#242](https://github.com/precice/micro-manager/pull/242)

docs/configuration.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The Micro Manager is configured with a [JSON](https://en.wikipedia.org/wiki/JSON
1111

1212
```json
1313
{
14-
"micro_file_name": "python/micro.py",
14+
"micro_file_names": ["python/micro.py"],
1515
"coupling_params": {
1616
"precice_config_file_name": "precice-config.xml",
1717
"macro_mesh_name": "Macro-Mesh",
@@ -29,13 +29,13 @@ The Micro Manager is configured with a [JSON](https://en.wikipedia.org/wiki/JSON
2929

3030
These parameters are in the outer section.
3131

32-
| Parameter | Description | Default |
33-
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
34-
| `micro_file_name` | Path to the file containing the Python importable micro simulation class. If the file is not in the working directory, give the relative path from the directory where the Micro Manager is executed. | - |
35-
| `micro_stateless` | Boolean if micro simulation is stateless allowing model instancing. | False |
36-
| `output_directory` | Path to output directory for logging and performance metrics. Directory is created if not existing already. | `.` |
37-
| `memory_usage_output_type` | Set to either `local`, `global`, or `all`. `local` outputs rank-wise peak memory usage. `global` outputs global averaged peak memory usage. `all` outputs both local and global levels. | Empty string. |
38-
| `memory_usage_output_n` | Interval of output. | 1 |
32+
| Parameter | Description | Default |
33+
|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
34+
| `micro_file_names` | Paths to the files containing the Python importable micro simulation classes. If the files are not in the working directory, give the relative paths from the directory where the Micro Manager is executed. | - |
35+
| `micro_stateless_flags` | List of booleans if micro simulation is stateless allowing model instancing. | False |
36+
| `output_directory` | Path to output directory for logging and performance metrics. Directory is created if not existing already. | `.` |
37+
| `memory_usage_output_type` | Set to either `local`, `global`, or `all`. `local` outputs rank-wise peak memory usage. `global` outputs global averaged peak memory usage. `all` outputs both local and global levels. | Empty string. |
38+
| `memory_usage_output_n` | Interval of output. | 1 |
3939

4040
All output is to a CSV file with the peak memory usage (RSS) in every time window, in MBs.
4141

@@ -192,19 +192,15 @@ To turn on model adaptivity, set `"model_adaptivity": true` in `simulation_param
192192

193193
| Parameter | Description |
194194
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
195-
| `micro_file_names` | List of paths to the files containing the Python importable micro simulation classes, in order of decreasing model fidelity. If the files are not in the working directory, give the relative path from the directory where the Micro Manager is executed. Requires a minimum of 2 files. |
196195
| `switching_function` | Path to the file containing the Python importable switching function. If the file is not in the working directory, give the relative path from the directory where the Micro Manager is executed. |
197-
| `micro_stateless` | List of boolean values, whether the respective micro simulation model is stateless and can use model instancing. |
198196

199197
Example of model adaptivity configuration is
200198

201199
```json
202200
"simulation_params": {
203201
"model_adaptivity": true,
204202
"model_adaptivity_settings": {
205-
"micro_file_names": ["python-dummy/micro_dummy", "python-dummy/micro_dummy", "python-dummy/micro_dummy"],
206203
"switching_function": "mada_switcher",
207-
"micro_stateless": [False, True, True]
208204
}
209205
}
210206
```

examples/micro-manager-cpp-adaptivity-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"micro_file_name": "cpp-dummy/micro_dummy",
2+
"micro_file_names": ["cpp-dummy/micro_dummy"],
33
"coupling_params": {
44
"precice_config_file_name": "precice-config-adaptivity.xml",
55
"macro_mesh_name": "Macro-Mesh",

examples/micro-manager-cpp-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"micro_file_name": "cpp-dummy/micro_dummy",
2+
"micro_file_names": ["cpp-dummy/micro_dummy"],
33
"coupling_params": {
44
"precice_config_file_name": "precice-config.xml",
55
"macro_mesh_name": "Macro-Mesh",

examples/micro-manager-python-adaptivity-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"micro_file_name": "python-dummy/micro_dummy",
2+
"micro_file_names": ["python-dummy/micro_dummy"],
33
"coupling_params": {
44
"precice_config_file_name": "precice-config-adaptivity.xml",
55
"macro_mesh_name": "Macro-Mesh",

examples/micro-manager-python-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"micro_file_name": "python-dummy/micro_dummy",
2+
"micro_file_names": ["python-dummy/micro_dummy"],
33
"coupling_params": {
44
"precice_config_file_name": "precice-config.xml",
55
"macro_mesh_name": "Macro-Mesh",

examples/micro-manager-python-mada-config.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"micro_file_name": "python-dummy/micro_dummy",
2+
"micro_file_names": ["python-dummy/micro_dummy", "python-dummy/micro_dummy", "python-dummy/micro_dummy"],
33
"coupling_params": {
44
"precice_config_file_name": "precice-config.xml",
55
"macro_mesh_name": "Macro-Mesh",
@@ -11,7 +11,6 @@
1111
"macro_domain_bounds": [0.0, 25.0, 0.0, 25.0, 0.0, 25.0],
1212
"model_adaptivity": true,
1313
"model_adaptivity_settings": {
14-
"micro_file_names": ["python-dummy/micro_dummy", "python-dummy/micro_dummy", "python-dummy/micro_dummy"],
1514
"switching_function": "mada_switcher"
1615
}
1716
},

micro_manager/adaptivity/model_adaptivity.py

Lines changed: 37 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Class ModelAdaptivity provides methods to change micro simulation resolution on the fly.
33
"""
4-
from typing import Union, Optional
4+
from typing import Union, Optional, List, Dict, Any
55

66
from micro_manager.config import Config
77
from micro_manager.micro_simulation import (
@@ -29,8 +29,6 @@ def __init__(
2929
comm: MPI.Comm,
3030
rank: int,
3131
log_file: str,
32-
conn: Connection,
33-
num_ranks: int,
3432
) -> None:
3533
"""
3634
Class constructor.
@@ -49,44 +47,14 @@ def __init__(
4947
Rank of the MPI communicator.
5048
log_file : str
5149
Path to the log file to write to.
52-
conn: Connection
53-
Connection to workers
54-
num_ranks : int
55-
Number of workers
5650
"""
5751
self._logger = Logger(__name__, log_file, rank)
5852

5953
self._comm = comm
6054
self._model_manager = model_manager
6155
self._sim_container = sim_container
62-
self._model_files = config.model_adaptivity_file_names()
6356
self._switching_func_name = config.model_adaptivity_switching_function()
6457

65-
stateless_flags = config.model_adaptivity_micro_stateless()
66-
self._model_classes = []
67-
pos = 0
68-
for model_file in self._model_files:
69-
try:
70-
model = load_backend_class(model_file)
71-
self._model_classes.append(
72-
create_simulation_class(
73-
self._logger, model, model_file, num_ranks, conn
74-
)
75-
)
76-
self._model_manager.register(
77-
self._model_classes[pos], stateless_flags[pos]
78-
)
79-
pos += 1
80-
except Exception as e:
81-
self._logger.log_info_rank_zero(
82-
f"Failed to load model class with error: {e}"
83-
)
84-
if (
85-
len(self._model_classes) != len(self._model_files)
86-
or len(self._model_classes) == 0
87-
):
88-
raise RuntimeError("Not all models were loaded. Stopping!")
89-
9058
FUNC_NAME = "switching_function"
9159
self._switching_func = ModelAdaptivity.switching_interface
9260
try:
@@ -105,8 +73,8 @@ def switching_interface(
10573
resolution: int,
10674
location: np.ndarray,
10775
t: float,
108-
input: dict,
109-
prev_output: Optional[dict],
76+
input: Dict[str, Any],
77+
prev_output: Optional[Dict[str, Any]],
11078
) -> int:
11179
"""
11280
Switching interface function, use as reference
@@ -119,9 +87,9 @@ def switching_interface(
11987
Array with gaussian point for respective sim. D is the mesh dimension.
12088
t : float
12189
Current time in simulation.
122-
input : dict
90+
input : Dict[str, Any]
12391
input object.
124-
prev_output : [None, dict-like]
92+
prev_output : Optional[Dict[str, Any]]
12593
Contains the output of the previous model evaluation.
12694
12795
"""
@@ -148,26 +116,22 @@ def should_iterate(self) -> bool:
148116
def switch_models(
149117
self,
150118
t: float,
151-
inputs: list[dict],
152-
prev_output: Optional[list[dict]],
153-
active_sim_ids: Optional[list] = None,
154-
) -> list[int]:
119+
inputs: List[Dict[str, Any]],
120+
prev_output: Optional[List[Dict[str, Any]]],
121+
active_sim_ids: Optional[List[int]] = None,
122+
) -> List[int]:
155123
"""
156124
Switches models within sims list. If active_sim_ids is None, all sims are considered as active.
157125
158126
Parameters
159127
----------
160-
locations : np.array - shape(N,D)
161-
Array with gaussian points for all sims. D is the mesh dimension.
162128
t : float
163129
Current time in simulation.
164-
inputs : list[dict]
130+
inputs : List[Dict[str, Any]]
165131
List of input objects.
166-
prev_output : [None, list[dict]]
132+
prev_output : Optional[List[Dict[str, Any]]]
167133
Contains the outputs of the previous model evaluation.
168-
sims : list
169-
List of all simulation objects.
170-
active_sim_ids : [list, None]
134+
active_sim_ids : Optional[List[int]]
171135
List of all active simulation ids.
172136
173137
Returns
@@ -192,7 +156,9 @@ def switch_models(
192156

193157
sim = self._sim_container[lid]
194158
gid = self._sim_container.local_gids[lid]
195-
target_class = self.get_resolution_sim_class(target_res[lid])
159+
target_class = self._model_manager.get_cls_by_idx(
160+
clamp_in_range(target_res[lid], 0, self.get_num_resolutions() - 1)
161+
)
196162

197163
# we store state for each resolution separately
198164
# keys are sim names of respective resolution
@@ -225,9 +191,9 @@ def switch_models(
225191
def check_convergence(
226192
self,
227193
t: float,
228-
inputs: list,
229-
prev_output: Optional[list[dict]],
230-
active_sim_ids: Optional[list] = None,
194+
inputs: List[Dict[str, Any]],
195+
prev_output: Optional[List[Dict[str, Any]]],
196+
active_sim_ids: Optional[List[int]] = None,
231197
) -> None:
232198
"""
233199
Similarly to switch_models, checks whether models would be switched in next step.
@@ -237,11 +203,11 @@ def check_convergence(
237203
----------
238204
t : float
239205
Current time in simulation.
240-
inputs : list[dict]
206+
inputs : List[Dict[str, Any]]
241207
List of all input objects.
242-
prev_output : [None, list[dict]]
208+
prev_output : Optional[List[Dict[str, Any]]]
243209
Contains the outputs of the previous model evaluation.
244-
active_sim_ids : [list, None]
210+
active_sim_ids : Optional[List[int]]
245211
List of all active simulation ids.
246212
"""
247213
locations = self._sim_container.local_coords
@@ -265,45 +231,7 @@ def get_num_resolutions(self) -> int:
265231
num_resolutions : int
266232
Number of loaded resolutions.
267233
"""
268-
return len(self._model_classes)
269-
270-
def get_resolution_sim_class(
271-
self, resolution: Union[int, np.ndarray]
272-
) -> Union[MicroSimulationClass, list[MicroSimulationClass]]:
273-
"""
274-
Looks up the class associated with the provided resolution.
275-
276-
Parameters
277-
----------
278-
resolution : [int, np.array]
279-
target resolution
280-
281-
Returns
282-
-------
283-
sim_class : class
284-
associated class
285-
"""
286-
return self._model_classes[
287-
clamp_in_range(resolution, 0, len(self._model_classes) - 1)
288-
]
289-
290-
def get_sim_class_resolution(self, sim: MicroSimulationClass) -> int:
291-
"""
292-
Looks up the resolution associated with the provided simulation object.
293-
294-
Parameters
295-
----------
296-
sim : Simulation
297-
Simulation object
298-
299-
Returns
300-
-------
301-
resolution : int
302-
target resolution
303-
"""
304-
return next(
305-
(idx for idx, cls in enumerate(self._model_classes) if cls.name == sim.name)
306-
)
234+
return self._model_manager.num_models
307235

308236
def _gather_current_resolutions(self, active_sims: np.ndarray) -> np.ndarray:
309237
"""
@@ -321,7 +249,7 @@ def _gather_current_resolutions(self, active_sims: np.ndarray) -> np.ndarray:
321249
"""
322250
return np.array(
323251
[
324-
self.get_sim_class_resolution(self._sim_container[lid])
252+
self._model_manager.get_idx_of_sim(self._sim_container[lid])
325253
if active_sims[lid] == 1
326254
else -1
327255
for lid in self._sim_container.range_lid
@@ -331,33 +259,33 @@ def _gather_current_resolutions(self, active_sims: np.ndarray) -> np.ndarray:
331259
def _gather_target_resolutions(
332260
self,
333261
cur_res: np.ndarray,
334-
locations: np.ndarray,
262+
locations: List[np.ndarray],
335263
t: float,
336-
inputs: list[dict],
337-
prev_output: Optional[list[dict]],
264+
inputs: List[Dict[str, Any]],
265+
prev_output: Optional[List[Dict[str, Any]]],
338266
active_sims: np.ndarray,
339267
) -> np.ndarray:
340268
"""
341269
Gathers target resolutions. Inactive sims have resolution -1.
342270
343271
Parameters
344272
----------
345-
cur_res : np.array
273+
cur_res : np.ndarray
346274
Current resolutions, from _gather_current_resolutions.
347-
locations : np.array
275+
locations : List[np.ndarray]
348276
Array with gaussian points for all sims. D is the mesh dimension.
349277
t : float
350278
Current time in simulation.
351-
inputs : list[dict]
279+
inputs : List[Dict[str, Any]]
352280
List of all input objects.
353-
prev_output : [None, list[dict]]
281+
prev_output : Optional[List[Dict[str, Any]]]
354282
Contains the outputs of the previous model evaluation.
355283
active_sims : np.array
356284
Boolean array indicating whether the model is active or not.
357285
358286
Returns
359287
-------
360-
resolutions : np.array
288+
resolutions : np.ndarray
361289
Target resolutions.
362290
"""
363291
switch_tgt = np.zeros_like(cur_res)
@@ -372,24 +300,26 @@ def _gather_target_resolutions(
372300
res_tgt[active_sims] = clamp_in_range(
373301
switch_tgt[active_sims] + cur_res[active_sims],
374302
0,
375-
len(self._model_classes) - 1,
303+
self.get_num_resolutions() - 1,
376304
)
377305
return res_tgt
378306

379-
def _create_active_mask(self, active_sim_ids: list, size: int) -> np.ndarray:
307+
def _create_active_mask(
308+
self, active_sim_ids: Optional[List[int]], size: int
309+
) -> np.ndarray:
380310
"""
381311
Converts list of active simulation ids to np boolean mask.
382312
383313
Parameters
384314
----------
385-
active_sim_ids : np.array
315+
active_sim_ids : Optional[List[int]]
386316
List of all active simulation ids.
387317
size : int
388318
size of active_sim_ids
389319
390320
Returns
391321
-------
392-
active_mask : np.array
322+
active_mask : np.ndarray
393323
Boolean mask of active simulation ids.
394324
"""
395325
if active_sim_ids is None:

0 commit comments

Comments
 (0)