Files
DCTS/.agents/skills/tlusty-synspec-test/SKILL.md
T
fmq b058e66722 feat(all): NaN 伪收敛否决与 nl_tight 能量回退、emflux 检验 4π 修正、nst 行宽与种子边界修复、gfATO 谱线表接通与网格加密 9216 点、阶段分项统计与跳板机部署
物理修复(400 失败点归因,见 docs/failed400_nan_pseudo_convergence_2026_08_17.md):
- runner: 阶段 converged 后复查 fort.7,含 NaN/Inf 即否决(fort.9 全零伪收敛,
  曾致 261 点误跳过 nl_direct 回退);否决阶段不产出种子,阻断污染传播
- runner: nl_tight 回退——仅能量边际失败时以 CHMAX 收紧 10× 从自身模型续迭代,
  残差降幅 ~10×;execute_tlusty_stage 抽取供主链与回退共用
- conv_check: fort.14 为 Eddington 通量 Hλ,积分需乘 4π 再比 σTeff⁴
  (旧版 ratio 稳定 0.0796=1/4π,全点系统性假阳性)+ 回归测试
- nst_writer: 单行超 80 字符被 TLUSTY 静默截断,IFALI/JALI/TRAD 等从未生效;
  按 75 字符自动换行
- seed_finder: Teff 容忍度改含边界 <=,相邻 5000K 档恢复互为种子 + 回归测试

谱线表与网格:
- 默认线表 gfVIS99 → gfATO(全波段 18-23000Å),TaskSpec.linelist 支持工作流
  级覆盖,节点按需下载(进程互斥锁防并发重复下载 238MB)
- sdB_cno Teff 加密至 5000K 步长,432 → 9216 点;tlusty/synspec 静态二进制更新

统计与部署:
- grid 汇总改按 tlusty_status/synspec_status 分项计数,新增 tlusty_failed/
  synspec_failed/synspec_pending,前端详情页双视图适配
- deploy/fetch_results 支持跳板机 ProxyJump 与 SSH 主连接复用,fetch 新增 --force;
- Docker 构建支持 CARGO_MIRROR/USE_MIRRORS 国内镜像参数;移除 tools/ 拷贝
- 新增 tlusty-synspec-test skill 与 6 篇根因分析/验证文档
2026-08-17 23:55:26 +08:00

14 KiB
Raw Blame History

name, description
name description
tlusty-synspec-test 规范直接运行 TLUSTY / SYNSPEC 二进制做物理验证测试的全流程——目录脚手架、输入文件生成与校验、冷启动链(lte→nc→nl)/种子步进链(seed_nc→nl)/SYNSPEC 执行、以及基于 DCTS 权威判据的事后物理审查。只要用户要在 DCTS 框架之外直接跑 tlusty_static / synspec_static 做收敛性、发散复现、物理解、光谱合成、fort.7/fort.9 分析、冷启动/种子链、网格点验证等测试,或在 dcts/test/ 下建测试目录,就必须用本 skill——即使用户没说"测试流程"四个字。

TLUSTY / SYNSPEC 物理测试流程

本 skill 把 docs/testing_workflow_2026_08_11.md 的规范、源码侧 tests/{tlusty,synspec}/ 的官方 Runtest 结构、以及 DCTS crates/common/src/ 的输入生成器与物理校验逻辑,统一成一套可执行流程。

核心原则(违反这些 = 测试无效)

  1. 测试必须建在 dcts/test/(已被 .gitignore 排除,不入库)。禁止在仓库根、data/runtime/、共享目录直接跑二进制。
  2. 二进制优先用 dcts/assets/tlusty_staticdcts/assets/synspec_static——它们比 data/runtime/ 下的旧版新,且是生产链实际使用的版本。data/runtime/ 的旧版仅用于历史对照。
  3. 物理性判定必须用 DCTS 权威判据 check_temperature_structure(见 §事后审查),不要用"表层应 0.51.5×Teff"等经验标准——后者曾误判物理模型为不物理。
  4. 执行前必须检查输入(见 §执行前检查)。一次输入错误会浪费一次 20 分钟级的计算。
  5. 执行后必须回头审查,不能只看"有没有 STOP"。有 STOP 但模型含 NaN = 非物理;无 STOP 但温度结构不物理 = 仍未完成。

第一步:判断测试类型(决策树)

先问用户(或从任务推断)属于哪类,再去读对应的 reference:

