Source code for climagrid.features.freeze_thaw

"""
FreezeThawtCycleCounter: counts freeze-thaw transitions in a rolling window.

Freeze-thaw cycling causes fatigue in:
- Overhead conductors (galloping, vibration)
- Insulator strings and polymer housings (moisture ingress, cracking)
- Wood and composite pole foundations (frost heave)
- Underground cable sheathing (ground movement)

A freeze-thaw cycle is counted each time temperature crosses 0°C in
either direction within a rolling time window.
"""

from __future__ import annotations

import pandas as pd


[docs] class FreezeThawtCycleCounter: """ Counts freeze-thaw transitions from an hourly temperature time series. Parameters ---------- temp_col: Column containing temperature in °C. freeze_threshold_c: Temperature below which the asset is considered "frozen". Default 0°C (water freezing point). rolling_window_h: Rolling window in hours over which to count cycles. Default 720 (30 days). Example ------- >>> ftc = FreezeThawtCycleCounter() >>> df = ftc.compute(asset_env_df) >>> df["feat_freeze_thaw_cycles"] """ def __init__( self, temp_col: str = "hrrr_temperature_2m", freeze_threshold_c: float = 0.0, rolling_window_h: int = 720, ): self._temp_col = temp_col self._freeze_threshold_c = freeze_threshold_c self._rolling_window_h = rolling_window_h
[docs] def compute(self, df: pd.DataFrame) -> pd.DataFrame: """Add feat_freeze_thaw_cycles column. Returns a copy.""" df = df.copy() temp_col = self._temp_col if temp_col not in df.columns: for fallback in ["nasa_temperature_2m", "ncei_temperature_max"]: if fallback in df.columns: temp_col = fallback break else: df["feat_freeze_thaw_cycles"] = float("nan") return df def _count_transitions(series: pd.Series) -> pd.Series: frozen = series < self._freeze_threshold_c # A transition occurs wherever the frozen state changes transitions = frozen.astype(int).diff().abs().fillna(0) # Each complete cycle = 2 transitions (freeze + thaw); count transitions # and divide by 2 so we report full cycles cycles = transitions.rolling(self._rolling_window_h, min_periods=1).sum() / 2.0 return cycles if "asset_id" in df.columns: result_parts = [] for _, grp in df.groupby("asset_id", sort=False): part = _count_transitions(grp[temp_col]) result_parts.append(part) df["feat_freeze_thaw_cycles"] = pd.concat(result_parts).reindex(df.index) else: df["feat_freeze_thaw_cycles"] = _count_transitions(df[temp_col]) return df