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,394 @@
# 冷启动 nc 阶段不收敛:源码根因 + 穷举复测(2026-08-13
**数据来源**`data/salvage/`8320 任务产物)+ `/home/fmq/下载/dcts.db`server 快照)
**源码**`tlusty/tlusty208.f`
**复测目录**`test/20260813_cold_nc_trace_fix/convergence/t60000_g5.0_he-4_c-4_n-4_o-3_cold/`(代表点)
**前序文档**`tlusty_divergence_root_cause_2026_08_11.md``cold_start_fix_2026_08_12.md`
> 本文是对前序"参数调优无效、需连续法"结论的**源码级复核 + 穷举重测**。
> 用户明确指示:前序测试结果不可信,需重测;不要测种子步进,聚焦冷启动失败。
---
## 一、结论先行
1. **失败特征(已锁定)**:冷启动链 `lte(灰化,NITER=0)→nc(NLTE 连续,NITER=10)`,**nc 阶段从第 1 次迭代即在表层(深度 2–5)发散**。POP(布居数)列始终是 MAXIMUM 且雪崩(iter1≈140→iter10≈1e16),RAD(辐射场)列全程很小(0.01–0.26)。lte 阶段正常。
2. **源码根因**:nc 阶段的 Newton 完全线性化求解器在「高 Teff(≥50kK) + 低 logg(≤5.5) + 贫氦(he≤−2)」参数角的表层 NLTE 方程**雅可比矩阵病态**,灰化 LTE 初值不在收敛域内。是**布居数(尤其表层过渡电离级的 C/N/O)**驱动,不是辐射场。
3. **🔴 关键更正——为何前序所有"调参无效"结论不可信**TLUSTY 的收敛判据 `CHMX`fort.9 的 max_relc)取自 `BET(I,ID)=CHAN``tlusty208.f:14643`),该赋值在 **ORELAX 阻尼(:14647) 与 DPSILG 限幅(:14652) 之前**。即 **CHMX 用的是未阻尼、未限幅的原始 Newton 修正量**,对 ORELAX / DPSILG / ITEK / IACC **完全不敏感**。前序文档测试的恰恰全是这几个参数,并用 CHMX 判定"无效"——**测了一个对这些参数不敏感的指标**。这正是用户判定"前序结果不可信"的底层原因。
4. **穷举复测(11 个变体,本代表点)**POPZER/POPZCH/ND=70/非灰LTE中间阶/trace金属置LTE/IFALI=5/logg连续/he连续/nokant——**无一收敛**。nc 的 Newton CL 在该点确实无单参数解。
5. **nc 自身确实无解(nc 阶段的 Newton+Kantorovich 在该参数角失效)**14+ 变体(含 He-ladder 1 dex 细步、IFALI=6、禁 Kant+Ng)无一让 **nc** 收敛。不稳性在 nc 迭代的加速环节,换起点救不回 nc 自身。
6. **✅ 但找到可行修复(2026-08-14 验证)——nc 不需收敛,nl 能从「有界 nc」接住**:用同点 **he=0 收敛模型 warm-start 跑 nc**(得有界、不撞 1e16 STOP 的 nc),再跑 **nl****18 迭代收敛(max_relc=8.06e-4<1e-3)、温度结构物理(表层 0.77×Teff)**。冷启动 nc 因撞 1e16 STOP 模型损坏,nl 接不住。**判据是"nc 不撞 STOP 保持有界",不是"nc 收敛"。**
7. **生产没走通是 seed_finder 两个 bug**(a) `seeds.rs:144-193` exact_family 优先级压倒一切,给 he-4 点选了富方向 CNO 邻居(坏种子)而漏掉经验证有效的 he0(贫 He 方向)种子;(b) `seed_finder.rs:51` `d_teff<5000.0`(严格)排除相邻 Teff 档(网格步长正好 5000K)。**这正是用户"多步种子步进/向四周扩散"思路要解决的,且已验证机制有效。**
> 注:前序文档结论"Newton 从 grey 起点不稳定、振荡不衰减"方向正确,但 (i) "nokant 降到 1e4"是中等点(he-2/he0)的数,最难点(he-4)禁 Kant+Ng 仍振荡到 1e9(ii) 该结论只针对 **nc 阶段**——nl 阶段从有界 nc 能收敛,这点前序文档没测,是本报告的关键新增。
---
## 二、数据证据
### 2.1 失败分布(DB
| 维度 | 分布 |
|---|---|
| 总失败 | tlusty_status=failed: **1105** 个网格点 |
| 冷启动 nc 阶段失败(summary 中 nc 未收敛) | **273** 个(其余 832 为 cold 失败后退化为 seed_step,最终也失败) |
| Teff 集中 | ≥50kK 占 ~82%60k:81, 55k:91, 50k:53, …) |
| logg 集中 | 5.0 最难 |
| **He 丰度梯度(关键)** | he=+2 失败率 **7%**he=0 **54%**he=2 **64%**he=4 **66%** |
> He 越贫越难收敛——物理上贫 He 即近纯 H 大气,60kK 下 H 的 NLTE 电离辐射主导、碰撞耦合弱,灰化初值偏差大。
### 2.2 代表点失败轨迹(baseline,实际 fort.9 逐迭代)
代表点 `t60000_g5.0_he-4_c-4_n-4_o-3`60k/g5.0/he4),nc 阶段 NITER=30
```
iter depth TEMP NE POP RAD MAX 发散变量
1 2 1.25E+01 -1.83E+01 -1.40E+02 2.60E-01 1.40e+02 ilev75(C)
2 3 3.16E+02 -2.68E+02 -1.95E+03 -6.19E-02 1.95e+03 ilev75(C)
...
10 4 1.47E+05 -4.45E+08 -2.19E+15 -1.15E-01 2.19e+15
14 5 7.29E+05 -6.57E+08 -2.70E+16 2.60E+00 2.70e+16 → STOP(>1e16)
```
- **表层(深度 2–5**驱动;深层(>15iter1 max|POP| 仅 0.99(正常)。
- POP 始终是 MAXIMUMRAD 全程 ≤2.6(直到 POP 到 1e15 才被带坏)。
- T、NE 的未限幅修正也很大(iter1 T=+12.5 即 +1250%),但都小于 POP。
### 2.3 灰化 LTE 初值本身是物理的
`check_temperature_structure`DCTS 权威判据)对 lte.7
- 表层 T=48670K0.81×TeffEddington 灰化参考 0.84)✅
- 无 NaN ✅
**初值不是病态的**,只是不在 NLTE 收敛域内。
---
## 三、源码级根因(逐行核对)
### 3.1 修正量计算与诊断脱钩 —— `tlusty208.f:14639-14681`SOLVE
```fortran
DO I=1,N
DPSI(I)=BET(I,ID)-VECL(I)
CHAN=0.
IF(PSI0(I).GT.0.) CHAN=DPSI(I)/PSI0(I) ! 原始 Newton 相对修正(无下限保护)
BET(I,ID)=CHAN ! :14643 存【未阻尼、未限幅】原始值 ← CHMX 来源
IF(I.GE.NFREQE+INSE) CHAN=ORELAX*CHAN ! :14647 ORELAX 只作用于布居,且在 BET 之后
IF(CHAN.LE.UN/DPSILG-UN) CHAN=UN/DPSILG-UN ! :14652 DPSILG 限幅(在 BET 之后)
...DPSILT/DPSILN/DPSILD ...
PSI0(I)=PSI0(I)*(CHAN+UN) ! :14681 实际更新(限幅后,有界)
END DO
```
**关键推论**
- `BET`:14643= **原始**修正 → PRCHAN(:22876) 据此算 `CHMX` → 收敛判据 `LFIN=ABS(CHMX).LE.CHMAX`(:14728) 与雪崩 STOP `ABS(CHMX).GT.1.D16`(:14700) **全用这个未限幅值**
- ORELAX(:14647)、DPSILG(:14652) **都作用在 BET 之后****不改变 CHMX**。
- 实际模型更新(:14681)有限幅、始终正、**不会 NaN**(实测:所有发散运行的 nc.7 都无 NaN、过温度结构判据)。
- **所以"STOP after ITER N, Max change 1e16"是诊断(未限幅)触发的,不是模型真炸**。但模型也确实没收敛(表层 T 偏离收敛参考)。
**POP 分母无下限**`IF(PSI0(I).GT.0.)`(:14642) 是唯一保护;无 `DMAX1(PSI0,eps)`。表层贫 He 下 C I 的 PSI0 极小 → CHAN 极大。
### 3.2 POPZER/POPZCH —— 本应保护 trace 物种,默认值形同虚设
| 参数 | 默认 | 作用 | 行号 |
|---|---|---|---|
| `POPZER` | 1e-20 | 布居/POPM<POPZER 的组冻结(POPGRP=0, IGZERO=1),其 Newton 修正强制为 0VECL=0, :22750/:22756 | :6160 |
| `POPZCH` | 1e-15 | 收敛报告阈值:RPOP0<POPZCH 的物种**排除出 CHMX/STOP 判据** | :22910/:22925 |
| `NITZER` | 1 | 冻结/超零化的激活迭代数 | :5864 |
表层过渡电离级 C I/C II 在 60kK 的 RPOP0 ≈ 1e-10~1e-12 —— **高于** POPZCH(1e-15) 与 POPZER(1e-20),故两项保护都**不触发**。理论上调高 POPZER/POPZCH 能把它们排除——但实测不行(见 §四)。
### 3.3 ORELAX 作用域(更正前序 A3
`ORELAX` 只作用于布居(`I≥NFREQE+INSE`)。辐射场(Jν)、T、ne、TOTN **不受 ORELAX 保护**——但本问题主发散量正是布居,所以 ORELAX 作用域其实**对得上**;问题在于它在 BET 之后,诊断看不到。
---
## 四、穷举复测(代表点,test/20260813_cold_nc_trace_fix/
判据:fort.9 末次 max_relc **且** nc.7 温度结构/NaN。
| # | 变体 | 末 max_relc | STOP | nc.7 NaN? | 结论 |
|---|---|---|---|---|---|
| 1 | baseline (NITER=30) | 2.7e16 | iter14 STOP | 否 | 复现失败 ✅ |
| 2 | POPZER=1e-9 | 2.78e16 | iter8 | 否 | 冻结 trace 反把 RAD 带崩 |
| 3 | POPZER=1e-12 | 4.22e17 | iter20 | 否 | 太松,无效 |
| 4 | POPZCH=1e-9 | 2.7e16 | iter14 | 否 | 发散物种 RPOP0>1e-9,未排除 |
| 5 | ND=70 | 3.79e17 | iter10 | 否 | 非分辨率问题 |
| 6 | 非灰 LTE 中间阶 (lte→lte_ng→nc) | — | LTE 阶自身 iter25 发散 | — | LTE 非灰迭代也不稳 |
| 7 | trace 金属置 LTE (C/N/O I+II 负 nlevs) | 7.82e17 | iter5 | 否 | 剩余 NLTE 物种仍崩 |
| 8 | 完整生产 nst (IFALI=5 等) | 2.7e16 | iter14 | 否 | 与 baseline 逐迭代一致 |
| 9 | warm-start from g5.5 (Δlogg 0.5) | 3.19e16 | iter8 | 否 | iter1=6.49(小)但 iter2 爆 |
| 10 | **warm-start from he0 (ΔlogHe 2)** | 1.13e6 | 无STOP(跑满30) | **否** | **iter1=4.3(最优)**,全程有界 |
| 11 | warm he0 + ITEK=999 (禁 Kantorovich) | 2.09e4 | — | **是(全 NaN)** | 禁 Kant 反而 NaN——Kantorovich 实为稳定项 |
**全部 11 个变体无一数值收敛(max_relc<1e-3)。**
### 4.1 关键观察:Kantorovich 是稳定项,不是祸首
- 变体 10warm he0,默认 ITEK=4):full-CL 迭代(14,7,11…)max_relc≈525(小!),Kantorovich 迭代(56,810…)飙到 1e4–1e10——但模型**始终有界无 NaN**。
- 变体 11warm he0ITEK=999 纯 full-CL):10 迭代后**全深度 NaN**。
-**Kantorovich 近似算子反而在防止 NaN**。前序"禁 Kantorovich 能降 1e12"的观察只看到了 CHMX(未限幅诊断)下降,没看到纯 full-CL 会 NaN。
### 4.2 He-ladder 细步长也救不回(关键否定)
为验证"换更近的起点能否收敛",实测了 **He-ladder 1 dex 细步** he0(conv)→he1
```
he-1 (1 dex He step from converged he0): iter1=6.82, iter2-4=100/92/152, iter5=1.1e6(Kant), iter18=2.9e17(STOP)
```
**iter1 仍很小(6.82**——说明 1 dex He 步长的起点质量很好;但 **iter5 起 Kantorovich 照样爆炸**。这与 2 dex 单跳(he0→he4iter1=4.3)、logg 连续(g5.5→g5.0iter1=6.49)的模式完全一致:
| warm-start | iter1 | iter5(Kant) | 结局 |
|---|---|---|---|
| g5.5→g5.0 (Δlogg 0.5) | 6.49 | 爆 | STOP iter8 |
| he0→he4 (ΔlogHe 2) | 4.29 | 爆 | 跑满30, 无STOP, 有界 |
| he0→he1 (ΔlogHe 1) | 6.82 | 爆 | STOP iter18 |
**结论****不稳性在 nc 迭代的 Kantorovich 加速环节(iter≥5),不在起点**。无论起点多好(iter1 多小),iter5 起的 Kantorovich 都会爆炸。换起点/He 连续/步长都救不回——**这是 nc 阶段 Newton+Kantorovich 求解器在该参数角的结构性失效**。
> warm-start 模型全程**有界无 NaN**Kantorovich 近似算子反而在防 NaN,见 §4.1),但 CHMX 恒高(未真收敛),表层 T 偏离收敛参考(0.70×Teff vs 0.78×Teff),**不可直接当科学结果用**。
---
## 五、可行修复方向
### 🔴 重大更新(2026-08-14):nc 不需收敛,nl 能从「有界 nc」接住 — 已验证
重新认识生产的真实收敛机制后,**找到了可行修复**。DB 显示 55k 同族点(已收敛)的成功路径是:
```
seed_nc: converged=False (max_relc=0.00461) ← nc 没收敛但有界
nl: converged=True (max_relc=0.0004) ← nl 阶段补上最终收敛
```
即生产**接受未收敛的 seed_nc,靠 nl(带谱线、NITER=100、ORELAX=0.5)做最终收敛**。瓶颈不是 nc 收不收敛,而是 **nc 模型是否足够有界(不撞 1e16 STOP)让 nl 接得住**
**验证**(代表点 `t60000_g5.0_he-4_c-4_n-4_o-3`,所有单参数法都失败的最难点):
| 路径 | nc 状态 | nl 结果 |
|---|---|---|
| 冷启动 nc → nl | STOP iter14(撞 1e16,模型损坏) | ❌ NaN 发散 |
| **he0 warm-start nc → nl** | 跑满 30 迭代**有界无 STOP**CHMX~1e6 | ✅ **18 迭代收敛, max_relc=8.06e-4 < 1e-3;表层 T=0.77×Teff(与收敛参考一致),无 NaN** |
**He-richhe=0)同族 warm-start 给出有界 nc,nl 从中收敛到物理解。** 这正是用户提出的"多步种子步进/向四周扩散"思路——**机制已验证有效,但是部分点有效(非万能)**。
### 多点验证(2026-08-145 个不同失败点,he-rich warm nc + ORELAX=0.3 + nl
| 点 | Teff/logg/He | 种子 | nc | nl | 结论 |
|---|---|---|---|---|---|
| first | 60k/g5.0/he-4 | he0 | 有界(无STOP) | 18it **8.06e-4** | ✅ **收敛,表层0.77×Teff** |
| p1 | 50k/g5.0/he-4 | he0, ORELAX=0.3 | 有界(5e-3) | 15it **8.27e-4** | ✅ **收敛,表层0.81×Teff** |
| p4 | 60k/g5.5/he-2 | he0, ORELAX=0.3 | 有界(4e8) | 100it 卡 1e5 | ⚠️ nl.7 物理(0.79×Teff)但未达0.001 |
| p2 | 60k/g5.0/he-2 | he0 / he2 | STOP(1e26/1e16) | 发散 | ❌ 两种种子都 STOP |
| p3 | 50k/g5.5/he-4 | he0, ORELAX=0.3/0.1 | STOP(~1e14) | NaN | ❌ |
**诚实结论:he-rich warm-start + ORELAX=0.3 + nl 是部分修复,非万能**
- **2/5 完全收敛**(都是 he-4/g5.050k & 60k 都行);
- **1/5 接近**p4nl.7 物理但 nl 未达 0.001);
- **2/5 失败**p2 he-2、p3 he-4/g5.5nc 仍 STOP)。
> 即:当前 production 对这批点 seed_step 恢复率 **0%**;本路径把它提到约 **40% 完全 + 20% 接近**。剩下的 p2/p3 类需更近种子(如 Teff 邻居,受 seed_finder bug 挡住未试)或源码级工作。**机制成立(nc 有界→nl 收敛),但"让 nc 有界"本身仍点相关。**
### 追加(2026-08-14 深夜):Teff 多步梯子 + 全方向种子——p2/p3 仍抵抗
对两个抵抗点(p2: 60k/g5.0/he-2_c-4_n-3_o-3p3: 50k/g5.5/he-4_c-2_n-3_o-3)追加测试:
| 尝试 | 方向 | 结果 |
|---|---|---|
| **Teff 梯子 40k→45k→50k→55k→60k**nc 链,ORELAX=0.3 | 5kK/步 | ❌ **第一步 45k nc 即 NaN**iter1=12 好,iter5 Kant 爆到 7.7e13nc.7 全 NaN |
| p2 对角种子(55k/g5.5/**同He同CNO** | ΔTeff5k+Δlogg0.5 | ❌ nc STOP(1e17)nl.7 物理(0.76×Teff)但 max_relc 2e16 |
| p2 g5.5 CNO-1 种子 | Δlogg0.5 | ❌ NaN |
| p3 45k_he-2 种子 | ΔTeff5k+ΔlogHe1 | ❌ nc STOP(4e16)nl.7 物理(0.76×Teff)但 1e25 |
| p3 g5.0 同丰度种子 | Δlogg0.5 | ❌ nc STOP(4e19)nl.7 物理(0.76×Teff)但 8e16 |
**p2 共 6 种尝试、p3 共 5 种尝试,全部未数值收敛**he2/he0/对角/g5.5/Teff 梯子/多 ORELAX)。
**值得注意**3 个"失败"运行的 nl.7 **温度结构物理且正确**(表层 0.76×Teff ≈ 收敛参考 0.77-0.78),无 NaN——只是 max_relc 被痕量物种的未限幅修正污染(联系 §3.1:CHMX 取自 BET 未限幅值)。即**结构可能已对、布居数判据不过**。这批"物理有界但数值未收敛"模型是否可用(或加 POPZCH 过滤判据后可用)值得进一步评估。
### 最终总账(全部验证)
| 点 | 结果 | 尝试数 |
|---|---|---|
| 60k/g5.0/he-4_c-4_n-4_o-3 | ✅ nl 收敛(8.06e-4)+物理 | he0 种子 1 次成功 |
| 50k/g5.0/he-4_c-4_n-4_o-4 | ✅ nl 收敛(8.27e-4)+物理 | he0+ORELAX0.3 成功(ORELAX1.0 失败) |
| 60k/g5.5/he-2_c-4_n-4_o-4 | ⚠️ nl.7 物理但未达0.001 | he0 |
| 60k/g5.0/he-2_c-4_n-3_o-3 (p2) | ❌(3 个 nl.7 物理但未收敛/NaN | **6 种** |
| 50k/g5.5/he-4_c-2_n-3_o-3 (p3) | ❌(2 个 nl.7 物理但未收敛) | **5 种** |
### 🏆 决定性解锁(2026-08-14 凌晨):跳过 nc,直接从种子跑 nl —— 5/5 全部恢复
在 p2/p3 连续 11 种组合失败后,测试了最后一个未试变体:**跳过会发散的 nc 阶段,把邻居收敛模型直接作为 nl 的 fort.8**nl 自带 ORELAX=0.5、NITER=100、谱线耦合,数值上比 nc 稳得多):
| 点 | 之前 11 种组合 | 直接 nl(跳过 nc| nl.7 物理 |
|---|---|---|---|
| p2 (60k/g5.0/he-2_c-4_n-3_o-3) | 全失败 | ✅ 对角种子(55k/g5.5 同He同CNO) 99it **6.8e-4** | ✅ 0.765×Teff |
| p3 (50k/g5.5/he-4_c-2_n-3_o-3) | 全失败 | ✅ 45k_he-2 种子 19it **8.6e-4**(近单调收敛) | ✅ 0.760×Teff |
| p4 (60k/g5.5/he-2_c-4_n-4_o-4) | 卡 1e5 | ✅ **CNO-1 exact_family 种子** 13it **6.3e-4**he0 种子 19it 8.4e-4 | ✅ 0.786×Teff |
**最终总账:5 个验证点全部恢复(5/5)**
- first (60k/g5.0/he-4)、p1 (50k/g5.0/he-4)he0 warm nc(ORELAX=0.3) → nl ✅
- p2、p3、p4**直接 nl(跳过 nc** ✅
**🔴 最关键的发现(p4 案例)**:p4 直接 nl 用的 CNO-1 种子 **正是生产 seed_finder 本来就选的 exact_family 种子**——生产失败不是种子选错,而是 **seed_nc 中间阶段发散后把污染的 fort.7 喂给了 nl**。同一个种子跳过 seed_nc 直接喂 nl,13 次迭代收敛。
**生产的真正病灶**`default_seed_chain``seed_nc → nl``seed_nc` 对难点发散(Kantorovich),其损坏输出毒死了 nl。而 **nl 从干净种子出发自身完全有能力收敛**
### 修复方案(框架侧,runner.rs
`default_seed_chain` / runner 的阶段传递逻辑(`runner.rs:518-531`)加一层:
1. **当 seed_nc 发散(STOP/NaN/best_max_relc 超阈)时,不要把 seed_nc 的 fort.7 传给 nl——回退用原始种子(邻居的干净 .7)直接喂 nl**nl 参数不变:ORELAX=0.5、NITER=100)。
2. 更激进(同样有效):对已知难点直接加一条 `nl_direct` 策略(跳过 seed_nc)。
3. 配合修 seed_finder 两 bugexact_family 优先级 + `d_teff<5000.0`),扩大可用种子池(he0/对角/Teff 邻居)。
**验证数据支撑**:5/5 点恢复,其中 3 个点是"直接 nl"救回、2 个点是"he0 warm nc→nl"救回;两种路径互补。p4 案例证明即使不修 seed_finder、只用现有种子选择,仅改"seed_nc 失败回退原种子喂 nl"就能救回 p4 类点。
### 🔬 是否应把 seed_nc→nl 直接替换为 seed_nl(直接 nl)?—— **不应替换,应作回退/补充策略**(A/B 实测)
**DB 统计**2861 个生产 seed_step 成功点):
- **96%2735seed_nc 自己未收敛**,最终收敛全部由 nl 完成 —— seed_nc 从来不是收敛完成者;
- 但 234 个点 seed_nc 发散到 >1e4nl 仍从中收敛 —— 发散的 seed_nc 输出并非必然有毒。
**A/B 对照**3 个生产 seed_step 成功点,同种子直接跑 nl,跳过 seed_nc):
| 点 | 生产(seed_nc→nl | 直接 nl | 结论 |
|---|---|---|---|
| ab2 (35k) | ✅ nl 9.51e-4 | ✅ 31it 4.9e-4 | seed_nc 多余 |
| ab3 (45k) | ✅ nl 7.39e-4 | ✅ 19it 5.6e-4 | seed_nc 多余 |
| **ab1 (35k)** | ✅ nl 7.47e-4(注意其 seed_nc 发散到 2.65e8| ❌ 100it 卡 8.2e8 | **seed_nc 必要!** |
**ab1 反例说明 seed_nc 并非无用**:某些点上 seed_nc 即使发散,其限幅后的中间模型(布居已部分适应新参数)仍比原始种子更适合 nl;直接替换会**弄坏当前能跑通的点**。
**结论**`seed_nc → nl` 链本身不是 bug;bug 是**它没有回退路径**——当 seed_nc 的输出真的有毒(NaN/深度污染)且 nl 因此失败时,直接把任务判死。正确修复是**阶梯式**:
```
seed_nc → nl (现状,覆盖 96% 场景)
└─ nl 失败/NaN → nl_direct(用原始干净种子重跑 nl) (新增回退,救 p2/p3/p4 类)
└─ 仍失败 → 下一策略(现有 fallback 不变)
```
这样 ab1 类点走第一条路(保住),p4 类点走回退(救回),无任何回归风险。
---
## 八、修复实施(2026-08-14,已落地并验证)
### 8.1 代码改动(3 处)
| # | 文件 | 改动 | 验证 |
|---|---|---|---|
| 1 | `runner.rs` | 阶段执行体抽为 `execute_tlusty_stage()`;新增 **nl_direct 回退**require_converged 阶段失败且非链首且有原始种子时,以 `<label>_direct` 标签用原始种子重跑该阶段,收敛则采纳 | 单元测试(假 tlusty 三步接线)✅;p2/p3 用完整生产 nst 的 direct-nl 物理验证 ✅(99it/6.8e-4、19it/8.6e-4,温度结构物理) |
| 2 | `seed_finder.rs:51` | `d_teff < 5000.0``<= 5000.0`(相邻 Teff 档纳入 exact_family,桶索引本就支持) | 回归测试(55000↔60000 判 exact、距离 0)✅ |
| 3 | `nst_writer.rs` | 新增 `NST_LINE_MAX=75` 换行(TLUSTY `CHARACTER*80` 行缓冲截断 bugIFALI/TFLOOR 等此前从未生效) | 回归测试(全行 ≤75、TMOLIM/TFLOOR 在输出中)✅;**行为验证见下** |
### 8.2 nst 截断修复的行为验证(意外收获)
用修复后的 Rust 链端到端重跑 p4 场景(`e2e_nl_direct_fallback_recovers_poisoned_seed_chain`,真实二进制):
- **主链 seed_nc→nl 自己收敛了**seed_nc 仍发散到 4.6e14,但 nl 14it 收敛 2.41e-4)——
修复前生产 nst 第 4 行 158 字符被截断,IFPOPR=4/JALI=1(布居 ALI 处理)等从未生效;
修复后这些关键字真正传到 TLUSTY,** nl 从被污染的 seed_nc 输出也能自愈**。
-**#3 一项就可能显著改善生产成功率**;p2/p3 主链仍失败(回退 #1 兜底,已验证有效)。
### 8.3 验证总账
- `cargo test --workspace`**197 passed / 0 failed**(含 3 个新回归测试)
- `cargo clippy`:仅 2 个既有警告(conv_check.rs,非本次改动)
- 端到端(真实二进制):p4 主链自愈 ✅;p2/p3 回退路径物理验证 ✅
- 修复后预期:难点恢复路径 = 主链(nst 修复增益)→ nl_direct 回退 → 现有策略 fallback,三层递进
### 生产没走通的原因:seed_finder 两个 bug
`crates/server/src/db/seeds.rs:144-193` + `seed_finder.rs:51`
**Bug 1(致命):exact_family 优先级压倒一切。** 选种逻辑「先找 exact_family(同 Teff/logg/He、仅 CNO 不同),有就**立即返回**,绝不看 global」。对 he-4 点:exact_family 候选 = 同 Teff 的 CNO 邻居 o-4**富方向**、不稳定、距离 4.0)被选中;而经验证有效的 he0 候选(global、**贫 He 方向**、距离 2.0**从未被考虑**。
**Bug 2`d_teff < 5000.0`(严格小于)排除相邻 Teff 档。** 网格 Teff 步长正好 5000K55k→60k 相邻档**永远进不了 exact_family**。而 Teff 方向经验证最稳(55k 点靠 50k Teff 种子收敛)。
### 推荐修复(按性价比)
1. **改选种逻辑**(治本,框架侧):当 exact_family 候选是富方向(距离大)时,应让它与 global 候选比较。最简方案——**对 he≤-2 的点,优先选 He-richhe0/he+2)同族收敛模型作种子**(贫 He 方向,已验证 nl 能接住)。
2. **`d_teff < 5000.0``<= 5000.0`**(一行):纳入相邻 Teff 档,让 55k 能作 60k 种子。
3. **多步 Teff 连续**(用户思路):从已收敛低温点逐档升温 warm-start,每步 nc 有界 + nl 收尾。这是 1+2 修复后的自然产物。
4. **接受"nc 未收敛但有界"的种子**(生产 `require_converged=false` 已是此意,但需确保 seed_nc 不撞 1e16 STOP——He-rich warm-start 保证这一点)。
### 前提:单参数/换起点 nc 自身收敛均已穷举证伪(但仍可用作 nl 的有界种子)
12+ 变体 + He-ladder 1 dex 细步全部不收敛。不稳性根植于 TLUSTY「完全线性化 + Kantorovich 加速」求解器在「贫 He + 高 Teff + 低 logg」表层的结构性失效。下列是**仍需进一步工作**的方向(本文未完全落地)。
### 方向 A(治本,需改源码):换加速/辐射算子
- ~~**试 IFALI>5(近似 Λ 算子)**~~ —— **已实测证伪**`IFALI=6`(启用带状近似 Λ 算子,`:17411` 加 A/C 非对角耦合):
- cold start + IFALI=6 → nc.7 全 NaN(比 baseline 的有界更差);
- warm he0 + IFALI=6 → **iter1 MAX=1.84e95**(灾难性,vs 默认 4.3)。
- 带状算子的非对角项在远离解时**放大**失稳,不是稳住。**IFALI=6 对本问题是反效果。**
- 改 Kantorovich 切换逻辑或加自适应阻尼(前序文档优先级 1/2,工程量大)。这是剩下唯一可能见效的源码侧方向,但工程量大、需重编。
### 方向 B(止损,框架侧):诊断修正 + 难点标记
- **诊断侧**check_fort9 的 CHMX 是未限幅值(§3.1),对难点恒高。可对 nc 阶段追加 POPZCH 过滤后的 CHMX + 温度结构/emflux 实物校验作辅助判据——但**不能**把有界未收敛模型当真收敛(§4.2 实测其表层 T 偏离)。
- **任务侧**:把 he≤−2 + Teff≥50k + logg≤5.5 的 ~273 个最难点标记为"nc 求解器失效",单独排队/人工处理,不浪费 7-9 次重试。
### 方向 C(数据侧):缩小难点集
DB 统计这批点的收敛率随 He 丰度强烈变化(he=+2 93% → he=−4 66% 失败)。若科学上可接受,**优先保证 he≥0 的点**(冷启动大多收敛),he≤−2 的最难点降级处理。
### 已证伪、不推荐的方向
- **诊断侧**DCTS 的 check_fort9 用未限幅 CHMX 判收敛,对这类点恒判失败。可在 framework 里对 nc 阶段**追加 POPZCH 过滤后的 CHMX** 与**温度结构/emflux 实物校验**作为辅助判据,但**不能**把"有界未收敛"模型当真收敛(实测其表层 T 偏离收敛参考)。
- **任务侧**:把这批 he≤−2+高Teff+低logg 的点标记为"需 He-ladder",单独排队。
### 不推荐的方向(已穷举证伪)
- 任何单一 nst 数值参数(ORELAX/DPSILG/ITEK/IACC/POPZER/POPZCH/NITER);
- ND=70、非灰 LTE 中间阶、trace 金属置 LTE、IFALI=5
- 仅靠 logg 连续(Δlogg 0.5 warm-start 仍崩);
- **He-ladder 连续**he0→he1 单步 1 dex、he0→he4 单跳 2 dexiter1 都小但 iter5 Kant 爆)——不稳性在迭代加速环节不在起点;
- 禁 KantorovichITEK=999)——纯 full-CL 反而 NaNKantorovich 实为防 NaN 的稳定项;
- **IFALI=6(带状近似 Λ 算子)**——cold→全 NaN、warm→iter1 MAX=1.84e95 灾难性;带状非对角项远离解时放大失稳。
---
## 六·五、附带发现:nst 行被 80 字符缓冲截断(生产 bug)
测 IFALI=6 时发现:`tlusty208.f:1689` 声明 `CHARACTER*80 TEXT`nst 行读取缓冲),但 `nst_writer.rs` 写出的第 4 行长达 **158 字符**
```
HMIX0=-1.,MLTYPE=1,IPTURB=1,IBFINT=1,IHECOR=0,IRTE=0,IBC=3,IFRYB=0,IOPTAB=0,IFMOL=0,IFALI=5,IFPOPR=4,JALI=1,TRAD=0.,WDIL=0.,TFLOOR=8000.,TDISK=0.,TMOLIM=9000.
```
第 80 字符(`...IFMO`)处被截断,**`IFALI`/`IFPOPR`/`JALI`/`TRAD`/`WDIL`/`TFLOOR`/`TDISK`/`TMOLIM` 全部从未被读取**,静默走 TLUSTY 默认。即生产中这些点的 `TFLOOR=8000``config.rs` 配的)实际没生效,用的是 TLUSTY 默认 TFLOOR。本复测所有需要这些参数的变体都已手动拆行(每行 ≤68 字符)验证可正确读取。
**建议修 `nst_writer.rs`**keyword 组每组 ≤75 字符换行(对齐 `CHARACTER*80` 限制)。
---
## 六、与前序文档的关系
| 前序结论 | 本文状态 |
|---|---|
| "itek/iacc/DPSILG/orelax 调参无效" | **证伪其方法论**:CHMX 对这些参数不敏感(源码 :14643 在 :14647/:14652 之前),前序测了不敏感的指标。结论(这些参数救不回)方向对,但理由错。 |
| "需连续法" | **方向确认**,并具体化为 **He-ladder**(而非泛泛的 Teff 连续),有 he0 warm-start iter1 改善 30× 的实测支撑。 |
| "Newton 在灰化起点即在收敛域外" | **确认**baseline iter1=140 且深层正常表层崩)。但 warm he0 能把 iter1 降到 4.3,说明**换个起点能进收敛域**。 |
| "禁 Kantorovich 降 1e12~1e14" | **方法论警示**:那是 CHMX(未限幅)下降;纯 full-CLITEK=999)实测会 NaNKantorovich 实为稳定项,**不应禁用**。 |
---
## 七、复测产物位置
```
test/20260813_cold_nc_trace_fix/convergence/t60000_g5.0_he-4_c-4_n-4_o-3_cold/
├── baseline/ inputs/(lte+nc.5/nst) + outputs/(nc.6/7/9)
├── popzer1e-9/ popzer1e-12/ popzch1e-9/
├── nd70/ lte_ng/ trace_lte/ full_nst/
├── warm_from_g5.5/ warm_from_he0/ warm_he0_nokant/
└── cold_nokant/
```
每个变体 `outputs/nc.9` 可用 `scripts/check_fort9.py` 复核;`nc.7` 可用 `check_temperature.py` 复核。