Spaces:
Paused
Paused
File size: 11,400 Bytes
4eefabb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | """
Central configuration for MicroClimate-X.
EVERY Veto threshold below has an academic / regulatory citation.
This is intentional โ at thesis defence the panel WILL ask
"why 3500 m, why -5 ยฐC, why 40 km/h?". Be ready to point to this file.
"""
from __future__ import annotations
import os
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
MODEL_DIR = ROOT / "models"
DATA_DIR = ROOT / "data"
DB_PATH = Path(os.environ.get("MICROCLIMATEX_DB", str(ROOT / "cache.sqlite3")))
def _detect_git_revision() -> str:
"""Best-effort short SHA. Returns "unknown" if git is not available
or this directory isn't a checkout (e.g. inside a Docker image)."""
env = os.environ.get("MICROCLIMATEX_GIT_REV")
if env:
return env
try:
out = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
cwd=ROOT, capture_output=True, text=True, timeout=2.0,
)
if out.returncode == 0:
return out.stdout.strip()
except (FileNotFoundError, subprocess.SubprocessError): # pragma: no cover
pass
return "unknown"
GIT_REVISION = _detect_git_revision()
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Veto thresholds โ one-vote rejection rules
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Citation: Luks et al. (2019) "Wilderness Medical Society Practice
# Guidelines for the Prevention and Treatment of Acute Altitude
# Illness." High altitude (>2500 m) carries clinical risk; severe
# hypoxia onset is well-documented above ~3500 m.
ALTITUDE_HYPOXIA_M = 3500.0
# Citation: WMO Beaufort scale โ Force 6 "Strong breeze" โ 39-49 km/h,
# the threshold above which outdoor activity becomes hazardous.
GALE_WIND_KMH = 40.0
# Citation: UIAA Medical Commission frostbite risk guidance โ exposed skin
# freezes rapidly below approximately -5 ยฐC with wind chill.
EXTREME_COLD_C = -5.0
# Citation: U.S. NWS convective forecasting handbook โ CAPE > 1000 J/kg
# indicates moderate-to-strong instability suitable for
# thunderstorm development.
HIGH_CAPE_JKG = 1000.0
# Citation: FAA AIM 7-1-12 โ visibility below 100 m is classified as
# Category III instrument-only conditions. Used here as an extreme
# low-visibility threshold (whiteout / dense fog).
LOW_VISIBILITY_M = 100.0
# Wind alignment with slope normal vector (orographic uplift). The
# threshold 0.7 corresponds to ~45 degrees of slope-facing wind.
OROGRAPHIC_DOT_THRESHOLD = 0.7
# Wet-flood trigger in a valley basin: high probability of localised rain
# combined with valley-floor topography.
VALLEY_FLOOD_PROB = 0.80
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Risk scoring (additive penalties when no Veto fires)
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
PENALTY = {
"ml_high_rain_prob": 35, # ML predicts >= 70 % rain probability
"ml_mid_rain_prob": 15, # ML predicts 40-70 % rain probability
"valley_floor": 10,
"windward_slope": 20,
"orographic_lift": 25,
"altitude_high": 15, # 2500-3500 m, sub-Veto altitude band
"wind_strong": 10, # 25-40 km/h
}
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Four hazard categories โ matches D5 proposal ยง3.7 / P4.3
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Fog risk:
# WMO surface synoptic code: fog โ visibility < 1 km, RH typically > 95 %,
# dew-point depression < ~2 ยฐC. Valley/Slope basins trap radiation fog.
FOG_HUMIDITY_PCT = 95.0
FOG_DEW_DEP_MAX_C = 2.0
FOG_CLOUD_BASE_MAX_M = 800.0 # from D5 ยง3.7.2 decision table
# Wind gust risk:
# On exposed ridges and mountain passes, sustained 25 km/h winds with
# topographic acceleration commonly gust to Beaufort F6 levels.
GUST_WIND_MIN_KMH = 25.0 # below GALE_WIND_KMH but still risky
# Thunderstorm risk:
# NWS "moderate instability" begins at CAPE 500 J/kg; sharp pressure drop
# often precedes convective initiation.
THUNDER_CAPE_MIN_JKG = 500.0
THUNDER_PRESSURE_DROP = -2.0 # hPa over past 3 h (matches D5 ยง1.3 example)
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Decision Table โ D5 ยง3.7.2 / Table 4.2 (one-to-one with the thesis)
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Each rule fires when ALL of its non-None conditions hold. The thesis
# narrative motivates this table as: "macro forecast says no rain, but
# the local terrain conditions imply hidden risk".
DECISION_TABLE_3_7_2 = {
"R1": {
"description": "Hidden rain risk โ macro says no, terrain says yes",
"macro_rain_prob_max": 0.30,
"macro_rain_prob_min": None,
"humidity_min_pct": 85.0,
"wind_into_slope": True,
"terrain": "WindwardSlope",
"pressure_change_3h_max": -1.5,
"cloud_base_max_m": FOG_CLOUD_BASE_MAX_M,
"conclusion_en": "Hidden rain risk: terrain analysis indicates orographic precipitation despite low macro probability.",
"conclusion_zh": "้่้้จ้ฃ้ฉ๏ผๅฎ่ง้ขๆฅๆฆ็ไฝ๏ผไฝๅฐๅฝขๅๆ่กจๆๅญๅจๅฐๅฝขๆฌๅ้ๆฐดใ",
},
"R2": {
"description": "No significant risk โ terrain not aligned",
"macro_rain_prob_max": 0.30,
"macro_rain_prob_min": None,
"humidity_min_pct": 85.0,
"wind_into_slope": False,
"terrain": "LeewardOrValley",
"pressure_change_3h_max": -1.5,
"cloud_base_max_m": FOG_CLOUD_BASE_MAX_M,
"conclusion_en": "No significant rainfall danger at this spot in this period.",
"conclusion_zh": "ๆญคๅฐๆญคๆถๆ ๆพ่้้จๅฑ้ฉใ",
},
"R3": {
"description": "Heavy downpour incoming โ avoid exposure",
"macro_rain_prob_max": None,
"macro_rain_prob_min": 0.70,
"humidity_min_pct": None,
"wind_into_slope": True,
"terrain": "WindwardSlope",
"pressure_change_3h_max": None,
"cloud_base_max_m": None,
"conclusion_en": "Heavy downpour incoming. Avoid mountains and valleys.",
"conclusion_zh": "ๅผบ้้จๅณๅฐๅฐๆฅใ่ฏท้ฟๅผๅฑฑๅบไธๅณก่ฐทใ",
},
"R4": {
"description": "Normal rain โ no terrain amplification",
"macro_rain_prob_max": None,
"macro_rain_prob_min": 0.70,
"humidity_min_pct": None,
"wind_into_slope": None,
"terrain": None,
"pressure_change_3h_max": None,
"cloud_base_max_m": None,
"conclusion_en": "Rain expected, but no terrain-induced amplification. Standard rain precautions apply.",
"conclusion_zh": "้ข่ฎกๆ้จ๏ผไฝๆ ๅฐๅฝขๆฌๅๆพๅคงใๆไธ่ฌ้จๅคฉๆชๆฝๅบๅฏนๅณๅฏใ",
},
}
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Activity-aware weighting โ D5 ยง3.7 / P4.4
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Composite = ฮฃ w_i ยท subscore_i, then renormalised to 0-100.
# Rows: activity. Cols: rainfall, fog, wind_gust, thunderstorm.
ACTIVITY_WEIGHTS = {
"hiker": {"rainfall": 1.0, "fog": 1.3, "wind_gust": 1.0, "thunderstorm": 1.4},
"driver": {"rainfall": 0.8, "fog": 1.5, "wind_gust": 1.3, "thunderstorm": 0.9},
"construction": {"rainfall": 1.0, "fog": 0.8, "wind_gust": 1.5, "thunderstorm": 1.4},
"general": {"rainfall": 1.0, "fog": 1.0, "wind_gust": 1.0, "thunderstorm": 1.0},
}
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Cache TTL (risk-adaptive)
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Safety-critical apps must not serve stale "Safe" verdicts during developing
# storms. Bucket TTL by risk band.
TTL_HIGH_RISK_SEC = 60 # any Veto fired OR risk >= 70
TTL_MID_RISK_SEC = 300 # risk 40-70
TTL_LOW_RISK_SEC = 600 # risk < 40
# Grid resolution used as cache key (0.01ยฐ โ 1.1 km at the equator).
GRID_RESOLUTION_DEG = 0.01
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# External API endpoints
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
OPEN_METEO_FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
OPEN_TOPO_URL = "https://api.opentopodata.org/v1/srtm30m"
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Domain constants
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# WMO definition of trace precipitation.
RAIN_THRESHOLD_MM = 0.1
|