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 篇根因分析/验证文档
This commit is contained in:
fmq
2026-08-17 23:55:26 +08:00
parent 43b82b1ae2
commit b058e66722
54 changed files with 5624 additions and 347 deletions
@@ -0,0 +1,114 @@
# 物理校验判据与 fort.7 解析(DCTS 权威)
参考源码:`dcts/crates/common/src/conv_check.rs`。**物理性判定必须以这里的逻辑为准**,不要用经验标准。
## 温度结构判据:`check_temperature_structure`
`conv_check.rs:463`。返回 `TempStructCheckResult``valid = violations.is_empty()`。三项全过才算物理:
1. **表层 T < max_factor × Teff**
- `surface_ratio = temps[0] / teff``temps[0]` = 最浅深度 = 表层)
- `surface_ratio > max_factor`(默认 **3.0**)→ 违规
- `teff <= 0` → ratio = +Inf → 必违规
2. **每个深度 T ∈ [temp_floor, temp_ceiling]**
- 默认 **[10.0, 1.0e8] K**
- 任一深度 T 越界 → 违规
3. **无 NaN/Inf**
- 非有限 T(NaN/Inf)→ 违规(被第 2 条的 `is_finite()` 捕获)
报告**首个**违规深度。
### 物理参考(不是硬判据,仅辅助判断)
- **Eddington 灰大气**`T(τ=0) ≈ 0.84 × Teff`。60kK 模型表层应约 5 万 K。
- **生产基准**:已收敛种子 `t60000_g5.0_he-2_c-3_n-4_o-4.7` 表层约 0.78×Teff。
- **内部温度**:无 1e7+ 异常隆起(如某修复前组合曾出现 6.2e9 K 内部尖峰)。
### ⚠️ 判据修正历史(为何只信 DCTS 三项)
- 旧经验"表层应 0.51.5×Teff":**过严**,曾误判物理模型为不物理(把表层 0.78×Teff 的好模型判死)。
- 旧经验"表层应 36 万 K":对 60kK 模型**偏低**,曾误判。
- **正确做法**:只用"表层 < 3×Teff 且各深度 ∈ [10,1e8] 且无 NaN"三项硬判据,Eddington 0.84 仅作物理直觉参考。
## fort.7 大气模型文件格式与解析
`conv_check.rs:470-502`,对应 TLUSTY 源 `tlusty208.f:14066-14108``OUTPUT` 子程序):
```
第 1 行: ND NUMPAR
ND = 深度点数; NUMPAR = 每深度参数数 = NLEVEL + NUMLT
接下来 ceil(ND/6) 行: DM 列阵(质量深度),每行 6 个值(FORMAT 502
接下来 ND 个深度块: 每块 ceil(NUMPAR/5) 行(FORMAT 503,每行 5 值)
每块【第一行第一个 token】= 该深度的温度 TEMP(ID)
块内其余 = 电子密度、质量密度、NLTE 能级布居/ departure 系数
```
`temps[0]` = 表层(ID=1,最浅);`temps[nd-1]` = 底层(最深)。
### 解析算法(`check_temperature.py` 复刻)
```
1. 读第1行 → nd, numpar = 前两个 token。任一为 0 → 跳过(返回 None,不判失败)
2. dm_lines = (nd + 5) // 6 # 跳过 DM 列阵
block_lines = (numpar + 4) // 5 # 每深度块行数
3. 跳过 dm_lines 行
4. 对 nd 个深度,每个:
取块第一行第一个 tokenparse_fortran_float → temps[id]
跳过 block_lines - 1 行
5. 检查三层判据
```
`parse_fortran_float``conv_check.rs:44`)兼容 Fortran 无-E 记数法:指数 ≥100 时 gfortran 挤掉 E,如 `-1.35E+118` 输出成 `-1.35+118`。解析:先标准 parse;失败则正则 `^([+-]?[\d.]+)([+-]\d+)$` 在尾符号前插 E 再 parse;溢出返回 ±Inf。
> 参考实现见 `conv_check.rs:1339 make_dot7(temps)` —— 构造测试用 fort.7 的辅助函数(写 `ND NUMPAR=3`,一行 DM 占位,每深度一行 `T 1.0E+10 1.0E-15`)。
## 收敛轨迹判据:`check_fort9`
`conv_check.rs:68`。解析 `fort.9`(迭代收敛记录)。
### fort.9 格式
```
RELATIVE CHANGES OF VECTOR PSI
ITER ID TEMP NE POP RAD MAXIMUM ilev ifr
1 50 -8.46E-04 1.53E-03 2.51E-02 -2.20E-03 2.51E-02 74 1
1 49 ...
...
2 50 ...
```
每行 = 一次迭代在一个深度点的相对变化:`ITER(迭代号) ID(深度) TEMP NE POP RAD MAXIMUM(本次迭代本深度的最大相对变化) ilev ifr`
### 判据
- 每次 ITER 的 `max_relc` = 该 ITER 所有深度行 `|MAXIMUM|` 的最大值
- **末次迭代**的 `max_relc` = 整个阶段的结果
- **收敛** = `max_relc < chmax`(默认 **0.001**)且 `is_finite()`
- 单调下降 = 健康;突然涨几个数量级 = 雪崩发散
- `chmax <= 0` 或非有限 = 非法配置(返回错误,max_relc=Inf
### STOP 检测(fort.6
`fort.6`stdout)里的 `**** STOP in SOLVE after ITER N` = 求解器发散中止。`conv_check.rs``SOLVER_STOP_RE` 匹配。这是比"非零 rc"更具体的发散信号。
## NaN / 伪收敛检测
### NaN 匹配模式(与 Rust 一致)
`conv_check.rs:18` `NAN_RE_PATTERN = (?i)(\bnan\b|\binf(?:inity)?\b|\*{3,}|[eE]\+(?:3\d{2}|[4-9]\d{2,}))`
匹配:`nan` / `inf` / `infinity` / `***`(3+星) / 正指数 `E+300`~`E+999`(超高正指数视同数值无效,因发散时布居数变化可达 ±100~±200 量级但温度/流量场溢出用 `***``E+300+`)。
相关函数:
- `atmosphere_has_nan`L246):扫 fort.7 全部数值 token
- `spec_is_valid`L290):扫光谱文件,同正则
### 伪收敛陷阱(必查)
`max_relc` 极低 ≠ 收敛。若模型已 NaN,**在 NaN 上迭代的相对变化为 0**,会伪装成 max_relc≈0 的"收敛"。
**正确流程**(每次事后审查都要做):
1. `check_fort9.py` 看轨迹 + STOP + max_relc
2. `check_temperature.py` 看温度结构三项(含 NaN 检查)
3. **两者都过才算真收敛**;仅 fort.9 看着收敛但 fort.7 有 NaN = 伪收敛
## 其他 DCTS 校验(了解即可)
`conv_check.rs` 还提供(生产链用,测试时按需):
- `check_energy_conservation`L374):能量守恒
- `check_emflux_bolometric`L578):辐射通量
- `check_convergence_trace`L691):收敛轨迹综合
- `check_bfactor`L759):departure 系数(.bfac 文件,同样 ND NUMPAR + DM 头格式)
- `extract_failure_hint`L851):失败原因提取
测试中常用前两类(温度结构 + 收敛轨迹)即可覆盖绝大多数判断。