Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,20 +532,24 @@ Important source notes:
- `chirts` is temperature-only, so climatology will omit precipitation outputs.

SPEI and xclim helpers are library functions, not standalone console scripts.
Use them from Python after fetching/preprocessing climate data:
Use them from Python after fetching/preprocessing climate data. VPD helpers use
`xclim` for thermodynamic calculation and prefer CHC-consistent
moisture-informed inputs (`relative_humidity` or `dewpoint`) when available:

```python
from climate_tookit.climatology.spei import (
compute_monthly_spei,
prepare_monthly_climatic_water_balance,
)
from climate_tookit.climatology import summarize_vpd_period
from climate_tookit.climatology.xclim_reference import (
compute_xclim_precip_indices,
)

monthly_balance = prepare_monthly_climatic_water_balance(df, lat=-1.286)
spei_12 = compute_monthly_spei(monthly_balance, scale=12)
xclim_precip = compute_xclim_precip_indices(df)
vpd = summarize_vpd_period(df)
```

---
Expand Down Expand Up @@ -769,7 +773,7 @@ Core current-state modules:
| `compare_datasets` | source-vs-source comparison workflow | shared fetch pipeline |
| `weather_station` | station discovery, ingestion, selection, station-vs-grid validation | NOAA/custom station inputs, gridded fetch layer |
| `crop_calendar` | GGCMI crop calendar lookups and presets | `season_analysis`, `climate_statistics`, `compare_periods` |
| `climatology` | SPEI and xclim-backed climatology helpers | `climate_statistics`, `compare_periods`, `weather_station` |
| `climatology` | SPEI and xclim-backed climatology helpers, including CHC-aligned moisture-informed VPD support | `climate_statistics`, `compare_periods`, `weather_station` |

Notes:

Expand Down
46 changes: 44 additions & 2 deletions climate_tookit/climate_statistics/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
describe_thi_method,
list_thi_livestock_profiles,
resolve_thi_profile,
summarize_vpd_period,
)
try:
from climate_tookit.fetch_data.preprocess_data.preprocess_data import preprocess_data
Expand Down Expand Up @@ -699,6 +700,16 @@ def _livestock_heat_stress_summary(
),
}


def _vpd_summary(df: pd.DataFrame) -> Optional[Dict[str, Any]]:
"""Summarize VPD when humidity or dewpoint-backed inputs are available."""
if df is None or df.empty or 'date' not in df.columns:
return None
summary = summarize_vpd_period(df)
if not summary:
return None
return summary

