Skip to content

Test data fixture generator

tests/fixtures/generator.py provides deterministic, in-memory fixture generators for climate risk test data. Every function accepts a seed parameter so that identical inputs always produce identical byte-level output. Nothing is written to disk; callers receive plain numpy arrays plus a metadata dict.

Why a shared generator?

Without a shared generator, each test file rolls its own hazard/exposure arrays by hand. This leads to:

  • Inconsistent intensity ranges — some tests accidentally use intensities below the curve threshold (silent zero-damage) or above saturation.
  • Fragile SHA256 references — no easy way to assert a test dataset is stable across code changes.
  • Copy-paste amplification — adding a new hazard type means touching a dozen test files.

The generator centralises the calibration knowledge (intensity ranges, units, curve shapes) in one place.

API

make_hazard_fixture

from tests.fixtures.generator import make_hazard_fixture

result = make_hazard_fixture(
    hazard_type="RF",  # "RF" | "TC" | "WF" | "WS"
    n_events=3,
    bbox=(23.0, 37.0, 24.0, 38.0),  # (lon_min, lat_min, lon_max, lat_max)
    seed=42,
)

Returned keys:

Key Type Description
hazard_type str Two-letter engine code passed in
n_events int Number of events
n_centroids int Number of centroids (≥ n_events)
intensity ndarray (n_events, n_centroids) Float32 intensity values
frequency ndarray (n_events,) Marginal event frequency
centroids_lon ndarray (n_centroids,) Centroid longitudes
centroids_lat ndarray (n_centroids,) Centroid latitudes
intensity_unit str Physical unit (m, m/s, K)
bbox tuple As passed

make_exposure_fixture

from tests.fixtures.generator import make_exposure_fixture

result = make_exposure_fixture(
    n_points=5,
    bbox=(23.0, 37.0, 24.0, 38.0),
    seed=42,
)

Returned keys:

Key Type Description
n_points int Number of assets
lons ndarray (n_points,) Longitudes inside bbox
lats ndarray (n_points,) Latitudes inside bbox
values ndarray (n_points,) Asset values in EUR (100 k – 10 M)
value_unit str "EUR"
bbox tuple As passed

make_impact_function_fixture

from tests.fixtures.generator import make_impact_function_fixture

result = make_impact_function_fixture(
    hazard_type="RF",
    curve_shape="linear",  # "linear" | "step" | "sigmoid"
    seed=42,
)

Returned keys:

Key Type Description
hazard_type str Two-letter engine code
curve_shape str Shape used
intensity_unit str Physical unit
n_points int Number of breakpoints (6)
mdd_x ndarray (n_points,) Intensity breakpoints
mdd_y ndarray (n_points,) Mean damage degree (0–1)
paa_x ndarray (n_points,) Same breakpoints as mdd_x
paa_y ndarray (n_points,) Percentage of assets affected

Hazard-type worked examples

The calibrated intensity ranges ensure that produced fixtures return positive but non-saturating damage against the built-in impact curves seeded by src/climate_lama/core/impact_function_seeder.py.

RF — River flood

haz = make_hazard_fixture("RF", n_events=2, seed=0)
# intensity in metres (0.5 – 3.0 m)
# Sits above the JRC flood-Europe threshold and below the 6 m saturation tail.
impf = make_impact_function_fixture("RF", curve_shape="linear", seed=0)

TC — Tropical cyclone

haz = make_hazard_fixture("TC", n_events=2, seed=0)
# intensity in m/s gust (35 – 75 m/s)
# Above Emanuel 2011 v_thresh=25.7, below v_half=110.1 where MDD → 1.
impf = make_impact_function_fixture("TC", curve_shape="sigmoid", seed=0)

WF — Wildfire

haz = make_hazard_fixture("WF", n_events=2, seed=0)
# intensity in K brightness temperature (305 – 320 K)
# Above Lüthi 2021 I_thresh=295 K; values stay just above threshold.
impf = make_impact_function_fixture("WF", curve_shape="linear", seed=0)

WS — Storm Europe

haz = make_hazard_fixture("WS", n_events=2, seed=0)
# intensity in m/s gust (25 – 45 m/s)
# Above Klawa-Ulbrich 2003 v98=20 m/s, below saturation plateau.
impf = make_impact_function_fixture("WS", curve_shape="sigmoid", seed=0)

Reproducibility and SHA256 assertions

Because the generator is deterministic, you can pin expected checksums:

import hashlib
import numpy as np
from tests.fixtures.generator import make_hazard_fixture

result = make_hazard_fixture("RF", seed=42)
digest = hashlib.sha256(result["intensity"].tobytes()).hexdigest()
assert digest == "..."  # pinned at fixture creation time

This catches unintended changes to the generation logic (e.g. a NumPy RNG API change) without requiring large binary fixtures committed to the repo.

Design constraints

  • No disk I/O — all data lives in memory. No fixture files are committed.
  • No external dependencies — only NumPy; no CLIMADA, no engine imports.
  • Deterministic across platformsnumpy.random.default_rng guarantees identical output on any OS and NumPy ≥1.17.