aridkpi.core

Core KPIs of the aridkpi package.

Implements the 5 CORE-tier indicators from the KPI Comparison Matrix v1.0:

  • IOD — Indoor Overheating Degree

  • CCOR — Climate Change Overheating Resistivity

  • UDH — Unmet Degree Hours during power outage (Passive Survivability)

  • DEDT — Energy sensitivity to climate (delta E / delta T)

  • DTDT_MAX — Maximum indoor thermal change rate (dT/dt max)

All formulas follow the canonical definitions in the matrix. Each function takes pandas DataFrames or Series with a DatetimeIndex and returns either a scalar or a pandas Series, depending on the indicator.

References for each KPI are listed in the matrix entry; this module only re-states the operational form.

aridkpi.core.ccor(iod_baseline: float, iod_strategy: float, delta_T_climate: float) float[source]

Climate Change Overheating Resistivity (CCOR).

Measures the effectiveness of a passive strategy under a climate-driven temperature shift:

CCOR = (IOD_baseline - IOD_strategy) / Delta_T_climate

Higher CCOR ⇒ better resistivity to climate-change-induced overheating.

Parameters:
  • iod_baseline – IOD of the baseline building (no passive strategy), in °C·h.

  • iod_strategy – IOD of the same building with the passive strategy applied, in °C·h.

  • delta_T_climate – Mean annual temperature increase under the SSP scenario considered relative to the baseline period, in °C. Must be positive.

Returns:

float – CCOR in (°C·h)/°C.

Raises:

ValueError – If delta_T_climate <= 0.

Notes

Formula source: Rahif et al. (2022); IEA EBC Annex 80. Limitation in BWk/BSk: a locally meaningful baseline is required (importing a European baseline produces non-comparable results). See KPI Matrix v1.0.

Examples

>>> round(ccor(iod_baseline=120.0, iod_strategy=80.0, delta_T_climate=2.5), 2)
16.0
aridkpi.core.energy_climate_sensitivity(df: DataFrame, eui_col: str = 'EUI', tmean_col: str = 'T_mean') dict[str, float][source]

Energy sensitivity to climate (delta E / delta T).

Linear regression slope of EUI vs mean annual outdoor temperature across SSP scenarios and time horizons:

delta_E / delta_T = slope of regression EUI ~ T_mean

Parameters:
  • df – DataFrame with one row per (SSP × horizon) combination. Must contain columns eui_col (Energy Use Intensity, kWh/m²/yr) and tmean_col (mean annual outdoor temperature, °C).

  • eui_col – Column names.

  • tmean_col – Column names.

Returns:

dict – Dict with keys slope (kWh·m⁻²·yr⁻¹/°C), intercept (kWh·m⁻²·yr⁻¹), r_squared (dimensionless), p_value (dimensionless), std_err (slope standard error) and n_points (int).

Notes

Formula source: this work, building on Flores Larsen, Filippín & Barea (2019). Linearity must be tested empirically: highly insulated envelopes can produce non-linear responses. See KPI Matrix v1.0.

Examples

>>> import pandas as pd
>>> data = pd.DataFrame({
...     "T_mean": [16.0, 17.5, 19.0, 20.5],
...     "EUI":    [80.0, 85.0, 92.0, 98.0],
... })
>>> r = energy_climate_sensitivity(data)
>>> round(r["slope"], 2)
4.04
aridkpi.core.iod(T_op: Series, T_comf: Series | float = 26.0, occupancy: Series | None = None, dt_hours: float | None = None) float[source]

Indoor Overheating Degree (IOD).

Computes the magnitude of overheating during occupied hours, weighted by the duration of each exceedance:

IOD = sum_t max(T_op(t) - T_comf(t), 0) * dt * occ(t)

/ sum_t occ(t) * dt

The result is reported in °C·h, normalised by occupied hours (so it can be compared between buildings with different occupancy patterns).

