Source code for fridom.framework.modules.module_container
"""A module container that can hold multiple modules."""
from __future__ import annotations
from typing import Iterator, Literal
import fridom.framework as fr
[docs]
class ModuleContainer(fr.modules.Module):
"""
A module container that can hold multiple modules.
Description
-----------
A module container holds a list of modules and is a module itself. It can
start, stop, and update all the modules it contains.
Parameters
----------
name : str
Name of the module container.
module_list : list
A list of modules to be added to the container.
"""
[docs]
def __init__(self,
name: str = "Module Container",
module_list: list | None = None) -> None:
super().__init__()
self.name = name
self.module_list = module_list or []
def __iter__(self) -> Iterator[fr.modules.Module]:
"""Iterate over the modules in the container."""
return iter(self.module_list)
[docs]
@fr.modules.module_method
def setup(self,
mset: fr.ModelSettingsBase,
setup_mode: Literal["default", "forced"] = "default",
) -> None:
"""Set all modules up."""
super().setup(mset, setup_mode=setup_mode)
_ = [module.setup(mset=mset, setup_mode=setup_mode) for module in self]
[docs]
@fr.modules.module_method
def start(self) -> None:
"""Start all modules."""
_ = [module.start() for module in self]
[docs]
@fr.modules.module_method
def stop(self) -> None:
"""Stop all modules."""
_ = [module.stop() for module in self]
[docs]
def reset(self) -> None:
"""Reset all modules."""
_ = [module.reset() for module in self]
[docs]
@fr.modules.module_method
def update(self, mz: fr.ModelState) -> fr.ModelState:
"""Update all modules."""
for module in self:
mz = module.update(mz=mz)
return mz
[docs]
def add_module(self, module: fr.modules.Module) -> None:
"""
Add a module to the end of the module list.
Parameters
----------
module : Module
The module to be added to the list.
"""
self.module_list.append(module)
self._setup_new_module(module)
def _setup_new_module(self, module: fr.modules.Module) -> None:
if self.is_setup and module.required_halo > self.grid.halo:
# force a new setup of the model settings
self.mset.setup(setup_mode="forced")
return
if self.is_setup:
# Otherwise we only need to setup the individual module
module.setup(mset=self.mset)
[docs]
def get(self, name: str) -> list[fr.modules.Module]:
"""
Get a module by name.
Parameters
----------
name : str
Name of the module.
Returns
-------
list[Module]
List of modules with the given name. If no module is found, an
empty list is returned. If multiple modules are found, all of them
are returned.
"""
return [module for module in self.module_list if module.name == name]
@property
def required_halo(self) -> int:
"""The maximum required halo points of all modules."""
if self._required_halo is not None:
return self._required_halo
return max([module.required_halo for module in self] + [0])
@property
def mpi_available(self) -> bool:
"""Whether all modules are available in MPI mode."""
return all(module.mpi_available for module in self)
@mpi_available.setter
def mpi_available(self, value: bool) -> None:
pass # do nothing
def __repr__(self) -> str:
"""Format the module container as a string."""
# format the title into a 48 character string of format
res = f"{self.name}"
for module in self:
res += f"\n## {module}"
return res