TLUSTY/cno_grid/EXPERIENCE copy.md
2026-07-21 22:25:14 +08:00

17 KiB
Raw Permalink Blame History

CNO 热亚矮星光谱:调试经验与批量网格操作手册

本文档记录在 tl208-s54/cno_grid/ 上构建含 C/N/O 金属线的热亚矮星理论光谱 过程中积累的全部经验,重点是收敛调试中踩过的坑已验证可行的配方。 基础背景知识见 tests/cno_sdspectrum/GUIDE.md(必读)。


1. 核心结论(先读这一节)

  1. 金属线必须进大气模型。 实测证实:纯 H+He 大气下跑 synspecC/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 一个 稳定种子。跳过 ncgrey-LTE → 直接含线 NLTE必定发散(见第 3 节)。

  3. 端到端已验证跑通35000K / logg 5.5 / logHe=-2 / CNO=-1 lte✓ → nc✓ → nl 收敛 (max_relc=0.0043) → synspec✓产出 111500 个有效 通量点CNO 吸收线清晰可见30003500Å 吸收深度 68%)。单模型约 14 分钟。


2. 已验证的精确配方

2.1 .5 文件三阶段配置

三个阶段用相同的 NATOMS8和 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.datPeter 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^logXlogX = 1 → abn=0.1
  • <0 = 太阳丰度的倍数0.1 = 0.1×太阳5 = 5×太阳

本次网格统一用 abn = 10^logXlogX 为 4..2 / 2..1)。

2.5 synspec 波长窗口

fort.55.lin 第 6 行 WLMIN WLMAX WLSTEP ...。验证用的是 30007000Å光学 覆盖 C II 4267、C III 4647 等)。要 UV 段(含更多 CNO 线)用 fort.55.uvopt 9008000Åtests/cno_sdspectrum/)。谱线表用 data/gfVIS99.dat(含 C 1412 / N 2396 / O 1885 条线)或 data/gfATO.dat(更全)。


3. 调试踩坑记录(避免重蹈覆辙)

坑 1跳过 nc 步骤 → NLTE 发散

现象:从 grey-LTE 种子直接跑含线 NLTEilvlin=100max_relc 在 iter 4 起 爆炸到 1e20 并产生 NaN。 根因grey-LTE 的 LTE 布居数与 NLTE 解差距巨大,加上数千条谱线的扰动同时 涌入,线性化必然爆。 解决:插入 nc 步骤ilvlin=0先无谱线地收敛 NLTE 电离平衡。 这是最重要的经验。

坑 2ITEK=100 反而更差

现象nc/nl 不收敛时把 ITEK 升到 100Peter 邮件提过 3/15/100结果ITEK=100 导致 overshootmax_relc 飙到 1e29比 ITEK=3 更糟。 解决ITEK 回退链上限设为 15不要用 100。

坑 3纯 H+He 种子喂含 CNO 模型 → 全 NaN

现象:把纯 H+He 的 .738 能级)作 fort.8 种子跑含 CNO 模型530 能级), fort.7 全 NaN。 根因种子与目标的能级结构不匹配tlusty 无法映射布居数。 解决:三步法的三个阶段用相同 NATOMS 和 ions 配置(只改 ilvlin/LTE 标志), 保证种子结构兼容。lte 的 .7 已经包含全部 CNO 能级位置LTE 填充),所以能 正确喂给 nc。

坑 4NITER=0 的 LTE 阶段不产生 fort.9

现象lte 阶段 NITER=0灰大气不迭代不输出 fort.9 收敛日志,自动化脚本 判定"失败"并跳过种子复制。 解决:脚本里对 fort.7大气单独判存在NITER=0 时无 fort.9 是正常的, 直接把 fort.7 当种子传给下一阶段。

坑 5grey-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 判收敛,不要手写解析。

坑 7logg 跨度太大的种子 → 发散

现象:用 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/<model_name>/  # 每个模型的 .5/.7/.9/.spec/.cont/conv.json

