Files
DCTS/docs/spectrum_correctness_analysis.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

386 lines
37 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.
# 收敛恒星光谱的物理正确性保证调研
> 调研日期:2026-08-04
> 范围:TLUSTY 208 + SYNSPEC 54 计算的恒星大气与光谱的**物理正确性**保证方法。
> 前置文档:`docs/tlusty&synspec收敛性判断.md`(程序"是否崩溃/收敛"的判定,是本调研的下层;该文档 §4 记录了漏洞 1-5 的修复历程)。
> 核心命题:**程序正常退出(rc=0)且 max_relc < chmax ≠ 光谱在物理上正确**。本文调研介于两者之间的静默错误来源与验证方法。
> 方法:源码级静态分析 + 天体物理方法学综述 + 三方独立复核。
>
> **修订说明(2026-08-07**:本调研初版(2026-08-04)基于修复前的代码状态撰写。经核查,漏洞 1(`.spec` 内容校验)、漏洞 2fort.9 缺失分支虚构 best_max_relc)、漏洞 4NaN 阈值)已在后续提交中修复。**fort.55 字段错位(§5)也已修复**——`SynspecInput` 重构为 9 个按行子结构体,与 SYNSPEC `READ(55,*)` 语句一一对应,IDSTD 从 50 修正为 0(自动 2ND/3)。本次修订同步了这些已修复项的现状,仅保留仍真实存在的缺口(`.6` 能量守恒解析、诊断产物未解析)。
>
> **修订说明(2026-08-08)**:经源码级二次核查,同步近期重构导致的引用漂移与数据更新:① 行号校正——`runner.rs:613→:598`、`runner.rs:660→:645`、`nst_writer.rs:16`ND 默认已迁至 `config.rs:712 default_nd()`)、`synspec54.f:1157→:1230`IBFAC 经 `READ(84,*)` 读出)、`hhe35nl.6:340→:341`;② 结构体名 `StageSummary` 更正为 `StepSummary``models.rs:590`);③ ND/ions 表述更正——二者已非硬编码,分别在 `config.rs:712/833` 作为可配默认值(经 YAML 覆盖,见 `workflows/sdB_cno.yaml:63-90`);④ §5.2 DOPSTD 公式补全为 `DSTD=SQRT(1.4E7*TSTD+VTS)` + `DOPSTD=1.E7/ALAM0*DSTD`;⑤ §1.3 fort.13 改述为"默认结构性排除";⑥ 数据统计刷新——网格点 284→361`.iden` 格式分类 44/221→92 逐线表/243 频率网格/26 其他;⑦ EQW 断言更正——逐线表 EQW **并非全为 0**,约 43/92(≈47%)含非零值(强线可达上千 mA),但作为系统化 EW 来源仍不可靠,结论(改走 `.spec` 数值积分)不变。
---
## 0. TL;DR
当前 pipeline 对光谱正确性的保证**基本停留在"数值收敛"层面**,完全没有做物理不变量校验。三方调研发现:
1. **最高 ROI 缺口**:TLUSTY 已经无条件地把**能量守恒诊断**(`TOTAL SURFACE FLUX`、逐深度 `RAD/TOT``CON/TOT``(RAD+CON)/TOT`)写进已归档的 `.6` 文件,**却从未被任何 Rust 代码解析**。这是 Hubeny 官方推荐的 1% 判据,零成本可得,是物理正确性的第一道硬门槛。
2. **系统性的配方风险(已修复,见 §5)**~~`fort55_writer.rs` 写的字段 `idrv/ifreq` 与 SYNSPEC 实际读取的 `IDSTD/IPRIN` 错位~~。已重构为 9 个按行子结构体与 SYNSPEC `READ(55,*)` 语句一一对应,IDSTD 修正为 0(自动 2ND/3),并补全了原漏生成的分子表行(避免 VTB 行被吞导致 vturb 错误覆盖)。
3. **网格级错误传播**:通过 `converged && !nan` 门控上传的种子大气(`.7`),会把"伪收敛但物理未平衡"的点扩散给整个物理族——这是网格计算特有的静默放大机制,现有检查无法识别。
4. **完整的方法学分层**(§6):硬门槛(线上每点)→ 质量监控(抽样)→ 深度核查(线下基准比对),覆盖辐射平衡、光谱自洽、网格一致性、外部基准四个维度。**外部基准的特殊性**(§3.4):本网格是 CNO 全 NLTE 独立扫描,社区经典网格均不含 CNO NLTE,故外部基准对 CNO 维度失效——需用**网格内部 CNO 物理自洽验证**(路线 A)+ **同代码栈 Pacheco 2021**(路线 C)替代。
---
## 1. 当前已产出但未利用的物理诊断
> 这是后续做正确性校验的基础——很多信息 TLUSTY/SYNSPEC 已经产出,只是我们没解析。
### 1.1 能量守恒诊断(最关键,零成本可得)
TLUSTY 在 `LFIN`(最终迭代)时由 `OUTPRI` 子程序无条件写出,位于 `.6` 文件(unit 6,已归档):
```
TOTAL SURFACE FLUX 6.77712339D+12 ← 实际积分通量 TOTF
ID MASS TAUROSS TEMP NE DENS P_gas LOG(G_rad) RAD/TOT CON/TOT (RAD+CON)/TOT
1 ... 1.001E+00 0.000E+00 1.00106E+00
```
- `FLTT = SIG4P * TEFF**4`(预期通量 = σTeff⁴/4π,`SIG4P``BASICS.FOR:51`
- `RAD/TOT` = 辐射通量 / σTeff⁴
- `(RAD+CON)/TOT` = 总通量(辐射+对流)/ σTeff⁴ —— **此列应≈1.0**
- 实测样本(收敛良好的 sdB 模型,28 次迭代,`hhe35nl.6:341`):`(RAD+CON)/TOT = 1.00106E+00`(逐深度恒定,能量守恒误差 0.1%)。**未收敛/发散模型**的此列会显著偏离 1(典型表现是表层偏离或逐深度不单调),是物理正确性的第一道硬门槛。
**关键事实**`.6` 已在归档白名单(`STAGE_SNAPSHOT_SUFFIXES` 含 "6"),但 `crates/` 下无任何代码解析它。**数据已就位,只差一个 `.6` 解析器写入 conv.json**。
### 1.2 辐射平衡逐深度残差(被覆盖丢失)
TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,逐深度计算 `re=(吸收−发射)/发射` 写入 unit 17fort.17)。但 unit 17 随后被 SYNSPEC `OUTPRI` 覆盖写连续谱(`synspec54.f:3371`)——**成功路径下 RECHCK 输出永久丢失**,仅 SYNSPEC 失败时 `.cont` 里残留的才是 RECHCK 表(这也是 `.cont` 后缀二义性的来源)。
修复方向:仿照 `.bfac`/`.emflux` 的快照做法,在 RECHCK 后/SYNSPEC 前快照 fort.17。
### 1.3 其他未启用 / 未解析的诊断
| 诊断 | 子程序 | unit→文件 | 启用条件 | 现状 |
|------|--------|----------|---------|------|
| 能量守恒总表 | `OUTPRI` | 6→`.6` | 无条件 | **已产出,未解析**(最高 ROI |
| 辐射平衡逐深度 | `RECHCK` | 17→`.cont` | 无条件 | **被 SYNSPEC 覆盖丢失** |
| 统计平衡残差 | `CHCKSE` | 16→fort.16 | `ICHCKP≠0`nst 未设) | **未启用** |
| 冷却/加热率 | `COOLRT` | 86/87/88 | `ICOOLP≠0`nst 未设) | **未启用** |
| 出射辐射场谱 | `OUTPRI` | 13→fort.13 | 无条件 | **默认结构性排除**(非 `BARE_KEEPS`、不带 `<name>.` 前缀,`result_filter.rs:67-72`;与 fort.1/2/3 等中间单元同等处理,非专门列入排除名单) |
| TLUSTY 出射谱 | `OUTPRI` | 14→`.emflux` | 无条件 | **已快照归档,未解析**(可做 bolometric 积分) |
| b 因子(NLTE 偏离) | `OUTPRI` | 12→`.bfac` | 无条件 | **已快照归档,未解析** |
| SYNSPEC 谱线统计 | START/OUTPRI | 6→`.log` | 无条件 | **已归档,未解析**(含 `LINES-TOTAL`、波长范围、等效宽度) |
### 1.4 conv.json 的现状
`ModelSummary` / `StepSummary` / `ConvCheckResult``models.rs:616/590/564`)记录的字段**全部是数值收敛指标**max_relc, last_iter, worst_depth, synspec_rc 等),**没有任何物理正确性字段**(能量守恒、辐射平衡、统计平衡残差)。
---
## 2. 静默错误的物理来源
> "程序正常退出但光谱物理错误"的四层来源,按严重度排序。每层给出源码证据与可检测性。
### 2.1 第一层:TLUSTY 大气结构错误(光谱错误的根因)
| 错误来源 | 物理后果 | 源码证据 | 当前是否捕获 | 严重度 |
|---------|---------|---------|------------|--------|
| **NITER 截断被当收敛** | 大气未达统计平衡,布居数错误 | `tlusty208.f:14728` `LFIN=ABS(CHMX).LE.CHMAX.OR.ITER.GE.NITER`OR 短路:CHMX 再大也会因 `ITER≥NITER` 判 LFIN | 部分(`conv_check.rs` 重判 max_relc<chmax,但若截断时"刚好"<chmax 则通过) | 极高 |
| **ACCEL2/Ng 加速伪收敛** | 加速外推后下次 CHMX 人为变小,提前判收敛但平衡未达成 | `tlusty208.f:29693-29797`,无加速后发散回退检查 | 未捕获(max_relc 变小与真收敛难区分) | 高 |
| **模型截断但物理不充分** | 关键 H/He 跃迁被省略,NLTE 种群错误但程序正常 | 默认原子模型 `default_ions()``config.rs:833`,可经 YAML `tlusty_input.dot5.ions` 覆盖,`workflows/sdB_cno.yaml:63-90` 即显式列出完整 22 行表)H=9lev、HeI=14、HeII=14;远低于 MLEVEL=1134 上限 | 未捕获(需物理判断) | 高 |
| **ND=50 深度网格偏稀** | 大气结构分辨率不足,谱合成输入质量差 | `default_nd()``config.rs:712`,经 `TlustyInput.nst.depth.nd` 可配)ND=50 默认;`BASICS.FOR:9` MDEPTH=100 | 未捕获(需对比 ND=99 | 中-高 |
### 2.2 第二层:SYNSPEC 谱合成错误
| 错误来源 | 物理后果 | 源码证据 | 当前是否捕获 | 严重度 |
|---------|---------|---------|------------|--------|
| **★fort.55 字段错位(已修复,见 §5)** | ~~标准深度取最深层(IDSTD=50=ND~~、输出详尽度偏离;IOPHLI 无论写何值都被 SYNSPEC 无条件置 0(见 §5.2 | 旧 `fort55_writer.rs:5``imode,idrv,ifreq` 与 SYNSPEC `synspec54.f:253``IMODE,IDSTD,IPRIN` 错位。**已重构为 9 子结构体按行一一对应**(`config.rs` Fort55Line1-6 + Fort55LineMol/Vtb/Ang | 已修复(IDSTD 现 0=自动 2ND/3 | ~~高(系统性)~~ → 已消解 |
| **NLTE 静默降级为 LTE** | 匹配不上的 NLTE 线被 set to LTE,线强度错误但程序继续 | `synspec54.f:9114-9176`,仅写 fort.11 计数 | 未捕获(DCTS 不解析 fort.11 | 高 |
| **IBFAC=0 默认全程按 LTE 读种群** | fort.8 不含/不全含 NLTE 种群时种群保持 LTE 不替换 | `synspec54.f:1230` `READ(84,*)` 读出 IBFAC,默认 0`:11220` NUMP>IP 才替换 | 未捕获 | 高 |
| **波长网格步长固定且窗口窄** | 窄线被漏掉,谱"正常但缺线" | SPACE=0.01`config.rs:1519` `default_space()`);默认窗口仅 1400-1410Å(`config.rs:1508,1511` `default_alam0/alast`),sdB_cno 工作流显式配 3000-7000Å | 未捕获 | 中-高 |
| **线表缺失(只链 fort.19** | 缺关键谱线,有计数但无"应有线却缺失"校验 | `runner.rs:598` symlink 单一 `runtime.linelist``synspec54.f:9157` NLIN0 计数 | 未捕获 | 高 |
### 2.3 第三层:数据传递与种子继承(网格级错误放大)
| 错误来源 | 物理后果 | 源码证据 | 当前是否捕获 | 严重度 |
|---------|---------|---------|------------|--------|
| **★种子继承的错误传播** | 伪收敛点的 `.7` 通过 `converged && !nan` 门控上传为种子,被相邻 SeedStep 点热启动,**错误沿网格拓扑传播**;`directed_cno_distance` 倾向选贫方向,一个坏种子可污染整个 Teff/logg 族 | `reporter.rs:122` 种子门控 `converged && !atmo_has_nan``executor.rs:221` 读取种子 bytes`seed_finder.rs:26-40` 有向最近邻选种(RICH_PENALTY=4.0 > POOR_PENALTY=1.0,倾向贫方向) | **未捕获**(坏种子本身"通过"了所有检查) | **极高** |
### 2.4 第四层:编译/数值精度
| 错误来源 | 物理后果 | 源码证据 | 当前是否捕获 | 严重度 |
|---------|---------|---------|------------|--------|
| **生产编译无 -fcheck/-ffpe-trap** | 数组越界、IEEE 异常 → rc=0 + 静默写越界内存,产出无 NaN 标记但物理错误的大气 | `deployment.md:86,90` `gfortran -fno-automatic -O3` | 未捕获(详见 `tlusty&synspec收敛性判断.md` §4 漏洞 3 | 高 |
> 详见 `tlusty&synspec收敛性判断.md` 的 gfortran 运行时错误退出码实测表。
---
## 3. 天体物理验证方法学
> 每个方法标注:验证的物理不变量、操作步骤、容差判据、所需数据、实现难度、线上/线下。
### 3.1 大气模型层面(TLUSTY 产出)
| 方法 | 验证的物理不变量 | 容差/判据 | 所需数据 | 难度 | 时机 |
|------|----------------|----------|---------|------|------|
| **通量守恒** | 每深度 πF(rad+conv)=σTeff⁴ | `(RAD+CON)/TOT` 偏离 1 ≤1%(Hubeny 官方判据);任一深度 >5% 直接判坏 | `.6` 末表 | 低 | **线上** |
| **max_relc 收敛门槛** | 状态向量相对修正已可忽略 | < chmax(默认 10⁻³,对 sdB/早型星合理;精密线分析可收紧到 10⁻⁴) | `*_chmax*.9` | 低(已实现) | **线上** |
| **假收敛排查** | max_relc 单调下降 ≥3 个量级 | 初始/最终 max_relc ≥10³ | fort.9 全序列 | 中 | 抽样 |
| **NaN/Inf 否决(收紧)** | 所有深度点温度/密度为有限物理值 | **任意 1 行命中即否决**(已实现 0 行容忍,`conv_check.rs:264-265` | `.7` | 低 | **线上** |
| **温度结构边界** | T(τ) 落在物理合理范围 | 表层 T ≲ 2-3×Teffidstd 处 T≈(2/3)Teff;总体 10¹-10⁸ K | `.7` | 中 | 抽样 |
| **统计平衡残差(NLTE** | 跃迁平衡 R_ij·n_i=R_ji·n_j、粒子数守恒 | b 因子无 NaN/Inf;关键能级 b∈10⁻³-10³ | `.bfac` | 中 | 抽样 |
**对 TLUSTY 的已知坑**(a) fort.9 缺失分支虚构 `best_max_relc=0.0`**已修复**:现按 `niter==0`/`niter>0` 区分——grey start 保留 converged=true 但 best_max_relc=None;异常返回 INFINITY`runner.rs:448-477``conv_check.rs:88-98`);(b) Kantorovich 加速的伪收敛风险;(c) 表层(worst_depth=1)最易出 NaN。
### 3.2 光谱合成层面(SYNSPEC 产出)
| 方法 | 验证的物理不变量 | 容差/判据 | 所需数据 | 难度 | 时机 |
|------|----------------|----------|---------|------|------|
| **NaN/负值/零行否决(最关键)** | 归一化谱∈[0,1],绝对流量≥0,无 NaN/Inf | **0 容忍**(任意一个即判坏) | `.spec``.cont` | 低 | **线上** |
| **Rayleigh-Jeans 尾 Planck 检验** | 长波端连续谱 → Bλ(Teff) | 斜率与 Bλ(Teff) 一致;偏离 >20% 异常 | `.cont` + Teff | 中 | 抽样 |
| **总积分流量能量守恒** | ∫Fλ dλ ≈ σTeff⁴(bolometric | 偏离 <1-2% | `.emflux`(全频率范围) | 中 | 抽样 |
| **连续谱自洽(TLUSTY vs SYNSPEC** | 同波长点 `.emflux``.cont` 水平一致 | 连续谱区差异 <1-2% | `.emflux` + `.cont` | 低 | 抽样 |
| **Balmer 线基准** | Hα/Hβ EW 随 Teff/logg 已知行为 | 线心深度∈(0,1)sdB 的 Hβ EW 约 5-15Å;高 Teff(>30kK) 不应异常强 | 光学 `.spec` | 中 | 抽样 |
| **He 线电离序列** | He II/He I 强度比随 Teff 单调 | 比值突变 = NLTE 电离平衡错 | 光学 `.spec` | 中 | 抽样 |
**归一化谱 vs 绝对流量谱**:归一化谱只验线形/线强,不能验能量守恒;绝对流量谱(`.emflux``.cont`)才能验通量守恒与 Planck。两者必须分别处理。
### 3.3 网格层面(内部一致性)
| 方法 | 验证的物理不变量 | 容差/判据 | 所需数据 | 难度 | 时机 |
|------|----------------|----------|---------|------|------|
| **相邻点平滑性(一阶差分)** | 光谱随参数连续变化 | 连续谱单步 Δ<10-15%;线 EW<30% | 全网格 `.cont`/EW | 中 | 线下 |
| **二阶差分 / 离群点检测** | 光谱无孤立异常 | r>4σ 或 \|残差\|>20% 标 outlier | 全网格光谱 | 中 | 线下 |
| **沿等参数线单调性** | Balmer/He 线随 Teff/logg 已知单调趋势 | 出现锯齿/反转 = 可疑 | 全网格光谱序列 | 低 | 线下 |
| **收敛质量地图** | max_relc 在网格上无孤立坏区 | 孤立高 max_relc 点(邻居都收敛)= 可疑 | 全网格 conv.json | 低 | 线下 |
**已知坑**:网格在 80kK+He-poor+富金属区有真实物理极限(CNO 高价离子主导不透明度,线性化无法阻尼)——这片成片失败是物理真实的,不应误判为 bug;但**孤立单点失败**(邻居都好)才需要复查。
### 3.4 外部基准比对(ground truth
> **核心矛盾与适用性说明(本网格特有)**:本网格是 6 维 sdB + H/He/**CNO 全 NLTE** 独立扫描(Teff 20-60kK、logg 5.0-6.5、logHe 4..2、logC/logN/logO 各 4..1,见 `workflows/sdB_cno.yaml`)。社区经典网格 OSTAR2002/BSTAR2006/NLTE-OBGRID **均不含 CNO 的 NLTE 同时建模**CNO 按 LTE 或仅 H/He NLTE),PHOENIX/ATLAS9 更无。因此**逐点外部比对 CNO 维度几乎不可能**——任何外部基准最多只能验证 H/He 部分。正确破局:用**代码同源性**验证实现正确性,用**网格内部物理规律**验证 CNO NLTE 物理正确性。
#### 路线 A(最高 ROI):网格内部 CNO 物理自洽验证
这是本网格**独有的、外部基准反而做不到**的验证。CNO NLTE 有强可检验预测:**线强对丰度的单调响应 + CNO 互不耦合**。已有 361 个网格点数据(`data/result/` 实测,持续增长),零额外计算成本。
| 校验 | 物理不变量 | 判据 | 所需数据 | 难度 |
|------|----------|------|---------|------|
| **C 线 EW 随 logC 单调** | 固定 Teff/logg/logHeC II/C III 线强随 C 丰度单调递增 | 同族 4 个 logC 点 EW 单调,无反转/平台 | 同族 `.spec` + 线表 | 中 |
| **CNO 线互不耦合** | 固定 C/N,扫 O 时 C/N 线 EW 几乎不变(NLTE 交叉耦合弱) | C 线 EW 跨 logO 变化 <5% | 同族 `.spec` + 线表 | 中 |
| **电离序列协同** | N III 4634-4641 与 He II 4686 协同增减(共依赖 ne/T) | 两线强度比在网格上平滑无突变 | `.spec` 序列 | 中 |
> **EW 数据来源的现实约束**:理想情况下从 `.iden`SYNSPEC fort.12 逐线表)直接读 EQW 字段(`synspec54.f:9719` `WRITE(12,603) ...EQW...`)。但经核查,当前归档的 361 个 `.iden` 存在两个问题:(1) **EQW 字段(第 7 列)大面积缺失**——SYNSPEC 仅在线心强度权重 `WW1` 非零时才填 EQW(`:9705`),当前配置下多数逐线表行的 EQW 仍为 `0.0`,仅约 43/92(≈47%)的逐线表含非零 EQW(强线如 He II 3203Å 的 EQW 可达上千 mA),作为系统化 EW 来源并不可靠;(2) **格式不统一**——仅 92 个是逐线表格式,243 个是频率网格格式,另有 26 个其他形态(`data/result/` 实测)。因此 EW 实测需改走**对 `.spec` 归一化谱按线窗口数值积分**(`∫(1−F)dλ`),不能依赖 `.iden`。线波长定位参考线表(fort.19)。此为路线 A 脚本的实现前提。
#### 路线 BTLUSTY 官方 hhe 样本的退化比对(端到端回归)
仓库已有 `hotsd/tlusty (副本)/hhe35nl.6` 等 TLUSTY 官方 H/He NLTE 标准样本(Teff=35000K**不含 CNO**)。这是**同源码、同原子数据**的最干净回归测试。
- **操作**:用本 pipeline 跑一个 `logC=logN=logO=10`(等效无 CNO)的点,配置与 hhe35nl 对齐。
- **判据**`.7` 大气结构(T(τ)、逐深度温度/密度/电子密度)与官方样本逐深度一致到 **<0.1%**——CNO 丰度趋零时物理上退化为纯 H/He,任何偏离都指向 pipeline 的输入封装/参数传递 bug。
- **覆盖**:直接验证 §2.1(深度网格、模型截断)、§2.2(fort.55 字段错位的副作用)是否影响大气结构。投入低(跑 1 个点)。
#### 路线 C:同代码栈社区网格——Pacheco & Díaz 2021
唯一能做**近似逐点比对**的社区基准,因为与本网格用**完全相同的代码栈**:
| 维度 | 本网格 | Pacheco 2021ApJS 256, 41 | 可比性 |
|------|--------|------------------------------|--------|
| 代码 | TLUSTY **v208** + SYNSPEC **54** | TLUSTY **v205/v208** + SYNSPEC **54** | ✅ 同版本,差异最小 |
| Teff | 20k/30k/40k/50k/60k | 20k/25k/30k/35k/45k/65k | ✅ 重叠 20k/30k(余插值) |
| logg | 5.0/5.5/6.0/6.5 | 4.5/5.5/6.5 | ✅ 重叠 5.5/6.5 |
| logHe | 4..2 | He-poor/He-rich 两支(约 3..+2 | ⚠️ 仅两端可比 |
| CNO | logC/N/O 各 4..1 **独立扫描** | **整体 solar 或 [Fe/H]1.5 两组固定比例** | ❌ **无法逐点比对 CNO 维度** |
| 波长 | 3000-7000Å | 1000-10000Å @ 0.01Å | ✅ 本网格是其子集 |
| 数据 | — | 公开:SpecModels / SVO / Vizier | ✅ 可下载 |
**比对策略**(绕开 CNO 丰度差异):
1. **连续谱形状**3200-3700Å、4400-4800Å 无强 CNO 线窗口):取本网格 CNO 最接近太阳(logC≈−3.6/N≈−4.2/O≈−3.3)的点,在相同 Teff/logg/logHe 下比对连续谱,容差 **1-2%**
2. **CNO 线波长定位**:线强因丰度不同而异,但 C II 4267、C III 4640-4650、N III 4630-4640 的**线心波长必须一致**(同套原子数据),漂移 = 线表加载错。
3. **非重叠点插值**Teff=40k/50k 用其 35k/45k/65k 插值曲线,本网格点应落 ±5% 内。
**数据获取**`http://specmodels.iag.usp.br/``http://svo2.cab.inta-csic.es/theory//newov2/`
#### 路线 D:实测光谱 ground truth——SDSS / LAMOST sdB 库
终极基准是真实恒星。大量 sdB 有实测光谱 + 已测定参数:
- **SDSS hotSubDwarf catalog**Kepler et al. 2015-2019~10⁴ 颗):每颗有 Teff/logg/logHe 及部分 C/N 丰度。
- 选 5-10 颗参数落入本网格的 sdB,取最近邻网格点,卷积仪器轮廓(SDSS R≈2000)后比对。
- **容差**:线轮廓吻合、Balmer/He EW 10-15%(含噪声与投影自转)。
#### 经典社区网格的适用范围(仅 H/He 部分)
下列网格**不含 CNO NLTE**,只能用于验证 H/He 连续谱与 Balmer/He 线,**不能验证 CNO 维度**
| 基准 | CNO 处理 | 可验证 | 数据来源 |
|------|---------|--------|---------|
| OSTAR2002 / BSTAR2006Lanz & Hubeny 2003/2007 | LTE | H/He 连续谱、线轮廓 | tlusty.oca.eu |
| NLTE-OBGRID | LTE | H/He 连续谱(更完整 H 原子) | MAST archive.stsci.edu/hlsp/nlte-obgrid |
| PHOENIX / ATLAS9 | LTE(独立代码) | 独立代码交叉(光学几个百分点;UV 可达 10-20% | STScI |
| CALSPEC 标准星(Bohlin+2020 | — | TLUSTY WD 网格 1500Å-30μm 1% 一致(**仅适用 WD,非 sdB** | MASTGD153/GD71 |
#### 优先级与实测系统误差
**务实排序(针对"无 CNO 外部基准"现实)**
1. **P0 路线 A**(网格内部 CNO 单调性/解耦)——数据现成,CNO 独有验证,投入最低。
2. **P0 路线 B**(hhe 退化比对)——同源码回归测试,最干净。
3. **P1 路线 C**Pacheco 2021)——唯一同版本同物理基准,连续谱/线位。
4. **P2 路线 D**SDSS 实测)——终极 ground truth,投入最高。
**实测比对(路线 D)必须考虑的系统误差**:消光(RV=3.1)、视向速度、instrumental broadening(需 ROTINS 卷积)、距离/标度因子。
---
## 4. 物理红旗清单(一票否决)
以下任一出现即光谱不可信,必须标记复查:
| 红旗 | 物理含义 | 检测方法 |
|------|---------|---------|
| 连续谱出现负值或 NaN | 辐射转移数值失败 | `.spec`/`.cont` 任意点 <0 或 NaN |
| 归一化谱线心深度 >1(流量 <0) | 线吸收过度/数值错 | 归一化谱任意点 <0 或 >1.05 |
| `.spec` 全零或行数异常少 | SYNSPEC 中途 quit 留脏数据 | 行数 <预期×0.9 或全零 |
| 总积分流量偏离 σTeff⁴ >2% | 能量不守恒 | 积分 `.emflux` |
| 通量守恒表末列偏离 1 >1%(任一深度) | 辐射平衡未达 | 读 `.6` 末表 |
| 表层温度 >>Teff(如 >3×Teff | T(τ) 结构非物理 | 读 `.7` 表层 T |
| 热星(Teff>30kKBalmer 线异常强 | NLTE 电离算错 | 测 Hα/Hβ EW 比对基准 |
| 连续谱斜率与 Teff 不符 | 温度结构错 | RJ 尾比对 Bλ(Teff) |
| 沿 Teff 序列 Balmer/He 线非单调 | 局部点 NLTE 错 | 网格级单调性检查 |
| 孤立单点与四邻插值偏离 >20% | 该点异常 | 离群点检测 |
---
## 5. fort.55 字段错位问题(已修复)
> **状态:已修复(2026-08-07)**。本节保留问题描述作为历史记录,处置见 §5.3。
> 影响所有光谱的系统性 bugRust 端 `SynspecInput` 的字段命名与 SYNSPEC `READ(55,*)` 语句的变量名脱节,且大量字段硬编码不可配。修复方式是重构为 9 个按行分组的子结构体,字段名与 SYNSPEC 源码一一对应。
### 5.1 事实(修复前的错位)
`SynspecInput` 是扁平 7 字段结构体,与 SYNSPEC fort.55 的 9 行 30+ 字段规范脱节:
- **第 1 行错位**`fort55_writer.rs:5`(旧)写 `imode, idrv, ifreq`SYNSPEC `synspec54.f:253``IMODE, IDSTD, IPRIN`。即 **`idrv=50` 被当作 `IDSTD=50``ifreq=1` 被当作 `IPRIN=1`**。而真正的 `IFREQ`(频率网格开关)在第 4 行(`synspec54.f:2146`)。
- **第 6 行错位**:旧字段 `abs_cutoff=0.01` 被写到 SPACE 位置(相邻频率点间距),而非任何"截止"参数。真正的截止参数 CUTOF0(行6第3列)被硬编码为 `10`、CUTOFS(连续谱间距,行6第4列)硬编码为 `0`,均不可配。
- **第 2-5 行全硬编码**INMOD/INTRPL/ICHANG/ICHEMC(行2)、IOPHLI/nun*(行3)、IFREQ/INLTE/ICONTL/INLIST/IFHE2(行4)、IHYDPR/IHE1PR/IHE2PR(行5)全部不可通过 YAML 配置。
- **行 7/8 完全缺失**VTB(湍流速度,行7)、NMU0/ANG0/IFLUX(角度/比强度,行8)不生成,靠 SYNSPEC 的 `err=/end=` 标号侥幸跳过——脆弱巧合。
### 5.2 影响(修复前的物理后果)
- **IDSTD=50**=ND,最深层):SYNSPEC 的标准深度(`synspec54.f:2152-2155`,仅在 `IDSTD=0` 时自动取 `2*ND/3≈33`)本应自动取 `2*ND/3≈33`,现被强制取最深层。该层的 T/ne 用于谱线多普勒宽度(`DSTD=SQRT(1.4E7*TSTD+VTS)``synspec54.f:2265`;再 `DOPSTD=1.E7/ALAM0*DSTD``:8131/8728`)、参考丰度(AVAB)、波长网格步长的归一化基准。实测 8 个不同 Teff/logg/He/CNO 的收敛点,`sqrt(T50/T33)` 稳定在 **2.0-2.13**,即 DOPSTD 系统性偏大 ~100-113%VTS 与 `1.E7/ALAM0` 系数对 IDSTD 错层带来的相对放大不改变此量级结论)。`AB0=EXP(...)/DOPSTD/AVAB``:8913`)直接影响每条谱线的线强归一化,且 `IF(AB0.LT.UN) GO TO 10``:8914`)还改变哪些谱线被保留。
- **IPRIN=1**SYNSPEC 的输出详尽度开关,仅影响 `.log` 里打印的诊断量多少,不影响光谱数值。
- **IOPHLI 被 SYNSPEC 无条件置 0**SYNSPEC `synspec54.f:255` 读取 IOPHLI 后,`:269` 紧接 `iophli=0`(注释 `disabling an old option`)**无条件覆盖**。即无论 fort.55 写什么,Lyman 线翼准分子卫星线处理都被**必然关闭**。这是 SYNSPEC 源码的既定行为,非 fort.55 错位所致。
### 5.3 处置(已实施)
**修复方式**:重构 `SynspecInput``config.rs`)为 9 个按行分组的子结构体(`Fort55Line1`..`Fort55Line6` + `Fort55LineMol` + `Fort55LineVtb` + `Fort55LineAng`),字段名严格使用 SYNSPEC 源码变量名,行内字段顺序与 `READ(55,*)` 语句完全一致。每字段带文档注释(物理含义 + `synspec54.f` 行号)。`generate_fort55_content``fort55_writer.rs`)改为按子结构体逐行序列化。`SynspecInput::validate()``config.rs`)在配置加载时校验不触发不支持的条件读取路径(DM 数组/显式角度/风模式/分子 iunitm)。
**覆盖范围**fort.55 全部 9 行 30+ 字段均可通过 YAML `synspec_input` 块按行配置,支持后续不同工作流(sdB/sdO/WD 等)的差异化参数。单元测试覆盖行数、字段对齐、VTB 默认负值、默认值关键字段、全自定义五个维度。
**行为变化**
| 项 | 修复前 | 修复后 | 影响 |
|----|--------|--------|------|
| **IDSTD** | 50(最深层,错位) | **0(自动取 2ND/3≈33** | **光谱线强归一化修正**,所有光谱需重算 |
| IPRIN | 1(被 ifreq 错位占用) | 1(语义正确,显式可配) | 无行为变化 |
| 真正的 IFREQ | 1(硬编码行4) | 1(显式可配) | 无行为变化 |
| **行7 分子表** | 漏生成(VTB 行被吞) | 显式生成 `nmlist=0` | 修正行序错位(原靠 EOF 兜底) |
| **行8 VTB** | 漏生成(end=30 跳过→vturb 保持 2 | 生成 `-1.0``vtb<0` 不覆盖→vturb 保持 2) | 无行为变化(均保持大气 vturb=2) |
| 行9 NMU0 | 漏生成(end=100 跳过) | 生成 `0`(不算比强度) | 无行为变化 |
| 其余字段 | 硬编码 | 可配,默认值与原硬编码一致 | 无行为变化 |
> **行7 分子表的关键性**SYNSPEC 在 IMODE∈[-3,1](默认满足)时会额外读一行分子表(`synspec54.f:2208`)。gfortran list-directed READ 满足变量列表后消费整行剩余——若漏生成此行,VTB 行会被分子表 READ 吞掉,导致 `vtb` 读到 NMU0 行的首值,`if(vtb.ge.0.)` 触发 vturb 被覆盖为 0(审查 Critical-1)。显式生成 `nmlist=0` 行可保证行序对齐。VTB 默认 -1.0 是双重保险:即使行序错位,`vtb<0` 也不会触发覆盖。
**重算指引**fort.55 只影响 SYNSPEC 阶段,TLUSTY 大气结构(`.7`)不受影响,种子库可继续用。重算只需把 `synspec_stage.policy` 临时改为 `force_recompute` 重跑 SYNSPEC 阶段,无需重算 TLUSTY。
---
## 6. 分层验证策略建议
> 按"必须做 / 建议做 / 可选做"分层,标注每项的现状与所需投入。
### 第一层:硬门槛(线上,每个点必跑,0 容忍,失败即标记重算)
| # | 校验项 | 现状 | 所需投入 | 依据 |
|---|--------|------|---------|------|
| 1 | SYNSPEC `.spec`/`.cont` 内容校验(NaN/Inf/负值/全零/行数) | **已修复(漏洞 1**`spec_is_valid``conv_check.rs:287-335`)在 `runner.rs:645` 调用,校验 NaN/Inf/溢出/行数不足/全零 | — | `tlusty&synspec收敛性判断.md` §4 漏洞 1P0 |
| 2 | 大气 NaN 否决收紧到 0 行容忍 | **已修复(漏洞 4**`atmosphere_has_nan` 现 0 行容忍(`conv_check.rs:264-265`),含 NaN/Inf/`***` 溢出/E+300 超高指数四类 | — | `conv_check.rs:264` |
| 3 | max_relc < chmax + is_finite + fort.9 真实存在 | **已修复(漏洞 2 进阶)**fort.9 缺失返回 INFINITY`niter==0`grey start)与 `niter>0`(异常)分流,best_max_relc=None 不再虚构 0.0 | — | `runner.rs:448-477``conv_check.rs:88-98` |
| 4 | **通量守恒 `(RAD+CON)/TOT` 偏离 1 ≤1%**(从 `.6` 提取) | **未做(数据已就位)** | 低(写一个 .6 解析器) | §1.1,Hubeny 官方判据 |
### 第二层:质量监控(线上抽样 + 线下定期)
| # | 校验项 | 现状 | 所需投入 |
|---|--------|------|---------|
| 5 | 总积分流量 vs σTeff⁴(`.emflux`,偏离 <2%) | 未做,数据已归档 | 中(bolometric 积分) |
| 6 | Rayleigh-Jeans 尾 Planck 斜率检验 | 未做 | 中 |
| 7 | TLUSTY `.emflux` 与 SYNSPEC `.cont` 连续谱自洽 | 未做 | 低 |
| 8 | 假收敛排查(max_relc 下降 ≥3 个量级) | 未做 | 中 |
| 9 | 收敛质量热图(网格级 max_relc 分布) | 未做 | 低(数据在 conv.json |
| 10 | 温度结构边界(表层 T、idstd 处 T) | 未做 | 中 |
### 第三层:深度核查(线下,建库时 + 每次配方变更时)
> 外部基准的适用性见 §3.4:经典网格(OSTAR/BSTAR/NLTE-OBGRID**不含 CNO NLTE**,仅能验证 H/He 部分;CNO 维度的外部比对首选 Pacheco 2021(同代码栈),其余靠网格内部物理规律(路线 A)。
| # | 校验项 | 所需投入 | 对应路线 |
|---|--------|---------|---------|
| 11 | **网格内部 CNO 单调性/解耦**(固定 Teff/logg/logHe 扫 logC/N/O | 中 | §3.4 路线 A |
| 12 | **hhe 官方样本退化比对**CNO=10 退化到纯 H/He,与 hhe35nl 逐深度对比) | 低 | §3.4 路线 B |
| 13 | **Pacheco 2021 连续谱/线位比对**(同代码栈,重叠 Teff/logg/logHe 点) | 中 | §3.4 路线 C |
| 14 | 与 OSTAR2002/BSTAR2006/NLTE-OBGRID 重叠点 **H/He 部分**比对(不含 CNO 验证) | 中 | §3.4 经典网格表 |
| 15 | 与 PHOENIX/ATLAS9 独立代码比对(H/He 连续谱交叉) | 中-高 | §3.4 经典网格表 |
| 16 | 与 SDSS/LAMOST 实测 sdB 比对(卷积仪器轮廓后) | 高 | §3.4 路线 D |
| 17 | Balmer/He 线序列单调性与基准 | 中 | — |
| 18 | 网格二阶差分离群点检测 | 中 | — |
| 19 | 统计平衡残差 / b 因子合理性(`.bfac` | 中 | — |
### 配方变更触发规则
任何以下变更必须重跑第三层深度核查:CHMAX/NITER/ND 改动、原子模型/线表(fort.19)更新、TLUSTY/SYNSPEC 重编译(尤其加 `-fcheck`/`-ffpe-trap`)、丰度网格范围调整、**fort.55 字段修复(§5**。
---
## 7. 综合判断
**当前 pipeline 在"光谱物理正确性校验"上基本没做** —— 只做了数值收敛性(max_relc)和最低限度的完整性(`.7` 无 NaN、synspec 退出码),完全没有能量守恒、辐射平衡、统计平衡这三项核心物理不变量校验。
**投入产出比排序**
1. **P0:解析 `.6` 的能量守恒诊断**(§1.1)—— 数据已就位,Hubeny 官方 1% 判据,是物理正确性的第一道硬门槛。第一层硬门槛 #1/#2/#3 已在近期提交修复(`.spec` 内容校验、NaN 0 行容忍、fort.9 缺失分流),**此项 #4 是第一层唯一仍真实存在的缺口**。
2. **~~P1fort.55 字段错位修复~~(已完成,见 §5)**——`SynspecInput` 已重构为 9 子结构体与 SYNSPEC 一一对应,IDSTD 修正为 0。**待办:重算受影响光谱**(把 `synspec_stage.policy``force_recompute` 重跑 SYNSPEC 阶段,TLUSTY 大气不受影响)。
3. **P1:网格级种子传播校验**(§2.3)—— 这是网格计算特有的静默放大机制,可通过"同一物理族下游点的 max_relc/通量守恒分布异常"间接检测。
4. **P2:质量监控层**(§6 第二层)—— bolometric 积分、Planck 检验、连续谱自洽。
5. **P2:深度核查层**(§6 第三层,§3.4)—— 分两支:**(a) 网格内部 CNO 单调性 + hhe 退化比对**(路线 A/B,数据现成、CNO 独有,优先做);**(b) 外部基准**——同代码栈的 Pacheco 2021(路线 C)是唯一能比 CNO 连续谱/线位的社区网格;经典网格(OSTAR/BSTAR/NLTE-OBGRID)不含 CNO NLTE,仅能验证 H/He 部分。
---
## 8. 参考资料
**官方文档**
- Hubeny 2017, "A Brief Introductory Guide to TLUSTY and SYNSPEC"arXiv:1706.01859)—— 通量守恒 1% 判据、假收敛、ND=50 B 星例、改丰度警告
- TLUSTY User's Guide IIIarXiv:1706.01937)—— fort.9 收敛日志格式、unit 6 辐射平衡表
- TLUSTY User's Guide IV, Hubeny 2021arXiv:2104.02829)—— CHMAX=10⁻⁴ 高精度例
**社区基准网格(ground truth**
> 适用性:以下经典网格**均不含 CNO NLTE 同时建模**CNO 按 LTE),只能验证 H/He 部分(§3.4)。本网格(sdB + CNO 全 NLTE)的外部基准首选 Pacheco 2021(同代码栈、含 CNO NLTE)。
- **Pacheco & Díaz 2021**ApJS 256, 41, DOI:10.3847/1538-4365/ac2508)—— **TLUSTY v208 + SYNSPEC 54**(与本网格同版本),sdB/sdO NLTE 含 C/N/O NLTE 线覆盖;Teff 10-65kK、logg 4.5-6.5、He-poor/rich 两支;1000-10000Å @ 0.01Å;数据公开(SpecModels `specmodels.iag.usp.br` / SVO `svo2.cab.inta-csic.es/theory//newov2` / Vizier)。**CNO 丰度为整体 solar 或 [Fe/H]−1.5 两组固定比例,非独立扫描**——不能逐点比对 CNO 维度,但可比对连续谱形状与线波长定位。
- **Németh et al. 2014**ASPC 481, 95arXiv:1308.0252)—— Pacheco 2021 的前身,TLUSTY/SYNSPEC NLTE sdB/sdO 网格(3200-7200Å),覆盖 (He-)sdB/sdO 全参数空间。
- **Latour et al. 2011**ApJ 733, 100)—— sdO NLTE 网格(TLUSTY/SYNSPEC),含 H+He 及可选 C/N/O NLTE 模型。
- OSTAR2002 / BSTAR2006tlusty.oca.eu)—— Lanz & Hubeny 2003/2007 社区标准网格(**H/He NLTECNO LTE**
- NLTE-OBGRIDMAST archive.stsci.edu/hlsp/nlte-obgrid)—— Hubeny 更新版(**H/He NLTECNO LTE**
- WD-GRIDMAST archive.stsci.edu/hlsp/wd-grid)—— Bohlin+2020 白矮星 NLTE 网格,1500Å-30μm 对 CALSPEC 1% 一致(**仅适用 WD,非 sdB**
**交叉代码比对**
- Witzke+2021, MPS-ATLASA&A)—— 三代码流量差异:~400nm 最大,其他波段几个百分点
- CASTELLI/Kurucz ATLAS9STScI
**本仓库内相关文档**
- `docs/tlusty&synspec收敛性判断.md` —— rc 不可靠性、fort.9/NaN 阈值漏洞 1-5 修复历程、gfortran 退出码实测(第一层硬门槛的依据;注意该文档 §4 部分漏洞描述仍是修复前状态,本文档已同步现状)
- `docs/tlusty_result_artifacts.md` —— fort 单元语义、`.emflux`/`.bfac` 快照、TLUSTY fort.14 与 SYNSPEC `.spec` 关系
- `docs/PIPELINE.md` —— 阶段链、nst 配方(ND=50、默认 CHMAX=0.001)、物理极限区
**源码证据**
- 能量守恒诊断:`tlusty208.f:14179-14191,14271-14273`OUTPRI);`:49854`RECHCK);实测 `hotsd/tlusty (副本)/hhe35nl.6:341``(RAD+CON)/TOT=1.00106E+00`,收敛良好)
- fort.55 错位(已修复):旧 `fort55_writer.rs:5``imode,idrv,ifreq` vs SYNSPEC `synspec54.f:253``IMODE,IDSTD,IPRIN`;现重构为 `config.rs``Fort55Line1`-`Fort55Line6` + `Fort55LineMol`/`Fort55LineVtb`/`Fort55LineAng` 9 子结构体按行对应。`synspec54.f:269` `iophli=0` 无条件覆盖(已核实,SYNSPEC 既定行为)。
- 种子传播:`reporter.rs:122`(门控)、`executor.rs:221`(读种子 bytes)、`seed_finder.rs:26-40`(有向最近邻)
- 漏洞 1 修复:`conv_check.rs:287-335`spec_is_valid)、`runner.rs:645`(调用点)
- 漏洞 4 修复:`conv_check.rs:264-265`0 行容忍)
- 漏洞 2 进阶修复:`runner.rs:448-477``conv_check.rs:88-98`