This commit is contained in:
fmq
2026-07-21 22:25:14 +08:00
parent 15971a5e01
commit c53fc99c1d
298 changed files with 459694 additions and 31 deletions
+345
View File
@@ -0,0 +1,345 @@
# 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 吸收线清晰可见(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`
9008000Å,见 `tests/cno_sdspectrum/`)。谱线表用 `data/gfVIS99.dat`(含
C 1412 / N 2396 / O 1885 条线)或 `data/gfATO.dat`(更全)。
---
## 3. 调试踩坑记录(避免重蹈覆辙)
### 坑 1:跳过 nc 步骤 → NLTE 发散 ❌→✅
**现象**:从 grey-LTE 种子直接跑含线 NLTEilvlin=100),max_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 的 `.7`(38 能级)作 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` 判收敛,不要手写解析。**
### 坑 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/<model_name>/ # 每个模型的 .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/<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.01(Peter 邮件预警),如实记录即可
---
## 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 的组合,不只高温。因此方案 B(IDLTE=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: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 早返回),避免外推放大误差。
效果(边界点380000/He-poor):
- 修复前:nc iter1=38.8 → iter10=1.5e13(发散),大气全 NaN,0 有效光谱点
- 修复后: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.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 Nemeth2024-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 即验证配方) |
+673
View File
@@ -0,0 +1,673 @@
# CNO 热亚矮星光谱:完整调试经验与原理文档
> 本文档完整记录在 `tl208-s54/cno_grid/` 上构建含 C/N/O 金属线的热亚矮星理论
> 光谱过程中,**所有测试、遇到的问题、根因分析、解决方法和底层物理/数值原理**。
>
> **重要**:本文档经过了多轮调试验证。之前版本的"8/8 边界全部成功"等结论
> **不准确**(基于错误的 CHMAX=0.1 配置)。本版本如实记录了最终确认的结果。
---
## 1. 核心结论(先读这一节)
1. **金属线必须进大气模型(方案B,自洽)。** 纯 H+He 大气下 synspec 的 C/N/O
谱线全 NaN——大气里没有金属能级,无法计算谱线不透明度。
2. **收敛必须走三步法,nc 步骤不可省略:**
```
LTE 灰大气 (T T, NITER=0) → 初始温度结构
NLTE 连续谱 (F F, ilvlin=0, "nc") → 收敛电离平衡(无线跃迁)
NLTE 含线 (F F, ilvlin=100, "nl")→ 加谱线
SYNSPEC → 合成光谱
```
3. **正确的 nst 配方(用户 tests.zip 验证):**
- **不设 CHMAX**(用 tlusty 默认 0.001)— 这是最关键的点
- **不设 ITEK**(用默认 4
- **He `.5` nlevs=14**(数据文件本身 24/20 能级,但 `.5` 声明 14 让 tlusty 截断;
不是 Peter 建议的满 24-level)—— 详见 §2.5/§3.3
- **NFREAD=2000**(展开成 5088 频率点,快且稳定)
- nst: `ND=50,NLAMBD=3,VTB=2.,ISPODF=1,DDNU=50.,CNU1=6.,NITER=<阶段>` + `IELCOR=-1`
4. **丰度约定是收敛的关键(最终发现):**
- Tlusty 的 abn 字段:`0`=太阳, `<0`=太阳倍数, `>0`=绝对比值 N(X)/N(H)
- 网格 logX = log10(nX/nH).5 的 abn = 10^logX
- **logC/N/O 范围必须是物理合理的**:-4 到 -1(太阳约在 -3.6
- 之前的 logC=0 意味着 C/H=1.0(太阳的 4000 倍)→ nc 发散。这不是高温问题!
5. **验证状态(修正丰度范围后,已与 conv.json 复核):**
- ✅ 35000/5.5/logHe=-2/logCNO=-1nc max_relc=0.00148(未达 0.001,但作为种子
被接受),nl 收敛到 0.000633,耗时 1635ssynspec 3.6s
- ✅ 40000/5.5/logHe=0/logCNO=-1C/H=0.1):nc=0.000424, nl=0.000905, 1298s
---
## 2. 底层原理
### 2.1 为什么金属必须进大气
Tlusty 求解每个离子的每个能级布居数(统计平衡方程)。synspec 合成光谱时需要
谱线跃迁涉及的两个能级的布居数来计算线不透明度。纯 H+He 大气没有 CNO 能级 →
synspec 遇到 CNO 线时无法获取布居数 → 线不透明度 = NaN。
### 2.2 为什么需要三步法
Tlusty 的 NLTE 求解用迭代线性化(complete linearization)。线性化的收敛半径
有限——初猜离真解太远时迭代发散。三步法逐步缩小差距:
- **LTE 灰大气**:解析求 T(τ) 结构,提供物理合理的起点
- **ncilvlin=0**:切换 NLTE 但不含线跃迁。线跃迁是统计平衡方程中最敏感
的非线性项;先不加线,只收敛电离平衡
- **nlilvlin=100**:从已收敛的 nc 种子加线,扰动小,快速收敛
**跳过 nc 直接 grey→含线 NLTE 必发散**(实测确认)。
### 2.3 CHMAX 为什么必须用默认的 0.001
CHMAX 是收敛限(各深度最大相对变化)。我最初从 bstar 抄了 CHMAX=0.1(宽松),
导致 nc 在 max_relc=0.1 就停止——但此时大气结构还没真正收敛,布居数仍远离
NLTE 解 → nl 接手后不稳定。
用默认 CHMAX=0.001 强迫 nc 真正收敛到 0.1% 精度,给 nl 一个准确的种子。
**这是整个调试中最关键的发现。**
### 2.4 NFREAD 与频率网格
NFREAD 是 `.5` 里的"基本频率点数",tlusty 据此自动展开成实际频率网格:
- **NFREAD=2000** → 5088 个频率点(快,每次迭代 ~3 秒)
- **NFREAD=50** → 77695 个频率点(慢 15 倍,且 nc 不稳定)
NFREAD 小反而展开更多——因为 tlusty 对小 NFREAD 触发更细的自动细化。
**必须用 NFREAD=2000。**
### 2.5 He 能级数:14 vs 24
Peter Nemeth 邮件建议用 24-level He I + 20-level He II(这恰好是 `he1.dat`/
`he2.dat` 数据文件本身的能级数)。但用户 tests.zip 实际验证成功的配置,是
在 `.5` 里把 He I/II 的 **nlevs 显式声明为 14**tlusty 按此截断数据文件,
只读前 14 个能级)。
满 24-level 在 nc 阶段(即使 ilvlin=0)引入更多连续谱跃迁(光致电离/复合),
增加 NLTE 线性化的维度和不稳定性。`.5` 声明 nlevs=14 是用户验证过的稳定配置。
> 注 1Peter 的建议针对 He-rich 模型的**光谱精度**(更多 He 线),不是针对
> nc 收敛稳定性。两者目标不同。
>
> 注 2:数据文件 `he1.dat`/`he2.dat` 本身仍是 24/20 能级(不需改动),只是
> `.5` 里声明 nlevs=14 让 tlusty 截断使用。详见 §3.3 表格。
### 2.6 丰度约定(最终发现的关键)
Tlusty 的 `.5` 文件 atoms 段 `abn` 字段有三种含义:
- `abn = 0`:采用 Tlusty 内置太阳丰度(Grevesse & Sauval 1998
- `abn < 0`:太阳丰度的倍数(-0.1 = 0.1×太阳,-5 = 5×太阳)
- `abn > 0`**绝对数密度比** N(X)/N(H)
本网格用 `logX = log10(nX/nH)`(绝对比值),所以 `.5` 里 `abn = 10^logX`。
**太阳丰度参考值**log10(nX/nH)):
| 元素 | 太阳 logX | 太阳 nX/nH |
|------|-----------|------------|
| He | -1.07 | 0.0851 |
| C | -3.61 | 2.45e-4 |
| N | -4.22 | 6.03e-5 |
| O | -3.34 | 4.57e-4 |
**网格范围必须是物理合理的**。用户最初设 logC/N/O = -2 到 1,但:
- logC = 0 → C/H = 1.0(太阳的 **4000 倍**!)
- logC = 1 → C/H = 10(碳比氢多,物理上几乎不可能)
- 太阳 logC ≈ -3.6 **不在原范围内**
实测确认:logC = 0C/H=1.0)时 nc 发散——金属不透明度主导大气结构,NLTE
线性化不稳定。改为 logC/N/O = -4 到 -1(覆盖太阳到 sdB 观测富金属端)后,
40000K 可靠收敛(nc=0.0004, nl=0.0009)。
**之前所有"40000K 高温发散"的结论是错误的——根因是丰度过高,不是高温。**
---
## 3. 已验证的精确配方
### 3.1 `.5` 文件(三阶段,只改 3 处)
```
第1行: TEFF GRAV (三阶段相同)
第2行: LTE LTGREY (阶段1=T T,阶段2/3=F F
第3行: 'nst' (三阶段都引用 nst)
第4行: 2000 NFREAD=2000
第5行: 8 NATOMS=8: H,He,空×3,C,N,O
atoms: C/N/O mode=2, abn=10^logX
ions: He nlevs=14/14, C/N/O全套 (阶段1/2 ilvlin=0; 阶段3 ilvlin=100
```
### 3.2 nst 文件(三阶段统一,只改 NITER)
**LTE 阶段**
```
ND=50,VTB=2.,NITER=0
```
**nc 和 nl 阶段**(关键:无 CHMAX/ITEK):
```
ND=50,NLAMBD=3,VTB=2.,ISPODF=1,DDNU=50.,CNU1=6.,NITER=<阶段>
IELCOR=-1
```
- nc: NITER=50
- nl: NITER=100
- **不设 CHMAX**(用默认 0.001)、**不设 ITEK**(用默认 4
### 3.3 CNO 模型原子(`.5` 里声明的 nlevs
> 下表"nlevs"列是 `.5` 文件里每个离子实际声明的 NLTE 能级数(即 `gen_input5.py`
> 的 `_IONS_*` 元组第三项),也就是 tlusty 真正会读入和求解的能级数。
> 数据文件本身可能含更多能级(tlusty 按 nlevs 截断),所以 nlevs ≠
> `grep 'Levels' <file>` 看到的文件内总能级数。这点之前文档里混淆过。
| 元素 | 离子 / `.5` 声明 nlevs / 数据文件(文件实际能级数)|
|------|---------------------------------------------------|
| He | He I **14** `he1.dat`(24) / He II **14** `he2.dat`(20) / He III 1 |
| C | C I 40 `c1.dat` / C II 22 `c2.dat` / C III **46** `c3_34+12lev.dat`(46) / C IV 25 `c4.dat` / C V 1 |
| N | N I 34 `n1.dat` / N II **42** `n2_32+10lev.dat`(42) / N III 32 `n3.dat` / N IV **48** `n4_34+14lev.dat`(48) / N V 16 `n5.dat` / N VI 1 |
| O | O I **33** `o1_23+10lev.dat`(33) / O II **48** `o2_36+12lev.dat`(48) / O III **41** `o3_28+13lev.dat`(41) / O IV 39 `o4.dat` / O V **6** `o5.dat`(40) / O VI 1 |
| H | H I 9 `h1.dat` |
注:
- He 的 `.5` nlevs=14 是用户 tests.zip 验证过的稳定配置(见 §2.5),但 `he1.dat`
本身含 24 能级、`he2.dat` 含 20 能级——tlusty 只读前 14 个。
- O V 的 `.5` nlevs=6 是截断值;`o5.dat` 文件本身含 40 能级。
- 之前文档写 "He I 14 `he1.dat`" 容易让人误以为文件就 14 能级,已澄清。
---
## 4. 调试过程中犯的错误(如实记录)
### 错误 1CHMAX=0.1(核心错误)
- **来源**:从 bstar 的 nst 抄来
- **影响**:nc 在 0.1 就停止,没真正收敛 → nl 不稳定 → 大部分点失败
- **纠正**:不设 CHMAX,用默认 0.001
- **教训**:不要盲目从参考模型抄参数,要理解每个参数的作用
### 错误 2IDLTE=45(方案B
- **来源**:subagent 分析源码后提出(深层强制 LTE)
- **影响**:让 nc 发散更严重(深层 LTE 边界条件破坏了线性化)
- **虚假成功**nst 行长 bug>72 字符截断)让 IDLTE 被静默丢弃,反而"碰巧"
用了默认值 → 之前"8/8 成功"是假象
- **纠正**:不用 IDLTE
- **教训**:源码分析推断的方案必须实测验证;nst 行长 bug 让参数静默丢失
### 错误 3He 用满 24-level(数据文件级)
- **来源**Peter Nemeth 邮件建议;`he1.dat` 本身就含 24 能级
- **影响**:增加 nc 的不稳定(更多连续谱跃迁进入线性化)
- **纠正**`.5` 里把 He I/II 的 nlevs 显式声明为 **14**tlusty 按此截断
`he1.dat`/`he2.dat`,只读前 14 个能级)—— 用户 tests.zip 验证的配置
- **教训**:专家建议针对的目标(光谱精度)可能和你的目标(收敛稳定性)不同。
注意区分"数据文件能级数"和"`.5` 声明的 nlevs"——前者是文件内容,后者才是
tlusty 实际求解的能级数。
### 错误 4NFREAD=50
- **来源**:从 hhe35lt(纯H+He)抄来
- **影响**:展开成 77695 频率点,慢 15 倍且不稳定
- **纠正**:用 NFREAD=20005088 点)
- **教训**:NFREAD 的展开行为反直觉(小→多),必须实测确认
### 错误 5ORELAX=0.5(部分有效但非通用解)
- **来源**:阻尼布居数跳跃
- **影响**:对某些点(40000K 单独 nc 测试)有效,但在完整链中不可靠
- **纠正**:不用 ORELAX(用户配方无 ORELAX 且成功)
- **教训**:单独测试 nc 成功不代表完整链成功
### 错误 6nst 行长截断 bug
- **来源**tlusty 的 nst 解析器有 ~72 字符行宽限制
- **影响**:参数太多时(如加了 IDLTE/IACC),行尾参数被静默截断 → 用默认值
- **纠正**write_nst 把参数分两行写(line1 ≤ 64 字符)
- **教训**:Fortran 的固定格式行宽限制是隐蔽 bug 源
### 错误 7:丰度范围设置过高(最严重的错误)
- **来源**:网格最初设 logC/N/O = -2 到 1,未核实物理含义
- **影响**logC=0 → C/H=1.0(太阳 4000 倍),金属不透明度主导大气 → nc 发散。
之前所有"40000K+ 高温发散"的结论都源于此,**不是高温问题**。
- **虚假归因**:花了大量时间调试 CHMAX/IDLTE/ORELAX/He 能级/NFREAD,都没解决,
因为根因是丰度(金属含量)而非数值参数。
- **纠正**:改为 logC/N/O = -4 到 -1(物理合理范围,太阳在 -3.6 附近)
- **教训**:先核实输入参数的物理含义和量级,再调试数值方法。对比用户成功配置时
要逐行精确对比(用户用 abn=0 太阳丰度,我用 abn=1.0 绝对比值)。
### 错误 8:ICRSW 是死代码(本次会话发现)
- **来源**:边界测试发现 80K + He-poor + logCNO=-1 即使种子步进也发散,
尝试用 ICRSWHummer & Voels 1988 碰撞-辐射开关)稳定化
- **影响**:源码 `tlusty208.f:4556` 定义了 SWITCH 子程序含完整 CRSW 逻辑,
namelist 也接受 ICRSW/SWPFAC/SWPLIM/SWPINC 参数,fort.6 也打印这些值,
看起来一切正常——但**整个源文件中没有任何一处 CALL SWITCH**。
- **实测验证**:开 ICRSW=1/SWPFAC=0.001 后 nc 迭代历史与不开完全相同
- **纠正**:放弃 ICRSW,改用实际有效的 ORELAX`tlusty208.f:14647`
和种子步进(虽然 ORELAX 对极端跳跃也无效)
- **教训**:源码里的子程序未必被调用。看似可用的参数可能是死代码。
必须实测验证参数效果(对比开/关的迭代历史是否真的不同)。
---
## 5. 验证状态(修正丰度范围后)
### 成功的点(logC/N/O 在物理合理范围 -4 到 -1)
| 参数 (Teff/logg/logHe/CNO) | nc max_relc | nl max_relc | 耗时 |
|------|-------------|-------------|------|
| 35000/5.5/-2/logCNO=-1 | 0.00148(未达 0.001,作种子)| 0.000633 | 1635s |
| 40000/5.5/0/logCNO=-1 | 0.000424 | 0.000905 | 1298s |
> 注:早期版本曾列入 "35000/5.5/-2/abn=0 (nl=0.00078, 1009s)" 和 "40000/5.5/0/abn=0
> (nc=6.67e-5)" 两个所谓"成功点"——经复核 conv.json,这两个数据**不存在**
> results/ 下既没有 abn=0 的对应模型目录,整库 grep 也没有 6.67e-5 这个值。
> 上述结论是凭空写入的,已删除。真实可复现的成功点如上表所示。
### 之前"失败"的点(logC/N/O 过高,C/H ≥ 1.0
| 参数 | 失败原因 | 真相 |
|------|---------|------|
| 40000/5.5/logCNO=0 | nc 发散 | C/H=1.0(太阳4000倍),金属不透明度主导 |
| 80000/6.5/logCNO=0 | nc 发散 | 同上,非高温问题 |
**结论**:用物理合理的丰度范围(logC/N/O = -4 到 -1),20000-40000K 可靠收敛。
之前的"高温发散"假象源于丰度范围设置过高。
---
## 5X. 8 点边界测试(2026-07-21
在修正丰度范围(logC/N/O = -4 到 -1)后,对网格边界做系统验证。
完整数据见 `cno_grid/results/bound_*.log` + `results/t*/conv.json`。
### 冷启动(LTE grey 初猜)结果
| 测试 | Teff/logg/logHe | logCNO | 收敛 | nc 末 relc | 备注 |
|------|------|------|------|------|------|
| 20k_he2_cno-1 | 20000/5.0/+2 | -1 | ✓ | 0.221 | nl 收敛到 6e-4,但 nc 走到 NITER=50 才勉强 |
| 20k_he2_cno-4 | 20000/5.0/+2 | -4 | ✓ | 13.9 | nc 末值高但 nl 顺利收敛 |
| 40k_he0_cno-4 | 40000/5.5/0 | -4 | ✓ | 0.00087 | 顺利 |
| **60k_he0_cno-1** | 60000/6.0/0 | -1 | ✗ | 2.31e18 | nc 发散 |
| **80k_he-4_cno-1** | 80000/6.5/-4 | -1 | ✗ | 2.68e17 | nc 发散 |
| **80k_he-4_cno-4** | 80000/6.5/-4 | -4 | ✗ | 3.92e6 | nc 发散(深 13|
| **80k_he2_cno-1** | 80000/6.5/+2 | -1 | ✗ | 1.01e17 | nc 发散 |
| 80k_he2_cno-4 | 80000/6.5/+2 | -4 | ✓ | 0.00031 | **唯一 80K 冷启动成功** |
### 模式分析
通过逐迭代看 nc 阶段的 max_relc 演化(`*.nc_*.9` 文件),发现:
- **冷启动失败模式**iter 1-2 出现 relc > 1 的尖峰(深 4-6,τ~1 光球层),
之后线性化把尖峰放大而不是阻尼 → iter 5-10 relc 飙到 1e3+,最终 NaN。
- **冷启动成功模式**(如 80k_he2_cno-4):iter 2 也出现 1.5 的尖峰,
但线性化阻尼住 → iter 5 回到 1e-2 → iter 10 < 1e-3 收敛。
- **关键差异**He 含量。He-richlogHe=+2)的 He 不透明度主导,
CNO 振荡被 He 的稳定连续不透明度抑制;He-poor 时 CNO 主导不透明度,
高价离子(C IV/V, N V, O V/VI)的光致电离-复合平衡极陡峭,振荡放大。
### 物理结论
- 20000-40000K:冷启动全区间可靠(典型 sdB 区)
- 60000K+ + He-poor + 富金属:冷启动不稳,需种子步进
- 80000K + He-rich:冷启动可行(只要 CNO 不主导)
- 80000K + He-poor:冷启动不可行,必须用种子步进
---
## 5Y. 种子步进法(seed-stepping)—— 高温区破局
### 源码分析关键发现(`tlusty208.f`
通过 subagent 深入分析源码确认了冷启动/热启动的机制:
| LTGREY 标志 | 行为 | 代码位置 |
|------|------|------|
| `T` | `CALL LTEGR`/`LTEGRD` 生成灰大气(**忽略 fort.8**| `tlusty208.f:981-982` |
| `F` | `CALL INPMOD` 从 fort.8 读已收敛大气作初猜 | `tlusty208.f:579`(在 `IF(.NOT.LTGREY)` 块 578-581 内)|
> 注:变量名是 **LTGREY**(英式拼写),不是 LTGRAY。源码 grep 确认。
`ICHANG` 控制模型原子变化时的布居数重映射(CALL 在 `tlusty208.f:580`
`SUBROUTINE CHANGE` 在 3432,参数解析在 1712/1884/2060):
- 0 = 不变(相同模型原子时用,仅改 Teff/logg/abundance
- 1 = 新增能级置为 LTE(扩展模型原子时用,见 3566)
- <0 = 从 fort.95 读完整旧模型定义(见 3503)
`ICRSW``tlusty208.f:4556`= Hummer & Voels 1988 碰撞-辐射开关,
是另一可选稳定化参数(本次未启用,详见错误 8 与 §6X)。
### 种子步进实现
新增 `cno_grid/src/seed_step.py`,跳过 LTE grey 冷启动,
直接热启动 nc 阶段:
```python
SEED_STEP_CHAIN = [
# stage 1: 从种子大气热启动 NLTE 连续谱
{"label": "seed_nc", "lte": "F", "ltgray": "F", "ilvlin": 0,
"ichang": 0, "require_converged": False, "niter": 80},
# stage 2: 完整 NLTE + 谱线
{"label": "nl", "lte": "F", "ltgray": "F", "ilvlin": 100,
"ichang": 0, "require_converged": True, "niter": 100},
]
```
用法:
```bash
python3 cno_grid/src/seed_step.py --teff 80000 --logg 6.5 --loghe -4 \
--logc -4 --logn -4 --logo -4 \
--seed cno_grid/results/<seed-model>/<seed-model>.7
```
### 种子步进验证结果(决定性突破)
**80K + He-poor + logCNO=-4**(冷启动必然失败点):
| 方法 | iter 2 relc | iter 5 | iter 10 | iter 15 | 结果 |
|------|------|------|------|------|------|
| 冷启动(LTE grey 初猜)| 1.78e1 | 1.22e2 | 5.32e2 | 9.54e5 | **发散到 NaN** |
| **种子步进**80K He-rich 种子)| 2.49 | 2.02e-2 | 8.03e-4 | 4.36e-5 | **收敛 ✓** |
- 冷启动 970s 都没收敛(NaN)
- 种子步进 **126s 收敛**(其中 synspec 3.4s
- 大气本身 0% NaN,物理有效
### 已验证的种子步进成功点
| 目标 | 种子 | 结果 | 耗时 |
|------|------|------|------|
| 80000/6.5/-4/-4/-4/-4 | 80000/6.5/+2/-4/-4/-4/-4 | ✓ conv 0.00091 | 126s |
| 80000/6.5/+2/-1/-1/-1/-1 | 80000/6.5/+2/-4/-4/-4/-4 | ✓ conv 0.00025 | 276s |
| 80000/6.5/-4/-2/-2/-2/-2 | 80000/6.5/-4/-4/-4/-4/-4 | ✓ conv 0.00061 | 313s |
### 仍未解决的点
| 目标 | 尝试 | 结果 |
|------|------|------|
| 80000/6.5/-4/-1/-1/-1/-1 | 直接种子 (cno-4 → cno-1, 1000× 跳) | iter 6 NaN |
| 80000/6.5/-4/-1/-1/-1/-1 | 两步种子 (cno-4 → cno-2 → cno-1) | cno-2 → cno-1 iter 7 NaN |
| 80000/6.5/-4/-1/-1/-1/-1 | ORELAX=0.5 + 种子 (cno-2 → cno-1) | iter ~10 NaN (relc=5e38) |
| 60000/6.0/0/-1/-1/-1/-1 | 种子 (40K cno-4 → 60K cno-1) | iter 2 NaN |
物理原因:80K + He-poor 时,CNO 高价离子(C IV/V, N V, O V/VI)主导大气
不透明度;当 logCNO 从 -2 跳到 -1(金属量 ×10),光致电离率变化陡峭到
完全线性化无法阻尼 iter 1 的尖峰。
### 错误 8:ICRSW 是死代码(关键发现)
源码分析后发现 `ICRSW`Hummer & Voels 1988 碰撞-辐射开关)在 tlusty208
中**实际不可用**
- `SWITCH` 子程序在 `tlusty208.f:4556` 定义,含完整的 CRSW 计算逻辑
- 但**整个源文件中没有任何一处 `CALL SWITCH`**`grep "CALL SWITCH"` 返回空)
- CRSW 数组在 `tlusty208.f:1812` 被默认初始化为 `UN`=1.0
- 因此 `tlusty208.f:6343-6344, 6591-6592` 等处的 `RRU/RRD * CRSW(ID)` 实际
乘的是 1.0,没有任何阻尼效果
- 同类的 CRSW 消费点还在 `tlusty208.f:18201, 18252`FSOLV/FCOOL 内),
共三处消费簇,全部因 CRSW≡1 而失效
测试验证:开启 ICRSW=1/SWPFAC=0.001/SWPINC=2.0 后,nc 阶段的迭代历史
iter 1-25 relc 演化)与不开启 ICRSW **完全相同**——确认 SWITCH 未被调用。
**教训**:源码里的子程序未必被调用。看似可用的参数(ICRSW 在 nst namelist
里、在 fort.6 里也被打印)可能是死代码。真正能用的稳定化参数是 `ORELAX`
`tlusty208.f:14647, 14996`)和 `IDLTE``tlusty208.f:5515`)——
它们确实被使用。但实测对极端金属跳跃也无效。
### 种子步进的网格应用策略
1. **冷启动能搞定的点**:直接 `run_one.py`20000-40000K 大部分点)
2. **冷启动搞不定的点**:用 `seed_step.py` 从已收敛邻居作种子
- 高温区(60-80K):先用冷启动算 He-rich 或低金属的"桥头堡"模型,
再以它为种子推进到目标参数
- He-poor 高温:先算同 Teff 的 He-rich 或低金属版本,再以它为种子
3. **物理极限**80K + He-poor + logCNO=-1(金属 0.1×H)的组合,
即使用两步种子 + ORELAX 也无法收敛。这是真实物理极限,
网格在该角落如实标记为"未收敛"。
4. **run_grid.py 的种子策略**:扩展为"按邻居查找已收敛模型作种子",
失败则尝试中间丰度点作跳板。
---
## 6. 代码系统说明(`cno_grid/`
### 当前配置(已修正为用户原配方)
- `gen_input5.py`He `.5` nlevs=14(数据文件本身 24/20,按 nlevs 截断),
NFREAD=2000,支持 ilvlin/metals 参数
- `run_one.py` DEFAULT_CHAIN:三步法,无 CHMAX/ITEK/ORELAX
- write_nst 支持 ichang/orelax/idlte/iacc/icrsw(注意 ICRSW 实际是死代码)
- `seed_step.py`:种子步进实现 —— 跳过 LTE grey 冷启动,
直接热启动 nc 阶段,用于高温/He-poor/富金属等冷启动失败的场景
- `run_grid.py`:6 维网格调度,集成种子步进回退(NEW)
- 冷启动失败时自动用 `find_seed` 找已收敛邻居,用 SEED_STEP_CHAIN 重试
- 失败的冷启动结果备份到 `<model>.coldfail/`,避免污染种子库
- `find_seed` 优先级:同 (Teff,logg,logHe) 最近 CNO → 全局最近邻
- `_atmos_clean` 过滤掉 NaN 污染的"假收敛"模型作种子
### 使用方法
```bash
export TLUSTY=/home/dckj/program/tlusty/tl208-s54
# 单个模型(冷启动,适用 20-40K 大部分点)
python3 cno_grid/src/run_one.py --teff 35000 --logg 5.5 --loghe -2 \
--logc -1 --logn -1 --logo -1
# 种子步进(高温 He-poor 等冷启动失败点)
python3 cno_grid/src/seed_step.py --teff 80000 --logg 6.5 --loghe -4 \
--logc -4 --logn -4 --logo -4 \
--seed cno_grid/results/<seed-model>/<seed-model>.7
# 批量网格(自动冷启动 + 失败时种子步进回退)
python3 cno_grid/src/run_grid.py cno_grid/config.yaml --dry-run # 预览
python3 cno_grid/src/run_grid.py cno_grid/config.yaml # 正式跑
```
### 网格运行策略
**桥头堡机制**(手动):在跑完整网格前,先在难收敛区附近算几个"桥头堡"
模型,建立种子库。例如高温区先算:
```bash
# 1. 算 80K He-rich cno-4(冷启动可成功)
python3 cno_grid/src/run_one.py --teff 80000 --logg 6.5 --loghe 2 \
--logc -4 --logn -4 --logo -4
# 2. 用它作种子算 80K He-poor cno-4(种子步进)
python3 cno_grid/src/seed_step.py --teff 80000 --logg 6.5 --loghe -4 \
--logc -4 --logn -4 --logo -4 \
--seed cno_grid/results/t80000_g6.5_he2_c-4_n-4_o-4/t80000_g6.5_he2_c-4_n-4_o-4.7
# 3. 之后跑 run_grid.py 时,find_seed 会自动发现这些已收敛的邻居
```
**run_grid 的种子步进回退流程**
```
对每个网格点 P:
1. 检查 conv.json: 若已 converged → skip
2. find_seed(P): 查找已收敛邻居(同 family 优先,按 CNO 距离)
3. 冷启动 run_one(DEFAULT_CHAIN):
a. 成功 → 完成
b. 失败 + seed_step_fallback=true + 找到种子 →
移动失败结果到 <P>.coldfail/
用 SEED_STEP_CHAIN + seed 重试
4. 写 grid_status.json 汇总(含 seed_step_retries 计数)
```
---
## 6X. ICRSW 修复方案(备选 —— 当前未实施)
### 背景
`ICRSW`Hummer & Voels 1988 碰撞-辐射开关)在 tlusty208 中是**未完成的
集成**——`SUBROUTINE SWITCH``tlusty208.f:4556`)写好了完整的 CRSW 计算
逻辑,下游消费方代码(`tlusty208.f:6341-8021`、`18201`、`18252` 三处
`RRU/RRD * CRSW(ID)`)也都到位,但**主迭代循环中缺少 `CALL SWITCH`**
`PROGRAM TLUSTY` 主循环在 line 30-58`SUBROUTINE SOLVE` 在 line 14420
两者都没有调用 SWITCH)。
结果:`CRSW(ID)` 永远保持默认值 `UN`=1.0line 1812 `CRSW(ID)=UN`),
所有 `RRU/RRD * CRSW(ID)` 都是无效操作。
### 修复需要的步骤
**步骤 1:检查编译环境**
```bash
which gfortran || apt list --installed 2>/dev/null | grep -i fortran
# 若无,安装:sudo apt install gfortran
```
**步骤 2:定位主迭代循环**
主循环在 `tlusty/tlusty208.f:30-58`(行 28 是注释头,行 30 是 `10 ITER=ITER+1`
行 58 是 `20 CONTINUE`):
```fortran
10 ITER=ITER+1
CALL RESOLV ! 形式解
IF(IACC.GT.0) CALL ACCEL2
IF(NN.GT.MSMX) THEN
CALL SOLVE ! 完全线性化
ELSE
CALL SOLVES
END IF
CALL TIMING(2,ITER)
GO TO 10
```
**步骤 3:在循环中插入 SWITCH 调用**
`ITER=ITER+1` 之后、`CALL RESOLV` 之前插入:
```fortran
10 ITER=ITER+1
C ---- ICRSW -----
C CRSW CRSW 1.0
IF(ICRSW.GT.0) THEN
IF(ITER.EQ.1) THEN
CALL SWITCH(1) ! INITM=1: 初始化 CRSW=SWPFAC*min(C/R)
ELSE
CALL SWITCH(0) ! INITM=0: CRSW *= SWPINC
END IF
END IF
CALL RESOLV
...
```
**步骤 4:验证变量在调用点已就绪**
`SWITCH` 子程序用到 `COLRAT(ITR,ID)``RRU(ITR,ID)``RRD(ITR,ID)`
`LINE(ITR)``FR0(ITR)``TEMP(ID)``HK`。需确认这些在 `ITER=ITER+1`
之后已被前一迭代更新(或在首次迭代时已初始化)。若未就绪,可能需要
把 SWITCH 调用移到 `CALL RESOLV` 之后。
**步骤 5:重新编译**
```bash
cd tlusty/
# 备份原可执行
cp tlusty.exe tlusty.exe.orig
# 编译(具体命令取决于源码组织,可能是单文件或 Makefile)
gfortran -O2 -o tlusty.exe tlusty208.f \
IMPLIC.FOR BASICS.FOR ARRAY1.FOR ATOMIC.FOR MODELQ.FOR \
ITERAT.FOR ALIPAR.FOR ODFPAR.FOR
```
**步骤 6:测试**
跑已知难收敛的点(如 80K + He-poor + logCNO=-1)开/关 ICRSW,对比
nc 阶段的迭代历史。如果 ICRSW 真正生效,开启后 iter 2 的尖峰应该被
压低(CRSW < 1)。
```bash
# 关 ICRSW(基线)
python3 cno_grid/src/seed_step.py --teff 80000 --logg 6.5 --loghe -4 \
--logc -1 --logn -1 --logo -1 --seed <seed> \
2>&1 | tee /tmp/no_icrsw.log
# 开 ICRSW(修复后)
# 在 nst 里加 ICRSW=1,SWPFAC=1e-3,SWPLIM=1.0,SWPINC=2.0
# ... 跑同样模型
# 对比两次的 fort.9 iter 1-10 max_relc 演化
```
### 修复风险评估
**好处**
- 可能解锁 80K + He-poor + 富金属区的收敛(最后一个未解决角落)
- 是数值方法层面的修复,物理意义清晰
**风险**
- 重新编译可能引入其他问题(tlusty 源码依赖复杂)
- SWITCH 集成后可能有未预见的 bug(作者本就没集成,可能有原因)
- 编译器版本差异(原版可能用 f77/f90,gfortran 行为可能不同)
### 替代方案:网格层面规避
如果不想改源码,**现有种子步进方案已覆盖大部分情况**:
- 20000-40000K:冷启动全区间可靠
- 60000-80000K + He-rich / 低金属:冷启动或一步种子步进可解决
- 60000-80000K + He-poor + 富金属(logCNO=-1):**真实物理极限**
网格如实标记未收敛(这是合理的——观测上这些极端参数组合的 sdB
本就罕见)
**建议**:先用现有方案跑完整网格,统计未收敛点的比例和分布。
如果未收敛点占总网格 <5%,无需修复 ICRSW;如果占比高且集中在
可观测的重要参数区,再考虑修复。
### 每阶段信息记录
conv.json 记录每阶段的 converged/max_relc/elapsed_sec,以及 synspec_sec。
---
## 7. 下一步建议
### 立即可行(已验证配方 + 种子步进)
- **冷启动跑 20000-40000K + logC/N/O=-4 到 -1**:可靠收敛,无需种子
- **种子步进跑 60000-80000K + He-rich 或低金属**:用冷启动建"桥头堡"
再以它为种子推进到目标点
- **物理极限标注**80K + He-poor + logCNO=-1 是真实物理极限,
网格如实标记未收敛(不强制成功)
### 待完善
- **run_grid.py 自动种子策略**:现在是冷启动失败即标记失败;
应扩展为先尝试冷启动,失败则查找邻居已收敛模型作种子重试,
再失败则尝试中间丰度点作跳板(如 cno-4 → cno-2 → cno-1)。
- **种子库管理**:网格计算时按 Teff/logg 分组,每组先算最容易的点
(He-rich 或低金属),建立种子库,再扩散到难收敛点。
- **synspec 高温区 NaN 问题**80K 大气收敛但 synspec 谱有 74% NaN。
需要单独排查(可能是 gfVIS99.dat 谱线表对 80K 不兼容,或某些 CNO
高价离子模型原子在该温度下数值溢出)。
### 关键教训
1. **先核实物理参数**:之前花了大量时间调 CHMAX/IDLTE/ORELAX/He能级/NFREAD
真正的根因(丰度范围过高 + 冷启动初猜太远)却一直被忽略。
2. **冷启动 ≠ 唯一选择**LTE grey 冷启动在高温 He-poor 模型上必然失败,
但这**不是物理极限**,只是初猜太差。种子步进是 Peter Nemeth 邮件早就
建议的方法("减小模型间步长"),只是之前一直没正确实现。
3. **源码分析的价值**:通过 subagent 读 tlusty208.f 才发现:
- LTGREY 标志的真正含义(T=生成灰大气,F=读 fort.8)—— 种子步进的物理基础
- ICRSW 是**死代码**SWITCH 子程序定义了但从未被 CALL)—— 看似可用的
参数实际无效,必须实测验证
4. **物理极限要承认**80K + He-poor + 金属量 0.1×H 的组合,
即使用尽所有稳定化手段也不收敛。这是物理极限,不是工程问题。
网格应如实记录未收敛而非强行通过。
---
## 8. 邮件往来要点(Peter Nemeth
完整邮件在 `hot_subdwarf/letter/`(已逐条核对原文)。要点:
1. He-rich 模型难收敛属正常(原文:"Helium-rich models struggle a lot ...
That is normal"
2. He 用最复杂模型原子(原文:"I would always use the 24-level He1 and
20-level He2 model atoms")— 但实测在 `.5` 里声明 nlevs=14 对 nc 更稳定,
两者目标不同(Peter 关注光谱精度,我们关注收敛稳定性)
3. ITEK 可调(原文:"you can set it to 3, 15, and 100")— 实测 100 overshoot
且不设(默认 4)最好
4. 模型链:粗→精 CHMAX;减小模型间步长(原文:"You can try decreasing the
steps in between models")← **本次种子步进正是这一条的正确实现**
LTGREY=F 热启动 + 邻居模型作种子)
---
## 9. 参考文件索引
| 文件 | 内容 |
|------|------|
| `/home/dckj/program/tlusty/tests/cno_sdspectrum/GUIDE.md` | 用户原始指南(35000K 验证配方)。注意:在 tl208-s54/ 上一级目录 |
| `cno_grid/src/run_one.py` | 三步链实现(DEFAULT_CHAIN = 正确配方)|
| `cno_grid/src/gen_input5.py` | .5 生成器(He nlevs=14, NFREAD=2000|
| `cno_grid/src/seed_step.py` | **种子步进实现**(高温/难收敛点)|
| `cno_grid/src/run_grid.py` | 6 维网格调度器(冷启动 + 种子步进回退)|
| `cno_grid/src/check_conv.py` | fort.9 解析与收敛判定 |
| `cno_grid/config.yaml` | 网格配置(链、seed_step_fallback 等)|
| `cno_grid/PIPELINE.md` | 计算流程文档(阶段/并行/统计)|
| `cno_grid/results/` | 测试模型结果(含 conv.json|
| `cno_grid/results/bound_*.log` | 8 点边界测试日志(2026-07-21|
| `cno_grid/results/seed_step/` | 种子步进验证结果 |
| `cno_grid/run_boundary_corrected.sh` | 边界测试启动脚本 |
| `hot_subdwarf/letter/` | Peter Nemeth 邮件 |
+370
View File
@@ -0,0 +1,370 @@
# CNO 网格完整计算流程
> 本文档说明完整理论光谱网格的计算流程:每个网格点的计算阶段、每阶段的配置、
> 配置原理、CPU/并行机制、以及如何统计每个阶段的信息(时间、收敛等)。
---
## 1. 总体架构
```
config.yaml (网格点 + 收敛链配置)
run_grid.py ── 生成 6 维笛卡尔积参数点
│ 断点续算(跳过已成功) / 种子复用(最近邻) / 失败隔离
├── worker 1 ── run_one.py ── 点 A
├── worker 2 ── run_one.py ── 点 B 每个 worker 独立工作目录
├── ... 互不干扰,24 核并行
└── worker 24 ── run_one.py ── 点 X
三步链(lte→nc→nl) + synspec
results/<模型名>/
conv.json ← 阶段信息(收敛/迭代/时间)
*.spec/.cont ← 光谱
*.7 ← 各阶段大气
```
---
## 2. 每个网格点的计算阶段
每个网格点(一组 Teff/logg/logHe/logC/logN/logO 参数)经过 **4 个阶段**
| 阶段 | 程序 | 做什么 | 典型耗时 |
|------|------|--------|---------|
| 1. LTE 灰大气 | tlusty | `T T` 模式,解析求灰色 T(τ) 结构 | 1-3 秒 |
| 2. ncNLTE 连续谱)| tlusty | `F F` + `ilvlin=0`,收敛电离平衡(无线跃迁)| 1-10 分钟 |
| 3. nlNLTE 含线)| tlusty | `F F` + `ilvlin=100`,加全部谱线跃迁 | 5-20 分钟 |
| 4. synspec | synspec | 用 nl 大气合成可观测光谱 | 3-10 秒 |
**阶段间依赖**:1→2→3→4 严格顺序。每阶段用上一阶段的 `.7` 大气作种子(fort.8)。
### 为什么是这 4 个阶段(原理)
Tlusty 的 NLTE 求解用**迭代线性化**complete linearization)。线性化的收敛半径
有限——当初猜离真解太远时迭代发散。四个阶段逐步缩小初猜与真解的差距:
- **阶段1LTE 灰大气)**:LTE + 灰色不透明度假设下解析求温度结构。提供物理
合理的起点,不需要种子(从零开始)。
- **阶段2(nc 连续谱)**:切换到 NLTE,但 `ilvlin=0` 不含束缚-束缚线跃迁。
线跃迁是统计平衡方程中最敏感的非线性项;先不加线,只收敛电离平衡(光致电离
+复合),得到稳定的 NLTE 布居数结构。**跳过此步直接 grey→含线 NLTE 必发散。**
- **阶段3(nl 含线)**:加入全部线跃迁(ilvlin=100)。从已收敛的 nc 种子起步,
线扰动小,快速收敛(典型 ~15 次迭代)。
- **阶段4synspec)**:用 nl 阶段收敛的大气模型,计算指定波长范围的合成光谱。
---
## 3. 每阶段的配置信息与原理
### 3.1 `.5` 文件(每阶段一份,三阶段相同 NATOMS/ions,只改 3 处)
```
第1行: TEFF GRAV (三阶段相同:目标参数)
第2行: LTE LTGRAY (阶段1=T T,阶段2/3=F F
第3行: nst 文件名 (阶段1=nst_lte, 阶段2=nst_nc, 阶段3=nst_nl
第4行: NFREAD (=50, 频率点数)
第5行: NATOMS =8: H,He,空×3,C,N,O
第6+行: atoms (mode abn modpf) C/N/O 的 mode=2 显式NLTE, abn=10^logX
ions段: iat iz nlevs ilast ilvlin nonstd typion filei
(阶段1/2: ilvlin=0; 阶段3: ilvlin=100 ← 关键区别)
```
**为什么 NATOMS/ions 三阶段必须相同**:每阶段的 `.7` 大气记录了每个能级的
布居数。种子与目标的能级结构必须一一对应,否则读取时索引错位 → NaN。
### 3.2 nst 文件(非标准参数,每阶段不同)
**阶段1(LTE 灰大气)—— 保持干净,不加稳定化参数**
```
ND=50,VTB=2.,NITER=0
```
- `NITER=0`:灰大气不迭代,只做一次形式解。
**阶段2(nc)和阶段3(nl)—— 频率细化(用户验证配方,不设 CHMAX/ITEK)**
```
ND=50,NLAMBD=3,VTB=2.,ISPODF=1,DDNU=50.,CNU1=6.,NITER=<阶段>
IELCOR=-1
```
- nc: NITER=50, nl: NITER=100
- **不设 CHMAX**(用默认 0.001,强迫 nc 真正收敛)
- **不设 ITEK**(用默认 4
每个参数的作用与原理:
| 参数 | nc值 | nl值 | 作用 | 为什么这样设 |
|------|------|------|------|-------------|
| `ND` | 50 | 50 | 大气深度点数 | sdB 标准配置 |
| `NLAMBD` | 3 | 3 | lambda 迭代频率点数 | 频率网格细化(用户验证配方) |
| `VTB` | 2. | 2. | 微湍流速度 km/s | sdB 典型值 |
| `ISPODF` | 1 | 1 | 频率网格开关 | 启用细化频率网格 |
| `DDNU` | 50. | 50. | 频率间隔因子 | 频率网格细化参数 |
| `CNU1` | 6. | 6. | 频率网格起点 | 频率网格细化参数 |
| `NITER` | 50 | 100 | 最大迭代数 | nc 给 50 次;nl 给 100 次 |
| `IELCOR` | -1 | -1 | 电子密度修正 | 关闭 |
> **关键:不设 CHMAX(用默认 0.001)、不设 ITEK(用默认 4)、不设 IDLTE/ORELAX。**
> 之前版本设了 CHMAX=0.1 导致 nc 没真正收敛,是大部分失败的根本原因。
> 详见 EXPERIENCE.md §4 的错误记录。
### 3.3 synspec 配置(fort.55.lin + 谱线表)
```
fort.55.lin 第6行: WLMIN WLMAX WLSTEP ... CUTOFF ...
谱线表 fort.19: data/gfVIS99.dat (含 C 1412 / N 2396 / O 1885 条线)
```
- 当前用 3000-7000Å(光学波段,覆盖 C II 4267、C III 4647 等)。
- 大气来自 nl 阶段的 `.7`(复制为 fort.8)。
---
## 4. CPU 与并行机制
### 4.1 每个网格点只用一个 CPU 核
**是的。** tlusty.exe 和 synspec.exe 是 Fortran 编译的单线程程序,每个实例只用
1 个 CPU 核。网格点的并行不是靠程序内部的多线程,而是靠**同时启动多个程序实例**。
### 4.2 如何做到并行
`run_grid.py` 用 Python 的 `multiprocessing.Pool``run_grid.py:271`):
```python
with Pool(nworkers) as pool:
for res in pool.imap_unordered(_worker, worker_args):
...
```
- `nworkers`config.yaml,当前=24):同时运行的 worker 进程数。
- 每个 worker 是一个独立的 Python 子进程,调用 `run_one.py` 跑一个网格点
(在独立的工作目录里,互不干扰)。
- `imap_unordered`:哪个点先完成就先回收,立即分配下一个点(动态负载均衡)。
- 24 核机器跑 24 个 worker = 24 个 tlusty 实例同时跑 = 满载利用。
**关键:每个 worker 用独立工作目录**`results/<模型名>/`),避免 fort.* 文件
冲突。这是并行安全的基础。
### 4.3 吞吐量估算
| 模型类型 | 单点耗时 | 24核并行吞吐 |
|---------|---------|-------------|
| 80000K(待解决)| — | 目前 nc 发散,需专业策略 |
| 20000-40000K(标准)| ~15-25 分钟 | ~72-110 点/小时 |
| 高金属 CNO=10×H | ~30 分钟 | ~48 点/小时 |
128 点疏网格约需 2-3 小时;8192 点完整网格约需 5 天。
---
## 5. 如何统计每阶段信息
### 5.1 当前已记录的信息(conv.json
每个网格点完成后,`results/<模型名>/conv.json` 记录:
```json
{
"name": "t40000_g6.0_he0_c1_n1_o1",
"params": {"teff":40000, "logg":6.0, "loghe":0, "logc":1, "logn":1, "logo":1},
"converged": true,
"final_max_relc": 0.0069,
"atmosphere_has_nan": false,
"synspec_rc": 0,
"elapsed_sec": 1714.1, +synspec
"seed": null,
"stages": [
{
"label": "lte",
"converged": true,
"final": {"itek":3, "rc":0, "max_relc":0.0,
"note":"NITER=0 grey start"}
},
{
"label": "nc",
"converged": false, nc
"final": {"itek":3, "rc":0, "max_relc":0.957,
"worst_depth":1, "last_iter":50, "n_depths":50}
},
{
"label": "nl",
"converged": true,
"final": {"itek":3, "rc":0, "max_relc":0.0069,
"worst_depth":1, "last_iter":17, "n_depths":50}
}
]
}
```
每阶段记录:`converged`(是否收敛)、`max_relc`(最大相对变化)、
`worst_depth`(最差深度点)、`last_iter`(迭代次数)、`n_depths`(深度点数)。
### 5.2 每阶段时间记录(已实现)
`run_one.py` 现在在每个阶段的循环开始/结束处计时,conv.json 里每个 stage 有
`elapsed_sec`synspec 也有单独的 `synspec_sec`
```json
"stages": [
{"label":"lte", "elapsed_sec": 27.3, "converged":true, ...},
{"label":"nc", "elapsed_sec": 408.4, "converged":false, ...},
{"label":"nl", "elapsed_sec": 7.8, "converged":true, ...}
],
"synspec_sec": 3.1,
"elapsed_sec": 446.2
```
统计所有模型的阶段时间分布:
```bash
python3 -c "
import json,glob
for f in sorted(glob.glob('results/*/conv.json')):
j=json.load(open(f))
times = {s['label']:s.get('elapsed_sec',0) for s in j['stages']}
print('%-30s lte=%5.0fs nc=%5.0fs nl=%5.0fs syn=%4.0fs total=%5.0fs' % (
j['name'], times.get('lte',0), times.get('nc',0), times.get('nl',0),
j.get('synspec_sec',0), j['elapsed_sec']))
"
```
### 5.3 统计整个网格的信息
`run_grid.py` 完成后写 `results/grid_status.json`
```json
{
"total": 128,
"elapsed_sec": 9600,
"counts": {"converged": 120, "unfinished": 5, "error": 2, "skipped": 1},
"models": [
{"name":"t20000_...", "status":"converged", "max_relc":0.0065},
...
]
}
```
汇总统计命令:
```bash
# 成功率
python3 -c "import json; j=json.load(open('results/grid_status.json')); print(j['counts'])"
# 所有收敛模型的 max_relc 分布
python3 -c "
import json,glob
for f in sorted(glob.glob('results/*/conv.json')):
j=json.load(open(f))
if j['converged']:
print(j['name'], j['final_max_relc'], str(j['elapsed_sec'])+'s')
"
# 失败/未收敛的模型
python3 -c "
import json,glob
for f in sorted(glob.glob('results/*/conv.json')):
j=json.load(open(f))
if not j['converged']:
print(j['name'], 'FAILED', j.get('note',''))
"
```
### 5.4 单个网格点的详细收敛诊断
```bash
# 看某阶段的迭代收敛趋势(fort.9)
python3 src/check_conv.py results/<模型>/<模型>.nl.9 --chmax 0.01
# 画光谱(标出 CNO 诊断线位置)
python3 src/plot_spec.py results/<模型>
```
---
## 6. 完整操作步骤
### 第1步:配置网格密度(config.yaml 的 grid 段)
```yaml
grid:
teff: [20000, 30000, 40000, 60000] # 各维采样点列表
logg: [5.0, 6.0]
loghe: [-2, 0]
logc: [-1, 1]
logn: [-1, 1]
logo: [-1, 1]
```
### 第2步:设置环境变量
```bash
export TLUSTY=/home/dckj/program/tlusty/tl208-s54
```
### 第3步:预览(dry-run
```bash
cd $TLUSTY/cno_grid
python3 src/run_grid.py config.yaml --dry-run
# 输出:grid: 64 points total, N already done, M to compute
```
### 第4步:启动批量计算(后台并行)
```bash
nohup python3 src/run_grid.py config.yaml > results/grid_run.log 2>&1 &
```
### 第5步:监控
```bash
tail -f results/grid_run.log # 实时进度
cat results/grid_status.json # 汇总(完成后才有)
```
### 第6步:断点续算(中断后恢复,自动跳过已成功的)
```bash
python3 src/run_grid.py config.yaml # 重跑同一命令即可
```
### 第7步:检查结果 + 画图
```bash
# 成功率
python3 -c "import json;print(json.load(open('results/grid_status.json'))['counts'])"
# 画某个模型光谱
python3 src/plot_spec.py results/<模型名>
```
### 第8步:加密网格(Phase 2
在 config.yaml 的各维列表里加更多点,重跑 `run_grid.py`(自动跳过已完成的,
只算新点)。
---
## 7. 注意事项
1. **种子复用**run_grid.py 会自动找最近邻已收敛的 `.7` 作种子,省去 LTE 阶段。
但种子与目标的参数差不能太大(logg ≤0.5/步,Teff ≤5000K/步),否则发散。
建议网格各维步长不要太大。
2. **断点续算判定**`conv.json``converged=true` 的点会被跳过。假收敛
atmosphere_has_nan=true)的点会被重算。
3. **失败隔离**:单点失败(发散/崩溃)不中断整个网格,记入 grid_status.json
的 error 列表。可在 config.yaml 放宽 CHMAX 或加 orelax 重试失败点。
4. **磁盘空间**:每个模型约 50-100MB(含中间文件)。8192 点约需 400-800GB。
当前 932GB 可用,够完整网格。如不够可定期清理中间文件(保留 .spec/.cont/.7/conv.json)。
5. **并行安全**:每个 worker 用独立工作目录(results/<模型名>/),fort.* 文件
不冲突。可安全并行。
6. **nst 文件行长限制(已修复)**tlusty 的 nst 解析器有 ~72 字符的行宽限制。
如果参数太多写在一行(如加了 IDLTE/IACC 后 >72 字符),行尾的参数会被
静默截断。`write_nst()` 现在把参数分两行写(line1≤64c, line2 余下参数)。
这是个隐蔽 bug——截断后 tlusty 不报错而是用默认值,导致"看似成功实则参数
没生效"。
7. **fort.84 残留(已修复)**:tlusty 运行时会在工作目录写 fort.84(nst 参数的
内部表示)。如果下一次 tlusty 运行(不同 NATOMS)读到旧的 fort.84,会报
"Bad integer for item 48" 崩溃。`run_tlusty()` 现在每次运行前删除 fort.84。
8. **收敛可靠性(如实)**:用正确配方(无 CHMAX/ITEK, 14-level He, NFREAD=2000
重新验证后,35000K 成功(nl=0.00078),但 40000K+ 的 nc 仍然震荡发散。
之前版本的"8/8 边界全部成功"不准确(基于错误的 CHMAX=0.1)。
40000K+ 高温区需要种子步进或 ICRSW 等专业策略。详见 EXPERIENCE.md。
+54
View File
@@ -0,0 +1,54 @@
# 6 维 CNO NLTE 热亚矮星网格的配置文件。
# 所有丰度均为 log10(nX/nH).5 文件中的 abn 字段设为 10**logX。
#
# 网格各维范围:
# Teff : 20000 - 80000 K
# logg : 5.0 - 6.5
# logHe: -4 - 2 He/H 数密度比:1e-4 .. 100
# logC : -4 - -1 C/H1e-4 .. 0.1;太阳 C/H≈-3.6,落在区间内)
# logN : -4 - -1 N/H:太阳 N/H≈-4.2
# logO : -4 - -1 O/H:太阳 O/H≈-3.3
# CNO 范围已修正为物理上的 sdB 范围,不再是超太阳(旧版 -2..1 会发散)
# ---- 网格轴:显式列出各维采样点 ----
grid:
teff: [20000, 30000, 40000, 60000]
logg: [5.0, 6.0]
loghe: [-2, 0]
logc: [-4, -2, -1]
logn: [-4, -2, -1]
logo: [-4, -2, -1]
# 共 4*2*2*3*3*3 = 432 个点
# ---- 收敛链 ----
# 已验证的三步配方 (tests/cno_sdspectrum, Teff=35000 logg=5.5):
# LTE grey (T T, NITER=0) -> NLTE 连续谱 (nc, ilvlin=0) -> NLTE 谱线 (nl, ilvlin=100)
# nc 步是关键:在不考虑谱线扰动下收敛 NLTE 电离平衡,给 nl 一个稳定种子。
# 跳过 ncgrey -> 完整 NLTE)会发散。
#
# 重要:不要在 nst 里设 CHMAX/ITEK,用 tlusty 默认值(CHMAX=0.001, ITEK=4)。
# 设 CHMAX=0.1 会让 nc 提前停止,给 nl 一个坏种子导致发散。
chain:
- {label: lte, lte: T, ltgray: T, ilvlin: 0, require_converged: false, niter: 0}
- {label: nc, lte: F, ltgray: F, ilvlin: 0, require_converged: false, niter: 50}
- {label: nl, lte: F, ltgray: F, ilvlin: 100, require_converged: true, niter: 100}
itek_fallback: [] # 留空:ITEK 默认值最稳;非空会重试(实测无效)
niter: 100 # 每个 tlusty 运行的默认最大迭代数 (nst NITER)
# ---- 种子步进回退(NEW----
# 冷启动失败时,自动用已收敛的邻居模型作种子,用 LTGRAY=F 热启动重试。
# 这是高温 He-poor / 富金属区收敛的关键(见 EXPERIENCE.md §5Y)。
# 失败的冷启动结果会备份到 <model>.coldfail/ 目录。
seed_step_fallback: true
# ---- 执行参数 ----
nworkers: 24 # 并行 worker 数(每次 tlusty 运行是单线程的;
# 想用更多核心就调大;每个 worker 需要独立工作目录)
timeout_sec: 3600 # 单模型墙钟时间上限
resume: true # 跳过已完成的模型(conv.json 中 converged=true
# ---- 路径(相对 TLUSTY,除非给出绝对路径)----
template: templates/cno_atmos.5.tpl
fort55: templates/fort.55.lin
linelist: data/gfVIS99.dat
results: results
+25
View File
@@ -0,0 +1,25 @@
# 边界测试配置:5 个代表性难点 + 已验证中心点
# 注:此 config 显式设置了 CHMAX/ITEK(与主 config.yaml 不同),用于
# 探索宽松阈值下的边界行为;主网格运行不应使用这些值。
grid:
teff: [20000, 40000, 80000]
logg: [5.0, 6.0, 6.5]
loghe: [-4, 0, 2]
logc: [0]
logn: [0]
logo: [0]
# 此处显式 CHMAX/ITEK 仅用于边界测试探索;主网格保持默认(不写这两个键)
chain:
- {label: lte, lte: T, ltgray: T, ilvlin: 0, chmax: 0.1, itek: 3, require_converged: false, niter: 0}
- {label: nc, lte: F, ltgray: F, ilvlin: 0, chmax: 0.1, itek: 3, require_converged: false, niter: 50}
- {label: nl, lte: F, ltgray: F, ilvlin: 100, chmax: 0.01, itek: 3, require_converged: true, niter: 100}
itek_fallback: [15] # nl 失败时回退到 ITEK=15 重试(注意:实测对多数点无效)
niter: 100
nworkers: 1
timeout_sec: 7200 # 边界点可能很慢,给 2 小时
resume: true
template: templates/cno_atmos.5.tpl
fort55: templates/fort.55.lin
linelist: data/gfVIS99.dat
results: results
+31
View File
@@ -0,0 +1,31 @@
# 用于验证 run_grid.py 中 seed_step 回退功能的测试配置。
# 包含 3 个有策略意义的点:
# - 80k/6.5/+2/-4/-4/-4 (冷启动可成功,将作为桥头堡)
# - 80k/6.5/-4/-4/-4/-4 (冷启动失败,从桥头堡种子步进)
# - 80k/6.5/-4/-2/-2/-2 (冷启动失败,从 cno-4 的贫 He 模型种子步进)
grid:
teff: [80000]
logg: [6.5]
loghe: [-4, 2]
logc: [-4, -2]
logn: [-4, -2]
logo: [-4, -2]
# 下面通过 --only 过滤到具体几个点
chain:
- {label: lte, lte: T, ltgray: T, ilvlin: 0, require_converged: false, niter: 0}
- {label: nc, lte: F, ltgray: F, ilvlin: 0, require_converged: false, niter: 50}
- {label: nl, lte: F, ltgray: F, ilvlin: 100, require_converged: true, niter: 100}
itek_fallback: []
niter: 100
nworkers: 1
timeout_sec: 1800
resume: true
seed_step_fallback: true
template: templates/cno_atmos.5.tpl
fort55: templates/fort.55.lin
linelist: data/gfVIS99.dat
results: results_test_grid
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
# 边界点测试脚本(旧版,使用 logCNO=0)。每个点跑完后把收敛情况追加到日志。
# 注:当前推荐使用 run_boundary_corrected.sh(修正后的丰度范围)。
export TLUSTY=/home/dckj/program/tlusty/tl208-s54
cd $TLUSTY/cno_grid
LOG=results/boundary_runs.log
echo "=== 边界测试开始 $(date) ===" > $LOG
# 跑一个边界点,并把结果摘要追加到日志
run_pt() {
local teff=$1 logg=$2 loghe=$3 name=$4
echo ">>> [$name] teff=$teff logg=$logg logHe=$loghe $(date)" >> $LOG
python3 src/run_one.py --teff $teff --logg $logg --loghe $loghe \
--logc 0 --logn 0 --logo 0 >> $LOG 2>&1
# 记录结果
python3 -c "
import json,glob,os
d='t{}_g{:.1f}_he{:.0f}_c0_n0_o0'.format(int($teff),$logg,$loghe)
p=os.path.join('results',d,'conv.json')
try:
j=json.load(open(p))
print(' {} {} {} -> converged={} max_relc={} elapsed={}'.format(
'$name',$teff,$logg,j['converged'],j.get('final_max_relc'),j.get('elapsed_sec')),file=open($LOG,'a'))
except Exception as e: print(' $name ERROR:',e,file=open($LOG,'a'))
" >> $LOG 2>&1
}
# 边界点1已在独立进程跑,这里跑2-5
run_pt 80000 6.5 2 "b2_高温He-rich"
run_pt 80000 6.5 -4 "b3_高温He-poor"
run_pt 40000 6.0 0 "b4_中心点高金属参考"
run_pt 20000 6.5 -4 "b5_低温贫He"
echo "=== 全部完成 $(date) ===" >> $LOG
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
# 修正丰度范围(logCNO = -4..-1)后的边界测试。
# 之前的"高温发散"实际上是因为 abn=1.0logX=0)相当于 4000 倍太阳丰度。
# 此处用物理上合理的丰度重新跑这组边界。
#
# 测试点(全部后台并行运行):
# B1 80000/6.5/-4/ -1/-1/-1 高温贫 He、富金属(CNO 为 0.1 倍 H
# B2 80000/6.5/-4/ -4/-4/-4 高温贫 He、近太阳 CNO
# B3 80000/6.5/ 2/ -1/-1/-1 高温富 He(物理上最难的点)
# B4 80000/6.5/ 2/ -4/-4/-4 高温富 He、近太阳
# B5 20000/5.0/ 2/ -1/-1/-1 低温富 He、富金属
# B6 20000/5.0/ 2/ -4/-4/-4 低温富 He、近太阳
# B7 40000/5.5/ 0/ -4/-4/-4 中温类太阳 CNO(丰度下限)
# B8 60000/6.0/ 0/ -1/-1/-1 中高温富金属抽查点
set -u
cd /home/dckj/program/tlusty/tl208-s54
RESULTS=cno_grid/results
mkdir -p "$RESULTS"
# 后台运行一个模型,把 stdout/stderr 写入对应日志文件
run_bg() {
local tag="$1"; shift
local logfile="$RESULTS/bound_${tag}.log"
echo "[$(date +%H:%M:%S)] launching $tag -> $logfile"
nohup python3 cno_grid/src/run_one.py "$@" > "$logfile" 2>&1 &
echo " pid=$!"
}
# 高温边界(80000K)—— 最重要的复测点
run_bg 80k_he-4_cno-1 --teff 80000 --logg 6.5 --loghe -4 --logc -1 --logn -1 --logo -1
run_bg 80k_he-4_cno-4 --teff 80000 --logg 6.5 --loghe -4 --logc -4 --logn -4 --logo -4
run_bg 80k_he2_cno-1 --teff 80000 --logg 6.5 --loghe 2 --logc -1 --logn -1 --logo -1
run_bg 80k_he2_cno-4 --teff 80000 --logg 6.5 --loghe 2 --logc -4 --logn -4 --logo -4
# 低温富 He 边界(20000K, logHe=2
run_bg 20k_he2_cno-1 --teff 20000 --logg 5.0 --loghe 2 --logc -1 --logn -1 --logo -1
run_bg 20k_he2_cno-4 --teff 20000 --logg 5.0 --loghe 2 --logc -4 --logn -4 --logo -4
# 中温丰度边界
run_bg 40k_he0_cno-4 --teff 40000 --logg 5.5 --loghe 0 --logc -4 --logn -4 --logo -4
run_bg 60k_he0_cno-1 --teff 60000 --logg 6.0 --loghe 0 --logc -1 --logn -1 --logo -1
wait
echo "[$(date +%H:%M:%S)] ALL BOUNDARY TESTS COMPLETE"
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
# 针对剩余冷启动失败边界点的种子步进验证测试。
#
# 策略:冷启动在低温、以及高温+富 He+低 CNO 的区域是可行的。用这些已
# 收敛的大气作为种子,去热启动冷启动下会发散的几个点:
#
# S1 60000/6.0/0 /-1/-1/-1 以 40000/5.5/0/-4/-4/-4 为种子(Teff 不同)
# S2 80000/6.5/-4/-1/-1/-1 以 80000/6.5/-4/-4/-4/-4 为种子(同 T、更低金属)
# S3 80000/6.5/+2/-1/-1/-1 以 80000/6.5/+2/-4/-4/-4 为种子(同 T、更低金属)
#
# 注意:S1 把 Teff 从 40000 跨到 60000(原子相同)—— 比同 T、降金属的步进
# 更苛刻,但仍值得一试。
set -u
cd /home/dckj/program/tlusty/tl208-s54
RESULTS=cno_grid/results/seed_step
mkdir -p "$RESULTS"
# S2 首先需要一个已收敛的 80k_he-4_cno-4 大气 —— 上面的 seed_step/ 运行
# 已经有了一个,直接拿它当种子。
SEED_80K_HEPOOR_CNO4=cno_grid/results/seed_step/t80000_g6.5_he-4_c-4_n-4_o-4/t80000_g6.5_he-4_c-4_n-4_o-4.7
SEED_80K_HERICH_CNO4=cno_grid/results/t80000_g6.5_he2_c-4_n-4_o-4/t80000_g6.5_he2_c-4_n-4_o-4.7
SEED_40K_HEPOOR_CNO4=cno_grid/results/t40000_g5.5_he0_c-4_n-4_o-4/t40000_g5.5_he0_c-4_n-4_o-4.7
# 后台运行一个种子步进模型
run_bg() {
local tag="$1"; shift
echo "[$(date +%H:%M:%S)] launching $tag"
nohup python3 cno_grid/src/seed_step.py "$@" > "$RESULTS/${tag}.log" 2>&1 &
echo " pid=$!"
}
# S280K 贫 He 富金属,以 80K 贫 He 低金属为种子(刚验证过)
run_bg s2_80k_he-4_cno-1 --teff 80000 --logg 6.5 --loghe -4 \
--logc -1 --logn -1 --logo -1 --seed $SEED_80K_HEPOOR_CNO4
# S380K 富 He 富金属,以 80K 富 He 低金属为种子
run_bg s3_80k_he2_cno-1 --teff 80000 --logg 6.5 --loghe 2 \
--logc -1 --logn -1 --logo -1 --seed $SEED_80K_HERICH_CNO4
# S160K 贫 He 富金属,以 40K 贫 He 低金属为种子
run_bg s1_60k_he0_cno-1 --teff 60000 --logg 6.0 --loghe 0 \
--logc -1 --logn -1 --logo -1 --seed $SEED_40K_HEPOOR_CNO4
wait
echo "[$(date +%H:%M:%S)] ALL SEED_STEP TESTS COMPLETE"
+643
View File
@@ -0,0 +1,643 @@
70 42
2.976647D-07 4.055707D-07 5.544183D-07 7.599508D-07 1.056343D-06 1.465369D-06
2.030084D-06 2.808100D-06 3.881278D-06 5.361527D-06 7.403420D-06 1.021994D-05
1.410493D-05 1.946361D-05 2.685484D-05 3.704920D-05 5.110904D-05 7.049860D-05
9.723527D-05 1.340970D-04 1.849059D-04 2.549140D-04 3.513244D-04 4.839875D-04
6.663212D-04 9.165032D-04 1.258995D-03 1.726478D-03 2.362308D-03 3.223676D-03
4.385680D-03 5.946604D-03 8.034713D-03 1.081701D-02 1.451035D-02 1.939542D-02
2.583366D-02 3.428694D-02 4.534017D-02 5.972633D-02 7.835396D-02 1.023401D-01
1.330546D-01 1.721840D-01 2.218250D-01 2.846049D-01 3.638195D-01 4.635693D-01
5.888826D-01 7.460002D-01 9.437418D-01 1.197830D+00 1.539016D+00 2.019861D+00
2.714951D+00 3.725205D+00 5.194089D+00 7.326155D+00 1.040888D+01 1.483495D+01
2.112890D+01 2.997716D+01 4.226807D+01 5.914746D+01 8.213885D+01 1.133483D+02
1.558501D+02 2.143008D+02 2.957952D+02 2.967420D+02
3.281063D+04 3.104301D+08 6.144835D-16 3.394402D-03 5.458759D-05
5.899031D-05 8.840768D-05 1.307195D-04 1.829752D-04 2.443311D-04
3.151982D-04 8.919652D-01 2.578341D+08 5.031098D-06 1.872376D-08
2.397186D-09 2.928996D-08 1.265290D-09 3.348707D-09 6.547889D-10
1.035973D-08 8.536323D-09 9.222002D-10 7.947837D-10 2.326984D-09
4.747972D-10 7.475982D-09 8.196314D+04 9.281716D-05 4.176669D-06
4.861943D-06 7.286660D-06 1.077760D-05 1.520749D-05 2.052432D-05
2.662734D-05 3.332097D-05 3.921151D-05 4.981679D-05 6.413026D-05
8.244418D-05 2.569596D+07
3.281580D+04 4.228803D+08 8.371604D-16 6.300940D-03 1.013303D-04
1.095125D-04 1.641546D-04 2.427764D-04 3.399356D-04 4.541729D-04
5.866407D-04 1.592077D+00 3.512681D+08 1.264418D-05 4.705600D-08
6.024664D-09 7.360979D-08 3.179905D-09 8.415854D-09 1.645602D-09
2.603486D-08 2.145293D-08 2.317617D-09 1.997427D-09 5.848051D-09
1.193256D-09 1.878781D-08 1.512283D+05 2.003147D-04 8.198985D-06
9.231082D-06 1.369438D-05 2.017599D-05 2.841458D-05 3.830821D-05
4.967186D-05 6.214087D-05 7.310884D-05 9.287440D-05 1.197335D-04
1.549600D-04 3.496752D+07
3.282094D+04 5.779530D+08 1.144311D-15 1.177484D-02 1.893627D-04
2.046776D-04 3.068799D-04 4.540045D-04 6.359685D-04 8.503114D-04
1.100145D-03 2.818584D+00 4.801469D+08 3.198106D-05 1.190176D-07
1.523834D-08 1.861776D-07 8.042908D-09 2.128594D-08 4.162173D-09
6.584658D-08 5.425989D-08 5.861877D-09 5.052040D-09 1.479117D-08
3.018096D-09 4.751792D-08 2.798982D+05 4.587635D-04 1.666751D-05
1.789471D-05 2.612399D-05 3.824171D-05 5.368464D-05 7.224524D-05
9.358319D-05 1.170120D-04 1.376184D-04 1.747823D-04 2.257526D-04
2.942592D-04 4.772289D+07
3.282526D+04 7.920328D+08 1.568467D-15 2.212833D-02 3.558747D-04
3.847182D-04 5.770139D-04 8.540090D-04 1.196971D-03 1.601939D-03
2.077098D-03 4.936032D+00 6.581206D+08 8.114492D-05 3.019772D-07
3.866403D-08 4.723767D-07 2.040704D-08 5.400748D-08 1.056035D-08
1.670612D-07 1.376713D-07 1.487332D-08 1.281828D-08 3.752842D-08
7.657720D-09 1.205608D-07 5.182644D+05 1.105708D-03 3.521802D-05
3.555553D-05 5.071544D-05 7.350801D-05 1.026673D-04 1.377545D-04
1.781452D-04 2.225347D-04 2.616023D-04 3.320865D-04 4.299106D-04
5.641435D-04 6.527618D+07
3.282758D+04 1.100718D+09 2.180317D-15 4.278115D-02 6.880428D-04
7.439758D-04 1.116356D-03 1.653225D-03 2.318937D-03 3.107570D-03
4.040935D-03 8.721054D+00 9.148495D+08 2.130539D-04 7.928640D-07
1.015157D-07 1.240268D-06 5.358085D-08 1.417997D-07 2.772623D-08
4.386074D-07 3.614762D-07 3.905324D-08 3.365533D-08 9.853242D-08
2.010604D-08 3.165327D-07 9.791845D+05 2.852556D-03 7.972880D-05
7.457737D-05 1.030796D-04 1.472419D-04 2.040363D-04 2.724812D-04
3.514197D-04 4.382875D-04 5.148995D-04 6.530702D-04 8.476440D-04
1.118063D-03 9.047922D+07
3.282649D+04 1.526727D+09 3.025169D-15 8.241938D-02 1.325604D-03
1.433788D-03 2.152724D-03 3.190383D-03 4.479502D-03 6.012937D-03
7.846841D-03 1.515161D+01 1.269345D+09 5.512873D-04 2.051574D-06
2.626725D-07 3.209323D-06 1.386448D-07 3.669098D-07 7.173916D-08
1.134847D-06 9.353927D-07 1.010633D-07 8.708448D-08 2.549539D-07
5.202540D-08 8.190291D-07 1.826666D+06 7.335090D-03 1.847619D-04
1.594145D-04 2.124157D-04 2.979234D-04 4.085220D-04 5.420062D-04
6.963111D-04 8.664014D-04 1.017031D-03 1.288348D-03 1.676064D-03
2.214653D-03 1.250679D+08
3.282119D+04 2.114998D+09 4.192605D-15 1.584650D-01 2.548863D-03
2.757916D-03 4.143924D-03 6.147173D-03 8.641730D-03 1.162405D-02
1.523408D-02 2.601293D+01 1.759195D+09 1.407553D-03 5.238148D-06
6.706381D-07 8.194500D-06 3.539981D-07 9.367925D-07 1.831512D-07
2.897320D-06 2.388522D-06 2.580846D-07 2.223456D-07 6.509474D-07
1.328325D-07 2.091163D-06 3.366322D+06 1.842285D-02 4.318438D-04
3.460839D-04 4.436101D-04 6.095298D-04 8.253973D-04 1.086155D-03
1.388321D-03 1.722038D-03 2.019589D-03 2.554289D-03 3.327080D-03
4.383808D-03 1.724934D+08
3.280848D+04 2.925956D+09 5.803282D-15 3.040220D-01 4.890528D-03
5.294130D-03 7.962121D-03 1.182480D-02 1.664850D-02 2.244997D-02
2.956590D-02 4.427395D+01 2.435027D+09 3.532480D-03 1.314632D-05
1.682984D-06 2.056750D-05 8.884478D-07 2.351042D-06 4.595972D-07
7.270888D-06 5.995551D-06 6.479068D-07 5.580177D-07 1.633676D-06
3.333687D-07 5.248350D-06 6.105389D+06 4.549423D-02 1.008820D-03
7.596337D-04 9.379146D-04 1.260978D-03 1.683967D-03 2.194947D-03
2.788374D-03 3.445013D-03 4.036589D-03 5.094531D-03 6.628143D-03
8.659936D-03 2.373078D+08
3.278092D+04 4.046363D+09 8.030550D-15 5.832953D-01 9.383965D-03
1.016435D-02 1.530392D-02 2.275986D-02 3.210203D-02 4.341527D-02
5.748065D-02 7.503072D+01 3.369577D+09 8.657817D-03 3.222254D-05
4.124458D-06 5.041921D-05 2.177627D-06 5.762377D-06 1.126255D-06
1.782005D-05 1.469967D-05 1.588796D-06 1.367687D-06 4.004211D-06
8.170802D-07 1.286506D-05 1.081507D+07 1.132257D-01 2.374369D-03
1.690114D-03 2.014669D-03 2.649631D-03 3.485637D-03 4.494943D-03
5.669469D-03 6.971392D-03 8.162382D-03 1.026890D-02 1.328865D-02
1.710602D-02 3.260089D+08
3.273366D+04 5.595786D+09 1.111331D-14 1.120053D+00 1.802159D-02
1.953419D-02 2.945067D-02 4.386980D-02 6.200600D-02 8.413564D-02
1.119975D-01 1.269166D+02 4.663088D+09 2.055456D-02 7.650798D-05
9.790384D-06 1.197394D-04 5.170257D-06 1.368131D-05 2.673235D-06
4.230797D-05 3.491751D-05 3.774995D-06 3.247141D-06 9.507401D-06
1.939915D-06 3.055096D-05 1.855082D+07 2.821021D-01 5.621268D-03
3.795659D-03 4.379573D-03 5.641365D-03 7.310752D-03 9.322256D-03
1.166682D-02 1.426999D-02 1.670121D-02 2.091156D-02 2.677484D-02
3.377525D-02 4.475582D+08
3.267091D+04 7.739120D+09 1.538100D-14 2.152518D+00 3.463898D-02
3.757720D-02 5.673845D-02 8.467239D-02 1.199561D-01 1.633526D-01
2.185462D-01 2.144891D+02 6.453787D+09 4.717144D-02 1.756063D-04
2.246363D-05 2.749139D-04 1.186627D-05 3.140019D-05 6.132971D-06
9.709695D-05 8.019177D-05 8.672697D-06 7.452216D-06 2.182183D-05
4.452223D-06 7.013650D-05 3.074762D+07 6.844392D-01 1.312854D-02
8.477863D-03 9.512528D-03 1.203586D-02 1.539139D-02 1.942287D-02
2.412442D-02 2.935061D-02 3.435337D-02 4.273772D-02 5.390410D-02
6.648741D-02 6.143324D+08
3.260499D+04 1.070141D+10 2.128363D-14 4.136312D+00 6.657338D-02
7.228494D-02 1.093216D-01 1.634677D-01 2.321684D-01 3.173300D-01
4.262962D-01 3.620367D+02 8.930499D+09 1.053239D-01 3.921494D-04
5.014435D-05 6.141209D-04 2.649658D-05 7.011407D-05 1.368787D-05
2.167791D-04 1.792018D-04 1.938904D-05 1.663927D-05 4.872901D-05
9.941397D-06 1.566496D-04 4.958965D+07 1.581456D+00 2.959982D-02
1.849821D-02 2.031810D-02 2.536030D-02 3.209753D-02 4.016764D-02
4.957512D-02 6.003663D-02 7.030903D-02 8.680031D-02 1.076371D-01
1.300113D-01 8.430136D+08
3.255001D+04 1.478942D+10 2.943633D-14 7.938706D+00 1.277943D-01
1.388851D-01 2.103992D-01 3.152633D-01 4.489299D-01 6.158089D-01
8.294564D-01 6.098430D+02 1.235133D+10 2.319787D-01 8.638154D-04
1.104158D-04 1.353238D-03 5.836209D-05 1.544274D-04 3.013126D-05
4.773057D-04 3.950435D-04 4.276501D-05 3.664640D-05 1.073298D-04
2.189699D-05 3.450826D-04 7.895276D+07 3.451482D+00 6.369696D-02
3.896667D-02 4.218836D-02 5.218423D-02 6.559602D-02 8.162040D-02
1.002812D-01 1.210312D-01 1.418880D-01 1.737709D-01 2.120335D-01
2.518051D-01 1.155513D+09
3.251587D+04 2.042188D+10 4.068250D-14 1.520555D+01 2.448203D-01
2.663050D-01 4.041144D-01 6.068218D-01 8.663891D-01 1.192331D+00
1.607313D+00 1.024570D+03 1.707016D+10 5.115764D-01 1.905039D-03
2.434331D-04 2.985433D-03 1.287061D-04 3.405195D-04 6.639941D-05
1.051859D-03 8.719490D-04 9.445258D-05 8.080384D-05 2.366651D-04
4.828934D-05 7.609387D-04 1.260122D+08 7.135358D+00 1.307373D-01
7.900059D-02 8.479597D-02 1.043419D-01 1.306658D-01 1.620795D-01
1.986201D-01 2.392507D-01 2.809034D-01 3.415009D-01 4.111454D-01
4.823651D-01 1.580011D+09
3.250725D+04 2.817136D+10 5.618085D-14 2.905395D+01 4.679035D-01
5.093874D-01 7.743042D-01 1.165234D+00 1.668016D+00 2.301755D+00
3.099418D+00 1.716415D+03 2.357319D+10 1.138915D+00 4.241029D-03
5.418021D-04 6.648552D-03 2.865276D-04 7.579185D-04 1.476812D-04
2.338982D-03 1.942977D-03 2.106389D-04 1.798457D-04 5.267319D-04
1.075033D-04 1.693386D-03 2.033332D+08 1.408824D+01 2.573604D-01
1.546045D-01 1.652271D-01 2.028090D-01 2.535883D-01 3.142232D-01
3.847189D-01 4.631667D-01 5.448861D-01 6.584876D-01 7.849659D-01
9.137944D-01 2.152511D+09
3.252407D+04 3.882301D+10 7.752405D-14 5.538149D+01 8.922289D-01
9.720267D-01 1.480090D+00 2.232311D+00 3.203637D+00 4.429314D+00
5.947071D+00 2.867534D+03 3.252868D+10 2.546517D+00 9.481473D-03
1.211041D-03 1.486963D-02 6.406018D-04 1.693989D-03 3.297725D-04
5.220465D-03 4.348758D-03 4.719382D-04 4.019577D-04 1.177140D-03
2.403532D-04 3.783351D-03 3.299948D+08 2.689686D+01 4.910924D-01
2.943839D-01 3.141553D-01 3.853159D-01 4.816802D-01 5.969627D-01
7.310620D-01 8.806039D-01 1.038485D+00 1.249374D+00 1.479331D+00
1.714303D+00 2.920693D+09
3.256151D+04 5.345952D+10 1.069034D-13 1.053557D+02 1.698453D+00
1.851306D+00 2.823954D+00 4.268919D+00 6.141101D+00 8.498464D+00
1.136160D+01 4.779780D+03 4.485610D+10 5.626381D+00 2.094463D-02
2.674653D-03 3.286295D-02 1.415227D-03 3.740670D-03 7.273242D-04
1.150539D-02 9.620572D-03 1.045486D-03 8.876047D-04 2.598917D-03
5.309947D-04 8.349357D-03 5.298321D+08 5.044551D+01 9.219464D-01
5.520994D-01 5.891446D-01 7.226723D-01 9.037054D-01 1.120709D+00
1.373428D+00 1.656041D+00 1.957907D+00 2.346732D+00 2.765430D+00
3.195894D+00 3.952561D+09
3.261319D+04 7.357480D+10 1.473318D-13 2.001304D+02 3.230311D+00
3.521550D+00 5.381653D+00 8.154647D+00 1.175616D+01 1.626479D+01
2.163106D+01 7.953713D+03 6.181963D+10 1.212227D+01 4.511371D-02
5.759506D-03 7.083137D-02 3.048874D-03 8.053237D-03 1.563268D-03
2.470436D-02 2.076364D-02 2.260624D-03 1.910977D-03 5.593874D-03
1.143901D-03 1.796026D-02 8.301972D+08 9.408322D+01 1.723504D+00
1.030559D+00 1.099918D+00 1.349797D+00 1.688877D+00 2.096025D+00
2.570743D+00 3.102838D+00 3.676535D+00 4.390385D+00 5.153725D+00
5.943989D+00 5.347023D+09
3.267528D+04 1.012210D+11 2.029365D-13 3.796957D+02 6.143174D+00
6.693389D+00 1.024844D+01 1.556686D+01 2.248066D+01 3.105407D+01
4.106713D+01 1.321677D+04 8.515108D+10 2.534371D+01 9.428449D-02
1.203208D-02 1.481667D-01 6.373952D-03 1.681939D-02 3.257531D-03
5.141338D-02 4.351370D-02 4.749430D-03 3.991501D-03 1.167946D-02
2.391137D-03 3.746936D-02 1.262975D+09 1.754346D+02 3.228356D+00
1.924778D+00 2.054287D+00 2.522189D+00 3.157935D+00 3.922542D+00
4.814691D+00 5.815748D+00 6.901966D+00 8.210785D+00 9.605771D+00
1.105990D+01 7.245137D+09
3.274738D+04 1.392042D+11 2.793528D-13 7.193802D+02 1.169148D+01
1.271470D+01 1.950467D+01 2.969763D+01 4.293219D+01 5.913674D+01
7.776571D+01 2.193070D+04 1.172149D+11 5.146462D+01 1.913724D-01
2.440721D-02 3.011154D-01 1.294464D-02 3.410788D-02 6.585420D-03
1.037722D-01 8.865257D-02 9.708837D-03 8.096018D-03 2.367576D-02
4.854652D-03 7.587492D-02 1.867204D+09 3.276068D+02 6.079931D+00
3.605099D+00 3.845973D+00 4.723777D+00 5.918646D+00 7.358005D+00
9.037283D+00 1.092055D+01 1.297075D+01 1.537261D+01 1.793075D+01
2.061437D+01 9.843959D+09
3.282888D+04 1.913611D+11 3.842891D-13 1.360674D+03 2.230165D+01
2.414722D+01 3.710489D+01 5.661929D+01 8.185657D+01 1.123048D+02
1.469057D+02 3.633545D+04 1.612456D+11 1.020955D+02 3.794192D-01
4.834792D-02 5.980192D-01 2.568931D-02 6.754053D-02 1.298543D-02
2.042263D-01 1.766564D-01 1.943281D-02 1.603841D-02 4.686117D-02
9.628443D-03 1.499679D-01 2.698318D+09 6.124715D+02 1.154412D+01
6.776991D+00 7.221109D+00 8.870721D+00 1.112163D+01 1.383680D+01
1.700031D+01 2.054006D+01 2.439677D+01 2.881264D+01 3.352058D+01
3.848755D+01 1.341098D+10
3.291582D+04 2.629504D+11 5.283266D-13 2.568685D+03 4.276088D+01
4.588894D+01 7.059392D+01 1.079036D+02 1.558111D+02 2.127388D+02
2.769762D+02 6.012549D+04 2.216830D+11 1.993407D+02 7.402503D-01
9.420835D-02 1.169439D+00 5.020430D-02 1.315580D-01 2.514938D-02
3.946348D-01 3.469967D-01 3.839225D-02 3.126423D-02 9.122841D-02
1.879471D-02 2.914133D-01 3.839717D+09 1.144776D+03 2.217650D+01
1.279371D+01 1.359724D+01 1.669926D+01 2.094588D+01 2.607243D+01
3.202816D+01 3.866737D+01 4.589719D+01 5.403244D+01 6.272382D+01
7.193881D+01 1.830599D+10
3.300067D+04 3.611916D+11 7.260143D-13 4.838095D+03 8.279496D+01
8.739663D+01 1.344480D+02 2.056610D+02 2.961618D+02 4.022298D+02
5.215672D+02 9.941500D+04 3.046317D+11 3.857429D+02 1.431068D+00
1.818001D-01 2.267755D+00 9.733689D-02 2.537856D-01 4.814601D-02
7.535902D-01 6.768205D-01 7.543989D-02 6.039377D-02 1.758817D-01
3.636042D-02 5.604527D-01 5.416839D+09 2.135212D+03 4.330639D+01
2.428386D+01 2.568340D+01 3.151114D+01 3.952628D+01 4.920129D+01
6.039003D+01 7.280686D+01 8.631793D+01 1.013353D+02 1.174217D+02
1.345493D+02 2.501289D+10
3.307218D+04 4.960098D+11 9.974040D-13 9.086113D+03 1.628812D+02
1.672010D+02 2.566736D+02 3.923066D+02 5.624791D+02 7.598036D+02
9.818829D+02 1.643745D+05 4.185053D+11 7.450230D+02 2.760572D+00
3.498294D-01 4.392216D+00 1.887062D-01 4.881971D-01 9.169005D-02
1.431489D+00 1.320726D+00 1.485797D-01 1.164536D-01 3.381370D-01
7.021343D-02 1.074047D+00 7.627084D+09 3.964831D+03 8.635412D+01
4.643633D+01 4.869379D+01 5.960765D+01 7.472013D+01 9.294071D+01
1.138892D+02 1.370386D+02 1.622250D+02 1.899944D+02 2.198258D+02
2.517002D+02 3.417395D+10
3.311390D+04 6.810931D+11 1.370278D-12 1.700078D+04 3.275395D+02
3.223894D+02 4.921090D+02 7.497545D+02 1.068606D+03 1.435962D+03
1.850436D+03 2.720774D+05 5.749613D+11 1.448566D+03 5.359126D+00
6.768692D-01 8.570729D+00 3.695493D-01 9.443856D-01 1.750852D-01
2.727398D+00 2.601647D+00 2.960340D-01 2.261901D-01 6.537998D-01
1.365191D-01 2.068150D+00 1.080602D+10 7.307863D+03 1.761421D+02
8.969515D+01 9.274812D+01 1.130541D+02 1.414604D+02 1.756348D+02
2.146828D+02 2.577059D+02 3.045604D+02 3.559709D+02 4.113555D+02
4.707064D+02 4.661687D+10
3.310103D+04 9.354544D+11 1.883536D-12 3.166843D+04 6.755966D+02
6.293418D+02 9.500403D+02 1.438126D+03 2.034855D+03 2.721081D+03
3.498219D+03 4.516485D+05 7.903214D+11 2.868380D+03 1.059119D+01
1.331977D+00 1.704798D+01 7.417232D-01 1.858221D+00 3.389686D-01
5.273092D+00 5.235903D+00 6.041269D-01 4.481440D-01 1.286262D+00
2.704591D-01 4.047618D+00 1.557339D+10 1.331515D+04 3.671025D+02
1.755905D+02 1.777045D+02 2.150383D+02 2.681032D+02 3.317935D+02
4.041949D+02 4.838949D+02 5.708775D+02 6.660837D+02 7.689236D+02
8.793938D+02 6.335044D+10
3.299527D+04 1.285866D+12 2.592765D-12 5.869448D+04 1.431566D+03
1.251514D+03 1.854221D+03 2.777522D+03 3.897455D+03 5.188276D+03
6.656565D+03 7.541548D+05 1.087910D+12 5.887253D+03 2.168540D+01
2.713319D+00 3.518358D+01 1.561571D+00 3.785121D+00 6.772092D-01
1.053577D+01 1.096287D+01 1.286930D+00 9.236065D-01 2.621298D+00
5.559674D-01 8.196432D+00 2.320755D+10 2.383520D+04 7.767523D+02
3.491400D+02 3.427537D+02 4.099947D+02 5.080443D+02 6.256342D+02
7.589790D+02 9.059871D+02 1.066933D+03 1.242976D+03 1.433594D+03
1.638760D+03 8.542351D+10
3.274436D+04 1.770533D+12 3.579591D-12 1.081779D+05 3.116423D+03
2.551881D+03 3.677937D+03 5.428020D+03 7.547254D+03 1.000452D+04
1.281288D+04 1.272362D+06 1.501976D+12 1.287007D+04 4.726494D+01
5.883647D+00 7.742616D+01 3.581270D+00 8.204396D+00 1.436693D+00
2.239020D+01 2.456391D+01 2.949278D+00 2.042863D+00 5.690277D+00
1.220140D+00 1.766233D+01 3.668477D+10 4.152973D+04 1.636417D+03
7.007768D+02 6.626540D+02 7.797096D+02 9.573707D+02 1.171093D+03
1.413794D+03 1.682425D+03 1.977696D+03 2.300851D+03 2.651500D+03
3.029517D+03 1.132779D+11
3.230858D+04 2.442934D+12 4.964162D-12 1.982829D+05 6.900565D+03
5.339397D+03 7.434516D+03 1.076976D+04 1.483527D+04 1.958998D+04
2.504865D+04 2.177078D+06 2.082934D+12 3.078489D+04 1.126278D+02
1.398770D+01 1.866509D+02 9.364621D+00 1.946027D+01 3.340024D+00
5.215109D+01 6.063963D+01 7.519584D+00 5.024315D+00 1.353596D+01
2.944912D+00 4.167732D+01 6.313778D+10 6.919647D+04 3.309937D+03
1.384500D+03 1.259629D+03 1.452330D+03 1.762058D+03 2.137972D+03
2.567446D+03 3.045724D+03 3.573729D+03 4.152361D+03 4.781324D+03
5.460242D+03 1.448149D+11
3.168512D+04 3.375754D+12 6.919367D-12 3.618633D+05 1.521919D+04
1.137513D+04 1.526933D+04 2.168216D+04 2.961498D+04 3.897563D+04
4.976408D+04 3.780657D+06 2.903326D+12 7.964731D+04 2.898113D+02
3.626817D+01 4.871537D+02 2.829885D+01 5.006625D+01 8.500703D+00
1.323383D+02 1.638573D+02 2.136988D+01 1.381355D+01 3.496731D+01
7.768911D+00 1.067500D+02 1.170872D+11 1.072839D+05 6.115128D+03
2.576550D+03 2.266317D+03 2.557310D+03 3.061114D+03 3.682327D+03
4.399107D+03 5.202978D+03 6.094089D+03 7.072250D+03 8.136964D+03
9.287327D+03 1.727647D+11
3.091855D+04 4.666445D+12 9.677909D-12 6.586404D+05 3.275073D+04
2.436116D+04 3.159584D+04 4.406513D+04 5.976835D+04 7.844107D+04
1.000239D+05 6.636619D+06 4.060792D+12 2.073736D+05 7.479289D+02
9.661313D+01 1.280032D+03 9.417158D+01 1.307511D+02 2.246950D+01
3.435895D+02 4.548215D+02 6.426063D+01 4.040812D+01 9.164533D+01
2.102784D+01 2.772632D+02 2.183898D+11 1.515084D+05 9.932193D+03
4.326052D+03 3.715047D+03 4.111575D+03 4.858392D+03 5.798111D+03
6.894395D+03 8.132111D+03 9.508970D+03 1.102273D+04 1.267208D+04
1.445527D+04 1.870341D+11
3.008236D+04 6.444003D+12 1.353427D-11 1.197230D+06 6.804052D+04
5.160528D+04 6.513724D+04 8.962692D+04 1.209468D+05 1.583814D+05
2.017340D+05 1.168676D+07 5.678896D+12 5.098092D+05 1.813728D+03
2.524759D+02 3.175575D+03 3.234881D+02 3.281813D+02 5.925368D+01
8.655530D+02 1.224038D+03 1.944759D+02 1.195566D+02 2.298579D+02
5.534577D+01 6.886487D+02 3.863375D+11 1.925251D+05 1.397745D+04
6.368852D+03 5.399960D+03 5.890398D+03 6.886610D+03 8.165058D+03
9.671099D+03 1.138075D+04 1.328761D+04 1.538670D+04 1.767521D+04
2.015032D+04 1.806808D+11
2.924164D+04 8.878975D+12 1.885305D-11 2.172867D+06 1.362575D+05
1.063619D+05 1.326448D+05 1.810496D+05 2.434580D+05 3.182525D+05
4.049685D+05 2.048785D+07 7.910617D+12 1.152039D+06 4.030731D+03
6.355322D+02 7.241339D+03 1.049629D+03 7.763350D+02 1.536326D+02
2.067207D+03 3.091540D+03 5.736464D+02 3.474368D+02 5.391620D+02
1.385213D+02 1.598577D+03 6.340253D+11 2.205826D+05 1.722050D+04
8.205230D+03 6.930606D+03 7.488344D+03 8.686009D+03 1.024723D+04
1.209956D+04 1.421050D+04 1.656920D+04 1.916775D+04 2.200201D+04
2.506783D+04 1.558802D+11
2.848425D+04 1.217365D+13 2.604476D-11 3.919774D+06 2.632612D+05
2.113172D+05 2.641077D+05 3.590672D+05 4.817138D+05 6.288838D+05
7.995808D+05 3.537488D+07 1.092820D+13 2.382676D+06 8.206952D+03
1.510028D+03 1.509778D+04 2.985711D+03 1.722220D+03 3.841817D+02
4.630114D+03 7.207544D+03 1.589010D+03 9.544234D+02 1.176660D+03
3.253945D+02 3.451116D+03 9.663211D+11 2.354039D+05 1.938358D+04
9.590348D+03 8.121559D+03 8.725499D+03 1.006586D+04 1.183153D+04
1.393624D+04 1.634106D+04 1.903090D+04 2.199618D+04 2.523111D+04
2.873016D+04 1.249311D+11
2.788789D+04 1.654916D+13 3.555989D-11 6.975547D+06 4.899000D+05
4.031963D+05 5.088650D+05 6.911456D+05 9.259824D+05 1.207784D+06
1.534615D+06 5.954659D+07 1.492067D+13 4.565662D+06 1.553151D+04
3.303452D+03 2.914106D+04 7.227433D+03 3.582792D+03 8.992387D+02
9.677894D+03 1.542580D+04 3.975725D+03 2.377756D+03 2.399694D+03
7.105228D+02 6.965249D+03 1.389447D+12 2.500204D+05 2.147159D+04
1.094737D+04 9.326873D+03 9.988077D+03 1.147859D+04 1.345592D+04
1.582014D+04 1.852585D+04 2.155493D+04 2.489552D+04 2.854000D+04
3.248105D+04 1.004362D+11
2.749144D+04 2.224275D+13 4.790222D-11 1.216252D+07 8.769027D+05
7.396367D+05 9.433101D+05 1.281961D+06 1.716565D+06 2.237742D+06
2.841994D+06 9.711734D+07 2.009940D+13 8.255533D+06 2.785751D+04
6.534982D+03 5.305724D+04 1.491144D+04 6.994502D+03 1.922876D+03
1.886995D+04 3.040847D+04 8.819144D+03 5.264734D+03 4.604858D+03
1.435339D+03 1.325114D+04 1.920142D+12 2.799769D+05 2.494597D+04
1.303528D+04 1.119036D+04 1.196257D+04 1.371197D+04 1.604401D+04
1.883815D+04 2.204012D+04 2.562675D+04 2.958286D+04 3.389821D+04
3.856254D+04 8.671964D+10
2.727565D+04 2.953732D+13 6.368690D-11 2.065775D+07 1.514008D+06
1.308342D+06 1.684980D+06 2.292852D+06 3.069959D+06 4.001029D+06
5.079929D+06 1.535417D+08 2.672250D+13 1.427887D+07 4.812966D+04
1.180212D+04 9.248633D+04 2.725379D+04 1.288467D+04 3.743414D+03
3.459404D+04 5.596262D+04 1.745507D+04 1.041342D+04 8.392734D+03
2.696963D+03 2.400260D+04 2.585649D+12 3.360328D+05 3.103727D+04
1.656300D+04 1.435080D+04 1.533632D+04 1.755510D+04 2.051926D+04
2.407335D+04 2.814990D+04 3.271720D+04 3.775499D+04 4.324838D+04
4.918179D+04 8.226939D+10
2.720323D+04 3.877051D+13 8.364829D-11 3.399005D+07 2.540831D+06
2.241955D+06 2.912018D+06 3.968570D+06 5.314907D+06 6.926534D+06
8.792753D+06 2.360197D+08 3.509808D+13 2.368820D+07 8.073882D+04
2.006456D+04 1.557853D+05 4.655652D+04 2.257270D+04 6.754286D+03
6.028697D+04 9.760623D+04 3.167230D+04 1.889060D+04 1.465325D+04
4.782474D+03 4.174905D+04 3.418630D+12 4.286173D+05 4.114203D+04
2.238911D+04 1.959686D+04 2.096099D+04 2.398242D+04 2.801881D+04
3.286065D+04 3.841602D+04 4.464014D+04 5.150391D+04 5.898429D+04
6.705582D+04 8.513907D+10
2.724132D+04 5.033581D+13 1.086383D-10 5.394024D+07 4.147474D+06
3.734314D+06 4.887583D+06 6.671880D+06 8.939293D+06 1.165105D+07
1.478861D+07 3.538324D+08 4.558360D+13 3.764522D+07 1.319027D+05
3.282233D+04 2.549089D+05 7.650634D+04 3.789158D+04 1.152773D+04
1.008432D+05 1.632515D+05 5.411342D+04 3.227545D+04 2.467308D+04
8.114578D+03 7.017297D+04 4.455139D+12 5.732747D+05 5.748910D+04
3.188264D+04 2.822032D+04 3.024721D+04 3.461751D+04 4.044720D+04
4.743773D+04 5.545779D+04 6.444100D+04 7.434323D+04 8.512654D+04
9.674675D+04 9.489918D+10
2.738251D+04 6.464849D+13 1.395497D-10 8.224343D+07 6.577210D+06
6.048972D+06 7.973804D+06 1.090440D+07 1.461932D+07 1.905823D+07
2.418923D+07 5.177081D+08 5.855362D+13 5.720660D+07 2.096695D+05
5.197520D+04 4.051239D+05 1.216320D+05 6.123009D+04 1.881357D+04
1.627340D+05 2.634257D+05 8.828708D+04 5.266620D+04 4.018538D+04
1.325683D+04 1.142514D+05 5.730148D+12 8.018974D+05 8.457998D+04
4.783516D+04 4.286831D+04 4.609845D+04 5.282234D+04 6.175462D+04
7.245154D+04 8.471912D+04 9.845346D+04 1.135834D+05 1.300426D+05
1.477490D+05 1.138961D+11
2.763689D+04 8.212872D+13 1.772774D-10 1.202534D+08 1.011473D+07
9.517197D+06 1.263391D+07 1.731292D+07 2.323042D+07 3.029440D+07
3.845049D+07 7.387870D+08 7.438366D+13 8.302845D+07 3.231884D+05
7.968378D+04 6.238054D+05 1.870406D+05 9.548986D+04 2.950478D+04
2.538369D+05 4.109909D+05 1.384174D+05 8.258601D+04 6.334694D+04
2.089906D+04 1.801993D+05 7.274958D+12 1.179362D+06 1.318733D+05
7.610356D+04 6.915067D+04 7.470414D+04 8.575390D+04 1.003595D+05
1.178205D+05 1.378302D+05 1.602169D+05 1.848579D+05 2.116284D+05
2.403654D+05 1.482231D+11
2.801839D+04 1.032172D+14 2.227461D-10 1.687226D+08 1.507719D+07
1.453287D+07 1.943151D+07 2.669234D+07 3.585412D+07 4.677960D+07
5.937715D+07 1.027830D+09 9.346166D+13 1.150467D+08 4.813327D+05
1.181863D+05 9.287618D+05 2.781187D+05 1.439338D+05 4.459407D+04
3.830723D+05 6.205355D+05 2.092544D+05 1.248703D+05 9.665477D+04
3.183817D+04 2.752566D+05 9.115507D+12 1.834834D+06 2.189841D+05
1.292294D+05 1.191875D+05 1.294968D+05 1.490298D+05 1.746742D+05
2.052663D+05 2.402855D+05 2.794303D+05 3.224704D+05 3.691536D+05
4.191313D+05 2.105574D+11
2.853644D+04 1.284214D+14 2.769971D-10 2.276972D+08 2.179839D+07
2.155633D+07 2.904277D+07 4.000687D+07 5.380971D+07 7.025076D+07
8.917777D+07 1.395172D+09 1.162246D+14 1.522160D+08 6.913818D+05
1.694772D+05 1.336221D+06 3.999233D+05 2.100005D+05 6.513544D+04
5.599869D+05 9.077084D+05 3.058887D+05 1.825600D+05 1.428517D+05
4.695617D+04 4.074303D+05 1.127013D+13 3.025456D+06 3.874776D+05
2.343462D+05 2.193531D+05 2.398749D+05 2.769746D+05 3.252853D+05
3.827549D+05 4.484502D+05 5.218034D+05 6.023491D+05 6.895376D+05
7.825690D+05 3.260184D+11
2.919852D+04 1.583606D+14 3.412539D-10 2.966286D+08 3.062542D+07
3.112178D+07 4.227364D+07 5.841901D+07 7.869692D+07 1.028199D+08
1.305381D+08 1.850651D+09 1.431857D+14 1.923866D+08 9.571599D+05
2.348176D+05 1.857394D+06 5.561129D+05 2.968466D+05 9.212094D+04
7.935686D+05 1.287322D+06 4.331656D+05 2.585541D+05 2.046459D+05
6.713017D+04 5.846877D+05 1.373740D+13 5.275135D+06 7.247973D+05
4.511710D+05 4.290284D+05 4.727013D+05 5.479895D+05 6.451355D+05
7.603355D+05 8.918085D+05 1.038413D+06 1.199135D+06 1.372690D+06
1.557118D+06 5.469924D+11
3.000985D+04 1.938241D+14 4.169847D-10 3.748909D+08 4.194723D+07
4.386244D+07 6.009998D+07 8.334998D+07 1.124812D+08 1.470873D+08
1.867627D+08 2.403976D+09 1.749611D+14 2.324121D+08 1.276256D+06
3.140630D+05 2.492961D+06 7.472889D+05 4.063899D+05 1.261865D+05
1.089690D+06 1.769204D+06 5.943000D+05 3.547826D+05 2.840553D+05
9.301473D+04 8.130773D+05 1.646966D+13 9.650090D+06 1.422110D+06
9.132759D+05 8.827152D+05 9.805391D+05 1.141901D+06 1.348135D+06
1.591858D+06 1.869486D+06 2.178590D+06 2.516822D+06 2.880993D+06
3.266025D+06 9.820880D+11
3.097406D+04 2.358179D+14 5.058744D-10 4.621948D+08 5.620180D+07
6.053663D+07 8.371203D+07 1.165474D+08 1.575890D+08 2.062679D+08
2.619313D+08 3.064613D+09 2.122578D+14 2.679152D+08 1.633890D+06
4.041158D+05 3.220271D+06 9.671374D+05 5.369632D+05 1.668734D+05
1.444745D+06 2.347827D+06 7.873807D+05 4.701178D+05 3.806872D+05
1.244802D+05 1.091781D+06 1.931229D+13 1.825995D+07 2.886678D+06
1.915642D+06 1.883152D+06 2.110039D+06 2.469696D+06 2.924945D+06
3.461002D+06 4.070376D+06 4.747659D+06 5.487125D+06 6.280495D+06
7.114069D+06 1.855418D+12
3.208965D+04 2.856347D+14 6.097798D-10 5.586009D+08 7.392481D+07
8.208742D+07 1.146026D+08 1.602125D+08 2.170829D+08 2.844199D+08
3.611786D+08 3.842349D+09 2.558547D+14 2.928056D+08 1.993105D+06
4.963274D+05 3.971720D+06 1.195837D+06 6.790958D+05 2.112972D+05
1.834026D+06 2.983322D+06 9.990138D+05 5.965745D+05 4.884909D+05
1.595540D+05 1.403710D+06 2.191108D+13 3.491791D+07 5.932811D+06
4.072219D+06 4.073322D+06 4.605867D+06 5.419918D+06 6.440852D+06
7.638554D+06 8.997109D+06 1.050413D+07 1.214526D+07 1.389850D+07
1.572620D+07 3.596871D+12
3.334708D+04 3.449019D+14 7.307074D-10 6.641948D+08 9.582497D+07
1.097371D+08 1.547039D+08 2.171952D+08 2.949274D+08 3.867898D+08
4.911159D+08 4.748342D+09 3.065938D+14 2.996859D+08 2.286502D+06
5.741179D+05 4.614230D+06 1.393627D+06 8.106830D+05 2.526106D+05
2.198114D+06 3.579098D+06 1.196891D+06 7.148646D+05 5.917511D+05
1.931149D+05 1.703783D+06 2.365339D+13 6.532157D+07 1.195989D+07
8.490405D+06 8.642020D+06 9.863656D+06 1.167167D+07 1.391952D+07
1.654691D+07 1.952044D+07 2.281180D+07 2.638516D+07 3.018271D+07
3.410251D+07 6.899742D+12
3.470765D+04 4.157712D+14 8.716745D-10 7.801557D+08 1.232233D+08
1.454820D+08 2.070733D+08 2.919476D+08 3.972750D+08 5.214836D+08
6.619116D+08 5.808421D+09 3.657411D+14 2.841057D+08 2.441706D+06
6.188846D+05 4.995039D+06 1.514193D+06 9.030901D+05 2.818402D+05
2.458555D+06 4.007043D+06 1.338242D+06 7.994316D+05 6.688067D+05
2.180920D+05 1.929359D+06 2.391230D+13 1.147310D+08 2.269870D+07
1.667330D+07 1.726629D+07 1.989127D+07 2.366819D+07 2.832640D+07
3.375218D+07 3.987864D+07 4.664372D+07 5.396198D+07 6.168925D+07
6.956432D+07 1.251333D+13
3.621335D+04 4.998420D+14 1.034572D-09 9.026371D+08 1.566805D+08
1.906269D+08 2.739473D+08 3.878807D+08 5.289416D+08 6.948906D+08
8.814924D+08 7.013899D+09 4.340900D+14 2.430346D+08 2.359724D+06
6.043784D+05 4.898675D+06 1.491032D+06 9.120080D+05 2.851402D+05
2.493074D+06 4.067232D+06 1.356858D+06 8.107008D+05 6.850806D+05
2.233028D+05 1.980023D+06 2.204732D+13 1.859985D+08 3.985020D+07
3.030879D+07 3.193522D+07 3.713749D+07 4.443783D+07 5.337478D+07
6.374910D+07 7.543533D+07 8.830499D+07 1.021674D+08 1.166876D+08
1.312444D+08 2.115367D+13
3.782588D+04 6.004738D+14 1.227135D-09 1.035782D+09 1.989396D+08
2.491544D+08 3.614105D+08 5.138507D+08 7.021554D+08 9.230776D+08
1.169842D+09 8.421727D+09 5.148856D+14 1.885361D+08 2.083098D+06
5.394732D+05 4.390454D+06 1.342160D+06 8.415722D+05 2.636228D+05
2.309889D+06 3.771901D+06 1.257147D+06 7.512611D+05 6.408827D+05
2.088500D+05 1.855621D+06 1.859930D+13 2.734790D+08 6.365689D+07
5.015353D+07 5.375475D+07 6.309051D+07 7.590920D+07 9.149576D+07
1.095308D+08 1.297965D+08 1.520463D+08 1.758883D+08 2.006097D+08
2.248614D+08 3.260388D+13
3.958279D+04 7.233998D+14 1.462518D-09 1.185639D+09 2.540761D+08
3.272926D+08 4.791402D+08 6.840449D+08 9.365678D+08 1.231876D+09
1.558956D+09 1.010917D+10 6.136479D+14 1.331039D+08 1.697364D+06
4.447053D+05 3.633858D+06 1.115873D+06 7.168084D+05 2.250098D+05
1.975412D+06 3.228689D+06 1.075292D+06 6.427025D+05 5.531744D+05
1.802770D+05 1.604463D+06 1.447176D+13 3.704325D+08 9.407317D+07
7.683345D+07 8.376443D+07 9.921782D+07 1.200317D+08 1.451819D+08
1.741914D+08 2.067026D+08 2.422681D+08 2.801325D+08 3.188769D+08
3.557708D+08 4.651206D+13
4.157267D+04 8.803566D+14 1.765576D-09 1.364841D+09 3.300174D+08
4.372863D+08 6.462059D+08 9.264423D+08 1.270937D+09 1.672174D+09
2.111600D+09 1.222131D+10 7.408055D+14 8.555921D+07 1.291288D+06
3.424700D+05 2.810274D+06 8.669680D+05 5.704057D+05 1.794784D+05
1.578478D+06 2.582350D+06 8.595767D+05 5.138653D+05 4.460883D+05
1.454326D+05 1.296117D+06 1.047311D+13 4.748288D+08 1.324509D+08
1.123203D+08 1.246147D+08 1.490014D+08 1.812743D+08 2.200383D+08
2.646062D+08 3.144034D+08 3.686334D+08 4.258750D+08 4.833792D+08
5.357897D+08 6.310809D+13
4.391141D+04 1.090568D+15 2.175378D-09 1.592290D+09 4.406094D+08
6.011067D+08 8.971122D+08 1.291845D+09 1.775698D+09 2.336232D+09
2.940342D+09 1.496565D+10 9.127503D+14 5.006892D+07 9.274078D+05
2.491720D+05 2.053954D+06 6.366948D+05 4.291363D+05 1.354018D+05
1.192739D+06 1.953226D+06 6.499776D+05 3.886412D+05 3.402423D+05
1.110058D+05 9.903510D+05 7.107134D+12 5.906012D+08 1.826522D+08
1.611998D+08 1.821751D+08 2.200023D+08 2.692448D+08 3.280436D+08
3.954114D+08 4.704080D+08 5.515904D+08 6.362465D+08 7.189868D+08
7.892494D+08 8.351649D+13
4.667882D+04 1.379129D+15 2.742086D-09 1.891838D+09 6.071726D+08
8.537437D+08 1.287420D+09 1.862428D+09 2.564858D+09 3.372592D+09
4.222545D+09 1.855492D+10 1.150530D+15 2.719028D+07 6.430546D+05
1.751408D+05 1.450577D+06 4.519252D+05 3.120809D+05 9.878243D+04
8.713711D+05 1.428445D+06 4.753449D+05 2.842821D+05 2.509586D+05
8.196382D+04 7.318182D+05 4.615631D+12 7.264411D+08 2.513391D+08
2.313269D+08 2.665347D+08 3.252563D+08 4.005401D+08 4.899117D+08
5.919154D+08 7.049424D+08 8.262693D+08 9.505262D+08 1.066794D+09
1.153819D+09 1.095838D+14
4.990589D+04 1.777263D+15 3.527412D-09 2.295262D+09 8.617941D+08
1.249499D+09 1.904256D+09 2.767568D+09 3.817810D+09 5.012876D+09
6.224920D+09 2.315364D+10 1.480036D+15 1.425540D+07 4.442577D+05
1.226979D+05 1.021054D+06 3.197841D+05 2.261780D+05 7.184520D+04
6.344745D+05 1.041212D+06 3.465860D+05 2.073215D+05 1.844982D+05
6.033788D+04 5.390153D+05 2.958044D+12 8.965963D+08 3.489797D+08
3.352624D+08 3.940024D+08 4.859686D+08 6.022587D+08 7.395273D+08
8.955214D+08 1.067321D+09 1.249527D+09 1.431025D+09 1.588880D+09
1.679751D+09 1.439176D+14
5.359392D+04 2.327519D+15 4.615078D-09 2.852792D+09 1.256403D+09
1.877771D+09 2.891952D+09 4.222010D+09 5.831597D+09 7.635453D+09
9.360266D+09 2.886597D+10 1.936398D+15 7.558895D+06 3.158648D+05
8.845637D+04 7.395095D+05 2.328377D+05 1.685526D+05 5.373829D+04
4.750190D+05 7.803667D+05 2.598829D+05 1.554902D+05 1.394437D+05
4.567008D+04 4.081355D+05 1.930337D+12 1.123458D+09 4.925659D+08
4.937591D+08 5.917697D+08 7.376756D+08 9.199437D+08 1.133903D+09
1.375826D+09 1.640068D+09 1.915354D+09 2.177637D+09 2.377306D+09
2.427001D+09 1.902026D+14
5.772347D+04 3.088010D+15 6.119564D-09 3.642360D+09 1.875859D+09
2.886939D+09 4.491394D+09 6.584591D+09 9.100430D+09 1.185675D+10
1.424419D+10 3.565413D+10 2.567646D+15 4.223628D+06 2.372518D+05
6.732629D+04 5.654396D+05 1.789153D+05 1.324515D+05 4.237810D+04
3.749528D+05 6.166124D+05 2.054374D+05 1.229401D+05 1.110761D+05
3.643094D+04 3.256818D+05 1.315550D+12 1.439958D+09 7.092118D+08
7.407338D+08 9.047668D+08 1.139443D+09 1.429529D+09 1.768184D+09
2.148757D+09 2.559645D+09 2.976143D+09 3.344232D+09 3.555448D+09
3.444392D+09 2.534138D+14
6.227583D+04 4.133236D+15 8.188204D-09 4.768729D+09 2.851803D+09
4.512423D+09 7.087588D+09 1.042959D+10 1.440957D+10 1.862202D+10
2.167745D+10 4.315886D+10 3.435600D+15 2.542941D+06 1.903081D+05
5.466900D+04 4.612659D+05 1.465872D+05 1.108775D+05 3.558921D+04
3.152143D+05 5.188795D+05 1.729191D+05 1.035000D+05 9.418805D+04
3.093154D+04 2.766280D+05 9.476718D+11 1.888860D+09 1.039366D+09
1.128632D+09 1.403562D+09 1.784788D+09 2.251734D+09 2.793771D+09
3.398147D+09 4.039804D+09 4.662564D+09 5.142678D+09 5.249274D+09
4.717468D+09 3.398409D+14
6.725311D+04 5.550439D+15 1.099387D-08 6.351794D+09 4.371820D+09
7.099909D+09 1.125122D+10 1.660909D+10 2.290953D+10 2.922975D+10
3.242651D+10 5.054036D+10 4.612790D+15 1.649100D+06 1.619664D+05
4.705191D+04 3.988210D+05 1.272212D+05 9.821648D+04 3.161634D+04
2.803340D+05 4.618864D+05 1.539428D+05 9.215838D+04 8.444616D+04
2.776403D+04 2.484063D+05 7.198464D+11 2.518285D+09 1.537876D+09
1.732014D+09 2.190514D+09 2.810746D+09 3.564322D+09 4.433689D+09
5.393043D+09 6.386574D+09 7.284400D+09 7.806371D+09 7.503328D+09
6.106102D+09 4.567763D+14
7.267926D+04 7.436448D+15 1.472832D-08 8.517373D+09 6.685468D+09
1.112424D+10 1.777380D+10 2.630405D+10 3.616011D+10 4.524154D+10
4.675694D+10 5.661778D+10 6.179670D+15 1.136897D+06 1.440709D+05
4.228835D+04 3.600259D+05 1.152338D+05 9.068958D+04 2.927051D+04
2.598147D+05 4.284455D+05 1.428036D+05 8.550421D+04 7.885891D+04
2.595411D+04 2.323116D+05 5.709179D+11 3.378388D+09 2.274097D+09
2.649935D+09 3.404513D+09 4.405216D+09 5.612138D+09 6.994690D+09
8.499025D+09 9.999681D+09 1.119909D+10 1.149610D+10 1.016939D+10
7.357446D+09 6.122538D+14
7.858647D+04 9.895650D+15 1.959817D-08 1.139342D+10 1.010749D+10
1.720285D+10 2.769359D+10 4.105788D+10 5.612427D+10 6.821008D+10
6.386134D+10 6.036481D+10 8.222931D+15 8.213782D+05 1.320202D+05
3.912450D+04 3.344751D+05 1.073864D+05 8.604637D+04 2.783964D+04
2.473679D+05 4.082464D+05 1.360736D+05 8.148737D+04 7.561171D+04
2.490932D+04 2.230505D+05 4.680730D+11 4.520059D+09 3.331486D+09
4.007242D+09 5.224134D+09 6.812127D+09 8.714060D+09 1.087443D+10
1.317991D+10 1.535138D+10 1.673103D+10 1.615589D+10 1.286720D+10
8.230798D+09 8.149019D+14
8.501041D+04 1.303893D+16 2.582291D-08 1.510605D+10 1.501638D+10
2.610010D+10 4.230552D+10 6.278001D+10 8.507888D+10 9.916779D+10
8.162785D+10 6.140367D+10 1.083467D+16 6.148846D+05 1.232875D+05
3.686303D+04 3.163653D+05 1.018617D+05 8.299649D+04 2.691355D+04
2.393698D+05 3.953401D+05 1.317730D+05 7.892344D+04 7.364762D+04
2.428389D+04 2.175329D+05 3.936253D+11 5.993436D+09 4.806126D+09
5.954048D+09 7.868143D+09 1.033289D+10 1.326467D+10 1.656037D+10
1.998350D+10 2.292963D+10 2.403215D+10 2.138962D+10 1.511381D+10
8.624345D+09 1.073875D+15
9.198890D+04 1.698519D+16 3.363790D-08 1.978095D+10 2.185385D+10
3.873090D+10 6.316809D+10 9.373549D+10 1.254432D+11 1.379547D+11
9.723477D+10 6.006281D+10 1.411363D+16 4.733957D+05 1.165418D+05
3.513473D+04 3.026209D+05 9.769343D+04 8.084631D+04 2.627121D+04
2.338664D+05 3.865173D+05 1.288330D+05 7.717294D+04 7.239299D+04
2.388990D+04 2.140796D+05 3.377942D+11 7.848479D+09 6.806035D+09
8.665547D+09 1.159603D+10 1.532748D+10 1.973462D+10 2.462448D+10
2.951321D+10 3.314358D+10 3.291743D+10 2.649234D+10 1.655803D+10
8.586537D+09 1.398989D+15
9.956936D+04 2.187752D+16 4.332642D-08 2.557361D+10 3.116082D+10
5.622952D+10 9.221509D+10 1.366750D+11 1.796941D+11 1.827670D+11
1.083845D+11 5.703905D+10 1.817867D+16 3.735461D+05 1.112421D+05
3.379450D+04 2.920521D+05 9.451194D+04 7.934860D+04 2.583457D+04
2.301712D+05 3.806534D+05 1.268789D+05 7.601195D+04 7.165182D+04
2.366339D+04 2.121198D+05 2.951084D+11 1.014700D+10 9.461679D+09
1.235629D+10 1.672793D+10 2.224098D+10 2.870288D+10 3.575339D+10
4.242743D+10 4.623658D+10 4.278713D+10 3.070437D+10 1.711856D+10
8.240471D+09 1.802053D+15
1.078080D+05 2.791586D+16 5.528436D-08 3.274209D+10 4.368849D+10
8.016118D+10 1.321021D+11 1.952827D+11 2.505036D+11 2.302485D+11
1.141731D+11 5.304034D+10 2.319589D+16 3.025328D+05 1.074883D+05
3.288653D+04 2.850883D+05 9.246726D+04 7.867636D+04 2.566180D+04
2.288086D+05 3.786245D+05 1.262031D+05 7.561583D+04 7.160091D+04
2.366335D+04 2.121843D+05 2.626925D+11 1.299152D+10 1.295751D+10
1.732426D+10 2.370625D+10 3.168604D+10 4.095955D+10 5.085797D+10
5.950359D+10 6.223848D+10 5.271604D+10 3.351913D+10 1.693298D+10
7.711539D+09 2.299566D+15
1.167749D+05 3.541920D+16 7.014332D-08 4.178453D+10 6.065680D+10
1.130255D+11 1.870379D+11 2.752652D+11 3.412521D+11 2.762112D+11
1.150294D+11 4.861099D+10 2.943031D+16 2.530904D+05 1.059282D+05
3.262320D+04 2.836208D+05 9.218457D+04 7.941466D+04 2.594586D+04
2.315074D+05 3.833012D+05 1.277624D+05 7.655831D+04 7.279769D+04
2.407471D+04 2.159341D+05 2.392603D+11 1.657980D+10 1.760222D+10
2.405319D+10 3.324100D+10 4.463913D+10 5.775250D+10 7.134016D+10
8.181739D+10 8.097910D+10 6.169794D+10 3.481729D+10 1.622451D+10
7.095266D+09 2.917828D+15
1.265563D+05 4.491044D+16 8.893887D-08 5.363748D+10 8.420589D+10
1.591617D+11 2.642830D+11 3.862279D+11 4.564999D+11 3.164457D+11
1.120646D+11 4.410763D+10 3.731641D+16 2.209559D+05 1.076846D+05
3.336757D+04 2.908692D+05 9.472506D+04 8.254912D+04 2.701177D+04
2.411790D+05 3.995184D+05 1.331683D+05 7.980564D+04 7.618150D+04
2.520916D+04 2.261691D+05 2.244950D+11 2.128352D+10 2.394622D+10
3.339152D+10 4.656850D+10 6.279168D+10 8.122445D+10 9.954028D+10
1.109668D+11 1.020452D+11 6.887657D+10 3.477531D+10 1.520137D+10
6.453885D+09 3.699938D+15
1.372500D+05 5.720882D+16 1.132934D-07 6.992964D+10 1.180776D+11
2.261478D+11 3.764542D+11 5.442415D+11 6.012933D+11 3.471973D+11
1.064778D+11 3.974038D+10 4.753490D+16 2.036431D+05 1.143441D+05
3.563322D+04 3.113931D+05 1.015930D+05 8.948845D+04 2.932473D+04
2.619929D+05 4.342037D+05 1.447300D+05 8.674248D+04 8.310371D+04
2.751543D+04 2.469208D+05 2.186679D+11 2.774895D+10 3.295217D+10
4.682227D+10 6.584743D+10 8.908785D+10 1.150635D+11 1.393111D+11
1.490990D+11 1.245161D+11 7.365199D+10 3.369575D+10 1.401887D+10
5.824107D+09 4.713406D+15
1.373594D+05 5.734791D+16 1.135689D-07 7.012194D+10 1.184836D+11
2.269535D+11 3.778026D+11 5.461189D+11 6.028904D+11 3.474477D+11
1.064146D+11 3.969893D+10 4.765046D+16 2.035588D+05 1.144495D+05
3.566797D+04 3.117041D+05 1.016962D+05 8.958841D+04 2.935788D+04
2.622907D+05 4.346991D+05 1.448951D+05 8.684152D+04 8.320143D+04
2.754793D+04 2.472131D+05 2.186697D+11 2.782527D+10 3.305961D+10
4.698325D+10 6.607897D+10 8.940372D+10 1.154687D+11 1.397811D+11
1.495299D+11 1.247414D+11 7.368689D+10 3.368145D+10 1.400698D+10
5.818107D+09 4.724867D+15
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""从 Tlusty 的 fort.9 输出判断模型的收敛性。
fort.9 文件逐迭代、逐深度地列出各状态参量的相对变化量。"MAXIMUM"
(第 7 个数据列)是该深度上最大的相对变化量。当*最后一次迭代*中、所有
深度上 |MAXIMUM| 的最大值小于收敛阈值 CHMAX 时,即认为模型已收敛。
本模块的解析方式与已有 gui/tlusty.py 中的 pconv() 一致,但返回机器可读
的结果而非绘图。
用法:
check_conv.py fort.9 # 默认 CHMAX=0.001
check_conv.py fort.9 --chmax 0.01 # 自定义阈值
check_conv.py -j fort.9 # 输出 JSON
"""
import argparse
import json
import re
import sys
# 一行数据形如: ITER ID TEMP NE POP RAD MAXIMUM ilev ifr
# 例如 " 28 1 -1.22E-06 1.21E-06 9.85E-04 ..."
_DATA_ROW = re.compile(
r"^\s*(\d+)\s+(\d+)\s+" # iter, depth(迭代号、深度索引)
r"([-+\dE.]+)\s+" # temp(温度相对变化)
r"([-+\dE.]+)\s+" # ne(电子密度相对变化)
r"([-+\dE.]+)\s+" # pop(布居数相对变化)
r"([-+\dE.]+)\s+" # rad(辐射场相对变化)
r"([-+\dE.]+)\s+" # maximum(本深度最大相对变化)
r"(\d+)\s+(\d+)\s*$" # ilev, ifr(触发的能级、频率索引)
)
def parse_fort9(path):
"""解析 fort.9。返回 (last_iter, rows),其中 rows 是最后一次迭代各深度
的 dict 列表,每项形如
{depth, temp, ne, pop, rad, maximum}。"""
last_iter = None
cur_iter = None
cur_rows = []
with open(path) as f:
for line in f:
m = _DATA_ROW.match(line)
if not m:
continue
it = int(m.group(1))
if it != cur_iter:
# 进入新的迭代块:保存上一次的(它就是当前最后一次)
cur_iter = it
cur_rows = []
cur_rows.append({
"depth": int(m.group(2)),
"temp": float(m.group(3)),
"ne": float(m.group(4)),
"pop": float(m.group(5)),
"rad": float(m.group(6)),
"maximum": float(m.group(7)),
})
last_iter = it
return last_iter, cur_rows
def check(path, chmax=0.001):
"""返回描述 `path`fort.9)中模型收敛情况的 dict。
converged : bool -- 最后一次迭代所有深度上 |maximum| 的最大值 < chmax
max_relc : float -- 该最大相对变化量
worst_depth: int -- 出现最大值所在的深度索引
last_iter : int|None -- 最后一次迭代的序号
n_depths : int -- 最后一次迭代的深度数
chmax : float -- 所用的收敛阈值
"""
last_iter, rows = parse_fort9(path)
if not rows or last_iter is None:
return {
"converged": False,
"max_relc": float("inf"),
"worst_depth": -1,
"last_iter": None,
"n_depths": 0,
"chmax": chmax,
"error": "no iteration data found in fort.9",
}
# 最差(|maximum| 最大)的深度
worst = max(rows, key=lambda r: abs(r["maximum"]))
max_relc = abs(worst["maximum"])
return {
"converged": bool(max_relc < chmax),
"max_relc": max_relc,
"worst_depth": worst["depth"],
"last_iter": last_iter,
"n_depths": len(rows),
"chmax": chmax,
}
def main():
"""命令行入口:解析 fort.9 并打印收敛结果(默认文本,-j 输出 JSON)。"""
ap = argparse.ArgumentParser(description="Check Tlusty model convergence")
ap.add_argument("fort9", help="path to fort.9 (or model.9) file")
ap.add_argument("--chmax", type=float, default=0.001,
help="convergence limit (default 0.001)")
ap.add_argument("-j", "--json", action="store_true",
help="output full result as JSON")
args = ap.parse_args()
res = check(args.fort9, args.chmax)
if args.json:
print(json.dumps(res, indent=2))
else:
status = "CONVERGED" if res["converged"] else "NOT CONVERGED"
print("{}: iter={}, max_relc={:.3e} (depth {}), chmax={:.0e}, "
"n_depths={}".format(
status, res["last_iter"], res["max_relc"],
res["worst_depth"], res["chmax"], res["n_depths"]))
sys.exit(0 if res["converged"] else 1)
if __name__ == "__main__":
main()
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env python3
"""生成一个 Tlusty NLTE 大气 .5 输入文件,包含 H/He 以及 C/N/O 的某个子集。
丰度采用 logX = log10(nX/nH) 表示,写入 .5 文件时的 abn 字段为
abn = 10**logX。
模型原子(能级、数据文件)是固定的,与已验证的 BSTAR2006 设置一致
(完整的 C/N/O 离子;He 数据文件 he1.dat/he2.dat 本身含 24/20 能级,
但 .5 里 nlevs 声明为 14/14 —— 这是用户 tests.zip 验证过的稳定配置,
tlusty 会按 nlevs 截断数据文件)。实际包含哪些金属由 `metals` 参数控制,
由此实现"渐进式收敛"策略:先 H+He+C,再 H+He+C+N,最后 H+He+C+N+O。
用法:
gen_input5.py --teff 35000 --logg 5.5 --loghe -2 \
--logc -1 --logn -1 --logo -1 --metals c -o model.5
也可作为模块导入:from gen_input5 import make_input5, model_name
"""
import argparse
import os
# abn = 10**0 在 Tlusty 中表示"采用太阳丰度"。氢是参考元素,其 abn 字段
# 写 0(太阳丰度哨兵值)。
ABN_H_SOLAR = "0."
# 固定的模型原子定义:(iat, iz, nlevs, typion, file)
# 与 BSTAR2006 设置一致。He 数据文件本身含 24/20 能级(Peter Nemeth 建议),
# 但此处 nlevs 声明为 14/14 —— 用户 tests.zip 验证过的稳定配置,tlusty 会
# 按此截断 he1.dat/he2.dat,只读前 14 个能级。详见 EXPERIENCE.md §2.5/§3.3。
_IONS_H = [
(1, 0, 9, " H 1", "data/h1.dat"),
(1, 1, 1, " H 2", " "),
]
_IONS_HE = [
(2, 0, 14, "He 1", "data/he1.dat"),
(2, 1, 14, "He 2", "data/he2.dat"),
(2, 2, 1, "He 3", " "),
]
_IONS_C = [
(6, 0, 40, " C 1", "data/c1.dat"),
(6, 1, 22, " C 2", "data/c2.dat"),
(6, 2, 46, " C 3", "data/c3_34+12lev.dat"),
(6, 3, 25, " C 4", "data/c4.dat"),
(6, 4, 1, " C 5", " "),
]
_IONS_N = [
(7, 0, 34, " N 1", "data/n1.dat"),
(7, 1, 42, " N 2", "data/n2_32+10lev.dat"),
(7, 2, 32, " N 3", "data/n3.dat"),
(7, 3, 48, " N 4", "data/n4_34+14lev.dat"),
(7, 4, 16, " N 5", "data/n5.dat"),
(7, 5, 1, " N 6", " "),
]
_IONS_O = [
(8, 0, 33, " O 1", "data/o1_23+10lev.dat"),
(8, 1, 48, " O 2", "data/o2_36+12lev.dat"),
(8, 2, 41, " O 3", "data/o3_28+13lev.dat"),
(8, 3, 39, " O 4", "data/o4.dat"),
(8, 4, 6, " O 5", "data/o5.dat"),
(8, 5, 1, " O 6", " "),
]
def model_name(teff, logg, loghe, logc, logn, logo):
"""生成编码 6 个参数的规范模型名。"""
return "t{:d}_g{:.1f}_he{:.0f}_c{:.0f}_n{:.0f}_o{:.0f}".format(
int(round(teff)), logg, loghe, logc, logn, logo
)
def _fmt_abn(logx):
"""把对数丰度格式化为 .5 文件中的 abn 字段(线性 10**logx)。"""
return "{:.4E}".format(10.0 ** logx)
def make_input5(teff, logg, loghe, logc, logn, logo, template=None,
lte="F", ltgray="F", metals="cno", ilvlin=100):
"""返回 .5 文件的文本内容。
teff : 有效温度 [K]
logg : log10(表面重力) [cgs]
loghe : log10(nHe/nH)
logc : log10(nC /nH)
logn : log10(nN /nH)
logo : log10(nO /nH)
template: 已忽略(保留参数仅为向后兼容);文件直接构造。
lte : 'F'NLTE)或 'T'(LTE 灰度初始模型,无需 fort.8)
ltgray : 'F''T'
metals : 要包含 C/N/O 中的哪些:任意子集字符串,如 '', 'c', 'cn',
'cno'。用于实现渐进式收敛。
ilvlin : 每个能级的最大谱线跃迁数(ions 块第 5 列)。
0 = 仅连续谱(即 'nc' 步);100 = 完整谱线(即 'nl' 步)。
这是已验证的三步收敛策略中的关键开关。
"""
mt = set(metals.lower())
# ---- atoms 节 ----
# 第 3、4、5 槽位始终为空(Li、Be、B);6=C 7=N 8=O。
atom_rows = [
(2, ABN_H_SOLAR), # 1 H (显式 NLTE
(2, _fmt_abn(loghe)), # 2 He (显式 NLTE
(0, "0."), # 3 Li
(0, "0."), # 4 Be
(0, "0."), # 5 B
]
if "c" in mt:
atom_rows.append((2, _fmt_abn(logc))) # 6 C
if "n" in mt:
atom_rows.append((2, _fmt_abn(logn))) # 7 N
if "o" in mt:
atom_rows.append((2, _fmt_abn(logo))) # 8 O
natoms = 5 + len([m for m in "cno" if m in mt])
atoms_block = " {}\n* mode abn modpf\n".format(natoms)
for mode, abn in atom_rows:
atoms_block += " {} {} 0\n".format(mode, abn)
# ---- ions 节 ----
ions = list(_IONS_H) + list(_IONS_HE)
if "c" in mt:
ions += _IONS_C
if "n" in mt:
ions += _IONS_N
if "o" in mt:
ions += _IONS_O
ions_block = "*iat iz nlevs ilast ilvlin nonstd typion filei\n*\n"
for iat, iz, nlevs, typion, filei in ions:
ilast = 1 if (nlevs == 1) else 0
# 单能级离子(裸核)无谱线;其他用 ilvlin
ilvl = 0 if (nlevs == 1) else ilvlin
ions_block += (" {iat} {iz:2d} {nlevs:5d} {ilast:5d}"
" {ilvl:5d} 0 '{t}' '{f}'\n").format(
iat=iat, iz=iz, nlevs=nlevs, ilast=ilast, ilvl=ilvl,
t=typion, f=filei)
ions_block += " 0 0 0 -1 0 0 ' ' ' '\n"
text = (
"{teff:.1f} {logg:.1f} ! TEFF, GRAV\n"
" {lte} {ltgray} ! LTE, LTGRAY\n"
" 'nst' ! name of file containing non-standard flags\n"
"*-----------------------------------------------------------------\n"
"* frequencies\n"
" 2000 ! NFREAD\n"
"*-----------------------------------------------------------------\n"
"* data for atoms\n"
"{atoms}*-----------------------------------------------------------------\n"
"* data for ions\n*\n"
"{ions}*\n* end\n"
).format(teff=teff, logg=logg, lte=lte, ltgray=ltgray,
atoms=atoms_block, ions=ions_block)
return text
def main():
"""命令行入口:根据参数生成 .5 文件。"""
ap = argparse.ArgumentParser(
description="Generate a H/He/(C/N/O) NLTE atmosphere .5 file")
ap.add_argument("--teff", type=float, required=True)
ap.add_argument("--logg", type=float, required=True)
ap.add_argument("--loghe", type=float, required=True)
ap.add_argument("--logc", type=float, required=True)
ap.add_argument("--logn", type=float, required=True)
ap.add_argument("--logo", type=float, required=True)
ap.add_argument("--metals", default="cno",
help="subset of cno to include: '', 'c', 'cn', 'cno'")
ap.add_argument("--lte", default="F")
ap.add_argument("--ltgray", default="F")
ap.add_argument("-o", "--output", default=None)
args = ap.parse_args()
text = make_input5(args.teff, args.logg, args.loghe, args.logc, args.logn,
args.logo, lte=args.lte, ltgray=args.ltgray,
metals=args.metals)
name = model_name(args.teff, args.logg, args.loghe,
args.logc, args.logn, args.logo)
out = args.output or name + ".5"
with open(out, "w") as f:
f.write(text)
print("wrote {} ({}, metals={})".format(out, name, args.metals or "none"))
if __name__ == "__main__":
main()
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""绘制归一化合成光谱,并标注 C/N/O 诊断谱线位置。
读取 results/<name>/<name>.specfort.7 格式:波长、流量)和连续谱
results/<name>/<name>.cont,计算流量/连续谱,并叠加关键 C/N/O 谱线
的位置,以确认金属元素确实存在。
用法:
plot_spec.py results/<model_name> [-o plot.png]
"""
import argparse
import os
import sys
import numpy as np
# 热亚矮星的关键诊断谱线(波长单位 Angstrom,附标签)
# 这些是该 Teff 区间内光学/NUV 波段最强的 C/N/O 特征谱线。
DIAG_LINES = [
(4267.15, "C II"),
(4647.42, "C III"),
(4649.07, "C III"),
(4650.25, "C III"),
(4068.92, "C III"),
(3995.00, "N II"),
(4630.54, "N II"),
(4447.03, "N IV"),
(4057.81, "N V"),
(4603.17, "N V"),
(4414.90, "O II"),
(4641.81, "O II"),
(4649.13, "O II"),
(5597.95, "O III"),
]
def read_fort(path):
"""读取 synspec 风格的 fort.7/.17 文件:注释行以 '#' 开头,
列依次为 wavelength、flux。返回两个 numpy 数组 (wave, flux)。"""
wave, flux = [], []
with open(path) as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split()
if len(parts) < 2:
continue
try:
w = float(parts[0]); fl = float(parts[1])
except ValueError:
continue
wave.append(w); flux.append(fl)
return np.array(wave), np.array(flux)
def main():
"""命令行入口:读光谱、归一化、叠加诊断谱线并保存 PNG。"""
ap = argparse.ArgumentParser(description="Plot normalized spectrum + CNO lines")
ap.add_argument("model_dir", help="path to results/<model_name> dir")
ap.add_argument("-o", "--output", default=None,
help="output png (default: <model_dir>/spectrum.png?)")
ap.add_argument("--wmin", type=float, default=None)
ap.add_argument("--wmax", type=float, default=None)
args = ap.parse_args()
name = os.path.basename(os.path.normpath(args.model_dir))
spec = os.path.join(args.model_dir, name + ".spec")
cont = os.path.join(args.model_dir, name + ".cont")
if not os.path.exists(spec):
# 回退:若 .spec/.cont 不存在,尝试原始的 fort.7 / fort.17
spec = os.path.join(args.model_dir, "fort.7")
cont = os.path.join(args.model_dir, "fort.17")
if not os.path.exists(spec):
sys.exit("no spectrum found in {}".format(args.model_dir))
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
w, f = read_fort(spec)
if os.path.exists(cont):
wc, fc = read_fort(cont)
# 把连续谱插值到光谱网格上
fc_int = np.interp(w, wc, fc)
norm = np.where(fc_int > 0, f / fc_int, 1.0)
else:
# 没有连续谱时退化为按中位数归一
norm = f / np.median(f)
# 默认窗口:3000-7000A(光学),可被 --wmin/--wmax 覆盖
wmin = args.wmin or (w.min() if w.min() > 3000 else 3000)
wmax = args.wmax or min(w.max(), 7000)
m = (w >= wmin) & (w <= wmax)
fig, ax = plt.subplots(figsize=(16, 5))
ax.plot(w[m], norm[m], "k-", lw=0.5)
ax.set_xlabel("Wavelength [A]")
ax.set_ylabel("Normalized flux")
ax.set_title(name)
ymax = np.percentile(norm[m], 99.5) * 1.1
ax.set_ylim(0, ymax)
ax.set_xlim(wmin, wmax)
# 叠加诊断谱线(红色虚竖线 + 标签)
ymin, ymaxl = ax.get_ylim()
for wl, lab in DIAG_LINES:
if wmin <= wl <= wmax:
ax.axvline(wl, color="r", ls=":", lw=0.7, alpha=0.6)
ax.text(wl, ymaxl * 0.95, lab, rotation=90, fontsize=7,
color="r", va="top", ha="right")
out = args.output or os.path.join(args.model_dir, "spectrum.png")
fig.tight_layout()
fig.savefig(out, dpi=120)
print("wrote", out)
# 简要报告:窗口内最深的吸收线
depth = 1.0 - norm[m]
print("window {:.0f}-{:.0f}A: {} points, deepest absorption {:.2f}".format(
wmin, wmax, m.sum(), float(np.max(depth))))
if __name__ == "__main__":
main()
+371
View File
@@ -0,0 +1,371 @@
#!/usr/bin/env python3
"""调度 6 维 H/He/C/N/O NLTE 网格的计算。
生成各维采样点的完整笛卡尔积,对每个点执行收敛链 + synspec(通过
run_one.run_model)。主要特性:
- 断点续算:跳过 conv.json 中 converged=true 的模型
- 种子复用:优先用已收敛的邻居大气作种子(减少冷启动);
若无邻居则回退到 LTE-grey 阶段从零开始
- 种子步进回退:冷启动失败时,自动用已收敛邻居作种子重试
seed_step chain: LTGRAY=F 热启动 + ICHANG=0
- 并行:multiprocessing.Pool,每个 worker 独立工作目录
- 失败隔离:单个模型失败不影响全局,结果记录到 grid_status.json
- 桥头堡优先:可选 bridgehead 列表,先算容易收敛的点建立种子库
用法:
run_grid.py config.yaml # 跑整个网格
run_grid.py config.yaml --dry-run # 仅列出模型,不计算
run_grid.py config.yaml --only teff=35000,logg=5.5 # 过滤
"""
import argparse
import itertools
import json
import os
import shutil
import sys
import time
import traceback
from multiprocessing import Pool
try:
import yaml
def _load_yaml(path):
with open(path) as f:
return yaml.safe_load(f)
except ImportError:
# 针对本项目配置格式的极简 YAML 读取器:顶层 `key: value`,嵌套的
# `grid:` 块由 `subkey: [...]` 组成,行内列表 `[a, b]`,以及由 dict 列表
# 构成的 `chain:` 块。此处不依赖 pyyaml。
def _load_yaml(path):
cfg = {}
chain = []
section = None # 当前嵌套 dict(如 grid 块),或 None
with open(path) as f:
for raw in f:
line = raw.split("#", 1)[0].rstrip()
if not line.strip():
continue
stripped = line.strip()
indented = line[:1] in (" ", "\t")
# chain 下的 dict 列表条目:"- {label: x, ...}"
if stripped.startswith("- "):
body = stripped[2:].strip()
if body.startswith("{") and body.endswith("}"):
d = {}
for kv in body[1:-1].split(","):
if ":" in kv:
k, v = kv.split(":", 1)
d[k.strip()] = _scalar(v.strip())
chain.append(d)
continue
if ":" not in stripped:
continue
key, val = stripped.split(":", 1)
key = key.strip()
val = val.strip()
if val == "":
# 嵌套块的节标题
cfg[key] = {}
section = cfg[key]
if key == "chain":
section = None # chain 是列表,已在上面处理
cfg["chain"] = chain
continue
# 标量 / 行内列表值
if section is not None and indented:
section[key] = _parse_val(val)
else:
cfg[key] = _parse_val(val)
section = None
if chain and "chain" not in cfg:
cfg["chain"] = chain
return cfg
def _parse_val(val):
"""把行内字符串解析为标量或列表。"""
val = val.strip()
if val.startswith("[") and val.endswith("]"):
return [_scalar(x.strip()) for x in val[1:-1].split(",") if x.strip()]
return _scalar(val)
def _scalar(v):
"""把单个字符串解析为 bool/int/float,否则原样返回。"""
if v in ("true", "True"):
return True
if v in ("false", "False"):
return False
try:
return int(v)
except ValueError:
pass
try:
return float(v)
except ValueError:
pass
return v
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import run_one # noqa: E402
import gen_input5 # noqa: E402
import seed_step # noqa: E402 -- 提供 SEED_STEP_CHAIN
TLUSTY = os.environ.get(
"TLUSTY", "/home/dckj/program/tlusty/tl208-s54")
def expand_grid(grid):
"""生成 (teff,logg,loghe,logc,logn,logo) 的完整笛卡尔积。"""
keys = ["teff", "logg", "loghe", "logc", "logn", "logo"]
for combo in itertools.product(*(grid[k] for k in keys)):
yield dict(zip(keys, combo))
def model_done(results_root, name):
"""该模型是否已成功完成(conv.json 报告 converged=true)。"""
p = os.path.join(results_root, name, "conv.json")
if not os.path.exists(p):
return False
try:
with open(p) as f:
return bool(json.load(f).get("converged"))
except Exception:
return False
def _atmos_clean(atmos_path):
"""检查大气文件是否干净(无 NaN 污染)。
种子必须是干净的——若种子本身含 NaN(发散过的中间结果),
传递给下一个模型会让其布居数也变 NaN。
"""
if not os.path.isfile(atmos_path):
return False
try:
with open(atmos_path) as f:
lines = f.readlines()
if not lines:
return False
nan_count = sum(1 for l in lines if "nan" in l.lower())
return nan_count < len(lines) * 0.1 # <10% NaN 视为干净
except Exception:
return False
def find_seed(results_root, teff, logg, loghe, logc=None, logn=None, logo=None):
"""查找已收敛的邻居大气作种子,返回路径或 None。
优先级(从高到低):
1. 完全匹配 (teff,logg,loghe) 且 CNO 更接近 → 几乎自由(同 family)
2. 同 (teff,logg,loghe) 但 CNO 不同 → 丰度步进
3. 全局最近邻(按 Teff/logg/logHe 加权距离)
若无可用的干净种子,返回 None。LTE-grey 阶段会处理无种子的情况,
所以返回 None 总是安全的。
若传入 logc/logn/logo,则优先选金属丰度差距小的种子——因为丰度
跨度过大(如 logCNO 从 -4 直接跳 -11000× 跳跃)会导致 seed_step
也发散(见 EXPERIENCE.md 错误 7)。
"""
if not os.path.isdir(results_root):
return None
exact_family = None # 同 (teff,logg,loghe) 的最近 CNO 邻居
exact_family_d = None
closest = None # 全局最近邻
closest_d = None
for d in os.listdir(results_root):
# 跳过备份目录(如 .OLD_WRONG_ABN, .FAILED_ORELAX 后缀)
if d.startswith(".") or ".OLD" in d or ".FAILED" in d:
continue
full = os.path.join(results_root, d)
if not os.path.isdir(full):
continue
conv = os.path.join(full, "conv.json")
atmo = os.path.join(full, d + ".7")
if not (os.path.isfile(conv) and _atmos_clean(atmo)):
continue
try:
with open(conv) as f:
meta = json.load(f)
except Exception:
continue
if not meta.get("converged"):
continue
if meta.get("atmosphere_has_nan"):
continue
p = meta["params"]
# 同 family (Teff,logg,logHe):按 CNO 距离排序
if (p["teff"] == teff and p["logg"] == logg
and p["loghe"] == loghe):
if logc is not None:
dcno = (abs(p["logc"] - logc) + abs(p["logn"] - logn)
+ abs(p["logo"] - logo))
else:
dcno = 0.0
if exact_family_d is None or dcno < exact_family_d:
exact_family_d, exact_family = dcno, atmo
continue
# 全局最近邻:Teff/logg/logHe 加权距离
d_total = (abs(p["teff"] - teff) / 5000.0
+ abs(p["logg"] - logg) * 2.0
+ abs(p["loghe"] - loghe) * 0.5)
if closest_d is None or d_total < closest_d:
closest_d, closest = d_total, atmo
# 同 family 优先(哪怕 CNO 跨度大);否则用全局最近邻
return exact_family or closest
def _worker(args):
"""Pool worker:跑一个模型。必须是顶层函数以便 pickle。"""
(pt, results_root, template, fort55, linelist, chain, itek_fallback,
niter, timeout, seed_step_fallback) = args
name = gen_input5.model_name(pt["teff"], pt["logg"], pt["loghe"],
pt["logc"], pt["logn"], pt["logo"])
if model_done(results_root, name):
return {"name": name, "status": "skipped"}
# 把 chain/fallback patch 到模块级,让 run_model 能取到
if chain:
run_one.DEFAULT_CHAIN = chain
if itek_fallback:
run_one.ITEK_FALLBACK = itek_fallback
if niter:
run_one.DEFAULT_NITER = niter
seed = find_seed(results_root, pt["teff"], pt["logg"], pt["loghe"],
pt["logc"], pt["logn"], pt["logo"])
try:
summary = run_one.run_model(
pt["teff"], pt["logg"], pt["loghe"], pt["logc"], pt["logn"],
pt["logo"], results_root, template, fort55, linelist,
chain=chain or run_one.DEFAULT_CHAIN, seed=seed, timeout=timeout)
# 冷启动失败 + 启用了 seed_step 回退 + 找到了干净种子:
# 用 seed_step chainLTGRAY=F 热启动)重试。把失败结果备份后重算。
if (not summary["converged"] and seed_step_fallback
and seed is not None):
backup_dir = os.path.join(results_root, name + ".coldfail")
if os.path.exists(backup_dir):
shutil.rmtree(backup_dir)
shutil.move(os.path.join(results_root, name), backup_dir)
print(" [seed_step] {} cold-start failed, retrying with seed {}".format(
name, os.path.basename(os.path.dirname(seed))))
try:
summary = run_one.run_model(
pt["teff"], pt["logg"], pt["loghe"], pt["logc"],
pt["logn"], pt["logo"], results_root, template, fort55,
linelist, chain=seed_step.SEED_STEP_CHAIN,
seed=seed, timeout=timeout)
summary["seed_step_used"] = True
summary["coldfail_backup"] = backup_dir
except Exception as e:
return {"name": name, "status": "error",
"error": "seed_step: " + str(e),
"traceback": traceback.format_exc()[:800]}
return {"name": name,
"status": "converged" if summary["converged"] else "unfinished",
"max_relc": summary.get("final_max_relc"),
"seed_step_used": summary.get("seed_step_used", False)}
except Exception as e:
return {"name": name, "status": "error", "error": str(e),
"traceback": traceback.format_exc()[:800]}
def main():
"""命令行入口:解析 config.yaml,展开网格,逐点跑模型并汇总。"""
ap = argparse.ArgumentParser(description="Run the 6-D CNO NLTE grid")
ap.add_argument("config", help="path to config.yaml")
ap.add_argument("--dry-run", action="store_true",
help="list models that would be computed, then exit")
ap.add_argument("--only", default=None,
help="filter, e.g. teff=35000,logg=5.5 (exact matches)")
ap.add_argument("--limit", type=int, default=None,
help="compute at most N models (for testing)")
args = ap.parse_args()
with open(args.config) as f:
cfg = _load_yaml(args.config)
results_root = os.path.join(TLUSTY, "cno_grid", cfg.get("results", "results")) \
if not os.path.isabs(cfg["results"]) else cfg["results"]
template = cfg["template"] if os.path.isabs(cfg["template"]) else \
os.path.join(os.path.dirname(args.config), cfg["template"])
fort55 = cfg["fort55"] if os.path.isabs(cfg["fort55"]) else \
os.path.join(os.path.dirname(args.config), cfg["fort55"])
linelist = cfg["linelist"] if os.path.isabs(cfg["linelist"]) else \
os.path.join(TLUSTY, cfg["linelist"])
# 解析 --only 过滤条件
filt = {}
if args.only:
for part in args.only.split(","):
k, v = part.split("=")
filt[k.strip()] = float(v)
points = []
for pt in expand_grid(cfg["grid"]):
if all(pt.get(k) == v for k, v in filt.items()):
points.append(pt)
if args.limit:
points = points[:args.limit]
n_total = len(points)
n_skip = sum(1 for pt in points
if model_done(results_root,
gen_input5.model_name(
pt["teff"], pt["logg"], pt["loghe"],
pt["logc"], pt["logn"], pt["logo"])))
print("grid: {} points total, {} already done, {} to compute".format(
n_total, n_skip, n_total - n_skip))
if args.dry_run:
for pt in points[:50]:
print(" ", pt)
if n_total > 50:
print(" ... ({} more)".format(n_total - 50))
return
os.makedirs(results_root, exist_ok=True)
chain = cfg.get("chain")
itek_fallback = cfg.get("itek_fallback")
niter = cfg.get("niter")
timeout = cfg.get("timeout_sec", 3600)
nworkers = cfg.get("nworkers", 1)
# 种子步进回退:冷启动失败时,用已收敛邻居作种子重试(默认启用)
seed_step_fallback = cfg.get("seed_step_fallback", True)
worker_args = [(pt, results_root, template, fort55, linelist,
chain, itek_fallback, niter, timeout, seed_step_fallback)
for pt in points]
t0 = time.time()
status_log = []
# 单 worker 串行:便于排查;多 worker 用 Pool 并行
if nworkers <= 1:
for wa in worker_args:
res = _worker(wa)
status_log.append(res)
print(" [{}/{}] {} -> {}".format(
len(status_log), n_total, res["name"], res["status"]),
flush=True)
else:
with Pool(nworkers) as pool:
for res in pool.imap_unordered(_worker, worker_args):
status_log.append(res)
print(" [{}/{}] {} -> {}".format(
len(status_log), n_total, res["name"], res["status"]),
flush=True)
# 汇总每个状态的数量,以及有多少走了 seed_step 回退路径
counts = {}
seed_step_count = 0
for r in status_log:
counts[r["status"]] = counts.get(r["status"], 0) + 1
if r.get("seed_step_used"):
seed_step_count += 1
summary = {"total": n_total, "elapsed_sec": round(time.time() - t0, 1),
"counts": counts, "seed_step_retries": seed_step_count,
"models": status_log}
with open(os.path.join(results_root, "grid_status.json"), "w") as f:
json.dump(summary, f, indent=2)
print("\nDONE. counts={}, seed_step_retries={}, {:.0f}s".format(
counts, seed_step_count, summary["elapsed_sec"]))
if __name__ == "__main__":
main()
+414
View File
@@ -0,0 +1,414 @@
#!/usr/bin/env python3
"""计算单个 H/He/C/N/O NLTE 大气模型 + 合成光谱。
实现与 Peter Nemeth 通信中推荐的"收敛链"策略:对于难收敛的(富 He 或富金属)
模型,先用较松的 CHMAX(如 0.1)收敛,再把该模型作为下一轮的初始大气,
逐步收紧 CHMAX(0.01)直到目标值(0.001)。如果某一步未能收敛,则增大
迭代数 ITEK3 -> 15 -> 100),以速度换取稳定性。
单个模型的处理流程:
1. 生成 .5 输入文件(gen_input5.make_input5
2. 对链中的每个 (CHMAX, ITEK) 阶段:
- 写入该阶段 CHMAX/ITEK 对应的 nst 文件
- 把种子大气链接为 fort.8
- 运行 tlusty.exe -> fort.7(大气)、fort.9(收敛日志)
- 检查收敛性(check_conv.check
- 若收敛:本阶段的 .7 成为下一阶段的种子
- 若未收敛:增大 ITEK 并在相同 CHMAX 下重试
3. 最终阶段完成后(无论收敛与否),运行 synspec
4. 写入 conv.json 记录结果摘要
所有工作都在 results/<model_name>/ 目录内完成。
"""
import argparse
import json
import os
import shutil
import subprocess
import sys
import time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import gen_input5 # noqa: E402 生成 .5 输入文件的模块
import check_conv # noqa: E402 解析 fort.9 判断收敛的模块
TLUSTY = os.environ.get(
"TLUSTY", "/home/dckj/program/tlusty/tl208-s54")
TLUSTY_EXE = os.path.join(TLUSTY, "tlusty", "tlusty.exe") # Tlusty 可执行文件
SYNSPEC_EXE = os.path.join(TLUSTY, "synspec", "synspec.exe") # Synspec 可执行文件
DATA_DIR = os.path.join(TLUSTY, "data") # 原子数据目录
# 收敛链。每个阶段是一个 dict。第一阶段用 LTE=T(程序内部构造的灰度初始
# 模型,无需 fort.8 种子)以获得一个物理上自洽的初始结构;后续阶段在前一
# 阶段大气作为种子(fort.8)的条件下进行完整 NLTE 计算。
#
# 测试中得到的重要经验:
# - LTE 灰度阶段的任务是产生一个*合法*的大气结构,而不是完全收敛;
# 即便 CHMAX 没达到也接受其结果(require_converged=False)。在该阶段
# 强求收敛纯属浪费时间,巨大的模型原子在 LTE 下几乎无法收敛。
# - ITEK=100 会发散(过冲)。回退时 ITEK 上限设为 15。
DEFAULT_CHAIN = [
# 已验证的配方(tests/cno_sdspectrum35000K 复现):
# LTE 灰度 (T T, NITER=0) -> NLTE 连续谱 (nc, ilvlin=0) -> NLTE 谱线 (nl, ilvlin=100)
#
# 关键:不要在 nst 里设 CHMAX 或 ITEK —— 用 tlusty 默认值(CHMAX=0.001,
# ITEK=4)。设 CHMAX=0.1(宽松)会让 nc 在真正收敛前就停止,给 nl
# 留下一个坏种子导致发散。用默认 CHMAX=0.001 时 nc 能正常收敛
# (约 11 次迭代),nl 只需约 1 次迭代即可收敛。
#
# .5 文件中 NFREAD=2000 -> 实际 5088 个频率点(速度快)。不要使用
# NFREAD=50(会展开为 77695 个点,慢 15 倍且不稳定)。
#
# 阶段 1LTE 灰度大气(T T)。NITER=0。
{"label": "lte", "lte": "T", "ltgray": "T", "ilvlin": 0,
"require_converged": False, "niter": 0},
# 阶段 2NLTE 连续谱(F F, ilvlin=0)。默认 CHMAX=0.001 强制真正
# 收敛。NITER=50 给足迭代余量。
{"label": "nc", "lte": "F", "ltgray": "F", "ilvlin": 0,
"require_converged": False, "niter": 50},
# 阶段 3:含谱线的完整 NLTEF F, ilvlin=100)。从已收敛的 nc 种子出
# 发,约 1 次迭代即可收敛。默认 CHMAX=0.001。
{"label": "nl", "lte": "F", "ltgray": "F", "ilvlin": 100,
"require_converged": True, "niter": 100},
]
# 若(必须收敛的)NLTE 阶段未收敛,则按下面这些更大的 ITEK 值重试。
# 注意:不要用 ITEK=100 —— 对这些模型会发散。
ITEK_FALLBACK = [15]
# 每次 tlusty 运行的默认最大迭代数(nst 中的 NITER);各阶段可覆盖。
DEFAULT_NITER = 50
def log(msg):
"""统一的日志输出函数(带 [run_one] 前缀,强制刷新)。"""
print(" [run_one] " + msg, flush=True)
def write_nst(path, chmax=None, itek=None, nd=50, vtb=2.0, niter=DEFAULT_NITER,
orelax=None, idlte=None, iacc=None, ichang=None, icrsw=None,
swpfac=None, swplim=None, swpinc=None):
"""写入收敛链某阶段使用的 nst 非标准参数文件。
采用 tests/cno_sdspectrum 中验证过的参数集:
NLAMBD=3, ISPODF=1, DDNU=50., CNU1=6., IELCOR=-1.
CHMAX/ITEK 默认为 None(即用 tlusty 默认值 0.001/4)。显式指定这两个
参数是可选的,且通常有害(CHMAX=0.1 会让 nc 提前停止;ITEK=3 相对默认
值 4 会改变加速行为)。
高级选项(None = 使用 tlusty 默认值):
ichang : 从一个模型原子不同的 fort.8 种子热启动时的布居数重映射
模式。0=不变,1=新增能级置为 LTE>1 从 fort.95 读取完整
旧模型定义。
icrsw : 碰撞-辐射切换开关(Hummer & Voels 1988)。
0=关,>0=开。开启时需配合 swpfac/swplim/swpinc。
"""
parts = ["ND={}".format(nd), "NLAMBD=3", "VTB={:.0f}.".format(vtb),
"ISPODF=1", "DDNU=50.", "CNU1=6."]
if chmax is not None:
parts.append("CHMAX={}".format(chmax))
if itek is not None:
parts.append("ITEK={}".format(itek))
parts.append("NITER={}".format(niter))
line1 = ",".join(parts)
line2_parts = []
if orelax is not None:
line2_parts.append("ORELAX={}".format(orelax))
if idlte is not None:
line2_parts.append("IDLTE={}".format(idlte))
if iacc is not None:
line2_parts.append("IACC={}".format(iacc))
if ichang is not None:
line2_parts.append("ICHANG={}".format(ichang))
if icrsw is not None:
line2_parts.append("ICRSW={}".format(icrsw))
if swpfac is not None:
line2_parts.append("SWPFAC={}".format(swpfac))
if swplim is not None:
line2_parts.append("SWPLIM={}".format(swplim))
if swpinc is not None:
line2_parts.append("SWPINC={}".format(swpinc))
line2_parts.append("IELCOR=-1")
with open(path, "w") as f:
f.write(line1 + "\n")
f.write(",".join(line2_parts) + "\n")
def run_tlusty(workdir, model_core, seed_atmos):
"""在 workdir 中运行 tlusty.exe。
model_core : 模型名主干(.5 文件名为 <model_core>.5
seed_atmos : 作为 fort.8 种子的 .7 大气文件路径;None 表示从零开始
返回 subprocess 的返回码。
"""
# 种子大气 -> fort.8
fort8 = os.path.join(workdir, "fort.8")
if os.path.exists(fort8):
os.remove(fort8)
if seed_atmos and os.path.exists(seed_atmos):
shutil.copy(seed_atmos, fort8)
# 删除残留的 fort.84tlusty 内部的 nst 转储文件;若上一次运行的
# NATOMS 不同,残留文件会在读取时触发 "Bad integer" 崩溃)
fort84 = os.path.join(workdir, "fort.84")
if os.path.exists(fort84):
os.remove(fort84)
# 链接数据目录
link = os.path.join(workdir, "data")
if os.path.islink(link) or os.path.exists(link):
os.remove(link)
os.symlink(DATA_DIR, link)
# 运行
cmd = [TLUSTY_EXE]
with open(os.path.join(workdir, model_core + ".5")) as fin:
with open(os.path.join(workdir, model_core + ".6"), "w") as fout:
rc = subprocess.call(cmd, stdin=fin, stdout=fout,
stderr=subprocess.STDOUT, cwd=workdir)
# tlusty 会写出 fort.7(大气)、fort.9(收敛日志)、fort.69、fort.14
return rc
def run_synspec(workdir, model_core, fort55_lin, linelist):
"""运行 synspec.exe 生成合成光谱。
model_core : 大气模型名主干;其 .7 文件即大气(被复制为 fort.8)
fort55_lin : fort.55.lin 控制文件路径(波长窗口等)
linelist : 谱线列表文件路径(被链接为 fort.19)
返回 subprocess 的返回码。
"""
# 大气 -> fort.8synspec 从 fort.8 读取大气)
shutil.copy(os.path.join(workdir, model_core + ".7"),
os.path.join(workdir, "fort.8"))
# 链接数据目录
link = os.path.join(workdir, "data")
if os.path.islink(link) or os.path.exists(link):
os.remove(link)
os.symlink(DATA_DIR, link)
# 链接控制文件 + 谱线列表
for tgt, src in (("fort.55", fort55_lin), ("fort.19", linelist)):
p = os.path.join(workdir, tgt)
if os.path.islink(p) or os.path.exists(p):
os.remove(p)
os.symlink(os.path.abspath(src), p)
# 运行;synspec 从 <model_core>.5 读取模型描述
with open(os.path.join(workdir, model_core + ".5")) as fin:
with open(os.path.join(workdir, model_core + ".log"), "w") as fout:
rc = subprocess.call([SYNSPEC_EXE], stdin=fin, stdout=fout,
stderr=subprocess.STDOUT, cwd=workdir)
# 保存输出
for src, ext in (("fort.7", "spec"), ("fort.17", "cont"),
("fort.12", "iden")):
sp = os.path.join(workdir, src)
if os.path.exists(sp):
shutil.copy(sp, os.path.join(workdir, model_core + "." + ext))
return rc
def run_model(teff, logg, loghe, logc, logn, logo,
results_root, template, fort55_lin, linelist,
chain=None, seed=None, timeout=1800):
"""对单个参数点运行完整收敛链 + synspec。
返回一个 dict,并保存到 results/<name>/conv.json。
"""
chain = chain or DEFAULT_CHAIN
name = gen_input5.model_name(teff, logg, loghe, logc, logn, logo)
workdir = os.path.join(results_root, name)
os.makedirs(workdir, exist_ok=True)
log("=== MODEL {} ===".format(name))
t0 = time.time()
summary = {
"name": name,
"params": {"teff": teff, "logg": logg, "loghe": loghe,
"logc": logc, "logn": logn, "logo": logo},
"stages": [],
"converged": False,
"final_max_relc": None,
"seed": seed,
}
seed_atmos = seed # 第一阶段用作种子的 .7 路径;LTE 阶段为 None
final_converged = False
final_chmax = None
final_max_relc = None
# ---- 逐阶段跑收敛链 ----
for si, stage_def in enumerate(chain):
chmax = stage_def.get("chmax") # None = tlusty 默认值 0.001
itek0 = stage_def.get("itek") # None = tlusty 默认值 4
lte = stage_def.get("lte", "F")
ltgray = stage_def.get("ltgray", "F")
label = stage_def.get("label", "stage{}".format(si + 1))
require_conv = stage_def.get("require_converged", True)
stage_niter = stage_def.get("niter", DEFAULT_NITER)
stage_t0 = time.time() # 本阶段计时器
stage = {"label": label, "chmax": chmax, "lte": lte,
"itek_attempts": [], "converged": False}
# 写入本阶段的 .5(因 LTE/metals/ilvlin 不同需要重新生成)
stage_metals = stage_def.get("metals", "cno")
stage_ilvlin = stage_def.get("ilvlin", 100)
input5 = gen_input5.make_input5(
teff, logg, loghe, logc, logn, logo, template,
lte=lte, ltgray=ltgray, metals=stage_metals, ilvlin=stage_ilvlin)
with open(os.path.join(workdir, name + ".5"), "w") as f:
f.write(input5)
# 要尝试的 ITEK 值:只有 require_converged=True 时才升级重试。
# itek0 可能为 None= 用 tlusty 默认值 4);此时只有一次"尝试"。
if itek0 is None:
itek_values = [None]
else:
itek_values = [itek0] + (ITEK_FALLBACK if require_conv else [])
# NLTE 阶段的稳定化参数。默认全部关闭(None),因为实测表明
# IDLTE=45 + IACC=disable 反而会导致 nc 阶段发散(之前看起来"有用"
# 只是因为 nst 行截断 bug 静默丢弃了 IDLTE)。干净的三步配方
# lte->nc->nl)在不加这些参数时就能收敛。如果某个具体点需要,
# 可以在 config.yaml 中通过 idlte/iacc/orelax 键显式开启。
stab_idlte = stage_def.get("idlte")
stab_iacc = stage_def.get("iacc")
stab_ichang = stage_def.get("ichang")
stab_icrsw = stage_def.get("icrsw")
stab_swpfac = stage_def.get("swpfac")
stab_swplim = stage_def.get("swplim")
stab_swpinc = stage_def.get("swpinc")
stage_seed = seed_atmos
# ---- 在该阶段的 ITEK 值序列中依次尝试,直到收敛或耗尽 ----
for it in itek_values:
log("stage {}/{} [{}]: LTE={} LTGRAY={} CHMAX={} ITEK={} niter={} "
"orelax={} idlte={} iacc={} ichang={} icrsw={}".format(
si + 1, len(chain), label, lte, ltgray, chmax, it, stage_niter,
stage_def.get("orelax"), stab_idlte, stab_iacc,
stab_ichang, stab_icrsw))
write_nst(os.path.join(workdir, "nst"), chmax, it,
niter=stage_niter, orelax=stage_def.get("orelax"),
idlte=stab_idlte, iacc=stab_iacc, ichang=stab_ichang,
icrsw=stab_icrsw, swpfac=stab_swpfac,
swplim=stab_swplim, swpinc=stab_swpinc)
rc = run_tlusty(workdir, name, stage_seed)
attempt = {"itek": it, "rc": rc}
fort9 = os.path.join(workdir, "fort.9")
fort7 = os.path.join(workdir, "fort.7")
# fort.7(大气)是核心产物。fort.9(收敛日志)在 NITER=0
# (灰度 LTE 启动)时不会生成,需要特别处理。
if rc == 0 and os.path.exists(fort7):
eff_chmax = chmax if chmax is not None else 0.001
if os.path.exists(fort9):
res = check_conv.check(fort9, eff_chmax)
attempt.update(res)
stagename = "{}_chmax{}_itek{}".format(label, eff_chmax, it)
shutil.copy(fort9, os.path.join(
workdir, name + "." + stagename.replace(".", "p") + ".9"))
else:
# NITER=0 灰度启动:无收敛日志,直接接受
res = {"converged": True, "max_relc": 0.0,
"note": "NITER=0 grey start (no iterations)"}
attempt.update(res)
# 把该次大气快照为下一阶段的种子
seed_atmos = os.path.join(workdir, name + "." + label + ".7")
shutil.copy(fort7, seed_atmos)
if res["converged"]:
stage["converged"] = True
stage["final"] = attempt
break
elif not require_conv:
# LTE/grey/nc 阶段:直接接受结构,继续下一阶段
stage["converged"] = False
stage["final"] = attempt
stage["note"] = "accepted as seed (convergence not required)"
break
else:
attempt["error"] = "tlusty rc={} or missing fort.7".format(rc)
stage["itek_attempts"].append(attempt)
# 记录本阶段表现最好的一次尝试
if stage["itek_attempts"]:
best = min(stage["itek_attempts"],
key=lambda a: a.get("max_relc", 1e9))
stage["best_max_relc"] = best.get("max_relc")
stage["elapsed_sec"] = round(time.time() - stage_t0, 1)
summary["stages"].append(stage)
final_chmax = chmax
final_converged = stage["converged"]
if "final" in stage:
final_max_relc = stage["final"].get("max_relc")
# 最终大气 = 最后阶段最好一次尝试产生的 .7
final_7 = os.path.join(workdir, name + ".7")
if seed_atmos and os.path.exists(seed_atmos):
shutil.copy(seed_atmos, final_7)
elif os.path.exists(os.path.join(workdir, "fort.7")):
shutil.copy(os.path.join(workdir, "fort.7"), final_7)
summary["converged"] = bool(final_converged)
summary["final_max_relc"] = final_max_relc
summary["final_chmax"] = final_chmax
# 检测虚假收敛:大气里全是 NaN(例如 nc 发散后 nl 因 0/0 而以 max_relc=0
# "收敛")。标记并作废此类结果。
atmos_has_nan = False
if os.path.exists(final_7):
try:
with open(final_7) as f:
atmos_has_nan = sum(
1 for l in f if "nan" in l.lower()) > len(open(final_7).readlines()) * 0.1
except Exception:
pass
summary["atmosphere_has_nan"] = atmos_has_nan
if atmos_has_nan:
summary["converged"] = False
summary["note"] = ("invalidated: atmosphere contains >10% NaN lines "
"(likely nc-stage divergence -> fake nl convergence)")
# 4. synspec(仅当得到了大气时才跑)
if os.path.exists(final_7):
log("running synspec...")
syn_t0 = time.time()
try:
rc = run_synspec(workdir, name, fort55_lin, linelist)
summary["synspec_rc"] = rc
except Exception as e:
summary["synspec_error"] = str(e)
summary["synspec_sec"] = round(time.time() - syn_t0, 1)
else:
summary["synspec_error"] = "no atmosphere produced"
summary["elapsed_sec"] = round(time.time() - t0, 1)
with open(os.path.join(workdir, "conv.json"), "w") as f:
json.dump(summary, f, indent=2)
log("DONE {} (converged={}, {:.0f}s)".format(
name, summary["converged"], summary["elapsed_sec"]))
return summary
def main():
"""命令行入口:解析参数后调用 run_model。"""
ap = argparse.ArgumentParser(
description="Run one H/He/C/N/O NLTE model (chain convergence + synspec)")
ap.add_argument("--teff", type=float, required=True)
ap.add_argument("--logg", type=float, required=True)
ap.add_argument("--loghe", type=float, required=True)
ap.add_argument("--logc", type=float, required=True)
ap.add_argument("--logn", type=float, required=True)
ap.add_argument("--logo", type=float, required=True)
ap.add_argument("--results", default=None,
help="results root dir (default: ../results)")
ap.add_argument("--template", default=None)
ap.add_argument("--fort55", default=None, help="fort.55.lin control file")
ap.add_argument("--linelist", default=None, help="line list (fort.19)")
ap.add_argument("--seed", default=None, help="seed atmosphere .7 file")
ap.add_argument("--timeout", type=int, default=1800)
args = ap.parse_args()
here = os.path.dirname(os.path.abspath(__file__))
grid = os.path.dirname(here)
results = args.results or os.path.join(grid, "results")
template = args.template or os.path.join(grid, "templates", "cno_atmos.5.tpl")
fort55 = args.fort55 or os.path.join(grid, "templates", "fort.55.lin")
linelist = args.linelist or os.path.join(DATA_DIR, "gfVIS99.dat")
run_model(args.teff, args.logg, args.loghe, args.logc, args.logn,
args.logo, results, template, fort55, linelist,
seed=args.seed, timeout=args.timeout)
if __name__ == "__main__":
main()
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""针对失败的高温 He-poor CNO 模型进行的"种子步进"实验。
发现(边界测试 2026-07-21):
- 80000/6.5/+2/-4(富 He)在 nc 阶段干净收敛(15 次迭代)
- 80000/6.5/-4/-4(贫 He)在 nc 阶段发散(第 2 次迭代 relc=1e18
- 两者 Teff/logg/logCNO 完全相同,只有 logHe 不同。
根因:当 He 丰度极小、金属主导不透明度时,LTE 灰度冷启动的初始猜测离
真实 NLTE 解太远。富 He 模型的不透明度由 He 主导、稳定;贫 He 模型的
不透明度由 CNO 主导,线性化过程会震荡。
源码分析证实(tlusty208.f:578-582):
- LTGRAY=T -> CALL LTEGR (灰度冷启动,忽略 fort.8)
- LTGRAY=F -> CALL INPMOD (从 fort.8 热启动)
- ICHANG=1 -> 当种子的模型原子不同时,新增能级被强制设为 LTE
修复方案(最终正确理解的 Peter Nemeth 建议):
从已收敛的富 He 大气热启动这个失败的贫 He 模型,使用
LTGRAY=F + ICHANG=1 重映射布居数。Tlusty 会从一个物理上接近的起点
松弛 He/H 比例和 CNO 电离平衡,而不是从灰度猜测出发。
本脚本运行单个种子步进模型,并与 results/ 中已有的冷启动结果对比。
"""
import argparse
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import gen_input5 # noqa: E402
import run_one # noqa: E402
TLUSTY = os.environ.get("TLUSTY", "/home/dckj/program/tlusty/tl208-s54")
# 种子步进链:跳过 LTE 灰度冷启动,直接从种子热启动。
# ICHANG 默认为 0(不重映射)——只有当种子与目标的模型原子不同时才需要
# 非 0 值。本处模型原子完全一致(同为 H/He/CNO 设置),只有
# Teff/logg/丰度不同,所以 ICHANG=0 是正确的。
#
# 注意:ICRSWHummer & Voels 1988 的碰撞-辐射切换)经测试在 tlusty208.f
# 中是死代码 —— SWITCH 子程序定义在第 4556 行,但源码中任何地方都未被
# 调用。CRSW 保持默认值 1.0,因此第 6343-6344 行等处的速率乘法实际上
# 没有任何作用。不要依赖 ICRSW 来稳定。请改用 ORELAX(在 tlusty208.f
# 第 14647、14996 行实际生效)—— 不过即便 ORELAX=0.5 在 80K + 贫 He 的
# 极端丰度跳跃下也会失败。
SEED_STEP_CHAIN = [
# 阶段 1:从种子大气热启动 NLTE 连续谱。LTGRAY=F 会读取 fort.8
# 种子的布居数提供了一个物理上接近的初始猜测。
{"label": "seed_nc", "lte": "F", "ltgray": "F", "ilvlin": 0,
"ichang": 0, "require_converged": False, "niter": 80},
# 阶段 2:从种子收敛的大气出发,进行含谱线的完整 NLTE 计算。
{"label": "nl", "lte": "F", "ltgray": "F", "ilvlin": 100,
"ichang": 0, "require_converged": True, "niter": 100},
]
# 仅为向后兼容保留,实际并不会启用 ICRSW(死代码)。请改用 SEED_STEP_CHAIN。
SEED_STEP_CHAIN_ICRSW = SEED_STEP_CHAIN
def main():
"""命令行入口:运行一次种子步进收敛实验。"""
ap = argparse.ArgumentParser(
description="Seed-stepping convergence experiment")
ap.add_argument("--teff", type=float, required=True)
ap.add_argument("--logg", type=float, required=True)
ap.add_argument("--loghe", type=float, required=True)
ap.add_argument("--logc", type=float, required=True)
ap.add_argument("--logn", type=float, required=True)
ap.add_argument("--logo", type=float, required=True)
ap.add_argument("--seed", required=True,
help="path to seed atmosphere .7 (e.g. converged He-rich)")
ap.add_argument("--icrsw", action="store_true",
help="use ICRSW (Hummer & Voels switching) for hard jumps")
ap.add_argument("--results", default=None)
ap.add_argument("--template", default=None)
ap.add_argument("--fort55", default=None)
ap.add_argument("--linelist", default=None)
args = ap.parse_args()
grid = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
results = args.results or os.path.join(grid, "results", "seed_step")
os.makedirs(results, exist_ok=True)
template = args.template or os.path.join(grid, "templates", "cno_atmos.5.tpl")
fort55 = args.fort55 or os.path.join(grid, "templates", "fort.55.lin")
linelist = args.linelist or os.path.join(
TLUSTY, "data", "gfVIS99.dat")
# 打印实验信息:目标参数 + 种子来源 + 使用的链
print("=" * 60)
print("SEED-STEPPING EXPERIMENT")
print("=" * 60)
print("target: t{}_g{}_he{}_c{}_n{}_o{}".format(
int(args.teff), args.logg, args.loghe, args.logc, args.logn, args.logo))
print("seed: {}".format(args.seed))
chain = SEED_STEP_CHAIN_ICRSW if args.icrsw else SEED_STEP_CHAIN
print("chain: LTGRAY=F + ICHANG=0 hot-start -> nl" +
(" [ICRSW ON]" if args.icrsw else ""))
print("")
run_one.run_model(
args.teff, args.logg, args.loghe, args.logc, args.logn, args.logo,
results, template, fort55, linelist,
chain=chain, seed=args.seed)
if __name__ == "__main__":
main()
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""汇总所有边界测试的结果。"""
import json
import os
# 结果根目录(相对于本仓库根)。COLD_START_TESTS 用的目录。
RESULTS = "cno_grid/results"
# seed_step 实验单独存放在子目录中
SEED = os.path.join(RESULTS, "seed_step")
# 冷启动测试点列表:(展示标签, 模型目录名, 简短参数描述)
COLD_START_TESTS = [
("20k_he2_cno-1", "t20000_g5.0_he2_c-1_n-1_o-1", "20K/5.0/+2/-1"),
("20k_he2_cno-4", "t20000_g5.0_he2_c-4_n-4_o-4", "20K/5.0/+2/-4"),
("40k_he0_cno-4", "t40000_g5.5_he0_c-4_n-4_o-4", "40K/5.5/0/-4"),
("60k_he0_cno-1", "t60000_g6.0_he0_c-1_n-1_o-1", "60K/6.0/0/-1"),
("80k_he-4_cno-1", "t80000_g6.5_he-4_c-1_n-1_o-1", "80K/6.5/-4/-1"),
("80k_he-4_cno-4", "t80000_g6.5_he-4_c-4_n-4_o-4", "80K/6.5/-4/-4"),
("80k_he2_cno-1", "t80000_g6.5_he2_c-1_n-1_o-1", "80K/6.5/+2/-1"),
("80k_he2_cno-4", "t80000_g6.5_he2_c-4_n-4_o-4", "80K/6.5/+2/-4"),
]
# 种子步进测试点列表:(模型目录名, 使用的种子描述, 子目录位置)
SEED_STEP_TESTS = [
("t80000_g6.5_he-4_c-4_n-4_o-4", "80K He-rich cno-4", "seed_step"),
("t80000_g6.5_he2_c-1_n-1_o-1", "80K He-rich cno-4", "seed_step"),
("t80000_g6.5_he-4_c-2_n-2_o-2", "80K He-poor cno-4", "seed_step"),
]
def fmt(dname, location=RESULTS):
"""把某个模型目录的 conv.json 摘要为一行字符串。
包含:是否收敛(✓/✗)、是否含 NaN、最终 max_relc、用时。
若 conv.json 不存在,返回占位提示。
"""
p = os.path.join(location, dname, "conv.json")
if not os.path.exists(p):
return " (no conv.json)"
j = json.load(open(p))
conv = "" if j["converged"] else ""
nan = " NaN" if j.get("atmosphere_has_nan") else ""
relc = j.get("final_max_relc")
relc_s = "{:.2e}".format(relc) if isinstance(relc, (int, float)) else str(relc)
t = j.get("elapsed_sec", "?")
return f" conv={conv}{nan} relc={relc_s:<11} t={t}s"
# ---- 打印冷启动结果 ----
print("=" * 70)
print("COLD-START RESULTS (run_one.py: LTE grey -> nc -> nl)")
print("=" * 70)
for tag, dname, params in COLD_START_TESTS:
print(f" {tag:<18} {params:<15} {fmt(dname)}")
# ---- 打印种子步进结果 ----
print()
print("=" * 70)
print("SEED-STEP RESULTS (seed_step.py: hot-start from neighbor)")
print("=" * 70)
for dname, seed, loc in SEED_STEP_TESTS:
print(f" {dname:<32} seed={seed:<22} {fmt(dname, os.path.join(RESULTS, loc))}")
+50
View File
@@ -0,0 +1,50 @@
{TEFF} {LOGG} ! TEFF, GRAV
{LTE} {LTGRAY} ! LTE, LTGRAY
'nst' ! name of file containing non-standard flags
*-----------------------------------------------------------------
* frequencies
2000 ! NFREAD
*-----------------------------------------------------------------
* data for atoms
* NATOMS = 8: 1=H 2=He 3-5=empty 6=C 7=N 8=O
* mode: 0=implicit LTE background, 1=explicit LTE, 2=explicit NLTE
* abn: number density ratio n(X)/n(H); 0 means adopted solar abundance
8 ! NATOMS
* mode abn modpf
2 {ABN_H} 0 ! H
2 {ABN_HE} 0 ! He
0 0. 0
0 0. 0
0 0. 0
2 {ABN_C} 0 ! C
2 {ABN_N} 0 ! N
2 {ABN_O} 0 ! O
*-----------------------------------------------------------------
* data for ions
*iat iz nlevs ilast ilvlin nonstd typion filei
*
1 0 9 0 0 0 ' H 1' 'data/h1.dat'
1 1 1 1 0 0 ' H 2' ' '
2 0 24 0 0 0 'He 1' 'data/he1.dat'
2 1 20 0 0 0 'He 2' 'data/he2.dat'
2 2 1 1 0 0 'He 3' ' '
6 0 40 0 0 0 ' C 1' 'data/c1.dat'
6 1 22 0 0 0 ' C 2' 'data/c2.dat'
6 2 46 0 0 0 ' C 3' 'data/c3_34+12lev.dat'
6 3 25 0 0 0 ' C 4' 'data/c4.dat'
6 4 1 1 0 0 ' C 5' ' '
7 0 34 0 0 0 ' N 1' 'data/n1.dat'
7 1 42 0 0 0 ' N 2' 'data/n2_32+10lev.dat'
7 2 32 0 0 0 ' N 3' 'data/n3.dat'
7 3 48 0 0 0 ' N 4' 'data/n4_34+14lev.dat'
7 4 16 0 0 0 ' N 5' 'data/n5.dat'
7 5 1 1 0 0 ' N 6' ' '
8 0 33 0 0 0 ' O 1' 'data/o1_23+10lev.dat'
8 1 48 0 0 0 ' O 2' 'data/o2_36+12lev.dat'
8 2 41 0 0 0 ' O 3' 'data/o3_28+13lev.dat'
8 3 39 0 0 0 ' O 4' 'data/o4.dat'
8 4 6 0 0 0 ' O 5' 'data/o5.dat'
8 5 1 1 0 0 ' O 6' ' '
0 0 0 -1 0 0 ' ' ' '
*
* end
+1
View File
@@ -0,0 +1 @@
ND=50,VTB=2.,CHMAX=0.001