Files
DCTS/docs/convergence_analysis_2026_08_08.md
T
fmq 43b82b1ae2 feat(all): 物理正确性五重硬门槛、输入文件结构化与 fort.55 错位修复、conv 诊断 DB 化与阶段归因修复、ORELAX 收敛修复与导入工具下线
物理正确性校验体系(common/conv_check.rs +494 行)
- 新增 5 类硬门槛:能量守恒(.6)、温度结构(.7)、emflux 积分校验(.emflux,含全 NaN 判失败)、假收敛排查(itek 轨迹首末比)、b 因子合理性(.bfac)
- runner 在 TLUSTY 阶段结束后执行全部校验,任一失败判 final_converged=false
- GridConfig 新增 8 个可配阈值,经 scheduler→executor→runner 全链路透传

输入文件配置结构化重构(config.rs +1453 行)
- TlustyInput 拆为 dot5/nst 分层结构,字段名严格映射 tlusty208.f READ 语句;SynspecInput 重构为 9 个 Fort55Line 子结构体
- 移除 ChainStep.metals 字段,元素集改由 dot5.atoms/ions 显式声明(gen_input5/nst_writer 同步重写为三源融合 / 分层覆盖)
- fort.55 修复行结构 bug:补全分子表行(7→9 行),IDSTD 50→0 错位修正(影响全部光谱线强归一化,需重算 SYNSPEC 阶段)

conv 诊断 DB 化与阶段归因修复(server)
- 单点详情 conv 面板从磁盘 conv.json 改读 DB grid_points.summary_json;grid_points 新增 summary_json/last_elapsed_sec 两列(旧库幂等 ALTER)
- record_task_report 阶段归因列加 CASE 守卫 + clear_synspec 对称处理,修复 synspec-only/TLUSTY-only 重跑污染统计
- 新增 summary_merge.rs 点级增量合并,避免重跑覆盖诊断字段

收敛性 ORELAX 修复与 seed_chain 可配(sdB_cno.yaml + node)
- nl 阶段加 orelax=0.5、seed_nc 加 orelax=0.3,阻尼中温区 relc 振荡发散
- seed_chain 块可配,executor 优先采用用户配置而非内置默认链

导入工具下线
- 删除 import_results 客户端工具及 Windows 推送脚本;移除 /admin/import_seed 端点
- 改为服务端临时 migrate_conv 端点(扫 conv.json 增量合并入库,迁移后可删)

文档与分析
- 新增 1305 失败点根因分析、fort.14 全 NaN 物理含义分析两份深度文档
- spectrum_correctness_analysis 两次修订标注已修复项;fetch_results.sh 修 trap RETURN 的 set -u 报错
2026-08-09 12:09:48 +08:00

13 KiB
Raw Blame History

TLUSTY 收敛性分析与修复(2026-08-08 ~ 08-09

本文完整记录了对 sdB_cno 工作流 1305 个失败网格点的根因分析、实测验证过程、 以及最终落地的代码改动。所有 tlusty/synspec 实测均使用生产二进制 dcts/assets/tlusty_staticmd5=14cd144b...),而非开发目录的 tlusty/tlusty.exe md5=9b249e00...)——两者编译版本不同,数值行为有差异。


