Pyrocko + PSGRN/PSCMP小问题

1.先看看你的脚本,然后诊断 config 文件的问题。问题很明确------YAML 解析 config 文件时在 earthmodel_1d 块标量那里报错。大概率是 |2 缩进指示符和实际内容缩进不匹配。

让我先下载脚本看看,然后直接诊断:fomosto 不在当前环境,但不影响修脚本。问题就是 YAML config 文件里的 |2 块标量缩进指示符。

分析

复制代码
earthmodel_1d: |2 # ← |2 表示内容缩进为 2 格  0. 3.5 2.0 ... # ← 实际只缩进 1 格!矛盾 

|2 告诉 YAML 解析器"内容行缩进了 2 个空格",但实际内容只缩进 1 个空格。解析器找不到预期的缩进层级,后面的 tabulated_phases 就被当成了映射的 key,导致 did not find expected key

修复方案:|2 改成 |(自动检测缩进),同时确保内容行至少缩进 1 格。

修正后的脚本:修正完成。改动很小但关键:

改动对比:

复制代码
- earthmodel_1d: |2 # ← |2: 声明内容缩进2格,但实际只有1格 + earthmodel_1d: | # ← |: 自动检测缩进,兼容性最好 ``  **原理:** - `|2` = "内容缩进了 2 空格,请剥掉 2 空格" → 实际内容只缩了 1 空格 → 解析器读到 `0.` 变成 `.3.5...` → YAML 炸了 - `|` = 自动检测最小缩进 → 正确剥掉 1 空格 → 内容完整保留  在你的服务器上运行修正版:  ```bash cd ~/202402351 bash run_experiment_fixed.sh 

另外如果你跑完 fomosto ttt 之后 fomosto build 报错说找不到 psgrn/pscmp 二进制,确认一下:

复制代码
which psgrn 2>/dev/null; which pscmp 2>/dev/null ``  这俩需要从 `fomosto-psgrn-pscmp` 编译安装后才能用。 

2.好,YAML 解析通过了,现在是新问题:

根因: begin 阶段定义里有 Pv_(cmb)p,需要找 CMB(核幔边界,~2891km 深),但你的地球模型只到 200km。cake 模块找不到 cmb 不连续面就炸了。

对于 PSGRN/PSCMP(近场静态位移/震后变形),根本不需要引用 CMB 的远震震相。直接简化 phase 定义:改动说明:

复制代码
- definition: p,P,p\,P\,Pv_(cmb)p # 引用 CMB (2891km),模型只到 200km → 爆炸 + definition: '0.0' # 用固定时间值,不依赖深层不连续面 

begin/end 的作用: 它们只定义格林函数时间窗口的边界(最早到达、最晚到达),对近场 PSGRN/PSCMP 来说用固定秒数完全够用。

在你服务器上重新跑:

复制代码
cd ~/2024023551 bash run_experiment_fixed.sh 

3.如果后面 P/p/S/s 也报类似错(它们依赖特定射线路径),也可以简化掉,只留 beginend 就够了。PSGRN/PSCMP 计算的是近场静态/准静态位移,不是动态波形,震相表没那么关键。