4.2 跑单个模型

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 批量网格

# 编辑 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 检查收敛 / 画图

python3 cno_grid/src/check_conv.py results/<model>/<model>.nl.9 --chmax 0.01
python3 cno_grid/src/plot_spec.py results/<model>   # 出 spectrum.png + CNO 线标注

4.5 收敛链调参config.yaml 的 chain 段)

默认链已验证可行。若某些参数点He-rich / 高金属)不收敛,可调:

  • 放宽 nl 的 CHMAX0.01 → 0.05
  • 增加 nc 的 NITER50 → 200GUIDE 提到 200 可完全收敛)
  • 在 nc/nl 加 ORELAX 阻尼(orelax: 0.5
  • He-rich 点可能物理上无法收敛到 0.01Peter 邮件预警),如实记录即可

5. 已知的 NaN 问题(非阻塞)

synspec 输出的 .spec 在部分波长区有 NaN典型 ~70% 点有效)。这是 synspec54 在致密谱线区的数值行为("lines rejected based on opacities")。有效区间的 光谱是可信的——35000K 模型在 30003500Å 有 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/2C富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. 发散不只与高温有关:测试 #640000K/logg5.5/logHe=0初次也发散了 证明发散触发条件是 Teff/logg/logHe 的组合,不只高温。因此方案 BIDLTE=45 等)改为对所有 NLTE 阶段无条件启用——物理上深层 LTE 对所有 sdB 都成立。

  3. 非对称丰度可行:测试 #7C=10×H, N=O=0.01×H这种 sdB 典型的 C-rich 组合收敛良好relc=0.00376),说明 C/N/O 可独立取不同丰度值。

  4. 耗时80000K 点因方案 B 的 nc 快速收敛,仅 58s40000K 含线 nl 较慢 ~20-30 min。24 核并行约每小时 72 个模型。


5.6 80000K 高温发散:源码级根因与解决方案

问题

初次边界测试中,两个 80000K 点He-rich 和 He-poor在 nc 阶段发散: max_relc 从 iter1 的 ~40 单调爆炸到 iter10 的 1e131e17触发 tlusty208.f:14700ABS(CHMX).GT.1.D16 硬停止。35000K 同配方则正常收敛。

根因tlusty208.f 源码分析)

通过分析源码确认发散由三个叠加因素导致,全部集中在深层ID 1-8的能级 布居数POP 列),温度变化很小

  1. ORELAX=1.0 无阻尼tlusty208.f:14647ORELAX 只乘到布居数修正上。 默认 1.0 = 纯线性化无阻尼,首次迭代就把深层 CNO 高价离子O IV/V、C IV、 N IV/V布居数猛推向 NLTE每步最多变 10 倍DPSILG=10 限幅器,14652)。

  2. Ng 加速 iter7 起雪上加霜IACC=7 默认,tlusty208.f:29704Ng 外推 假定迭代已近线性收敛在强烈非线性区外推会放大误差。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=45tlusty208.f:5515):强制最深的 5 层走 LTE。物理合理——深层 τ≫1 本就该接近 LTE且消除深层高电离能级的发散源。对所有 sdB 都成立。
  • IACC=NITER+1tlusty208.f:29704):完全关闭 Ng 加速(设大于 NITER 使 ACCEL2 早返回),避免外推放大误差。

效果边界点380000/He-poor

  • 修复前nc iter1=38.8 → iter10=1.5e13(发散),大气全 NaN0 有效光谱点
  • 修复后nc iter1=2.39 → iter5=0.0675 次收敛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+1LTE 灰大气阶段NITER=0保持干净不加。

备选方案(若方案 B 仍不够,按推荐度)

  • 方案 A碰撞-辐射开关)ICRSW=1, SWPFAC=0.1, SWPINC=3.0SWITCH 子程序 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 Nemeth2024-10~11

完整邮件在 hot_subdwarf/letter/。关键技术建议:

  1. 收敛判定fort.9 各深度相对变化都 < CHMAXcheck_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 即验证配方)