1. 数据库分析(dcts.db

1.1 真实规模(纠正初期统计错误)

维度 数量
grid_points 总数 92169 Teff × 4 logg × 4 logHe × 4 logC × 4 logN × 4 logO
completed 791185.8%
failed 130514.2%

初期曾误把 tasks 表的 12344 行(含重试)当作网格点总数,得出"60% 失败"的错误结论。 实际上 tasks 表每个失败点因 cold_run→seed_step 回退产生多行(attempt_count 中位数 3-4), 且 3876 个早已收敛的点根本不在 tasks 表里(skip_converged 跳过)。

1.2 失败点参数空间分布

按 Teff × logHe(每 cell 256 点):

Teff logHe 失败/256 失败率 备注
60000 -4 148 58% 主体失败区
55000 -4 136 53% 主体失败区
60000 -2 126 49%
55000 -2 128 50%
60000 0 101 39%
50000 -2 83 32%
20000 2 89 35% 次要失败簇(富氦+低logg
25000~45000 各 He <16% 大多健康

两大失败簇:

  1. 高温贫氦区(55-60kK / He=-4,-2,0733 个,占失败总数 56%
  2. 20kK 富氦区(He=2 / logg=6.0-6.589 个(35%

按 logglogg=5.0 最难(661 个,占 51%),logg=6.5 最好(139 个)。

按 CNO 丰度(55000K/He=-4 cell 实测)CNO 越富失败率越高——

cno_sum ≤ -11:  失败率 33%   ← 贫金属,相对可收敛
cno_sum -7~-8:  失败率 92%
cno_sum ≥ -6:   失败率 100%  ← 最富金属,物理不可收敛

1.3 失败点的策略执行情况

所有 1305 个失败点均执行过 cold_run + seed_steppending_strategies 已空,策略穷尽)。 seed_step 的实际结果:

  • seed 高 relc 发散(≥100):1461 条 task 记录
  • seed NaN 发散:1091 条
  • seed 下载/IO 失败:34 条
  • seed 低 relc<100):14 条

2. 可能的失败原因(分析假设)

2.1 ORELAX 未设置 → 满牛顿步长振荡

所有 salvage 的 .nc.nst / .seed_nc.nst 只有 2 行,无 ORELAX

ND=50,NLAMBD=3,VTB=2.,ISPODF=1,DDNU=50.,CNU1=6.,CHMAX=0.001,NITER=10
IELCOR=-1

tlusty 源码 tlusty208.f:844IF(ORELAX.EQ.0.) ORELAX=UNUN=1.0),即默认 100% 牛顿步长。 满步长在硬网格点易振荡发散。

2.2 Ng/Kantorovich 加速器把解推飞

tlusty 默认 IACC=7Ng 加速从 iter7 开始)。fort.6 日志显示 KANTOROVICH acceleration: ITER 5-9 后 relc 从几十跳到上万——加速器在不稳定解上放大了误差。

2.3 灰 LTE 起点离高温 NLTE 解太远

55kK 点 cold nc iter1 relc 就高达 12.7-21.6(对比正常点 0.06-0.2),LTE 灰大气作为 NLTE 迭代 起点在高温下偏差太大。

2.4 富金属方向 seed_step 发散(物理原因)

seed_finder.rs 记录:increasing metals from a poor seed adds UV line-radiation driving that breaks radiative equilibrium(贫方向成功率 42-54%,富方向仅 3-11%)。

2.5 synspec 在 Balmer edge3645Å)数值发散

459 个点 tlusty 收敛了(max_relc<0.001)但 synspec 产出的 spec 在 3645Å(H n=2 bound-free edge 突然变 NaN。机制:ST0(I)=ET(IJ,I)/CH(IJ,I)synspec54.f:12990),表层 NLTE 粒子数为 0 导致 0/0=NaN。

2.6 IACC≤4 被源码强制改 7

tlusty208.f:1928IF(IACC.LE.4) IACC=7。设 IACC=0 无法禁用 Ng 加速器。


3. 实测验证(生产二进制 tlusty_static

3.1 关键教训:二进制选择

初期所有测试误用了 tlusty/tlusty.exe(开发版,Apr 2025)而非生产版 dcts/assets/tlusty_staticJul 2025)。 两者 md5 不同,数值行为不同 (如 55kK 点 iter1 relctlusty.exe=12.7salvage 记录=254)。 以下结论均以生产二进制为准,凡 tlusty.exe 结果均已标注作废。

3.2 部分读取陷阱

tlusty 运行中读取 fort.9 会得到不完整迭代的 relc(如 iter2=0.003 看似收敛), 但完整跑完后该迭代实际 relc 可能是 18700。所有结论必须等运行完成后才可靠。 本会话多次因部分读取得出错误中间结论,最终以完整运行结果为准。

3.3 测试点 At20000_g5.0_he-4_c-4_n-2_o-220kK,次要区)

注意:此点非主体失败区代表(20000/He=-4 cell 仅 2/256 失败),结论代表性有限。

配置 iter1 最终 iter fort.7 NaN 结论
default ORELAX=1.0tlusty.exe 0.217 iter7=NaN 4349 发散
ORELAX=0.3, NITER=10tlusty.exe 0.217 iter10=1.33 0 ⚠️ 非NaN但未收敛
ORELAX=0.1, NITER=15tlusty.exe 0.217 iter15=0.556 0 ⚠️ 振荡但无NaN
defaulttlusty_static 0.217 iter6=0.00672 0 收敛中

结论:此点在生产二进制下 default 直接收敛中。salvage 标记失败可能是 seed_nc 阶段 失败或旧二进制偶然发散。

3.4 测试点 Bt55000_g5.0_he-4_c-1_n-2_o-255kK,主体失败区)