def raw_climate_summary(df: pd.DataFrame) -> List[Dict[str, Any]]:
"""
Compact summary table -- mean / min / max / std per core variable.
Expand Down Expand Up @@ -761,6 +772,7 @@ def overall_statistics(
}
water_balance_stats.update(shared_wb)
heat_stress = _livestock_heat_stress_summary(df, thi_profile=thi_profile)
vpd = _vpd_summary(df)

result = {
'total_days': int(len(df)),
Expand All @@ -782,6 +794,8 @@ def overall_statistics(
},
'water_balance': water_balance_stats,
}
if vpd:
result['vpd'] = vpd
if heat_stress:
result['livestock_heat_stress'] = heat_stress
return result
Expand Down Expand Up @@ -869,6 +883,7 @@ def season_statistics(
)

heat_stress = _livestock_heat_stress_summary(sdf, thi_profile=thi_profile)
vpd = _vpd_summary(sdf)

result = {
'onset': onset_ts.strftime('%Y-%m-%d'),
Expand All @@ -890,6 +905,8 @@ def season_statistics(
'water_balance': water_balance_stats,
'water_balance_methodology': water_balance_methodology,
}
if vpd:
result['vpd'] = vpd
if heat_stress:
result['livestock_heat_stress'] = heat_stress
return result
Expand Down Expand Up @@ -1128,7 +1145,7 @@ def ltm_season_summary(
'length_days_avg': _avg([s.get('length_days') for s in seasons], 1),
}

for cat in ('precipitation', 'temperature', 'water_balance', 'livestock_heat_stress'):
for cat in ('precipitation', 'temperature', 'water_balance', 'vpd', 'livestock_heat_stress'):
pool: Dict[str, List[float]] = {}
for s in seasons:
for k, v in (s.get(cat) or {}).items():
Expand All @@ -1152,9 +1169,21 @@ def ltm_season_summary(
and (s.get(cat) or {}).get(meta_key) is not None
),
None,
)
)
if meta_value is not None:
block.setdefault(cat, {})[meta_key] = meta_value
if cat == "vpd":
method_value = next(
(
(s.get(cat) or {}).get("method")
for s in seasons
if isinstance(s.get(cat), dict)
and (s.get(cat) or {}).get("method") is not None
),
None,
)
if method_value is not None:
block.setdefault(cat, {})["method"] = method_value

ov_pool: Dict[str, Dict[str, List[float]]] = {}
for s in seasons:
Expand Down Expand Up @@ -2325,6 +2354,7 @@ def print_overall_by_season(seasons: List[Dict]) -> None:
('temperature', 'Temperature'),
('et0', 'ET0'),
('water_balance', 'Water Balance'),
('vpd', 'VPD'),
('livestock_heat_stress', 'Livestock THI'),
]:
if var_key not in stats:
Expand Down Expand Up @@ -2367,6 +2397,12 @@ def print_seasons(seasons: List[Dict]) -> None:
f"mean_tavg={t['mean_tavg']}°C | "
f"max_tmax={t['max_tmax']}°C | "
f"min_tmin={t['min_tmin']}°C")
v = s.get('vpd') or {}
if v:
print(f" VPD : "
f"mean_vpd={v.get('mean_vpd_kpa')} kPa | "
f"max_vpd={v.get('max_vpd_kpa')} kPa | "
f"method={v.get('method')}")
h = s.get('livestock_heat_stress') or {}
if h:
print(f" {_thi_profile_label(h)} : "
Expand Down Expand Up @@ -2453,6 +2489,12 @@ def print_ltm_by_season(ltm: Dict[str, Any],
f"mean_tavg={t.get('mean_tavg')}°C | "
f"max_tmax={t.get('max_tmax')}°C | "
f"min_tmin={t.get('min_tmin')}°C")
v = w.get('vpd') or {}
if v:
print(f" VPD : "
f"mean_vpd={v.get('mean_vpd_kpa')} kPa | "
f"max_vpd={v.get('max_vpd_kpa')} kPa | "
f"method={v.get('method')}")
h = w.get('livestock_heat_stress') or {}
if h:
print(f" {_thi_profile_label(h)} : "
Expand Down
8 changes: 8 additions & 0 deletions climate_tookit/climatology/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"compute_monthly_spei",
"compute_monthly_spi",
"compute_daily_thi",
"compute_daily_vpd",
"describe_thi_method",
"DEFAULT_LIVESTOCK_CLIMATE_PROFILE",
"DEFAULT_LIVESTOCK_TYPE",
Expand All @@ -23,6 +24,7 @@
"prepare_monthly_climatic_water_balance",
"prepare_monthly_precipitation_totals",
"resolve_thi_profile",
"summarize_vpd_period",
"summarize_thi_periods",
"assess_xclim_precip_annual_readiness",
"compare_xclim_precip_indices",
Expand Down Expand Up @@ -51,6 +53,12 @@ def __getattr__(name: str):
}:
module = import_module(".heat_stress", __name__)
return getattr(module, name)
if name in {
"compute_daily_vpd",
"summarize_vpd_period",
}:
module = import_module(".vpd", __name__)
return getattr(module, name)
if name in {
"compute_monthly_spei",
"compute_monthly_spi",
Expand Down
Loading
Loading