# CNO 热亚矮星光谱:调试经验与批量网格操作手册 > 本文档记录在 `tl208-s54/cno_grid/` 上构建含 C/N/O 金属线的热亚矮星理论光谱 > 过程中积累的全部经验,重点是**收敛调试中踩过的坑**和**已验证可行的配方**。 > 基础背景知识见 `tests/cno_sdspectrum/GUIDE.md`(必读)。 --- ## 1. 核心结论(先读这一节) 1. **金属线必须进大气模型。** 实测证实:纯 H+He 大气下跑 synspec,C/N/O 谱线 全部产生 NaN——因为大气里没有金属能级,synspec 无法计算谱线不透明度。 所以 C/N/O 必须作为显式 NLTE 原子(mode=2)写进 `.5` 的大气和 ions 段。 这正是 `tests/cno_sdspectrum/GUIDE.md` 的方案 B(自洽)。 2. **收敛必须走三步法,`nc` 步骤不可省略:** ``` LTE 灰大气 (T T, NITER=0) → 初始温度结构 NLTE 连续谱 (F F, ilvlin=0, "nc") → 收敛电离平衡+布居数(不含谱线扰动) NLTE 含线 (F F, ilvlin=100, "nl")→ 加谱线,从 nc 种子快速收敛 SYNSPEC → 合成可观测光谱 ``` `nc` 是关键:它在没有谱线扰动的情况下先收敛 NLTE 电离平衡,给 `nl` 一个 稳定种子。**跳过 nc(grey-LTE → 直接含线 NLTE)必定发散**(见第 3 节)。 3. **端到端已验证跑通**(35000K / logg 5.5 / logHe=-2 / CNO=-1): lte✓ → nc✓ → **nl 收敛 (max_relc=0.0043)** → synspec✓,产出 111500 个有效 通量点,CNO 吸收线清晰可见(3000–3500Å 吸收深度 68%)。单模型约 14 分钟。 --- ## 2. 已验证的精确配方 ### 2.1 `.5` 文件三阶段配置 三个阶段用**相同**的 NATOMS(8)和 ions 能级数,**只改三处**:第 2 行 LTE/LTGRAY 标志、第 3 行 nst 文件名、ions 段第 5 列 `ilvlin`。 | 阶段 | 第2行 | nst | ilvlin | 说明 | |------|-------|-----|--------|------| | lte | `T T` | nst (NITER=0) | 0/100 均可(LTE忽略线) | 灰大气,不迭代 | | nc | `F F` | nst_nc (NITER=50) | **0** | NLTE 连续谱,**无线跃迁** | | nl | `F F` | nst_nl (NITER=100) | **100** | NLTE 全谱线 | > `ilvlin=0` 让该离子的束缚-束缚跃迁不参与计算(只保留连续谱),这正是 nc 步骤 > 稳定收敛的原因。`ilvlin=100` 恢复全部线跃迁。 ### 2.2 CNO 模型原子(能级数,照搬 BSTAR2006) | 元素 | 离子 / 能级 / 数据文件 | |------|------------------------| | C (Z=6) | C I 40 `c1.dat` / C II 22 `c2.dat` / C III 46 `c3_34+12lev.dat` / C IV 25 `c4.dat` / C V 1 | | N (Z=7) | N I 34 `n1.dat` / N II 42 `n2_32+10lev.dat` / N III 32 `n3.dat` / N IV 48 `n4_34+14lev.dat` / N V 16 `n5.dat` / N VI 1 | | O (Z=8) | O I 33 `o1_23+10lev.dat` / O II 48 `o2_36+12lev.dat` / O III 41 `o3_28+13lev.dat` / O IV 39 `o4.dat` / O V 6 `o5.dat` / O VI 1 | | He | He I **24** `he1.dat` / He II **20** `he2.dat`(Peter Nemeth 邮件建议,勿用 14-level) | | H | H I 9 `h1.dat` | 合计约 530 个能级,全部文件在 `$TLUSTY/data/` 下已确认存在。 ### 2.3 nst 非标准参数(已验证) ``` ND=50,NLAMBD=3,VTB=2.,ISPODF=1,DDNU=50.,CNU1=6.,CHMAX=<阶段>,ITEK=3,NITER=<阶段> IELCOR=-1 ``` - lte 阶段:NITER=0(灰大气不迭代) - nc 阶段:NITER=50, CHMAX=0.1(不必完全收敛,作种子即可) - nl 阶段:NITER=100, CHMAX=0.01(要求收敛) - `NLAMBD=3, ISPODF=1, DDNU=50., CNU1=6.` 是频率网格细化参数,**不能省** - `IELCOR=-1` 关闭电子密度修正(这些 sdB 模型需要) ### 2.4 丰度约定 `.5` 的 atoms 段 `abn` 列: - `0` = 太阳丰度(Tlusty 内置 Grevesse & Sauval 1998) - `>0` = 绝对比值 N(elem)/N(H),即 `10^logX`(logX = −1 → abn=0.1) - `<0` = 太阳丰度的倍数(−0.1 = 0.1×太阳,−5 = 5×太阳) 本次网格统一用 `abn = 10^logX`(logX 为 −4..2 / −2..1)。 ### 2.5 synspec 波长窗口 `fort.55.lin` 第 6 行 `WLMIN WLMAX WLSTEP ...`。验证用的是 3000–7000Å(光学, 覆盖 C II 4267、C III 4647 等)。要 UV 段(含更多 CNO 线)用 `fort.55.uvopt` (900–8000Å,见 `tests/cno_sdspectrum/`)。谱线表用 `data/gfVIS99.dat`(含 C 1412 / N 2396 / O 1885 条线)或 `data/gfATO.dat`(更全)。 --- ## 3. 调试踩坑记录(避免重蹈覆辙) ### 坑 1:跳过 nc 步骤 → NLTE 发散 ❌→✅ **现象**:从 grey-LTE 种子直接跑含线 NLTE(ilvlin=100),max_relc 在 iter 4 起 爆炸到 1e20 并产生 NaN。 **根因**:grey-LTE 的 LTE 布居数与 NLTE 解差距巨大,加上数千条谱线的扰动同时 涌入,线性化必然爆。 **解决**:插入 nc 步骤(ilvlin=0),先无谱线地收敛 NLTE 电离平衡。 **这是最重要的经验。** ### 坑 2:ITEK=100 反而更差 ❌ **现象**:nc/nl 不收敛时把 ITEK 升到 100(Peter 邮件提过 3/15/100)。 **结果**:ITEK=100 导致 overshoot,max_relc 飙到 1e29,比 ITEK=3 更糟。 **解决**:ITEK 回退链上限设为 15,不要用 100。 ### 坑 3:纯 H+He 种子喂含 CNO 模型 → 全 NaN ❌ **现象**:把纯 H+He 的 `.7`(38 能级)作 fort.8 种子跑含 CNO 模型(530 能级), fort.7 全 NaN。 **根因**:种子与目标的能级结构不匹配,tlusty 无法映射布居数。 **解决**:三步法的三个阶段用**相同** NATOMS 和 ions 配置(只改 ilvlin/LTE 标志), 保证种子结构兼容。lte 的 `.7` 已经包含全部 CNO 能级位置(LTE 填充),所以能 正确喂给 nc。 ### 坑 4:NITER=0 的 LTE 阶段不产生 fort.9 ❌→✅ **现象**:lte 阶段 NITER=0(灰大气不迭代),不输出 fort.9 收敛日志,自动化脚本 判定"失败"并跳过种子复制。 **解决**:脚本里对 fort.7(大气)单独判存在;NITER=0 时无 fort.9 是正常的, 直接把 fort.7 当种子传给下一阶段。 ### 坑 5:grey-LTE 单独跑 synspec → 全 NaN ❌ **现象**:grey-LTE 大气(未做 nc/nl)直接跑 synspec,谱线全 NaN。 **根因**:grey 温度结构错误(标准深度 T=127834K),所有线被拒。 **结论**:synspec 必须用经过 nc→nl 收敛的 NLTE 大气,不能用 grey 或纯 LTE 大气。 ### 坑 6:分析脚本读错列 → 误判"整数发散"❌ **现象**:监控脚本显示 max_relc = 1.0, 2.0, 3.0... 精确整数线性增长,一度怀疑 二进制有未初始化变量 bug。 **真相**:正则捕获了迭代号(第 1 列)而非 max_relc(第 7 列)。 **教训**:fort.9 列顺序是 `ITER ID TEMP NE POP RAD MAXIMUM ilev ifr`, max_relc 在**第 7 列**。`check_conv.py` 一直是对的,是临时监控脚本错了。 **始终用 `check_conv.py` 判收敛,不要手写解析。** ### 坑 7:logg 跨度太大的种子 → 发散 ❌ **现象**:用 logg=4.0 的种子跑 logg=5.5 的模型(重力差 100 倍),发散。 **解决**:网格扩展时用"种子步进"——相邻参数点之间步长要小(logg 每步 ≤0.5, Teff 每步 ≤5000K),用最近邻已收敛模型作种子。`run_grid.py` 已实现此逻辑。 --- ## 4. 自动化系统使用说明(`cno_grid/`) ### 4.1 目录结构 ``` cno_grid/ ├── config.yaml # 6维网格 + 收敛链配置 ├── templates/ │ ├── cno_atmos.5.tpl # (备用)模板 │ ├── nst # (备用)nst │ └── fort.55.lin # synspec 波长窗口 ├── src/ │ ├── gen_input5.py # 6维参数 → .5(支持 ilvlin / metals 子集) │ ├── check_conv.py # fort.9 收敛判定 │ ├── run_one.py # 单模型三步链 + synspec(核心) │ ├── run_grid.py # 6维网格调度(断点续算/并行/种子复用) │ └── plot_spec.py # 归一化光谱 + CNO 诊断线标注 └── results// # 每个模型的 .5/.7/.9/.spec/.cont/conv.json ``` ### 4.2 跑单个模型 ```bash export TLUSTY=/home/dckj/program/tlusty/tl208-s54 python3 cno_grid/src/run_one.py \ --teff 35000 --logg 5.5 --loghe -2 --logc -1 --logn -1 --logo -1 # 结果在 cno_grid/results/t35000_g5.5_he-2_c-1_n-1_o-1/ # conv.json - 收敛状态(converged: true/false + 各阶段 max_relc) # *.spec/.cont - synspec 光谱/连续谱 # *.lte.7/nc.7/nl.7 - 各阶段大气快照 ``` ### 4.3 批量网格 ```bash # 编辑 config.yaml 的 grid 轴(各维点列表),然后: python3 cno_grid/src/run_grid.py cno_grid/config.yaml --dry-run # 先看总数 python3 cno_grid/src/run_grid.py cno_grid/config.yaml # 正式跑 python3 cno_grid/src/run_grid.py cno_grid/config.yaml --only teff=35000,logg=5.5 # 子集 python3 cno_grid/src/run_grid.py cno_grid/config.yaml --limit 3 # 只跑3个(测试) # 特性: # - resume: 跳过 conv.json 里 converged=true 的模型(断点续算) # - 种子复用: 新模型自动找最近邻已收敛 .7 作种子 # - nworkers: config.yaml 里设并行进程数 # - 失败隔离: 单点失败不中断网格,记入 grid_status.json ``` ### 4.4 检查收敛 / 画图 ```bash python3 cno_grid/src/check_conv.py results//.nl.9 --chmax 0.01 python3 cno_grid/src/plot_spec.py results/ # 出 spectrum.png + CNO 线标注 ``` ### 4.5 收敛链调参(config.yaml 的 chain 段) 默认链已验证可行。若某些参数点(He-rich / 高金属)不收敛,可调: - 放宽 nl 的 CHMAX(0.01 → 0.05) - 增加 nc 的 NITER(50 → 200,GUIDE 提到 200 可完全收敛) - 在 nc/nl 加 ORELAX 阻尼(`orelax: 0.5`) - He-rich 点可能物理上无法收敛到 0.01(Peter 邮件预警),如实记录即可 --- ## 5. 已知的 NaN 问题(非阻塞) synspec 输出的 `.spec` 在部分波长区有 NaN(典型 ~70% 点有效)。这是 synspec54 在致密谱线区的数值行为("lines rejected based on opacities")。**有效区间的 光谱是可信的**——35000K 模型在 3000–3500Å 有 73627 个有效点,吸收深度 68%, CNO 线清晰。降低波长采样密度或换 `gfATO.bin` 谱线表可能减少 NaN,但不影响 已验证的物理结论。 --- ## 5.5 网格边界测试结果(8/8 全部成功) 在网格边界挑 8 个代表性难点(覆盖 Teff/logg/logHe 和 CNO 丰度两个轴),用三步法 (lte→nc→nl)+ 稳定化(方案 B,对所有 NLTE 阶段无条件启用)实测,**全部成功**: ### Teff / logg / logHe 边界(CNO=0 太阳丰度) | # | 参数 (Teff/logg/logHe/CNO) | nl 收敛 | max_relc | 有效点 | 结论 | |---|---------------------------|---------|----------|--------|------| | 1 | 20000/5.0/+2/0(低温He-rich,最难) | YES | 0.00654 | 115362 | ✓ | | 2 | 80000/6.5/+2/0(高温He-rich) | YES | 0.00915 | 105183 | ✓ 修复后 | | 3 | 80000/6.5/−4/0(高温He-poor) | YES | 0.00769 | 106469 | ✓ 修复后 | | 4 | 40000/6.0/0/+1(高金属CNO=10×H) | YES | 0.0069 | 108693 | ✓ | | 5 | 20000/6.5/−4/0(低温贫He) | YES | 0.00927 | 115479 | ✓ | ### CNO 丰度边界(含非对称丰度) | # | 参数 (Teff/logg/logHe/C/N/O) | nl 收敛 | max_relc | 有效点 | 结论 | |---|------------------------------|---------|----------|--------|------| | 6 | 40000/5.5/0/−2/−2/−2(极贫金属,0.01×H) | YES | 0.00764 | 108693 | ✓ 修复后 | | 7 | 40000/5.5/0/+1/−2/−2(C富N/O贫,sdB典型) | YES | 0.00376 | 108576 | ✓ | | 8 | 80000/6.5/−4/−2/−2/−2(高温+极贫金属) | YES | 0.00394 | 105300 | ✓ | ### 关键发现 1. **8/8 全部收敛,全部产出有效光谱(10万+ 有效点)**——整个网格边界(Teff 20000-80000、logg 5.0-6.5、logHe −4..2、CNO −2..1,含非对称丰度)均可计算。 2. **发散不只与高温有关**:测试 #6(40000K/logg5.5/logHe=0)初次也发散了, 证明发散触发条件是 Teff/logg/logHe 的组合,不只高温。因此方案 B(IDLTE=45 等)改为**对所有 NLTE 阶段无条件启用**——物理上深层 LTE 对所有 sdB 都成立。 3. **非对称丰度可行**:测试 #7(C=10×H, N=O=0.01×H)这种 sdB 典型的 C-rich 组合收敛良好(relc=0.00376),说明 C/N/O 可独立取不同丰度值。 4. **耗时**:80000K 点因方案 B 的 nc 快速收敛,仅 58s;40000K 含线 nl 较慢 (~20-30 min)。24 核并行约每小时 72 个模型。 --- ## 5.6 80000K 高温发散:源码级根因与解决方案 ### 问题 初次边界测试中,两个 80000K 点(He-rich 和 He-poor)在 nc 阶段发散: max_relc 从 iter1 的 ~40 单调爆炸到 iter10 的 1e13–1e17(触发 `tlusty208.f:14700` 的 `ABS(CHMX).GT.1.D16` 硬停止)。35000K 同配方则正常收敛。 ### 根因(tlusty208.f 源码分析) 通过分析源码确认发散由三个叠加因素导致,**全部集中在深层(ID 1-8)的能级 布居数(POP 列),温度变化很小**: 1. **ORELAX=1.0 无阻尼**(`tlusty208.f:14647`):ORELAX 只乘到布居数修正上。 默认 1.0 = 纯线性化无阻尼,首次迭代就把深层 CNO 高价离子(O IV/V、C IV、 N IV/V)布居数猛推向 NLTE,每步最多变 10 倍(DPSILG=10 限幅器,`14652`)。 2. **Ng 加速 iter7 起雪上加霜**(IACC=7 默认,`tlusty208.f:29704`):Ng 外推 假定迭代已近线性收敛;在强烈非线性区外推会放大误差。fort.10 显示 iter7 正是 深层 POP 从 1e2 跳到 1e4 的转折点。 3. **深层无 LTE 保护**(IDLTE=1000 默认,`tlusty208.f:5515`):所有 50 层都解 NLTE。80000K 深层高电离能级的光致电离-复合平衡极陡峭,完全线性化给出的 修正方向不稳定。 物理本质:80000K 下 CNO 多次电离,LTE 灰大气初猜的布居数与真实 NLTE 解差太远。 与 He 丰度无关(He-rich 和 He-poor 都发散)。 ### 解决方案 B(已验证,已集成进代码) ``` IDLTE=45, IACC=NITER+1 (对所有 NLTE 阶段无条件启用) ``` - **IDLTE=45**(`tlusty208.f:5515`):强制最深的 5 层走 LTE。物理合理——深层 τ≫1 本就该接近 LTE,且消除深层高电离能级的发散源。对所有 sdB 都成立。 - **IACC=NITER+1**(`tlusty208.f:29704`):完全关闭 Ng 加速(设大于 NITER 使 ACCEL2 早返回),避免外推放大误差。 效果(边界点3,80000/He-poor): - 修复前:nc iter1=38.8 → iter10=1.5e13(发散),大气全 NaN,0 有效光谱点 - 修复后:nc iter1=2.39 → iter5=0.067(5 次收敛),nl iter6=0.0078(收敛), **106469 有效光谱点,零 NaN** > 注:ORELAX 当前未默认启用(保持 1.0),因为 IDLTE+IACC 已足够稳定。若遇极端 > 点仍发散,可在 config.yaml 的 chain 阶段加 `orelax: 0.5` 进一步阻尼。 `run_one.py` 已自动处理:所有 `lte=="F"` 的阶段(nc/nl)自动加 IDLTE=45 和 IACC=NITER+1;LTE 灰大气阶段(NITER=0)保持干净不加。 ### 备选方案(若方案 B 仍不够,按推荐度) - **方案 A(碰撞-辐射开关)**:`ICRSW=1, SWPFAC=0.1, SWPINC=3.0`(`SWITCH` 子程序 `tlusty208.f:4556`)。首轮压低辐射率使布居数接近 LTE,逐步恢复真 NLTE。Hummer & Voels (1988) 标准稳定化手段。 - **方案 C(强限幅)**:`DPSILG=3.0, ORELAX=0.3, ITEK=5`。把单步布居数变化 上限从 10× 降到 3×,更保守但迭代更多。 ### 关键源码位置(供进一步调参参考) | 参数 | 源码行 | 作用 | |------|--------|------| | ORELAX | 14647, 844 | 布居数过松弛(<1 阻尼) | | IDLTE | 5515 | 深层强制 LTE 的深度阈值 | | IACC/IACD | 29704, 29750 | Ng 加速起始/间隔 | | ITEK | 836-860 | 完全线性化迭代数(之后转 Kantorovich) | | DPSILG/DPSILT | 14652-14659 | 单步修正限幅(通用/温度) | | ICRSW/SWPFAC | 4556-4642 | 碰撞-辐射开关(逐步引入 NLTE) | | POPZCH | 22910 | max_relc 计算时跳过的小布居数阈值 | --- ## 6. 邮件往来要点(Peter Nemeth,2024-10~11) 完整邮件在 `hot_subdwarf/letter/`。关键技术建议: 1. 收敛判定:fort.9 各深度相对变化都 < CHMAX(用 `check_conv.py`) 2. He 用最复杂模型原子(24-level He I + 20-level He II) 3. He-rich 模型难收敛属正常;用模型链(粗→精 CHMAX);可减小模型间步长 4. ITEK 可调(3/15/100)提升稳定性——但实测 100 会 overshoot,勿超 15 5. CHMAX:普通星 0.001 可达;He-dominated 可放到几个%;先 10% 再 1% 逐步收紧 6. synspec 后处理(卷积、vsini)仅在比对观测时需要;纯理论光谱可直接用 fort.7 --- ## 7. 参考文件索引 | 文件 | 内容 | |------|------| | `tests/cno_sdspectrum/GUIDE.md` | 你写的原始指南(基础流程、.5 格式、nst 说明)—— **必读** | | `tests/cno_sdspectrum/R1_full` | 你验证过的完整四步运行脚本 | | `tests/cno_sdspectrum/sdB35000g550_{lte,nc,nl}.5` | 三阶段的范例 .5 文件 | | `tests/sdB_spectra/runs/test_cno/` | 已算好的 35000/5.5 CNO 模型结果 | | `tl208-s54/tests/tlusty/bstar/BGA20000g400v2a.5` | BSTAR2006 含全套金属参考模型 | | `hot_subdwarf/letter/` | 与 Peter Nemeth 的全部邮件 | | `cno_grid/src/run_one.py` | 三步链实现(DEFAULT_CHAIN 即验证配方) |