Parameters:
  • T_op – Operative temperature time series (°C). DatetimeIndex required.

  • T_comf – Comfort threshold. Either a constant (°C) or a Series aligned with T_op for an adaptive comfort model. Defaults to DEFAULT_TCOMF_FIXED.

  • occupancy – Boolean Series aligned with T_op. True where the zone is occupied. If None, every step is considered occupied.

  • dt_hours – Sampling step in hours. If None, inferred from the index.

Returns:

float – IOD in °C·h, occupancy-weighted average. Returns 0.0 if there are no occupied hours.

Notes

Formula source: Hamdy, Carlucci, Hoes & Hensen (2017). Limitation in BWk/BSk: T_comf should be derived from a locally validated adaptive comfort model. See KPI Matrix v1.0 entry for IOD.

Examples

>>> import pandas as pd
>>> idx = pd.date_range("2026-01-01", periods=24, freq="h")
>>> T = pd.Series([20]*8 + [28]*8 + [22]*8, index=idx, dtype=float)
>>> round(iod(T, T_comf=26.0), 2)
0.67
aridkpi.core.max_thermal_change_rate(T_in: Series, smoothing_window: int = 3) float[source]

Maximum indoor thermal change rate (dT/dt max).

Computes the peak indoor temperature change rate over the time series:

max_t | dT_in(t) / dt |

A 3-point moving average is applied by default to reduce sensor noise contribution to the maximum.

Parameters:
  • T_in – Indoor temperature time series (°C). DatetimeIndex required.

  • smoothing_window – Window size for the moving average pre-smoothing (in samples). Use smoothing_window=1 to disable smoothing.

Returns:

float – Maximum |dT/dt| in °C/h.

Notes

Formula source: this work. Rationale: in arid climates with diurnal range > 15 °C, dT/dt is a primary discriminator of envelope thermal mass. See KPI Matrix v1.0.

Examples

>>> import numpy as np
>>> import pandas as pd
>>> idx = pd.date_range("2026-01-01", periods=12, freq="h")
>>> # Linear ramp 1 °C/h
>>> T = pd.Series(np.arange(12, dtype=float), index=idx)
>>> round(max_thermal_change_rate(T, smoothing_window=1), 2)
1.0
aridkpi.core.udh(T_op: Series, outage_start: Timestamp | str, window: Literal['24h', '72h', '7d'] = '72h', threshold: float = 30.0, dt_hours: float | None = None) float[source]

Unmet Degree Hours during a sustained power outage.

Quantifies habitability during a power outage triggered at the peak of an Extreme Weather Year (EWY). Computed as:

UDH_w = sum_{t in W} max(T_op(t) - T_threshold, 0) * dt

where W is the time window starting at outage_start.

Parameters:
  • T_op – Operative temperature time series during the simulated outage (°C). DatetimeIndex required. Must include the entire window from outage_start.

  • outage_start – Timestamp at which the outage begins. Can be a pd.Timestamp or any string parseable by pandas.

  • window – Length of the outage window: "24h", "72h" or "7d".

  • threshold – Upper temperature threshold (°C). Defaults to DEFAULT_PASSIVE_THRESHOLD.

  • dt_hours – Sampling step in hours. If None, inferred from the index.

Returns:

float – UDH in °C·h.

Raises:

ValueError – If the window does not fit within the supplied series.

Notes

Formula source: Sun et al. (2021); IEA EBC Annex 80. Limitation in BWk/BSk: the 30 °C threshold inherits the North American convention. In the arid regime characterised by low RH (often < 30 %), the apparent temperature differs significantly from the dry-bulb. Recalibrate using SET / WBGT / UTCI when reporting in BWk/BSk. See KPI Matrix v1.0.

Examples

>>> import pandas as pd
>>> idx = pd.date_range("2026-01-15", periods=72, freq="h")
>>> T = pd.Series([32.0]*72, index=idx)
>>> round(udh(T, outage_start="2026-01-15", window="72h", threshold=30.0), 2)
144.0