配置 iter1 最终 iter fort.7 NaN 结论
default N30 12.7 iter10=1.95e8 0 发散
ORELAX=0.05 N50 12.7 iter10=9.9e11 0 发散
ORELAX=0.01 N50 12.7 iter9=1.05e6 0 发散
ORELAX=0.005 N50 12.7 iter10=1.95e8 0 发散
ITEK=-50 N50(禁Ng 12.7 iter11=NaN 2047 更糟
IACC=0(禁加速) 12.7 iter5=3.89e8 0 无效(源码强制 IACC=7
seed_nc default(收敛邻居种子) 19.5 iter8=1.28e8 0 富方向发散
seed_nc IACC=0 19.5 iter8=1.28e8 0 IACC=0 无效

结论55kK 主体失败区,所有 nst 参数组合都发散。iter1 relc=12.7(灰 LTE 起点太远), Ng 加速器 iter5-7 把解推飞。富方向 seed_step(ΔlogC=+2iter3 外层深度爆炸到 18700。 这是物理极限,非参数可解。

3.5 测试点 Ct25000_g5.0_he-4_c-4_n-4_o-325kK,中温区)

配置 最终 iter fort.7 NaN 结论
default N15 iter10=4.72e-6 0 收敛
ORELAX=0.3 N30 iter10=3.92e-6 0 收敛

结论:25kK 失败点在生产二进制下 default 和 ORELAX=0.3 都收敛了。 此点 salvage 标记失败是旧二进制偶然发散——用新代码重跑即可救回。

3.6 测试点 Dt45000_g6.0_he-4_c-1_n-3_o-345kK,中温区)

配置 iter1 最终 iter fort.7 NaN 结论
default N15 21.6 iter6=2.93e7 0 Ng 加速爆炸
ORELAX=0.3 N30 21.6 iter16=2.81e13 0 延迟但最终发散

结论45kK 点 ORELAX 无法阻止最终发散,但 default 和 ORELAX=0.3 全程 0 NaN。 (注:中间曾观察到 iter10=0.000335 的"收敛"假象,证实是部分读取 artifact。)

3.7 测试点 Et20000_g6.5_he2_c-4_n-3_o-420kK 富氦高重力簇)

conv.json 显示 seed_nc relc=0.0115(接近收敛),但 nl 爆炸到 5.32e16。 nc 阶段 default 和 ORELAX=0.3 轨迹接近(iter4: 1.31 vs 1.27),问题在 nl 阶段。