用户在做什么 测试类型 链类型 读哪个 reference
从零算一个网格点的大气(LTE 灰化起步 → NLTE 连续 → NLTE 谱线) convergence/physics 冷启动链 lte→nc→nl references/tlusty-stages.md
从一个已收敛邻居模型热启动算新网格点 convergence 种子步进链 seed_nc→nl references/tlusty-stages.md
复现生产的发散/失败,对比生产 DB 轨迹 validation 冷启动或种子链(看生产记录) references/tlusty-stages.md + physics-checks.md
给定一个收敛大气(fort.7),合成光谱 synspec SYNSPEC 单步 references/synspec-inputs.md

区分冷启动 vs 种子链的关键:冷启动 lte 阶段 ltgray=T,会删除 fort.8从灰大气重建;种子链第一阶 seed_nc邻居的 fort.7 复制成 fort.8 热启动。如果输入里有 fort.8(邻居种子)→ 种子链;如果没有、从 lte 开始 → 冷启动链。

第二步:建目录(用脚手架脚本)

四级目录结构(详见 references/directory-convention.md):

dcts/test/{test_id}/{type}/{grid}/{content}/
    ├── inputs/     # 执行前生成并检查的全部输入
    ├── scripts/    # run.sh(必有)
    ├── run/        # 执行工作目录,fort.* 在此生成
    └── outputs/    # 阶段产物(按阶段重命名)+ RESULT.md

直接用脚手架,不要手敲 mkdir

bash .agents/skills/tlusty-synspec-test/scripts/new_test.sh \
    20260813_my_purpose convergence t60000_g5.0_he-2_cold baseline

new_test.sh 会创建四级目录、按链类型拷贝对应 assets/run_*.sh 模板为 scripts/run.sh、放一个 README 骨架。它会询问链类型(cold/seed/synspec)来选模板。

命名规范

  • test_id = {日期}_{目的},如 20260813_tlusty_divergence
  • typeconvergence | physics | synspec | validation
  • grid = t{teff}_g{logg}_he{loghe},冷启动链加 _cold、种子链加 _seed(如 t60000_g5.0_he-2_coldt55000_g5.0_he-4_seed
  • content = 描述测试变量,如 baseline / itek0_iacc0_dpsilg15 / niter50 / nd70

第三步:生成输入文件

优先级:有 DCTS 代码生成器就用代码生成(保证与生产一致) > 无生成器则从已知正确模板 sed/python 改(必须留 diff 或注释) > 绝不凭空手写。

各链需要的输入(速查)

inputs/ 文件
冷启动链 lte.5 nc.5 nl.5 lte.nst nc.nst nl.nst无 fort.8
种子链 seed_nc.5 seed_nc.nst fort.8(邻居收敛模型的 .7);若跑到 nl 还需 nl.5 nl.nst
SYNSPEC fort.5 fort.55 fort.8(收敛大气)+ fort.19/data 软链

各文件的逐字段含义、各阶段差异、ilvlin/DPSILG/ORELAX/NITER 默认值——见 references/tlusty-stages.mdTLUSTY)与 references/synspec-inputs.mdSYNSPEC fort.55 九行)。

关键不变量(违反即输入错误,先记这几条):

  • 冷启动链 .5 第 2 行:lte.5=T Tnc.5/nl.5=F F
  • ions 行 ilvlinlte/nc 阶段=0nl 阶段=100裸核(nlevs=1)永远=0
  • .5 第 3 行的 'nst' 文件名必须与实际 nst 文件名一致(生产默认就是字面量 nst
  • nst 第 1 行含 NITER=;第 2 行可含 ORELAX=/IACC=/ICHANG=/IELCOR=DPSILG 通过 escape hatch 注入(默认无,生产常用 DPSILG=1.5 抑制雪崩)
  • data/ 软链必须指向 assets/data/133 个原子数据文件)

阶段变换与 A/B 对照

nc.5 → nl.5(阶段变换,ilvlin 0→100)用 scripts/stage_transform.py,它字节级安全地改写离子行的 ilvlin 列(裸核保持 0)并按需改第 2 行 LTE/LTGRAY。不要手写 awk 做这事——awk 按空格切分会把 'data/h1.dat' 这类带空格引号路径拆碎,破坏输入(实测踩坑)。

python3 .agents/skills/tlusty-synspec-test/scripts/stage_transform.py inputs/nc.5 -o inputs/nl.5 --to nl --diff

