# 收敛恒星光谱的物理正确性保证调研 > 调研日期: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` 内容校验)、漏洞 2(fort.9 缺失分支虚构 best_max_relc)、漏洞 4(NaN 阈值)已在后续提交中修复。**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 17(fort.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`、不带 `.` 前缀,`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_relcIP 才替换 | 未捕获 | 高 | | **波长网格步长固定且窗口窄** | 窄线被漏掉,谱"正常但缺线" | 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×Teff;idstd 处 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/logHe,C 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 脚本的实现前提。 #### 路线 B:TLUSTY 官方 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 2021(ApJS 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 / BSTAR2006(Lanz & 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**) | MAST(GD153/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>30kK)Balmer 线异常强 | NLTE 电离算错 | 测 Hα/Hβ EW 比对基准 | | 连续谱斜率与 Teff 不符 | 温度结构错 | RJ 尾比对 Bλ(Teff) | | 沿 Teff 序列 Balmer/He 线非单调 | 局部点 NLTE 错 | 网格级单调性检查 | | 孤立单点与四邻插值偏离 >20% | 该点异常 | 离群点检测 | --- ## 5. fort.55 字段错位问题(已修复) > **状态:已修复(2026-08-07)**。本节保留问题描述作为历史记录,处置见 §5.3。 > 影响所有光谱的系统性 bug:Rust 端 `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 漏洞 1(P0) | | 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. **~~P1:fort.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 III(arXiv:1706.01937)—— fort.9 收敛日志格式、unit 6 辐射平衡表 - TLUSTY User's Guide IV, Hubeny 2021(arXiv: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, 95;arXiv: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 / BSTAR2006(tlusty.oca.eu)—— Lanz & Hubeny 2003/2007 社区标准网格(**H/He NLTE,CNO LTE**) - NLTE-OBGRID(MAST archive.stsci.edu/hlsp/nlte-obgrid)—— Hubeny 更新版(**H/He NLTE,CNO LTE**) - WD-GRID(MAST archive.stsci.edu/hlsp/wd-grid)—— Bohlin+2020 白矮星 NLTE 网格,1500Å-30μm 对 CALSPEC 1% 一致(**仅适用 WD,非 sdB**) **交叉代码比对** - Witzke+2021, MPS-ATLAS(A&A)—— 三代码流量差异:~400nm 最大,其他波段几个百分点 - CASTELLI/Kurucz ATLAS9(STScI) **本仓库内相关文档** - `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`