Source code for htcie.core.registry

"""Correlation metadata loading and validation."""

from __future__ import annotations

from pathlib import Path
from typing import Any

import yaml
from pydantic import BaseModel, Field


[docs] class CorrelationMetadata(BaseModel): """Metadata for a single heat-transfer correlation loaded from a YAML file. Attributes: key: Dot-namespaced identifier, e.g. ``"internal.gnielinski"``. family: Domain family, e.g. ``"convection_internal"``. title: Human-readable name including author and year. output: Physical quantity produced (typically ``"Nu"``). formula_latex: LaTeX string of the primary correlation formula. flow_regime: Applicable regime: ``"laminar"``, ``"turbulent"``, ``"transitional_turbulent"``, or ``"all"``. boundary_conditions: List of applicable boundary condition types, e.g. ``["constant_wall_temperature", "constant_heat_flux"]``. required_inputs: Dimensionless groups or inputs required by the correlation, e.g. ``["Reynolds", "Prandtl"]``. validity: Dict of bounds used for applicability filtering, e.g. ``{"re_min": 3000, "re_max": 5000000, "geometry_type": "circular_tube"}``. literature_uncertainty_pct: Stated accuracy from the source publication, in percent, or None if not reported. assumptions: List of stated assumptions (smooth tube, fully developed flow, etc.) for traceability. source: Bibliographic reference dict with keys ``authors``, ``year``, ``title``, ``journal``, ``doi``. notes: List of implementation or usage notes for this correlation. """ key: str family: str title: str output: str formula_latex: str = "" flow_regime: str = "all" boundary_conditions: list[str] = Field(default_factory=list) required_inputs: list[str] = Field(default_factory=list) validity: dict[str, Any] = Field(default_factory=dict) literature_uncertainty_pct: float | None = None assumptions: list[str] = Field(default_factory=list) source: dict[str, Any] = Field(default_factory=dict) notes: list[str] = Field(default_factory=list)
[docs] class CorrelationRegistry: """In-memory store of correlation metadata loaded from YAML files. Load the registry once at startup via :meth:`load_from_dir`, then pass it to the pipeline or individual engines. The registry is intentionally stateless after loading — all correlation definitions live in ``data/correlations/`` YAML files, not in Python code. """ def __init__(self) -> None: self._items: dict[str, CorrelationMetadata] = {}
[docs] def load_from_dir(self, path: str | Path) -> None: """Recursively load all ``*.yaml`` files from *path* into the registry. Files are processed in sorted order so loading is deterministic. Each file must contain a single YAML document that validates against :class:`CorrelationMetadata`. Args: path: Root directory containing correlation YAML files (may be nested in subdirectories). Raises: pydantic.ValidationError: If a YAML file fails schema validation. """ base = Path(path) for yaml_file in sorted(base.rglob("*.yaml")): data = yaml.safe_load(yaml_file.read_text(encoding="utf-8")) meta = CorrelationMetadata.model_validate(data) self._items[meta.key] = meta
[docs] def get(self, key: str) -> CorrelationMetadata: """Return metadata for *key*, raising ``KeyError`` if not found.""" return self._items[key]
[docs] def all(self) -> list[CorrelationMetadata]: """Return all loaded correlations in insertion order.""" return list(self._items.values())
[docs] def by_family(self, family: str) -> list[CorrelationMetadata]: """Return all correlations in the given family.""" return [m for m in self._items.values() if m.family == family]
[docs] def families(self) -> list[str]: """Return sorted list of unique families.""" return sorted({m.family for m in self._items.values()})