A/B 测试:同 {grid} 下放多个 {content} 目录(baseline / 变体),生成后必须用 scripts/diff_configs.py 确认各组只有你刻意改的参数不同(带 * 的行应只有 ITEK/ORELAX/NITER 等),其余一致——这是对照有效的前提。

python3 .agents/skills/tlusty-synspec-test/scripts/diff_configs.py dirA/inputs dirB/inputs --teff 60000

第四步:执行前检查(必做,用脚本)

跑计算前,用校验脚本确认输入无误:

# TLUSTY 链:校验 .5 第2行/ions、nst 关键参数、软链
python3 .agents/skills/tlusty-synspec-test/scripts/check_inputs.py \
    path/to/{content}/inputs --chain cold --teff 60000

check_inputs.py 会检查上面"关键不变量"那几条。建议再做一次冒烟timeout 30 跑 lte 阶段),确认二进制能启动、fort.6 里回显的 NITER/ITEK/DPSILG 与输入一致,再全量跑。

第五步:执行(用 run.sh 模板)

每个测试目录的 scripts/run.sh 已由 new_test.shassets/ 拷好。直接:

cd dcts/test/{test_id}/{type}/{grid}/{content}
bash scripts/run.sh            # 可选参数:每阶段超时秒数(默认 1200)

