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
14 changes: 13 additions & 1 deletion climate_tookit/calculate_hazards/ensemble_hazards.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
add_et0,
water_balance_hazards,
heat_stress_hazards,
dry_days_hazard,
_severity_symbol,
DEFAULT_SOILCP,
DEFAULT_SOILSAT,
Expand Down Expand Up @@ -216,6 +217,8 @@ def _evaluate(crop: str, lat: float, lon: float,
hazards.update(water_balance_hazards(stats))
# NTx35 / NTx40 heat-stress severity (crop-specific bands)
hazards.update(heat_stress_hazards(stats, crop))
# NDD dry-days severity
hazards.update(dry_days_hazard(stats))
length = (datetime.fromisoformat(w['end'])
- datetime.fromisoformat(w['start'])).days
return {
Expand Down Expand Up @@ -286,6 +289,8 @@ def _avg_hazards(crop: str, agg: Dict) -> Dict:
out.update(water_balance_hazards(agg))
# NTx35 / NTx40 heat-stress severity on the ensemble-mean day counts
out.update(heat_stress_hazards(agg, crop))
# NDD dry-days severity on the ensemble-mean day counts
out.update(dry_days_hazard(agg))
return out

def _agg_hazard_statuses(bucket: List[Dict]) -> Dict:
Expand All @@ -295,7 +300,7 @@ def _agg_hazard_statuses(bucket: List[Dict]) -> Dict:
"""
from collections import Counter
indicators = ['precipitation', 'temperature', 'water_stress', 'water_logging',
'heat_stress', 'extreme_heat_stress']
'heat_stress', 'extreme_heat_stress', 'dry_days']
out = {}
for ind in indicators:
statuses = [
Expand Down Expand Up @@ -632,6 +637,10 @@ def _print_block(a: Dict, crop: str, lat: float, lon: float,
ehs = h['extreme_heat_stress']
print(f" {'Extreme Heat (NTx40)':<25} {s.get('NTx40', 0):>16.2f} d "
f"[{_severity_symbol(ehs['status'])}] {ehs['status'].replace('_', ' ').upper()}")
if 'dry_days' in h:
dd = h['dry_days']
print(f" {'Dry Days (NDD)':<25} {s.get('NDD', 0):>16.2f} d "
f"[{_severity_symbol(dd['status'])}] {dd['status'].replace('_', ' ').upper()}")
print(f"\n{'='*70}")

def print_results(r: Dict) -> None:
Expand Down Expand Up @@ -699,6 +708,9 @@ def print_results(r: Dict) -> None:
if 'extreme_heat_stress' in h:
print(f" NTx40 status: [{_severity_symbol(h['extreme_heat_stress']['status'])}] "
f"{h['extreme_heat_stress']['status'].replace('_', ' ').upper()}")
if 'dry_days' in h:
print(f" NDD status: [{_severity_symbol(h['dry_days']['status'])}] "
f"{h['dry_days']['status'].replace('_', ' ').upper()}")
print(f"\n{'='*70}\n")

# CLI
Expand Down
44 changes: 44 additions & 0 deletions climate_tookit/calculate_hazards/hazards.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ def evaluate_threshold(value: float, thresholds: Dict[str, Tuple]) -> str:
# Water-balance hazard severity classes (unit: days), per the Adaptation Atlas
NDWS_SEVERITY = (15, 20, 25)
NDWL0_SEVERITY = (2, 5, 8)
# Dry-days (NDD) severity class (unit: days), generic per the AE-project wiki
NDD_SEVERITY = (15, 20, 25)

def classify_water_hazard(value: float, bounds: Tuple[int, int, int]) -> str:
"""Map a day count to a severity class using the Atlas water-balance bands."""
Expand Down Expand Up @@ -315,6 +317,18 @@ def water_balance_hazards(stats: Dict[str, Any]) -> Dict[str, Any]:
}
return out

def dry_days_hazard(stats: Dict[str, Any]) -> Dict[str, Any]:
"""Build an NDD (dry-days) severity entry from season statistics."""
out: Dict[str, Any] = {}
if stats.get('NDD') is not None:
v = stats['NDD']
out['dry_days'] = {
'index': 'NDD',
'value_days': round(float(v), 2),
'status': classify_water_hazard(v, NDD_SEVERITY),
}
return out

# Heat-stress severity classes (unit: days per season), per the AE-project
# crop-specific hazard thresholds wiki. Bands give (moderate, severe, extreme)
# floors; values below the moderate floor are no_significant_stress.
Expand Down Expand Up @@ -474,6 +488,9 @@ def compute_ltm_baseline(
}
# NDWS / NDWL0 water-balance severity on the LTM means
hazard_eval.update(water_balance_hazards(agg))
# NTx35 / NTx40 heat-stress (crop-specific) and NDD dry-days on LTM means
hazard_eval.update(heat_stress_hazards(agg, crop_name))
hazard_eval.update(dry_days_hazard(agg))

ltm_blocks.append({
'season_number': sn,
Expand Down Expand Up @@ -642,6 +659,9 @@ def calculate_hazards(
}
# NDWS / NDWL0 water-balance severity (Adaptation Atlas classes)
hazard_eval.update(water_balance_hazards(stats))
# NTx35 / NTx40 heat-stress severity (crop-specific) and NDD dry-days
hazard_eval.update(heat_stress_hazards(stats, crop_name))
hazard_eval.update(dry_days_hazard(stats))
assessments.append({
'crop': crop_name,
'location': {'latitude': lat, 'longitude': lon},
Expand Down Expand Up @@ -742,6 +762,18 @@ def _print_ltm_block(ltm: Dict[str, Any]) -> None:
wl = h['water_logging']
sym = _severity_symbol(wl['status'])
print(f" {'Water Logging (NDWL0)':<25} {wl['value_days']:>16.2f} d [{sym}] {wl['status'].replace('_', ' ').upper()}")
if 'heat_stress' in h:
hs = h['heat_stress']
sym = _severity_symbol(hs['status'])
print(f" {'Heat Stress (NTx35)':<25} {hs['value_days']:>16.2f} d [{sym}] {hs['status'].replace('_', ' ').upper()}")
if 'extreme_heat_stress' in h:
ehs = h['extreme_heat_stress']
sym = _severity_symbol(ehs['status'])
print(f" {'Extreme Heat (NTx40)':<25} {ehs['value_days']:>16.2f} d [{sym}] {ehs['status'].replace('_', ' ').upper()}")
if 'dry_days' in h:
dd = h['dry_days']
sym = _severity_symbol(dd['status'])
print(f" {'Dry Days (NDD)':<25} {dd['value_days']:>16.2f} d [{sym}] {dd['status'].replace('_', ' ').upper()}")
print(f"\n{'='*70}\n")

def print_hazard_results(result: Dict[str, Any]) -> None:
Expand Down Expand Up @@ -874,6 +906,18 @@ def print_hazard_results(result: Dict[str, Any]) -> None:
wl = hazards['water_logging']
sym = _severity_symbol(wl['status'])
print(f" {'Water Logging (NDWL0)':<25} {wl['value_days']:>16.2f} d [{sym}] {wl['status'].replace('_', ' ').upper()}")
if 'heat_stress' in hazards:
hs = hazards['heat_stress']
sym = _severity_symbol(hs['status'])
print(f" {'Heat Stress (NTx35)':<25} {hs['value_days']:>16.2f} d [{sym}] {hs['status'].replace('_', ' ').upper()}")
if 'extreme_heat_stress' in hazards:
ehs = hazards['extreme_heat_stress']
sym = _severity_symbol(ehs['status'])
print(f" {'Extreme Heat (NTx40)':<25} {ehs['value_days']:>16.2f} d [{sym}] {ehs['status'].replace('_', ' ').upper()}")
if 'dry_days' in hazards:
dd = hazards['dry_days']
sym = _severity_symbol(dd['status'])
print(f" {'Dry Days (NDD)':<25} {dd['value_days']:>16.2f} d [{sym}] {dd['status'].replace('_', ' ').upper()}")

print(f"\n{'='*70}\n")

Expand Down
Loading