173 lines
4.7 KiB
Python
173 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Mapping
|
|
from dataclasses import dataclass
|
|
|
|
from PythonModels.components.cylinder import Cylinder
|
|
from PythonModels.components.orifice import Orifice
|
|
from PythonModels.components.resistive_pipe import ResistivePipe
|
|
from PythonModels.components.tank import Tank
|
|
from PythonModels.components.tee import Tee
|
|
from PythonModels.core.base import Component
|
|
from PythonModels.core.metadata import ParameterDefinition
|
|
from PythonModels.core.medium import IdealGasMedium
|
|
from PythonModels.core.ports import PortDefinition
|
|
|
|
|
|
ParameterSpec = ParameterDefinition
|
|
|
|
|
|
ComponentFactory = Callable[
|
|
[str, IdealGasMedium, Mapping[str, float]],
|
|
Component,
|
|
]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ComponentModelSpec:
|
|
model_type: str
|
|
ports: tuple[PortDefinition, ...]
|
|
parameters: tuple[ParameterDefinition, ...]
|
|
factory: ComponentFactory
|
|
|
|
@property
|
|
def parameter_by_name(self) -> dict[str, ParameterDefinition]:
|
|
return {parameter.name: parameter for parameter in self.parameters}
|
|
|
|
def create(
|
|
self,
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
values: Mapping[str, float],
|
|
) -> Component:
|
|
resolved = {
|
|
parameter.name: values.get(parameter.name, parameter.default)
|
|
for parameter in self.parameters
|
|
}
|
|
for parameter in self.parameters:
|
|
message = parameter.validation_message(resolved[parameter.name])
|
|
if message is not None:
|
|
raise ValueError(
|
|
f"Parameter '{parameter.name}' on component '{name}' {message}."
|
|
)
|
|
unknown = sorted(set(values) - set(self.parameter_by_name))
|
|
if unknown:
|
|
raise ValueError(
|
|
f"Component '{name}' contains unsupported parameters: "
|
|
+ ", ".join(unknown)
|
|
+ "."
|
|
)
|
|
component = self.factory(name, medium, resolved)
|
|
component.model_type = self.model_type
|
|
if component.port_definitions != self.ports:
|
|
raise ValueError(
|
|
f"Component implementation {self.model_type} does not match "
|
|
"its declared ports."
|
|
)
|
|
if component.parameter_values != resolved:
|
|
raise ValueError(
|
|
f"Component implementation {self.model_type} did not preserve its parameters."
|
|
)
|
|
return component
|
|
|
|
|
|
def _cylinder_factory(
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
values: Mapping[str, float],
|
|
) -> Component:
|
|
return Cylinder(
|
|
name=name,
|
|
medium=medium,
|
|
V=values["volume"],
|
|
p0=values["p0"],
|
|
T0=values["T0"],
|
|
)
|
|
|
|
|
|
def _tank_factory(
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
values: Mapping[str, float],
|
|
) -> Component:
|
|
return Tank(
|
|
name=name,
|
|
medium=medium,
|
|
V=values["volume"],
|
|
p0=values["p0"],
|
|
T0=values["T0"],
|
|
)
|
|
|
|
|
|
def _pipe_factory(
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
values: Mapping[str, float],
|
|
) -> Component:
|
|
return ResistivePipe(
|
|
name=name,
|
|
medium=medium,
|
|
L=values["length"],
|
|
D=values["diameter"],
|
|
lambda_darcy=values["lambda_darcy"],
|
|
p0=values["p0"],
|
|
T0=values["T0"],
|
|
)
|
|
|
|
|
|
def _orifice_factory(
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
values: Mapping[str, float],
|
|
) -> Component:
|
|
return Orifice(name=name, opening=values["opening"], K=values["K"])
|
|
|
|
|
|
def _tee_factory(
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
values: Mapping[str, float],
|
|
) -> Component:
|
|
return Tee(name=name)
|
|
|
|
|
|
COMPONENT_MODEL_REGISTRY: dict[str, ComponentModelSpec] = {
|
|
"cylinder": ComponentModelSpec(
|
|
model_type=Cylinder.MODEL_TYPE,
|
|
ports=Cylinder.PORTS,
|
|
parameters=Cylinder.PARAMETERS,
|
|
factory=_cylinder_factory,
|
|
),
|
|
"tank": ComponentModelSpec(
|
|
model_type=Tank.MODEL_TYPE,
|
|
ports=Tank.PORTS,
|
|
parameters=Tank.PARAMETERS,
|
|
factory=_tank_factory,
|
|
),
|
|
"pipe": ComponentModelSpec(
|
|
model_type=ResistivePipe.MODEL_TYPE,
|
|
ports=ResistivePipe.PORTS,
|
|
parameters=ResistivePipe.PARAMETERS,
|
|
factory=_pipe_factory,
|
|
),
|
|
"orifice": ComponentModelSpec(
|
|
model_type=Orifice.MODEL_TYPE,
|
|
ports=Orifice.PORTS,
|
|
parameters=Orifice.PARAMETERS,
|
|
factory=_orifice_factory,
|
|
),
|
|
"tee": ComponentModelSpec(
|
|
model_type=Tee.MODEL_TYPE,
|
|
ports=Tee.PORTS,
|
|
parameters=Tee.PARAMETERS,
|
|
factory=_tee_factory,
|
|
),
|
|
}
|
|
|
|
|
|
def get_component_model_spec(model_type: str) -> ComponentModelSpec:
|
|
try:
|
|
return COMPONENT_MODEL_REGISTRY[model_type]
|
|
except KeyError as exc:
|
|
raise ValueError(f"Unsupported model type: {model_type}.") from exc
|