模板 run.sh 已内置规范行为(assets/run_cold_chain.sh 等):

  1. 切到 run/,复制 inputs/* 进去,建 data 软链
  2. 逐阶段执行,每阶段 timeout 1200 tlusty_static < stage.5 > fort.6 2> fort.14
  3. 每阶段产物立即按阶段重命名fort.6→{stage}.6fort.7→{stage}.7fort.9→{stage}.9fort.14→{stage}.14(避免被下阶段覆盖)
  4. 阶段间种子传递:下一阶段 fort.8 = 上一阶段 fort.7 的复制;冷启动链 lte 阶段删 fort.8
  5. 归档到 outputs/

为什么按阶段重命名是关键TLUSTY 每次都写同名 fort.7/fort.9,不重命名的话 lte 的产物会被 nc 覆盖,事后无法分阶段审查。官方 RTlusty 脚本用 {model}.{ext} 命名(如 hhe35lt.7)解决同样问题;我们用 {stage}.7 等价。

第六步:事后审查(必做,用脚本 + 判据)

执行完,用脚本做数值与物理审查,不要只看 rc=0

⚠️ 审查时机必须等进程完全退出后再审查bash scripts/run.sh 完成、或 pgrep -f tlusty_static 为空)。TLUSTY 边算边写 fort.9——运行中实时读,最新一拍的迭代行是半写状态,max_relc 不可信(实测踩坑:运行中读到 iter6=0.477 貌似收敛,进程退出后同一迭代实为 1060)。run.sh 里每个阶段是串行的(一个阶段跑完才进下一阶段),所以等 run.sh 结束即可放心审查。

# 1. 收敛轨迹:解析 {stage}.9 的 max_relc 演化、检测 STOP/NaN。
#    --expected-niter N 传 nst 里配的 NITER:若末次 iter 远小于 N 且未收敛,
#    会提示"疑似 timeout 截断"(轨迹不完整,结论需谨慎)。rc=124 时的
#    outputs/{stage}.TIMEOUT 标记也会被自动发现。
python3 .agents/skills/tlusty-synspec-test/scripts/check_fort9.py outputs/nl.9 --teff 60000 --expected-niter 100

# 2. 温度结构物理性(DCTS 权威判据,复刻 conv_check.rs:463
python3 .agents/skills/tlusty-synspec-test/scripts/check_temperature.py outputs/nl.7 --teff 60000

收敛判据(数值)

  • {stage}.9 每次 ITER 跨所有深度的 max_relc = |MAXIMUM| 列最大值
  • 收敛 = 末次迭代 max_relc < chmax(默认 0.001)且有限
  • 单调下降 = 好;雪崩(突然涨几个数量级)= 发散
  • {stage}.6 里的 **** STOP in SOLVE after ITER N = 求解器发散中止

物理判据(DCTS 权威,必须用这套)

check_temperature_structureconv_check.rs:463)三项全过才算物理:

  1. 表层 T < 3 × Teffsurface_ratio = T[0]/Teff> 3 判不物理)
  2. 每个深度 T ∈ [10, 1e8] K
  3. 无 NaN/Inf

物理参考(不是硬判据):Eddington 灰大气 T(τ=0) ≈ 0.84×Teff(60kK 模型表层应约 5 万 K);已收敛生产种子 t60000_g5.0_he-2_c-3_n-4_o-4.7 表层约 0.78×Teff 可作基准。

⚠️ 判据修正历史:旧经验"表层 0.51.5×Teff"过严(误判物理模型为不物理);"表层 3–6 万 K"对 60kK 偏低(误判)。只信 DCTS 三项判据。详见 references/physics-checks.md

伪收敛陷阱

max_relc 极低 ≠ 收敛。若模型已 NaN,在 NaN 上迭代相对变化为 0,会伪装成收敛。务必同时跑温度结构检查 + NaN 检查。NaN 匹配模式(与 Rust 一致):nan/inf/infinity/***(3+星)/正指数 E+300+。

测试不算完成的情况(需继续)

  • 有 STOP 但模型含 NaN(非物理)
  • 无 STOP 但温度结构不物理
  • 无 STOP 且 max_relc 未达 0.001

第七步:记录结论(用 RESULT 模板)

outputs/RESULT.md(模板见 assets/result_template.md),诚实区分:

  • 数值收敛max_relc 达标)vs 物理收敛(温度结构三项全过)——只有后者才是真正成功
  • 记录:测试配置、各阶段迭代/末 max_relc/STOP/NaN/温度结构表、审查结论

完整的 README/RESULT 字段、阶段产物重命名表、目录示例——见 references/directory-convention.md


何时读哪个 reference

想知道
.5 每行含义、各阶段(lte/nc/nl)参数差异、ilvlin/DPSILG/ORELAX/NITER 默认、冷启动 vs 种子链、fort.7↔fort.8 传递 references/tlusty-stages.md
fort.55 九行控制卡逐行、fort.5/fort.8/fort.19、SYNSPEC 产物(.spec/.cont/.iden)、NaN 修复背景 references/synspec-inputs.md
check_temperature_structure 完整判据、fort.7 解析算法((nd+5)/6 行 DM、每块 (numpar+4)/5 行)、NaN/伪收敛/STOP、判据修正史 references/physics-checks.md
四级目录结构、命名规范、阶段文件重命名表、README/RESULT 模板 references/directory-convention.md

脚本速查

脚本 作用
scripts/new_test.sh 脚手架:建四级目录 + 拷 run.sh 模板 + README 骨架
scripts/check_inputs.py 执行前校验 .5/nst/软链(TLUSTY 链)
scripts/check_fort9.py 解析 {stage}.9 收敛轨迹、max_relc 演化、STOP/NaN--expected-niter N 检测 timeout 截断
scripts/check_temperature.py 复刻 check_temperature_structure,对 {stage}.7 做温度结构物理判定
scripts/stage_transform.py 冷启动链 .5 阶段变换:nc.5 → nl.5(非裸核 ilvlin 0→100,字节级安全,避免手写 awk 踩坑)
scripts/diff_configs.py A/B 测试配置对照:并排显示多组 nst/.5 参数,* 标出不一致项(应只有你刻意改的字段带 *

rc=124timeout)感知:三个 run_*.sh 模板在阶段被 timeout 杀死时会写 outputs/{stage}.TIMEOUT 标记并打印告警;check_fort9.py 会自动发现该标记(或用 --expected-niter 推断截断),在结论里提示"轨迹不完整,结论需谨慎"。不要把 timeout 截断的轨迹当成完整发散/收敛结论

所有脚本都自定位 skill 目录,可从任意测试目录调用,传相对路径即可。

固定路径速查

路径
测试根 dcts/test/
TLUSTY 二进制 dcts/assets/tlusty_static
SYNSPEC 二进制 dcts/assets/synspec_static
原子数据 dcts/assets/data/133 文件,运行时 data 软链指此)
全波段线表 dcts/assets/data/gfATO.datSYNSPEC fort.19 软链)
物理判据源码 dcts/crates/common/src/conv_check.rscheck_temperature_structure @ L463、check_fort9 @ L68
输入生成器源码 dcts/crates/common/src/{gen_input5,nst_writer,fort55_writer}.rs
链定义源码 dcts/crates/common/src/runner.rsdefault_cold_chain @ L20、default_seed_chain @ L67
流程规范文档 dcts/docs/testing_workflow_2026_08_11.md
源码侧参考测试 tests/tlusty/{hhe,cwd,disk,bstar,optab}/tests/synspec/{hhe,bstar,kurucz,hybrid,pseudonlt,optab}/