definition: '0.0' 被 Pyrocko 当成阶段名查找而非常量时间,还是触发射线追踪。换个策略------不再整个覆盖 config,只修补 earthmodel_1d 那一行,保留 fomosto init 自动生成的默认 phase 定义(它自带适合 psgrn_pscmp 的配置):等等,Pyrocko 的 YAML 有自定义 tag(!pyrocko.gf.meta.ConfigTypeA),yaml.safe_load 解析不了。去掉验证,同时保留你需要的自定义参数(深度范围、距离范围等):同时把 extra/psgrn_pscmp 的替换加回来(这个文件没有 YAML 问题):确认最终脚本:策略变了: 不再整个覆盖 config(之前手动写 phase 定义总出问题),改为:

  1. 1.fomosto init 生成默认配置(自带正确的 phase 定义)
  2. 2.用 Python 精准替换 earthmodel_1d(修掉 |2
  3. 3.regex 更新深度/距离参数
  4. 4.单独写 extra/psgrn_pscmp

在服务器上跑:

复制代码
cd ~/202402351 bash run_experiment_fixed2.sh 

如果 fomosto tt 还是报错,把完整输出贴过来。另外跑之前确认一下:

复制代码
which psgrn && which pscmp 

确保 PSGRN/PSCMP 的二进制已经编译安装好了,不然 fomosto build 也会挂。

4.两个独立的问题:

问题 1: PSGRN --- wrong no of distance samples! 距离采样参数与 PSGRN 内部期望不匹配。你用 regex 覆盖了 fomosto init 的默认 distance 值,但默认值已经是 PSGRN 兼容的。

问题 2: PSCMP --- NSCMAX defined too small! PSCMP Fortran 二进制编译时定义了最大快照数上限(通常 100-200)。你配置了 tmax_days: 3650 → 3650 个每日快照,远超上限。

修复策略: 不要覆盖 distance 参数(保留默认),同时减少震后时间窗口和快照数。改动总结:

复制代码
# extra/psgrn_pscmp - tmax_days: 3650.0 # 10年 → 3650个快照 → 超过 NSCMAX + tmax_days: 100.0 # 100天 → ~100个快照 → 安全  # config(Python部分) - 同时覆盖 distance_min/max/delta # PSGRN 不兼容 + 只覆盖 source_depth + receiver_depth # 保留默认 distance 

在服务器上跑:

复制代码
cd ~/202402351 bash run_experiment_fixed2.sh 

如果后续你想覆盖 10 年的震后变形,有两个方案:

  1. 1.增大 sampling_interval(比如改成 30.0,每月一个快照)
  2. 2.重新编译 PSGRN/PSCMP ,调大 NSCMAX 参数(在 Fortran 源码的 pscglob.h 里)

5.问题很清楚。从日志看有两个级联错误:

根因:Step 1 PSGRN 失败

复制代码
STOP Error: wrong no of distance samples! 

PSGRN 的格林函数生成直接挂了,所以 Step 2 PSCMP 找不到格林函数文件(uz.ep 不存在)也跟着崩。

问题出在哪

你的脚本把 distance_min 改成了 1000.0(单位是米),但 PSGRN 要求距离网格从 0 开始distance_min 应该设为 0.0,让第一行(零偏移距)的格林函数被计算。

同时注意单位------fomosto config 里距离和深度的单位通常是米(m),不是千米。

修复

脚本中这部分 sed 替换:

复制代码
config = re.sub(r'distance_min:\s*[\d.e+-]+', 'distance_min: 1000.0', config) 

改成:

复制代码
config = re.sub(r'distance_min:\s*[\d.e+-]+', 'distance_min: 0.0', config) 

另外也检查一下 distance_delta: 5000.0 是否和 PSGRN 兼容------某些版本要求 delta 必须是 2 的幂次或特定值。如果还是报 wrong no of distance samples,试试把 distance_delta 改成比如 1000.02000.0,同时保持 distance_max / distance_delta 为整数。

要我直接改好脚本给你吗?

6.Now I see the full picture. Let me get one more piece --- the default StoreConfig distance values for psgrn_pscmp.2008a:OK now I have the full picture. The default StoreConfig for psgrn_pscmp.2008a uses distance_max=50km, distance_delta=1km. The real problems are:Let me write the definitive fix. I've traced through the full pyrocko source --- the real issue is that the regex-based earthmodel_1d replacement corrupts the YAML config, causing fomosto to fall back to bad internal defaults which trigger the "wrong no of distance samples" error. The fix is to use Pyrocko's own API instead of regex surgery.Let me also verify the script is correct by checking indentation:找到根因了。问题出在 extra/psgrn_pscmp 里的 gf_distance_spacing: -1.0

看 pyrocko 源码:

复制代码
if cg.gf_distance_spacing is None: # 如果是 None,用 StoreConfig 的值  gf_distance_spacing = fc.distance_delta else: # 否则用它 * km  gf_distance_spacing = cg.gf_distance_spacing * km # -1.0 * 1000 = -100 n_steps_distance = int((distance_max - distance_min) / gf_distance_spacing) + 1  # int((5000 - 0) / -1000) + 1 = -49 ← 负数! 

YAML 里写的 -1.0 意思是"用默认",但 Pyrocko 不这么理解------它把 -1.0 当作实际值,乘以 km=1000 得到 -1000,算出来 负的步数 ,PSGRN 当然报 wrong no of distance samples

修复:extra/psgrn_pscmp删掉 gf_distance_spacinggf_depth_spacing,让它们保持 None(optional 字段不写就是 None)。脚本写好了:run_experiment_fixed3.sh

核心修复:找到真正的 bug

问题在 extra/psgrn_pscmp 里的这两行:

复制代码
gf_depth_spacing: -1.0 gf_distance_spacing: -1.0 ``  原意是 `-1.0` = "用默认值",但 Pyrocko 不这么理解。看源码:  ```python if cg.gf_distance_spacing is None: # None → 用 StoreConfig 的 distance_delta  gf_distance_spacing = fc.distance_delta else: # 非 None → 直接当值用  gf_distance_spacing = cg.gf_distance_spacing * km # -1.0 × 100 = -1000  n_steps = int((50000 - 0) / -1000) + 1 # = -49 ← 负数! 

PSGRN 收到 负的步数 ,当然报 wrong no of distance samples!

改动汇总

| 改动 | 原因 | |------| | 删 fomosto ttt | psgrn_pscmp 不需要走时表 | | 删 gf_distance_spacing / gf_depth_spacing | 根因修复 --- optional 字段不写 = None | | 删 observation / rectangular_fault_size_factor 等冗余字段 | 都有默认值,不需要显式写 | | source_depth_min 改 1000m (10km) | 避免浅层边界插值问题 | | 保留 distance 默认值 | 默认 0-50km, delta=1km 已验证兼容 | | 构建前打印关键参数 | 出问题方便排查 |

7.问题找到了。StaticTarget 继承自 MultiLocation,它接受的是 lats/lons 数组 ,不是标量 lat/lon

复制代码
# 你的代码(错) gf.StaticTarget(lat=31.06, lon=103.36, ...) # ← lat, lon 不是合法参数  # 正确写法 gf.StaticTarget(lats=np.array([...]), lons=np.array([...]), ...) 

修复的核心改动:

改动 原因
StaticTarget(lats=lats_flat, lons=lons_flat, ...) API 要求 1D numpy 数组
一个 StaticTarget 包含全部网格点 不再创建 14641 个单独 target
结果用 static_result.result['displacement.n'] StaticResult.result 是 dict,key 是 'displacement.n'/'displacement.e'/'displacement.d'
结果 .reshape(n_lats, n_lons) 把 1D 结果重排回 2D 网格

脚本已保存到 compute_deformation_fixed.py。在你那边复制到 /home/lx/2024023551/ 目录下跑就行:

复制代码
cd /home/lx/2024023551 python3 compute_deformation_fixed.py 

8.找到了!错误信息非常明确:

复制代码
ValueError: A value (7341.013570101181) in x_new is below the interpolation range's minimum value (10000.0). 

Pyrocko 把断层离散化成子补丁时,有些子补丁的中心深度到了 7341m(7.3 km) ,但 store 的 source_depth_min=10000m,查不到格林函数 → NaN。

根因:source_depth_min 设太高了。改成 0 即可。 问题确认了:Pyrocko 离散化断层时,子补丁中心深度到了 7341m ,但 store 只有 source_depth_min=10000m 的格林函数 → 查不到 → NaN。

修复:source_depth_min1000 改成 0

复制代码
cd psgrn_pscmp_experiment bash run_experiment_v6.sh python3 compute_deformation_fixed.py 

改动对照:

| 参数 | v5 | v6 | |------|----| | source_depth_min | 1000.0 (10 km) | 0.0 (地表) | | 其他 | 不变 | 不变 |

v6 的 source_depth_delta=5000 意味着从 0 到 50 km 每 5 km 一层,共 1 层(比 v5 的 9 层只多 2 层),建库时间增加不多。

补充

GF store(格林函数库) 就是一个预计算好的"地震响应查询表"

通俗理解

想象你要查"地震源在某个深度、距离你多少公里时,地面会怎么动"。

没有 GF store 的话: 每次都要从头解弹性波方程,算一次要几小时甚至几天。

有 GF store: 提前把各种"源深度 × 距离"组合的响应全算好,存成文件。之后查表就行,几秒出结果。

具体结构

复制代码
my_psgrn_pscmp_gfs_myanmar/ ├── config ← 配置:深度范围、距离范围、地球模型 ├── psgrn_functions/ ← 核心:PSGRN 算出的格林函数文件 │ ├── gd.cl ← 不同分量 (cl=cosine-longitudinal, ...) │ ├── gd.ds │ ├── gr.ss │ └── ... ├── index ← 索引,快速定位数据 └── extra/psgrn_pscmp ← PSGRN/PSCMP 特有配置 

config 里定义了网格:

复制代码
source_depth: 0, 5, 10, 15, 20, ... 50 km (11层) distance: 0, 3, 6, 9, ... 500 km (167层) 

每一组 (深度, 距离) 都存了一组格林函数。

计算流程

复制代码
断层参数 (strike, dip, rake, slip, ...)  ↓ Pyrocko 把断层离散成小补丁  ↓ 对每个补丁 → 查 GF store → 得到该补丁对每个网格点的响应  ↓ 所有补丁的响应叠加 = 最终位移场 

为什么之前一直 NaN

问题 原因
distance_max=50km store 只存了 50km 内的数据,远处查不到
source_depth_min=10km Pyrocko 离散化后有子补丁到 7.3km,查不到

就像字典里没收录的字,查不到就返回 NaN。

类比

概念 类比
GF store 乘法口诀表
建库 (fomosto build) 背口诀表(费时间)
查表计算 (engine.process) 遇到 7×8 直接查表秒出 56
distance_max 太小 口诀表只背到 5,问 7×8 就不会了

INDEX_OUT_OF_BOUNDS --- 300 km 长的断层,最远端的子补丁到接收器的距离超过了 500 km store 上限。

比如断层一端离震中 150 km,网格角点离震中 ~450 km,加起来 600 km > 500 km。

两个方案选一个:

方案 A(推荐):不重建 store,把断层拆成多段

把 300 km 的断层拆成 3 段 × 100 km,每段独立计算再叠加。300 km 拆成 3×100 km 段,每段最远子补丁离中心 ~50 km。震中最远的段偏移 ±100 km,加上网格对角 ~450 km,最大源-接收器距离 ≈ 100+50+450 = 600 km,还是有点紧。

稳妥起见,20 km 断层 + 现有 50km store 刚好够用。改成 2 段:分析一下距离:

  • 2×100 km 断层,中心在震中,向南北各延伸 100 km
  • 网格角点离震中最远 ~454 km
  • 角点到最近 断层子补丁:网格南角到断层南端 ≈ 454 - 10 = 354 km ✅ < 500 km

之前用的是离震中的 距离(454+100=554),但 Pyrocko 实际查的是接收器到最近子补丁的距离。2 段 × 100 km 应该够了。

直接跑:

python3 compute_deformation_myanmar.py

3.

Pyrocko 用的是源中心到接收器的距离(不是最近子补丁)。段中心偏移 -100km 后,网格角点到段中心的距离就 ~490 km,逼近 500 km 上限,离散化后就超了。

最简单的修复:缩小网格到 ±2.5°(对角 ~380 km),不用重建 store。 你的文件名是 1.2.py,我改的是工作区版本。在你的文件里直接改这两行:

`` GRID_LON_RANGE = 2.5 GRID_LAT_RANGE = 2.5

复制代码
然后跑: ```bash python3 compute_deformation_myanmar1.2.py 

或者如果你想保留 ±3° 的大范围,重建 store ,把 distance_max 改大到 7000选一个:

| 方案 | 操作 | 网格范围 | |------|----------| | A(快) | 改你文件里两行 GRID_LON_RANGE = 2.5 / GRID_LAT_RANGE = 2.5 | ±2.5° ≈ 280km | | B(完整) | 重建 store,把 build 脚本里 distance_max50000 改成 7000,再跑 bash build_gf_myanmar.sh | ±3° ≈ 330km |

推荐先试 A,马上能出结果。

4.问题清楚了。来看两个文件的关键问题:

  1. 1.build_gf_myanmar.sh : distance_max=500000 (500km) --- 但断层本身长 500km + 网格延伸,远端距离会超出
  2. 2.compute_deformation_myanmar.py: 只有 2 段 × 100km = 200km,但你说实际断层 ~500km

直接改:验证一下修改结果:build_gf_myanmar.sh 里的实际参数还是 5000,需要改:现在确认最终修改都正确:改好了。总结一下修改内容:

修改汇总

build_gf_myanmar.sh

| 参数 | 改前 | 改后 | 原因 | |------|------| | distance_max | 500 (500km) | 70000 (700km) | 500km 断层 + 网格余量,最远段到网格边缘可达 ~440km |

compute_deformation_myanmar.py

| 参数 | 改前 | 改后 | 原因 | |------|------| | N_SEGMENTS | 2 | 5 | 匹配 ~500km 断层总长 | | GRID_LON_RANGE | 3.0° | 4.0° | 500km 断层需要更大网格 | | GRID_LAT_RANGE | 3.0° | 4.0° | 同上 | | GRID_SPACING | 0.05° | 0.025° | 大断层需要更高分辨率 |

使用步骤

复制代码
# 1. 把修改后的文件复制到你的项目录 cp build_gf_myanmar.sh /home/lx/202402351/psgrn_pscmp_experiment/ cp compute_deformation_myanmar.py /home/lx/202402351/psgrn/  # 2. 重新构建 GF store(必须重新构建!distance_max 从 50→70km) cd /home/lx/2024023551/psgrn_pscmp_experiment bash build_gf_myanmar.sh  # 3. 运行变形计算 cd /home/lx/202402351/psgrn python3 compute_deformation_myanmar.py 

⚠️ 必须重新构建 GF store ,因为 distance_max 从 50km 改到了 70km,旧的 store 数据不够。构建时间会比之前长一些(网格点更多)。

5.还是同样的错误。问题不是简单的距离超限,让我深入分析 pyrocko 的源码逻辑。

核心在于:pyrocko 的 RectangularSource 会自动将断层离散化成许多子断层(sub-patches),子断层点的坐标 会超出名义中心位置。再加上 multilinear 插值需要查询相邻网格点,所以即使中心距离在范围内,子断层边缘也可能越界。

让我做一个更彻底的修复:找到根本原因了!让我重新计算距离:

第1段中心 (20.21°N) 到网格西北角 (91.94°E, 26.01°N): dlat = 5.8° → 64 km dlon = 3.97° (at avg lat ~23.1°) → ~409 km 实际距离 ≈ sqrt(644² + 409²) ≈ 763 km !! ← 超过 700km 了!

之前只算了正北方向(644km),没算斜角!距离_max=100km 才够。已经在 build_gf_myanmar.sh 里改好了。现在给 compute 脚本加上诊断和安全处理:Now add a distance diagnostic before each engine.process call:验证最终文件:好,所有修改完成。总结这次修复:

根本原因

之前只算了「正北方向」的距离 644km,但实际最远距离是到网格斜角

text

复制代码
text
复制代码
第1段(20.21°N) → 网格西北角(91.94°E, 26.01°N):  √(64² + 409²) ≈ 763 km > 700km ✗ 

最终修改

| 文件 | 参数 | 改前 | 改后 | |------|------| | build_gf_myanmar.sh | distance_max | 500km | 100km | | build_gf_myanmar.sh | distance_delta | 3km | 5km (大范围用粗网格,减少存储量) | | build_gf_myanmar.sh | source_depth_delta | 5km | 2.5km (更精细) | | compute_deformation_myanmar.py | 诊断 | 无 | 每段打印实际最大距离 |

使用

bash

复制代码
bash
复制代码
# 1. 复制到你的目录 # 2. 必须重新构建 GF store! cd /home/lx/2024023551/psgrn_pscmp_experiment bash build_gf_myanmar.sh  # 3. 运行 cd /home/lx/2024023551/psgrn python3 compute_deformation_myanmar2.py 

这次构建会更久(distance_max 从 50→100km,点数翻倍),但跑起来后每段会先打印实际距离,方便确认是否在 store 范围内。

相关推荐
大家的林语冰1 小时前
TypeScript 6 官宣,JS “最后之舞“,版本升级踩雷指南
前端·javascript·typescript
英俊潇洒美少年1 小时前
react useDeferredvalue和useTransition的讲解
前端·react.js·前端框架
爱学习的程序媛1 小时前
【WebRTC】呼叫中心前端技术选型:SIP.js vs JsSIP vs Verto
前端·javascript·typescript·音视频·webrtc·实时音视频·web
Amumu121382 小时前
Js: ES新特性(一)
开发语言·前端·javascript
scofield_gyb2 小时前
Redis 6.2.7安装配置
前端·数据库·redis
木斯佳2 小时前
前端八股文面经大全: 蓝色光标前端一面OC(2026-03-23)·面经深度解析
前端·面试·vue·校招·js·面经
Highcharts.js2 小时前
Highcharts for Python|用 Pythonic 的方式构建AI数据可视化图表
前端·人工智能·python·信息可视化·数据科学·highcharts·ai可视化
是罐装可乐2 小时前
SPA首屏接口过多导致卡顿?一套前端请求调度方案彻底解决
前端·性能优化·spa·前端架构·请求队列
子豪-中国机器人2 小时前
python AI自动化
java·前端·python