Files
DCTS/.agents/skills/tlusty-synspec-test/references/physics-checks.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

115 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 物理校验判据与 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):失败原因提取
测试中常用前两类(温度结构 + 收敛轨迹)即可覆盖绝大多数判断。