tsunami / src /tsuwave /api /endpoints /forecast.py
Gitdeeper4's picture
رفع جميع ملفات TSU-WAVE مع YAML
12834b7
"""Run-up forecast endpoints"""
from fastapi import APIRouter, HTTPException, Query, Path, Body
from typing import Dict, List, Optional
from datetime import datetime
import uuid
router = APIRouter()
@router.post("/runup", response_model=Dict)
async def forecast_runup(
zone: str = Query(..., description="Coastal zone name"),
source: Optional[str] = Query(None, description="Source region (e.g., 'sumatra', 'chile')"),
parameters: Optional[Dict] = Body(None, description="Parameter values for forecast")
):
"""Generate run-up forecast for a coastal zone"""
# Placeholder for different zones
zone_data = {
"hilo_bay_hawaii": {
"becf": 4.8,
"typical_beach_slope": 0.05,
"shelf_width_km": 22,
"population": 45000
},
"khao_lak_thailand": {
"becf": 4.1,
"typical_beach_slope": 0.03,
"shelf_width_km": 65,
"population": 20000
},
"banda_aceh_indonesia": {
"becf": 7.3,
"typical_beach_slope": 0.04,
"shelf_width_km": 65,
"population": 250000
}
}
if zone not in zone_data:
# Return generic forecast
runup = 5.0 # default
else:
data = zone_data[zone]
# Simplified forecast based on BECF
runup = 1.0 * (data['becf'] ** 0.5) * 2
return {
"forecast_id": str(uuid.uuid4()),
"zone": zone,
"timestamp": datetime.utcnow().isoformat(),
"runup_m": runup,
"confidence_interval": [runup * 0.88, runup * 1.12],
"lead_time_min": 67,
"parameters_used": parameters or {},
"zone_data": zone_data.get(zone, {}),
"method": "physical_scaling"
}
@router.post("/ensemble", response_model=Dict)
async def ensemble_forecast(
zone: str = Query(..., description="Coastal zone name"),
n_samples: int = Query(100, ge=10, le=1000, description="Number of ensemble samples")
):
"""Generate ensemble forecast with uncertainty quantification"""
import numpy as np
# Generate ensemble
base_runup = 5.0
uncertainties = np.random.normal(0, 0.5, n_samples)
ensemble = base_runup + uncertainties
return {
"forecast_id": str(uuid.uuid4()),
"zone": zone,
"timestamp": datetime.utcnow().isoformat(),
"ensemble_statistics": {
"mean": float(np.mean(ensemble)),
"median": float(np.median(ensemble)),
"std": float(np.std(ensemble)),
"p10": float(np.percentile(ensemble, 10)),
"p90": float(np.percentile(ensemble, 90)),
"min": float(np.min(ensemble)),
"max": float(np.max(ensemble)),
"n_samples": n_samples
},
"ensemble": ensemble.tolist()[:10] # sample first 10
}
@router.get("/zone/{zone_name}", response_model=Dict)
async def get_zone_forecast_history(
zone_name: str = Path(...),
days: int = Query(7, ge=1, le=30, description="Number of days of history")
):
"""Get forecast history for a zone"""
# Placeholder history
import numpy as np
from datetime import datetime, timedelta
history = []
for i in range(days):
date = datetime.utcnow() - timedelta(days=i)
history.append({
"date": date.isoformat(),
"forecast_runup": 5.0 + np.random.normal(0, 0.3),
"observed_runup": None # would be filled if observed
})
return {
"zone": zone_name,
"history": history
}
@router.post("/validate", response_model=Dict)
async def validate_forecast(
forecast_id: str = Query(..., description="Forecast ID"),
observed_runup: float = Query(..., description="Observed run-up height [m]")
):
"""Validate a forecast against observation"""
# Placeholder validation
error = abs(5.0 - observed_runup) / observed_runup * 100
return {
"forecast_id": forecast_id,
"observed_runup": observed_runup,
"forecast_runup": 5.0,
"error_percent": error,
"within_15_percent": error < 15,
"within_30_percent": error < 30,
"bias": 5.0 - observed_runup
}
@router.get("/scenarios", response_model=List[Dict])
async def get_forecast_scenarios():
"""Get pre-computed forecast scenarios"""
return [
{
"id": "tohoku_2011",
"name": "Tōhoku 2011",
"description": "2011 Tōhoku earthquake and tsunami",
"source": "Japan Trench",
"magnitude": 9.0,
"runup_m": 40.5
},
{
"id": "indian_ocean_2004",
"name": "Indian Ocean 2004",
"description": "2004 Sumatra-Andaman earthquake and tsunami",
"source": "Sumatra",
"magnitude": 9.1,
"runup_m": 30.0
},
{
"id": "hokkaido_1993",
"name": "Hokkaido 1993",
"description": "1993 Hokkaido Nansei-Oki earthquake",
"source": "Sea of Japan",
"magnitude": 7.8,
"runup_m": 31.0
}
]