Skip to content

Latest commit

 

History

History
180 lines (127 loc) · 6.04 KB

File metadata and controls

180 lines (127 loc) · 6.04 KB

海啸模拟算法改进(Demo / Testing)

免责声明:以下算法只用于法律合规的测试、演示与教育。它不具备真实业务可用的物理精度,不能用于实际防灾决策。

本项目的“模拟地震/海啸”目标更偏向:

  • 可控:用户能指定震中、深度、震级、发震/发报延迟,并按页递进送报。
  • 可解释:给出“为什么这些地区被选中”和“到达时间怎么来的”的直觉解释。
  • 可读:UI 不应一次塞入 60+ 个区域;更需要分层递进、可视化一致。
  • 隔离生产逻辑:不改真实海啸数据源的 ingestion,只在模拟路径运行。

下面是对现有“按最近区域 + 固定数量”方案的升级思路:引入两个可解释的量——到达时间影响强度(高度),用它们驱动区域选择与展示。


1) 距离:Haversine 球面距离

对每个海岸区域(用 centroid 近似)计算震中到区域中心的球面距离:

$$ d = 2R \arcsin\Big(\sqrt{ \sin^2\frac{\Delta\varphi}{2} \cos\varphi_1\cos\varphi_2 \sin^2\frac{\Delta\lambda}{2} }\Big) $$

  • $R \approx 6371,km$
  • $\varphi$ 为纬度(弧度),$\lambda$ 为经度(弧度)

这个距离用于:

  • 排序(近的优先)
  • 传播时间高度衰减的输入

2) 传播速度与到达时间:浅水波近似

深海海啸的相速度可用浅水波近似:

$$ v=\sqrt{g h} $$

  • $g \approx 9.80665,m/s^2$
  • $h$ 为有效水深(m)。在 demo 中可取常数(例如 $4000,m$)。

然后到达时间(相对发震时刻):

$$ t = \frac{d}{v}\cdot s $$

  • $d$:距离(m)
  • $s$:近岸减速因子(例如 $1.15$,表示到岸附近整体更慢)

最终:

$$ T_{arrival} = T_{origin} + t $$

关键点:到达时间应基于发震时刻而不是“本页发报时刻”。否则第 2 页/第 3 页到达时间会被错误“整体推迟”。


3) 影响强度(“最大波高”)的轻量启发式

真实海啸高度需要断层几何、海底形变、地形/水深等数据,这里用一个可调的启发式来满足:

  • 随震级增大而增大
  • 随震源深度增大而减小
  • 随距离增大而衰减

建议形式:

$$ H(d)=H_0(M_w)\cdot e^{-\frac{Depth}{D}} \cdot \frac{1}{1 + \left(\frac{d}{L}\right)^p} $$

其中:

$$ H_0(M_w) = H_{ref}\cdot 10^{0.5(M_w - M_{pivot})} $$

一组对 UI 友好的默认参数(可后续暴露为高级设置):

  • $H_{ref}=0.5m$
  • $M_{pivot}=6.8$
  • $D=50km$
  • $L=200km$
  • $p=1.3$

这会带来两个很实用的性质:

  • 更大的地震影响更广(更多区域满足阈值)
  • 更高等级(更高阈值)影响更“集中”(区域数量减少)

4) 用阈值驱动“区域自动选择”,保证分层递进

为了满足你提到的体验目标:

小警报覆盖范围应该比大警报更广(至少不应更小)

最直接的做法是:按等级设置高度阈值,选择 H >= threshold 的区域:

  • 注意报(status=1):$H \ge 0.3m$
  • 警报(status=2):$H \ge 1.0m$
  • 大警报(status=3):$H \ge 3.0m$

因为阈值单调递增,区域集合天然满足:

$$ \text{Areas}_{1} \supseteq \text{Areas}_{2} \supseteq \text{Areas}_{3} $$

空集回退(重要)

当震级较小/很深导致所有区域都低于阈值时,仍然需要“有内容可演示”。因此加一个回退:

  • 若筛选为空,按距离取最近 N 个区域(N 随等级递减,确保层次递进仍成立)

例如:

  • JMA:注意报 18、警报 12、大警报 6
  • NMEFC:注意报 5、警报 4、大警报 3(总共就 5 个)

再加一个 maxAreas 上限(例如 JMA 25),避免 UI 列表过长。


5) 代码实现(已落地到仓库)

实现文件:

  • src/utils/TsunamiSimulation.js

核心函数:

  • haversineDistanceKm(...)
  • calcTsunamiSpeedMs(h)
  • estimateTsunamiTravelTimeSec(d, h, slowdown)
  • estimateTsunamiWaveHeightM({Mw, depthKm, distanceKm, ...})
  • buildAutoTsunamiAreaImpacts(...)

关键实现片段(与公式一一对应):

// v = sqrt(g*h)
export const calcTsunamiSpeedMs = (oceanDepthM = 4000) =>
  Math.sqrt(9.80665 * Math.max(1, Number(oceanDepthM) || 4000))

// t = d/v * slowdown
export const estimateTsunamiTravelTimeSec = (distanceKm, oceanDepthM = 4000, slowdown = 1.15) => {
  const distM = Math.max(0, Number(distanceKm) || 0) * 1000
  return (distM / calcTsunamiSpeedMs(oceanDepthM)) * Math.max(1, Number(slowdown) || 1)
}

// H(d)=H0(Mw)*exp(-depth/D) / (1 + (d/L)^p)
export const estimateTsunamiWaveHeightM = ({
  magnitudeMw, depthKm, distanceKm,
  sourceHeightM = 0.5, magnitudePivot = 6.8,
  depthDecayKm = 50, distanceScaleKm = 200, distancePower = 1.3,
}) => {
  const mw = Number(magnitudeMw)
  const dep = Math.max(0, Number(depthKm) || 0)
  const dist = Math.max(0, Number(distanceKm) || 0)
  const h0 = sourceHeightM * 10 ** (0.5 * (mw - magnitudePivot))
  return h0 * Math.exp(-dep / depthDecayKm) / (1 + (dist / distanceScaleKm) ** distancePower)
}

模拟入口已改为使用这些输出:

  • src/components/components/MockEew.vue

关键行为变化:

  • 到达时间基于 originTime + travelTime(不再基于“发报时间 + 固定分钟数”)
  • 自动区域选择由“固定数量最近点”升级为“高度阈值筛选 + 空集回退”

6) 进一步可选增强(按投入/收益排序)

  1. 增加“是否近海”门槛:通过“离海岸线距离”粗略判断内陆震是否生成海啸(需要 coast polyline / landmask 数据)。
  2. 区域方向性(扇形):按震中到区域的方位角加入 direction factor,模拟能量主要沿海沟方向传播(需要断层走向/经验假设)。
  3. 离散 travel-time 表:离线预计算(网格化)到达时间表(比实时计算更贴近真实地形/路径,但需要数据与构建脚本)。
  4. 多级区域混合:同一份报文中允许同时出现注意报/警报/大警报区域(更接近真实 JMA),但会让“本页等级”语义变复杂,需要 UI 上明确说明“本页为最大等级”。