Source code for cfdmod.regroup.parameters

"""Configuration models for the regroup module.

The chain ``RegroupConfig.groupings`` is a superset of
:data:`cfdmod.geometry.grouping.GroupingSpec`. It accepts every standard
grouping kind plus :class:`BySizeRoundedPerComponent`, a regroup-local
fan-out spec that ``run_regroup`` resolves before invoking
:func:`cfdmod.geometry.grouping.apply_groupings`.
"""

from __future__ import annotations

import pathlib
from typing import Annotated, Literal

from pydantic import BaseModel, Field, field_validator

from cfdmod.geometry.grouping.regroup import BySizeRoundedPerComponent, RegroupSpec
from cfdmod.io.geometry.transformation_config import TransformationConfig
from cfdmod.utils import read_yaml

__all__ = [
    "BySizeRoundedPerComponent",
    "RegroupSpec",
    "RegroupConfig",
]


[docs] class RegroupConfig(BaseModel): """Top-level config for the regroup pipeline. Args: groupings: Chain of regroup specs (every standard ``GroupingSpec`` plus the regroup-local :class:`BySizeRoundedPerComponent`). Specs are applied left to right; ``BySizeRoundedPerComponent`` entries are expanded by ``run_regroup`` against the groups produced by the prior prefix of the chain. transformation: Optional rigid-body transform applied to a mesh *copy* before binning. Output geometry vertices stay in world coordinates; only the binning frame moves. Mirrors Ce. aggregation: Per-group HDF5 column policy. ``"per_triangle"`` reorders the input columns; one output column per (parent) triangle. ``"area_weighted_mean"`` writes one aggregated value per group, broadcast over the post-slice triangles of that group (so geometry and timeseries cardinality match for ParaView). timeseries_group: HDF5 group name used in the input (under which ``t{T}`` datasets live) and reused in the output. output_geometry_format: ``"lnas"`` writes only ``geometry.lnas``; ``"lnas_and_stl"`` also writes a ``geometry.stl`` companion (for quick ParaView lookup; surface labels are lost in STL). unassigned_policy: What to do with parent triangles that fall in zero groups. ``"drop"`` excludes them; ``"keep_as_unassigned"`` adds a synthetic ``unassigned`` group/surface. """ groupings: Annotated[ list[RegroupSpec], Field(..., min_length=1, description="Chain of regroup specs."), ] transformation: Annotated[ TransformationConfig | None, Field(None, description="Optional pre-binning transformation."), ] aggregation: Annotated[ Literal["per_triangle", "area_weighted_mean", "sliced"], Field( "area_weighted_mean", description=( "Per-group HDF5 column policy. " "'per_triangle': one column per parent triangle, reordered. " "'area_weighted_mean': one aggregated value per group, broadcast. " "'sliced': geometrically slice triangles at the cell boundaries " "(Ce-style 90-degree cuts) so output triangles never straddle " "two cells; per-fragment columns inherit their parent triangle's " "value." ), ), ] timeseries_group: Annotated[ str, Field("cp", description="HDF5 group name in input/output timeseries."), ] output_geometry_format: Annotated[ Literal["lnas", "lnas_and_stl"], Field("lnas", description="Output mesh format(s)."), ] unassigned_policy: Annotated[ Literal["drop", "keep_as_unassigned"], Field("drop", description="Policy for parent triangles in zero groups."), ] @field_validator("groupings") def _fan_out_must_be_last(cls, v: list[RegroupSpec]) -> list[RegroupSpec]: for i, spec in enumerate(v[:-1]): if isinstance(spec, BySizeRoundedPerComponent): raise ValueError( "BySizeRoundedPerComponent must be the last spec in the chain " f"(found at position {i} of {len(v)}); downstream specs cannot " "reference its expanded group names yet." ) return v @classmethod def from_file(cls, path: pathlib.Path) -> "RegroupConfig": return cls(**read_yaml(pathlib.Path(path)))