Source code for cfdmod.core.recipes.pedestrian_comfort

"""Pedestrian comfort recipe -- velocities at probes + climate -> per-probe stats.

Per the odt: a volume / point velocity field, evaluated at probe
positions, is then combined with climate data (Weibull / wind-rose
inputs from outside the pipeline) to compute comfort statistics on
points.

The recipe implemented here covers the *pipeline-internal* portion:

1. probe extraction from a source data source -> per-probe timeseries;
2. statistics (mean / rms / peak_max) on the per-probe series.

Climate ingestion is intentionally *not* a pipeline stage (per the
odt). A downstream consumer combines the per-probe statistics with a
``cfdmod.climate`` summary to produce comfort categories.
"""

from __future__ import annotations

__all__ = ["PedestrianComfortConfig", "build_pedestrian_comfort"]

from typing import Any

import numpy as np
from pydantic import BaseModel, ConfigDict

from cfdmod.core.data_source import DataSource, PointsDataSource
from cfdmod.core.ops.data_source_create.probe_extraction import (
    ProbeExtractionParams,
    probe_extraction,
)
from cfdmod.core.ops.data_source_create.statistics import (
    STAT_KINDS,
    StatisticsParams,
    compute_statistics,
)


[docs] class PedestrianComfortConfig(BaseModel): """Pedestrian comfort recipe parameters. Attributes: probes: ``(n_probes, 3)`` probe positions. field: Velocity field on the source (e.g. ``"u_mag"``). statistics: Statistics to compute per probe. """ model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True) probes: Any field: str = "u_mag" statistics: list[STAT_KINDS] = ["mean", "rms", "peak_max"]
def build_pedestrian_comfort( velocity_source: DataSource, cfg: PedestrianComfortConfig ) -> PointsDataSource: probes = np.asarray(cfg.probes, dtype=np.float64) extracted = probe_extraction( velocity_source, ProbeExtractionParams(probes=probes, field=cfg.field, mode="nearest"), ) return compute_statistics( extracted, StatisticsParams(kinds=cfg.statistics, field=cfg.field), )