Skip to content

weather

weather #

Multi-dimensional difference between two weather forecasts.

Each sigma_<field> keyword opts one of cg.Weather's numeric fields into the metric, and the enabled fields combine as a Euclidean norm.

NUMERIC_FIELDS = tuple(name for name, annotation in (cg.Weather.__annotations__.items()) if annotation in (float, float | None)) module-attribute #

The cg.Weather fields a weather distance can measure.

SIGMA_PREFIX = 'sigma_' module-attribute #

What a weather keyword prefixes onto the cg.Weather field it scales.

weather(**sigmas) #

Create a distance function over the enabled weather dimensions.

Parameters:

Name Type Description Default
**sigmas float

One sigma_<field> per cg.Weather field to measure, giving the normalization for that field's units — for example sigma_cloud_cover_ratio=0.3. Cloud cover and humidity are 0–1 ratios, not percentages. A field either side does not report drops out of the norm.

{}

Returns:

Type Description
CombinedDistance

A distance function (weather_a, weather_b) -> float.

Raises:

Type Description
ValueError

If no dimension is given, or a keyword does not name a numeric cg.Weather field.

Example
import capturegraph.scheduling as cgsh

# Only care about cloud cover for time-lapse (lighting changes).
dist_fn = cgsh.distance.weather(sigma_cloud_cover_ratio=0.3)

# Care about temperature and humidity for outdoor comfort.
dist_fn = cgsh.distance.weather(
    sigma_temperature_celsius=5.0,
    sigma_humidity_ratio=0.2,
)
Source code in capturegraph-lib/capturegraph/scheduling/distance/weather.py
def weather(**sigmas: float) -> CombinedDistance:
    """Create a distance function over the enabled weather dimensions.

    Args:
        **sigmas: One `sigma_<field>` per `cg.Weather` field to measure, giving
            the normalization for that field's units — for example
            `sigma_cloud_cover_ratio=0.3`. Cloud cover and humidity are 0–1
            ratios, not percentages. A field either side does not report drops
            out of the norm.

    Returns:
        A distance function `(weather_a, weather_b) -> float`.

    Raises:
        ValueError: If no dimension is given, or a keyword does not name a
            numeric `cg.Weather` field.

    Example:
        ```python
        import capturegraph.scheduling as cgsh

        # Only care about cloud cover for time-lapse (lighting changes).
        dist_fn = cgsh.distance.weather(sigma_cloud_cover_ratio=0.3)

        # Care about temperature and humidity for outdoor comfort.
        dist_fn = cgsh.distance.weather(
            sigma_temperature_celsius=5.0,
            sigma_humidity_ratio=0.2,
        )
        ```
    """
    if not sigmas:
        raise ValueError(
            "weather needs at least one dimension, one of: "
            + ", ".join(SIGMA_PREFIX + field for field in NUMERIC_FIELDS)
        )

    dimensions: list[tuple[str, DistanceFunction]] = []
    for keyword, sigma in sigmas.items():
        field = keyword.removeprefix(SIGMA_PREFIX)
        if not keyword.startswith(SIGMA_PREFIX) or field not in NUMERIC_FIELDS:
            raise ValueError(
                f"{keyword} does not name a weather dimension; expected one of: "
                + ", ".join(SIGMA_PREFIX + name for name in NUMERIC_FIELDS)
            )
        dimensions.append((field, ScalarDistanceFunction(sigma)))

    return CombinedDistance(dimensions)