3.8 synspec Balmer edge NaN 测试(t50000_g5.0_he-4_c-2_n-3_o-2

tlusty 收敛(max_relc=0.000247),但 synspec spec 在 3645.88Å(Balmer limit 变 NaN 前 87750 行正常,之后全 NaN313695 行)。

配置 第一个 NaN 波长 spec NaN 行数 结论
旧版 fort.55IDSTD=50 3645.54Å 30 万+
新版 fort.55IDSTD=0 3645.88Å 313695 IDSTD 无效
ICONTL=1(纯连续谱) 3645.88Å 313695 连续谱 H bf 本身发散

结论Balmer edge NaN 不是 fort.55 配置问题,是 synspec 连续谱氢 bound-free 在表层 NLTE 粒子数为 0 时产生 0/0=NaN。大气 depth-1 粒子数块确认含大量 0.000000E+00


4. 综合结论

4.1 失败分类(1305 个 grid failed 点)

类别 数量 可救性
高 relc 严重发散(≥100 746 高温区不可救;中温区可能重跑即救
NaN 物理发散 527 旧二进制 NaN → 新二进制可能改善
未知 24
接近收敛(relc<1 3 可救
中度发散(1-100 5 可能可救

另:459 个点 grid 状态为 completedsynspec 失败但 tlusty 收敛),不在 1305 之列。

4.2 什么能救、什么不能

能救(预计 ~150-300 点):

  • 中温区(20-45kK)旧二进制偶然失败的点——用新代码 tlusty_static 重跑即可
  • 20kK/He2/g6.5 簇 nl 阶段振荡发散——ORELAX=0.5 阻尼 nl 的 Newton 步长振荡
  • 部分旧二进制 NaN 发散——新二进制数值更稳定(55kK 点全程 0 NaN vs 旧版 NaN

不可救(~600-800 点):

  • 55-60kK/贫氦/富CNOcno_sum≥-6)——灰 LTE 起点离 NLTE 解太远,所有参数组合发散
  • synspec Balmer edge NaN——表层 NLTE 粒子数为 0 导致连续谱 H bf 数值发散

4.3 无效方案(实测验证,勿重复)

方案 测试结果 原因
ORELAX 对 55kK cold nc 全发散 灰 LTE 起点太远,ORELAX 压不住
IACC=0 禁用 Ng 无效 tlusty208.f:1928 强制 IACC=7
ITEK=-50 禁用 Ng 更快 NaN 失去加速后纯 Newton 也发散
新版 fort.55(IDSTD=0) 修 synspec 仍 NaN IDSTD 与 Balmer edge 无关
TFLOOR 防发散 零效果 生产二进制实测轨迹与无 TFLOOR 逐位相同
调大 chmax 有害 nc 提前停止给 nl 坏种子

5. 落地的代码改动

5.1 核心思路

给 nl/seed_nc 阶段加 ORELAX 欠松弛阻尼,并在 YAML 新增 seed_chain 字段让 seed_step 链 可配置(此前硬编码在 default_seed_chain())。配合当前 skip_converged 策略,重启工作流 自动重跑 1305 个失败点(收敛的 7911 点保留)。

5.2 改动清单(6 个文件)

文件 改动
crates/common/src/config.rs GridConfig 新增 seed_chain: Vec<ChainStep> 字段
crates/common/src/models.rs TaskSpec 新增 seed_chain_params 字段
crates/server/src/scheduler.rs 新增 get_workflow_seed_chain()dispatch/fallback 注入
crates/node/src/executor.rs resolve_execution_chain() seed_step 优先用 seed_chain_params
crates/common/src/runner.rs default_seed_chain() 兜底:seed_nc orelax: Some(0.3)nl orelax: Some(0.5)
workflows/sdB_cno.yaml tlusty_chain nl 加 orelax: 0.5;新增 seed_chain

5.3 ORELAX 取值依据

  • nl 步 orelax=0.5nl 含谱线(ilvlin=100)扰动大,0.5 是保守的欠松弛值, 阻尼 Newton 步长振荡但不至于过度阻尼导致谱线约束不足。
  • seed_nc 步 orelax=0.3:seed_nc 从已收敛邻居热启动,起点扰动小,可用更激进的 0.3。
  • nc 步不加 orelax:实测中温区 nc 用 default 即可收敛,不加更安全。

5.4 向后兼容

  • seed_chain 空时回退 default_seed_chain()(已同步加 orelax),与旧行为一致
  • TaskSpec.seed_chain_params 缺省 None(旧 MQ payload 兼容)
  • YAML 不配 seed_chain 时完全不影响现有行为

5.5 验证

  • cargo build 编译通过
  • cargo test 194 个测试全绿,0 失败 (含新增 seed_step_uses_custom_seed_chain_when_provided 测试)

6. 后续建议

6.1 重启工作流验证

当前 skip_converged 策略重启时自动重置 1305 个失败点回 pending。 部署新二进制 + 新代码后重启,观察失败率下降情况。预计中温区(20-45kK)的 ~150 个失败点可被救回。

6.2 接力热启动(更大收益,需改代码)

对高温区(55-60kK)的失败点,可把 seed_step 的大步长 ΔCNO=2 拆成 Δ=1 的多步接力 (如 c-4→c-3→c-2→c-1),降低单步富金属扰动。需改 scheduler.rs 的 fallback 逻辑 支持多跳 seed。这是唯一可能救高温区的物理手段。

6.3 synspec Balmer edge 修复(需改 synspec 源码)

表层 NLTE 粒子数为 0 导致连续谱 H bound-free 0/0=NaN。需在 synspec54.f 的 OPAC/连续谱计算加 NaN 守卫,或编译时启用 -ffpe-trap=invalid,zero 定位。

6.4 接受物理极限

55-60kK/贫氦/富CNOcno_sum≥-6)约 400-500 个点是 sdB 纯 He 大气模型的物理极限, 光谱应从相邻收敛点插值。


附:关键文件路径

  • 数据库: /home/fmq/下载/dcts.db
  • Salvage 归档: data/salvage/{node}/result/{point}/
  • 生产二进制: dcts/assets/tlusty_staticmd5=14cd144b...
  • 开发二进制(勿用于测试): tlusty/tlusty.exemd5=9b249e00...
  • tlusty 源码: tlusty/tlusty208.fIACC 强制改 7 在 :1928ORELAX 默认在 :844
  • synspec 源码: synspec/synspec54.fNaN 源头 ST0=ET/CH 在 :12990
  • seed_finder.rs: crates/common/src/seed_finder.rs(贫富方向逻辑 :14-40