Spaces:
Sleeping
Sleeping
| """Hydrodynamic Front Stability Index (HFSI) | |
| Quantifies wave front stability via the Boussinesq parameter. | |
| The primary early-warning indicator for tsunami breaking. | |
| HFSI = tanh(Bo) = tanh[H³ / (η·λ²)] | |
| """ | |
| import numpy as np | |
| class HydrodynamicFrontStabilityIndex: | |
| """ | |
| Hydrodynamic Front Stability Index - Parameter 3 of 7 | |
| Thresholds: | |
| SAFE: HFSI > 0.80 (highly stable dispersive propagation) | |
| MONITOR: 0.60 < HFSI ≤ 0.80 (weakly unstable) | |
| ALERT: 0.40 < HFSI ≤ 0.60 (strongly unstable) | |
| CRITICAL: HFSI ≤ 0.40 (breaking imminent) | |
| Instability onset validated at h/H₀ = 0.42 ± 0.05 | |
| """ | |
| def __init__(self): | |
| self.thresholds = { | |
| 'safe': 0.80, | |
| 'monitor': 0.60, | |
| 'alert': 0.40, | |
| 'critical': 0.40 | |
| } | |
| self.instability_onset = 0.42 # h/H₀ threshold | |
| def compute_boussinesq(self, H, eta, wavelength): | |
| """Compute Boussinesq parameter | |
| Bo = H³ / (η·λ²) | |
| Bo >> 1: Dispersion dominates (stable) | |
| Bo ~ 1: Transitional | |
| Bo << 1: Nonlinearity dominates (unstable) | |
| Args: | |
| H: water depth [m] | |
| eta: wave amplitude [m] | |
| wavelength: wavelength [m] | |
| Returns: | |
| Bo: Boussinesq parameter | |
| """ | |
| return H**3 / (eta * wavelength**2 + 1e-10) | |
| def compute_hfsi(self, H, eta, wavelength): | |
| """Compute Hydrodynamic Front Stability Index | |
| HFSI = tanh(Bo) | |
| Args: | |
| H: water depth [m] | |
| eta: wave amplitude [m] | |
| wavelength: wavelength [m] | |
| Returns: | |
| dict with HFSI value, status, and components | |
| """ | |
| Bo = self.compute_boussinesq(H, eta, wavelength) | |
| hfsi = np.tanh(Bo) | |
| # Compute h/H ratio for instability check | |
| h_H_ratio = eta / H | |
| return { | |
| 'value': float(hfsi), | |
| 'status': self.get_status(hfsi), | |
| 'boussinesq': float(Bo), | |
| 'h_H_ratio': float(h_H_ratio), | |
| 'instability_detected': h_H_ratio > self.instability_onset, | |
| 'stability': self._describe_stability(Bo) | |
| } | |
| def get_status(self, hfsi): | |
| """Get alert status based on HFSI value""" | |
| if hfsi > self.thresholds['safe']: | |
| return 'SAFE' | |
| elif hfsi > self.thresholds['monitor']: | |
| return 'MONITOR' | |
| elif hfsi > self.thresholds['alert']: | |
| return 'ALERT' | |
| else: | |
| return 'CRITICAL' | |
| def _describe_stability(self, Bo): | |
| """Describe stability regime based on Boussinesq parameter""" | |
| if Bo > 10: | |
| return "HIGHLY STABLE - Dispersion dominates" | |
| elif Bo > 1: | |
| return "STABLE - Dispersive propagation" | |
| elif Bo > 0.1: | |
| return "TRANSITIONAL - Weakly nonlinear" | |
| else: | |
| return "UNSTABLE - Nonlinearity dominates" | |
| def compute_breaking_criterion(self, H, eta, wavelength): | |
| """Compute wave breaking criterion | |
| Breaking condition: u_crest ≥ c_wave | |
| Breaking depth: H_break ≈ 1.28·η_max | |
| Returns: | |
| dict with breaking indicators | |
| """ | |
| hfsi_result = self.compute_hfsi(H, eta, wavelength) | |
| h_H_ratio = eta / H | |
| # Breaking imminent when HFSI < 0.40 or h/H > 0.42 | |
| breaking_imminent = (hfsi_result['value'] < 0.40) or (h_H_ratio > 0.42) | |
| # Breaking depth estimate | |
| H_break = 1.28 * eta | |
| return { | |
| 'breaking_imminent': breaking_imminent, | |
| 'h_H_ratio': float(h_H_ratio), | |
| 'instability_threshold': self.instability_onset, | |
| 'hfsi': hfsi_result['value'], | |
| 'estimated_breaking_depth': float(H_break), | |
| 'time_to_break': self._estimate_time_to_break(hfsi_result['value']) | |
| } | |
| def _estimate_time_to_break(self, hfsi): | |
| """Estimate time to breaking based on HFSI value""" | |
| if hfsi > 0.60: | |
| return "> 30 minutes" | |
| elif hfsi > 0.40: | |
| return "15-30 minutes" | |
| elif hfsi > 0.20: | |
| return "5-15 minutes" | |
| else: | |
| return "< 5 minutes - IMMINENT" | |
| def validate_event(self, H, eta, wavelength, observed_breaking=False): | |
| """Validate HFSI against observed data""" | |
| result = self.compute_hfsi(H, eta, wavelength) | |
| breaking = self.compute_breaking_criterion(H, eta, wavelength) | |
| validation = { | |
| 'hfsi': result, | |
| 'breaking_analysis': breaking, | |
| 'validation': {} | |
| } | |
| # Check against observed breaking | |
| if observed_breaking: | |
| predicted_breaking = breaking['breaking_imminent'] | |
| validation['validation']['observed_breaking'] = observed_breaking | |
| validation['validation']['predicted_breaking'] = predicted_breaking | |
| validation['validation']['correct'] = (predicted_breaking == observed_breaking) | |
| return validation | |