feat(all): 物理正确性五重硬门槛、输入文件结构化与 fort.55 错位修复、conv 诊断 DB 化与阶段归因修复、ORELAX 收敛修复与导入工具下线
物理正确性校验体系(common/conv_check.rs +494 行) - 新增 5 类硬门槛:能量守恒(.6)、温度结构(.7)、emflux 积分校验(.emflux,含全 NaN 判失败)、假收敛排查(itek 轨迹首末比)、b 因子合理性(.bfac) - runner 在 TLUSTY 阶段结束后执行全部校验,任一失败判 final_converged=false - GridConfig 新增 8 个可配阈值,经 scheduler→executor→runner 全链路透传 输入文件配置结构化重构(config.rs +1453 行) - TlustyInput 拆为 dot5/nst 分层结构,字段名严格映射 tlusty208.f READ 语句;SynspecInput 重构为 9 个 Fort55Line 子结构体 - 移除 ChainStep.metals 字段,元素集改由 dot5.atoms/ions 显式声明(gen_input5/nst_writer 同步重写为三源融合 / 分层覆盖) - fort.55 修复行结构 bug:补全分子表行(7→9 行),IDSTD 50→0 错位修正(影响全部光谱线强归一化,需重算 SYNSPEC 阶段) conv 诊断 DB 化与阶段归因修复(server) - 单点详情 conv 面板从磁盘 conv.json 改读 DB grid_points.summary_json;grid_points 新增 summary_json/last_elapsed_sec 两列(旧库幂等 ALTER) - record_task_report 阶段归因列加 CASE 守卫 + clear_synspec 对称处理,修复 synspec-only/TLUSTY-only 重跑污染统计 - 新增 summary_merge.rs 点级增量合并,避免重跑覆盖诊断字段 收敛性 ORELAX 修复与 seed_chain 可配(sdB_cno.yaml + node) - nl 阶段加 orelax=0.5、seed_nc 加 orelax=0.3,阻尼中温区 relc 振荡发散 - seed_chain 块可配,executor 优先采用用户配置而非内置默认链 导入工具下线 - 删除 import_results 客户端工具及 Windows 推送脚本;移除 /admin/import_seed 端点 - 改为服务端临时 migrate_conv 端点(扫 conv.json 增量合并入库,迁移后可删) 文档与分析 - 新增 1305 失败点根因分析、fort.14 全 NaN 物理含义分析两份深度文档 - spectrum_correctness_analysis 两次修订标注已修复项;fetch_results.sh 修 trap RETURN 的 set -u 报错
This commit is contained in:
+6
-1
@@ -157,6 +157,11 @@ IELCOR=-1
|
||||
- nl: NITER=100
|
||||
- **不设 CHMAX**(用默认 0.001)、**不设 ITEK**(用默认 4)
|
||||
|
||||
> 注(2026-08-07 起的当前实现):`nst_writer` 在前两行之外还会显式写出
|
||||
> line3/line4 的 NLTE 高频关键字(NMU/ISPLIN/CNU2/NFTAIL/DFTAIL 与
|
||||
> HMIX0…TMOLIM,共 23 个,值等于 TLUSTY 内置默认)。配方物理等价,仅行数
|
||||
> 由旧 2 行扩为 4+ 行。
|
||||
|
||||
### 3.3 CNO 模型原子(`.5` 里声明的 nlevs)
|
||||
|
||||
> 下表"nlevs"列是 `.5` 文件里每个离子实际声明的 NLTE 能级数(即 `gen_input5.py`
|
||||
@@ -220,7 +225,7 @@ IELCOR=-1
|
||||
### 错误 6:nst 行长截断 bug
|
||||
- **来源**:tlusty 的 nst 解析器有 ~72 字符行宽限制
|
||||
- **影响**:参数太多时(如加了 IDLTE/IACC),行尾参数被静默截断 → 用默认值
|
||||
- **纠正**:write_nst 把参数分两行写(line1 ≤ 64 字符)
|
||||
- **纠正**:write_nst 把参数分多行写(每行 ≤ 64 字符);当前实现为 4+ 行(见 §3.2 注)
|
||||
- **教训**:Fortran 的固定格式行宽限制是隐蔽 bug 源
|
||||
|
||||
### 错误 7:丰度范围设置过高(最严重的错误)
|
||||
|
||||
+11
-6
@@ -121,17 +121,20 @@ ions段: iat iz nlevs ilast ilvlin nonstd typion filei
|
||||
|
||||
### 3.2 nst 文件(非标准参数,每阶段统一结构,NITER 不同)
|
||||
|
||||
`nst_writer::generate_nst_content` 对**所有阶段(含 lte)统一生成两行**:
|
||||
`nst_writer::generate_nst_content` 对**所有阶段(含 lte)统一生成 4+ 行**:前 2 行与旧实现字节级一致(阶段参数 + `IELCOR=-1`),从第 3 行起显式写出 `global.nst` 的 NLTE 高频关键字(值等于 TLUSTY 内置默认,物理等价):
|
||||
```
|
||||
ND=50,NLAMBD=3,VTB=2.,ISPODF=1,DDNU=50.,CNU1=6.,[CHMAX=..][,ITEK=..],NITER=<阶段>
|
||||
[ORELAX=..][,IDLTE=..][,IACC=..][,ICHANG=..],IELCOR=-1
|
||||
NMU=3,ISPLIN=0,CNU2=3.,NFTAIL=21,DFTAIL=0.25
|
||||
HMIX0=-1.,MLTYPE=1,IPTURB=1,IBFINT=1,IHECOR=0,IRTE=0,IBC=3,IFRYB=0,IOPTAB=0,IFMOL=0,IFALI=5,IFPOPR=4,JALI=1,TRAD=0.,WDIL=0.,TFLOOR=8000.,TDISK=0.,TMOLIM=9000.
|
||||
```
|
||||
- lte: `NITER=0`(灰大气不迭代,只做一次形式解)
|
||||
- nc: `NITER=10`, nl: `NITER=100`, seed_nc: `NITER=20`
|
||||
- **不设 CHMAX**(用默认 0.001,强迫 nc 真正收敛)
|
||||
- **不设 ITEK**(用默认 4)
|
||||
- 尾部统一 `IELCOR=-1`(电子密度修正关闭)
|
||||
- 参数分行写(line1 ≤64c, line2 余下),避免 tlusty nst 解析器 ~72 字符行宽截断
|
||||
- 频率截断 `FRCMAX/MIN/FRLMAX/MIN` 仅当显式配非零值才写出(0=TLUSTY 内置默认,不覆盖)
|
||||
- 参数分行写(每行 ≤64c),避免 tlusty nst 解析器 ~72 字符行宽截断
|
||||
|
||||
每个参数的作用与原理:
|
||||
|
||||
@@ -300,9 +303,11 @@ synspec_stage: # SYNSPEC 阶段独立配置(enabled/
|
||||
enabled: true
|
||||
policy: skip_converged
|
||||
strategies: ["standard"]
|
||||
synspec_input: # SYNSPEC 数值参数(fort.55 波长范围等)
|
||||
wstart: 3000.0
|
||||
wend: 7000.0
|
||||
synspec_input: # SYNSPEC fort.55 控制卡(9 行子结构体,按行配置)
|
||||
line1: {imode: 0, idstd: 0, iprin: 1} # 行1: IMODE/IDSTD(=0 自动 2ND/3)/IPRIN
|
||||
line6: # 行6: 波长范围等(其余行走 SynspecInput::default)
|
||||
alam0: 3000.0 # 光谱波长起始点 (Å)
|
||||
alast: 7000.0 # 光谱波长终止点 (Å)
|
||||
```
|
||||
|
||||
### 第2步:创建并启动工作流(HTTP API / Dashboard)
|
||||
@@ -367,7 +372,7 @@ curl -X POST localhost:8090/api/workflows/sdB_cno/points \
|
||||
|
||||
6. **nst 文件行长限制(已修复)**:tlusty 的 nst 解析器有 ~72 字符的行宽限制。
|
||||
如果参数太多写在一行(如加了 IDLTE/IACC 后 >72 字符),行尾的参数会被
|
||||
静默截断。`nst_writer` 把参数分两行写(line1≤64c, line2 余下参数)。
|
||||
静默截断。`nst_writer` 把参数分多行写(每行 ≤64c),避免触及该行宽限制。
|
||||
这是个隐蔽 bug——截断后 tlusty 不报错而是用默认值,导致"看似成功实则参数
|
||||
没生效"。
|
||||
|
||||
|
||||
+11
-36
@@ -648,44 +648,19 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
-F 'seed_file=@/path/to/t35000_g5.5_he-1_c-2_n-2_o-2.7'
|
||||
```
|
||||
|
||||
### 4.3 历史种子批量导入 (`POST /api/admin/import_seed`)
|
||||
### 4.3 迁移端点 (`POST /api/admin/migrate_conv`)
|
||||
|
||||
把旧版单机 `run_grid.py` 产物(`conv.json` + `.7` 大气文件)批量回灌进 DCTS。与 `/task/report` 的关键区别:**跳过任务归属校验**(历史数据无领用语义),直接幂等落库。供 [`tools/import_results`](file:///home/fmq/program/tlusty/tl208-s54/dcts/tools/import_results/src/main.rs) 调用。
|
||||
临时迁移端点:扫 `seeds_dir` 下的 `conv.json` → 增量合并写入 `grid_points.summary_json`。
|
||||
让详情页 conv 诊断面板从 DB 读取(不再依赖磁盘文件)。**迁移完成后删除本端点即可。**
|
||||
|
||||
- **处理函数**: [`import_seed`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/server/src/api/task.rs)
|
||||
- **鉴权**: **Admin 角色**(admin token 或登录 session)
|
||||
- **请求格式**: `multipart/form-data`
|
||||
- Part `report`: 旧版 `conv.json` 的**原文 JSON**(映射为 `ModelSummary`,服务端解析出 `name` / `params` / `converged` / `final_max_relc`)
|
||||
- Part `seed_file`: 二进制数据(`.7` 大气种子文件;收敛点必传)
|
||||
- Part `tlusty_success_method` *(可选)*: 文本 `cold_run` / `seed_step`。由 `tools/import_results` 依据旧 `conv.json` 的 stages 是否含 `seed_nc` 判定后设置,写入导入点大气归因 `tlusty_success_method`(缺省按 seed_step 统计)
|
||||
- **Query 参数**: `workflow`(可选,默认 `imported`):目标工作流名,种子导入到该工作流的 `grid_points`。
|
||||
- **命名保真**: `point_name` 取旧 `conv.json` 的 `name` 字段(源精度真名,如 `t20000_g5.0_...`),**逐字符**落库(磁盘目录、`grid_points.name`、`seeds.point_name`),与旧版 Python `gen_input5.model_name` 完全一致。
|
||||
- **幂等**: `ON CONFLICT DO NOTHING` upsert `grid_points`、`conv.json` 与 `.7` 原子覆盖写,可重复运行。
|
||||
- **响应 Schema**:
|
||||
- `200 OK`:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"point_name": "t20000_g5.0_he-2_c-4_n-4_o-4",
|
||||
"converged": true,
|
||||
"max_relc": 0.000321
|
||||
}
|
||||
```
|
||||
- `400 Bad Request`: 缺少 `report` / `conv.json` 解析失败 / 非法网格点名称
|
||||
- `401 Unauthorized`: 缺少或无效 admin 凭据
|
||||
- **curl 示例**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:8090/api/admin/import_seed?workflow=sdB_cno" \
|
||||
-H "Authorization: Bearer <admin_token>" \
|
||||
-F 'report=@/path/to/old_results/t20000_g5.0_he-2_c-4_n-4_o-4/conv.json;type=application/json' \
|
||||
-F 'seed_file=@/path/to/old_results/t20000_g5.0_he-2_c-4_n-4_o-4/t20000_g5.0_he-2_c-4_n-4_o-4.7'
|
||||
```
|
||||
- **批量导入工具**: [`import_results`](file:///home/fmq/program/tlusty/tl208-s54/dcts/tools/import_results/src/main.rs) 自动扫描结果目录、校验无 NaN 且收敛、逐点调用本端点(同时把完整产物树迁移到 `data/result/`):
|
||||
```bash
|
||||
cargo run -p import_results -- --dir <旧 results 根目录> \
|
||||
--config workflows/sdB_cno.yaml \
|
||||
--server http://127.0.0.1:8090 --workflow sdB_cno --token <admin_token>
|
||||
```
|
||||
- **鉴权**: Admin 角色
|
||||
- **Query 参数**: `workflow`(必填)、`dir`(可选,缺省取 `seeds_dir`)
|
||||
- **特性**: 幂等(ON CONFLICT DO NOTHING)、增量合并(synspec-only 保留 TLUSTY 字段)、多版本兼容(旧 `converged` 键)
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8090/api/admin/migrate_conv?workflow=sdB_cno" \
|
||||
-H "Authorization: Bearer <admin_token>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ dcts/
|
||||
│ └── mq/ # [Library] SQLite 事务型分布式任务队列
|
||||
├── dashboard/ # [Web] ESM 模块化运维看板(Vite)
|
||||
├── workflows/sdB_cno.yaml # 示例网格工作流配置
|
||||
├── tools/
|
||||
│ └── import_results/ # [Binary] 历史计算结果导入工具(旧版网格结果 → 种子/收敛入库)
|
||||
├── scripts/ # 结果拉取等运维脚本
|
||||
└── docs/ # 分主题架构与规格技术文档
|
||||
```
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
# TLUSTY 收敛性分析与修复(2026-08-08 ~ 08-09)
|
||||
|
||||
> 本文完整记录了对 `sdB_cno` 工作流 1305 个失败网格点的根因分析、实测验证过程、
|
||||
> 以及最终落地的代码改动。所有 tlusty/synspec 实测均使用**生产二进制**
|
||||
> `dcts/assets/tlusty_static`(md5=`14cd144b...`),而非开发目录的 `tlusty/tlusty.exe`
|
||||
> (md5=`9b249e00...`)——两者编译版本不同,数值行为有差异。
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据库分析(`dcts.db`)
|
||||
|
||||
### 1.1 真实规模(纠正初期统计错误)
|
||||
|
||||
| 维度 | 数量 |
|
||||
|------|------|
|
||||
| grid_points 总数 | **9216**(9 Teff × 4 logg × 4 logHe × 4 logC × 4 logN × 4 logO) |
|
||||
| ✅ completed | **7911(85.8%)** |
|
||||
| ❌ failed | **1305(14.2%)** |
|
||||
|
||||
初期曾误把 `tasks` 表的 12344 行(含重试)当作网格点总数,得出"60% 失败"的错误结论。
|
||||
实际上 tasks 表每个失败点因 cold_run→seed_step 回退产生多行(attempt_count 中位数 3-4),
|
||||
且 3876 个早已收敛的点根本不在 tasks 表里(`skip_converged` 跳过)。
|
||||
|
||||
### 1.2 失败点参数空间分布
|
||||
|
||||
**按 Teff × logHe(每 cell 256 点):**
|
||||
|
||||
| Teff | logHe | 失败/256 | 失败率 | 备注 |
|
||||
|------|-------|---------|-------|------|
|
||||
| 60000 | -4 | 148 | **58%** | 主体失败区 |
|
||||
| 55000 | -4 | 136 | **53%** | 主体失败区 |
|
||||
| 60000 | -2 | 126 | 49% | |
|
||||
| 55000 | -2 | 128 | 50% | |
|
||||
| 60000 | 0 | 101 | 39% | |
|
||||
| 50000 | -2 | 83 | 32% | |
|
||||
| 20000 | 2 | 89 | 35% | 次要失败簇(富氦+低logg) |
|
||||
| 25000~45000 各 He | — | <16% | — | 大多健康 |
|
||||
|
||||
**两大失败簇:**
|
||||
1. **高温贫氦区(55-60kK / He=-4,-2,0)**:733 个,占失败总数 **56%**
|
||||
2. **20kK 富氦区(He=2 / logg=6.0-6.5)**:89 个(35%)
|
||||
|
||||
**按 logg**:logg=5.0 最难(661 个,占 51%),logg=6.5 最好(139 个)。
|
||||
|
||||
**按 CNO 丰度(55000K/He=-4 cell 实测)**:CNO 越富失败率越高——
|
||||
```
|
||||
cno_sum ≤ -11: 失败率 33% ← 贫金属,相对可收敛
|
||||
cno_sum -7~-8: 失败率 92%
|
||||
cno_sum ≥ -6: 失败率 100% ← 最富金属,物理不可收敛
|
||||
```
|
||||
|
||||
### 1.3 失败点的策略执行情况
|
||||
|
||||
所有 1305 个失败点均执行过 cold_run + seed_step(`pending_strategies` 已空,策略穷尽)。
|
||||
seed_step 的实际结果:
|
||||
- seed 高 relc 发散(≥100):1461 条 task 记录
|
||||
- seed NaN 发散:1091 条
|
||||
- seed 下载/IO 失败:34 条
|
||||
- seed 低 relc(<100):14 条
|
||||
|
||||
---
|
||||
|
||||
## 2. 可能的失败原因(分析假设)
|
||||
|
||||
### 2.1 ORELAX 未设置 → 满牛顿步长振荡
|
||||
|
||||
所有 salvage 的 `.nc.nst` / `.seed_nc.nst` 只有 2 行,**无 ORELAX**:
|
||||
```
|
||||
ND=50,NLAMBD=3,VTB=2.,ISPODF=1,DDNU=50.,CNU1=6.,CHMAX=0.001,NITER=10
|
||||
IELCOR=-1
|
||||
```
|
||||
tlusty 源码 `tlusty208.f:844`:`IF(ORELAX.EQ.0.) ORELAX=UN`(UN=1.0),即默认 100% 牛顿步长。
|
||||
满步长在硬网格点易振荡发散。
|
||||
|
||||
### 2.2 Ng/Kantorovich 加速器把解推飞
|
||||
|
||||
tlusty 默认 `IACC=7`(Ng 加速从 iter7 开始)。fort.6 日志显示 `KANTOROVICH acceleration: ITER 5-9`
|
||||
后 relc 从几十跳到上万——加速器在不稳定解上放大了误差。
|
||||
|
||||
### 2.3 灰 LTE 起点离高温 NLTE 解太远
|
||||
|
||||
55kK 点 cold nc iter1 relc 就高达 12.7-21.6(对比正常点 0.06-0.2),LTE 灰大气作为 NLTE 迭代
|
||||
起点在高温下偏差太大。
|
||||
|
||||
### 2.4 富金属方向 seed_step 发散(物理原因)
|
||||
|
||||
seed_finder.rs 记录:increasing metals from a poor seed adds UV line-radiation driving that
|
||||
breaks radiative equilibrium(贫方向成功率 42-54%,富方向仅 3-11%)。
|
||||
|
||||
### 2.5 synspec 在 Balmer edge(3645Å)数值发散
|
||||
|
||||
459 个点 tlusty 收敛了(max_relc<0.001)但 synspec 产出的 spec 在 3645Å(H n=2 bound-free edge)
|
||||
突然变 NaN。机制:`ST0(I)=ET(IJ,I)/CH(IJ,I)`(synspec54.f:12990),表层 NLTE 粒子数为 0 导致 0/0=NaN。
|
||||
|
||||
### 2.6 IACC≤4 被源码强制改 7
|
||||
|
||||
`tlusty208.f:1928`:`IF(IACC.LE.4) IACC=7`。设 IACC=0 无法禁用 Ng 加速器。
|
||||
|
||||
---
|
||||
|
||||
## 3. 实测验证(生产二进制 tlusty_static)
|
||||
|
||||
### 3.1 关键教训:二进制选择
|
||||
|
||||
**初期所有测试误用了 `tlusty/tlusty.exe`(开发版,Apr 2025)而非生产版
|
||||
`dcts/assets/tlusty_static`(Jul 2025)。** 两者 md5 不同,数值行为不同
|
||||
(如 55kK 点 iter1 relc:tlusty.exe=12.7,salvage 记录=254)。
|
||||
以下结论均以**生产二进制**为准,凡 tlusty.exe 结果均已标注作废。
|
||||
|
||||
### 3.2 部分读取陷阱
|
||||
|
||||
tlusty 运行中读取 fort.9 会得到**不完整迭代**的 relc(如 iter2=0.003 看似收敛),
|
||||
但完整跑完后该迭代实际 relc 可能是 18700。**所有结论必须等运行完成后才可靠。**
|
||||
本会话多次因部分读取得出错误中间结论,最终以完整运行结果为准。
|
||||
|
||||
### 3.3 测试点 A:t20000_g5.0_he-4_c-4_n-2_o-2(20kK,次要区)
|
||||
|
||||
**注意:此点非主体失败区代表(20000/He=-4 cell 仅 2/256 失败),结论代表性有限。**
|
||||
|
||||
| 配置 | iter1 | 最终 iter | fort.7 NaN | 结论 |
|
||||
|------|-------|----------|-----------|------|
|
||||
| default ORELAX=1.0(tlusty.exe) | 0.217 | iter7=NaN | 4349 | ❌ 发散 |
|
||||
| ORELAX=0.3, NITER=10(tlusty.exe) | 0.217 | iter10=1.33 | 0 | ⚠️ 非NaN但未收敛 |
|
||||
| ORELAX=0.1, NITER=15(tlusty.exe) | 0.217 | iter15=0.556 | 0 | ⚠️ 振荡但无NaN |
|
||||
| **default(tlusty_static)** | 0.217 | **iter6=0.00672** | 0 | ✅ **收敛中** |
|
||||
|
||||
**结论**:此点在生产二进制下 default 直接收敛中。salvage 标记失败可能是 seed_nc 阶段
|
||||
失败或旧二进制偶然发散。
|
||||
|
||||
### 3.4 测试点 B:t55000_g5.0_he-4_c-1_n-2_o-2(55kK,主体失败区)
|
||||
|
||||
| 配置 | iter1 | 最终 iter | fort.7 NaN | 结论 |
|
||||
|------|-------|----------|-----------|------|
|
||||
| default N30 | 12.7 | iter10=1.95e8 | 0 | ❌ 发散 |
|
||||
| ORELAX=0.05 N50 | 12.7 | iter10=9.9e11 | 0 | ❌ 发散 |
|
||||
| ORELAX=0.01 N50 | 12.7 | iter9=1.05e6 | 0 | ❌ 发散 |
|
||||
| ORELAX=0.005 N50 | 12.7 | iter10=1.95e8 | 0 | ❌ 发散 |
|
||||
| ITEK=-50 N50(禁Ng) | 12.7 | iter11=NaN | 2047 | ❌ 更糟 |
|
||||
| IACC=0(禁加速) | 12.7 | iter5=3.89e8 | 0 | ❌ 无效(源码强制 IACC=7) |
|
||||
| seed_nc default(收敛邻居种子) | 19.5 | iter8=1.28e8 | 0 | ❌ 富方向发散 |
|
||||
| seed_nc IACC=0 | 19.5 | iter8=1.28e8 | 0 | ❌ IACC=0 无效 |
|
||||
|
||||
**结论**:55kK 主体失败区,**所有 nst 参数组合都发散**。iter1 relc=12.7(灰 LTE 起点太远),
|
||||
Ng 加速器 iter5-7 把解推飞。富方向 seed_step(ΔlogC=+2)iter3 外层深度爆炸到 18700。
|
||||
这是物理极限,非参数可解。
|
||||
|
||||
### 3.5 测试点 C:t25000_g5.0_he-4_c-4_n-4_o-3(25kK,中温区)
|
||||
|
||||
| 配置 | 最终 iter | fort.7 NaN | 结论 |
|
||||
|------|----------|-----------|------|
|
||||
| default N15 | iter10=4.72e-6 | 0 | ✅ **收敛** |
|
||||
| ORELAX=0.3 N30 | iter10=3.92e-6 | 0 | ✅ **收敛** |
|
||||
|
||||
**结论**:25kK 失败点在生产二进制下 default 和 ORELAX=0.3 **都收敛了**。
|
||||
此点 salvage 标记失败是旧二进制偶然发散——用新代码重跑即可救回。
|
||||
|
||||
### 3.6 测试点 D:t45000_g6.0_he-4_c-1_n-3_o-3(45kK,中温区)
|
||||
|
||||
| 配置 | iter1 | 最终 iter | fort.7 NaN | 结论 |
|
||||
|------|-------|----------|-----------|------|
|
||||
| default N15 | 21.6 | iter6=2.93e7 | 0 | ❌ Ng 加速爆炸 |
|
||||
| ORELAX=0.3 N30 | 21.6 | iter16=2.81e13 | 0 | ❌ 延迟但最终发散 |
|
||||
|
||||
**结论**:45kK 点 ORELAX 无法阻止最终发散,但 default 和 ORELAX=0.3 全程 0 NaN。
|
||||
(注:中间曾观察到 iter10=0.000335 的"收敛"假象,证实是部分读取 artifact。)
|
||||
|
||||
### 3.7 测试点 E:t20000_g6.5_he2_c-4_n-3_o-4(20kK 富氦高重力簇)
|
||||
|
||||
conv.json 显示 seed_nc relc=0.0115(接近收敛),但 **nl 爆炸到 5.32e16**。
|
||||
nc 阶段 default 和 ORELAX=0.3 轨迹接近(iter4: 1.31 vs 1.27),问题在 nl 阶段。
|
||||
|
||||
### 3.8 synspec Balmer edge NaN 测试(t50000_g5.0_he-4_c-2_n-3_o-2)
|
||||
|
||||
tlusty 收敛(max_relc=0.000247),但 synspec spec 在 **3645.88Å(Balmer limit)** 变 NaN,
|
||||
前 87750 行正常,之后全 NaN(313695 行)。
|
||||
|
||||
| 配置 | 第一个 NaN 波长 | spec NaN 行数 | 结论 |
|
||||
|------|----------------|-------------|------|
|
||||
| 旧版 fort.55(IDSTD=50) | 3645.54Å | 30 万+ | ❌ |
|
||||
| 新版 fort.55(IDSTD=0) | 3645.88Å | 313695 | ❌ IDSTD 无效 |
|
||||
| ICONTL=1(纯连续谱) | 3645.88Å | 313695 | ❌ 连续谱 H bf 本身发散 |
|
||||
|
||||
**结论**:Balmer edge NaN 不是 fort.55 配置问题,是 synspec 连续谱氢 bound-free
|
||||
在表层 NLTE 粒子数为 0 时产生 0/0=NaN。大气 depth-1 粒子数块确认含大量 `0.000000E+00`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 综合结论
|
||||
|
||||
### 4.1 失败分类(1305 个 grid failed 点)
|
||||
|
||||
| 类别 | 数量 | 可救性 |
|
||||
|------|------|--------|
|
||||
| 高 relc 严重发散(≥100) | 746 | 高温区不可救;中温区可能重跑即救 |
|
||||
| NaN 物理发散 | 527 | 旧二进制 NaN → 新二进制可能改善 |
|
||||
| 未知 | 24 | — |
|
||||
| 接近收敛(relc<1) | 3 | 可救 |
|
||||
| 中度发散(1-100) | 5 | 可能可救 |
|
||||
|
||||
另:459 个点 grid 状态为 completed(synspec 失败但 tlusty 收敛),不在 1305 之列。
|
||||
|
||||
### 4.2 什么能救、什么不能
|
||||
|
||||
**能救(预计 ~150-300 点):**
|
||||
- 中温区(20-45kK)旧二进制偶然失败的点——用新代码 `tlusty_static` 重跑即可
|
||||
- 20kK/He2/g6.5 簇 nl 阶段振荡发散——ORELAX=0.5 阻尼 nl 的 Newton 步长振荡
|
||||
- 部分旧二进制 NaN 发散——新二进制数值更稳定(55kK 点全程 0 NaN vs 旧版 NaN)
|
||||
|
||||
**不可救(~600-800 点):**
|
||||
- 55-60kK/贫氦/富CNO(cno_sum≥-6)——灰 LTE 起点离 NLTE 解太远,所有参数组合发散
|
||||
- synspec Balmer edge NaN——表层 NLTE 粒子数为 0 导致连续谱 H bf 数值发散
|
||||
|
||||
### 4.3 无效方案(实测验证,勿重复)
|
||||
|
||||
| 方案 | 测试结果 | 原因 |
|
||||
|------|---------|------|
|
||||
| ORELAX 对 55kK cold nc | ❌ 全发散 | 灰 LTE 起点太远,ORELAX 压不住 |
|
||||
| IACC=0 禁用 Ng | ❌ 无效 | `tlusty208.f:1928` 强制 IACC=7 |
|
||||
| ITEK=-50 禁用 Ng | ❌ 更快 NaN | 失去加速后纯 Newton 也发散 |
|
||||
| 新版 fort.55(IDSTD=0) 修 synspec | ❌ 仍 NaN | IDSTD 与 Balmer edge 无关 |
|
||||
| TFLOOR 防发散 | ❌ 零效果 | 生产二进制实测轨迹与无 TFLOOR 逐位相同 |
|
||||
| 调大 chmax | ❌ 有害 | nc 提前停止给 nl 坏种子 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 落地的代码改动
|
||||
|
||||
### 5.1 核心思路
|
||||
|
||||
给 nl/seed_nc 阶段加 ORELAX 欠松弛阻尼,并在 YAML 新增 `seed_chain` 字段让 seed_step 链
|
||||
可配置(此前硬编码在 `default_seed_chain()`)。配合当前 `skip_converged` 策略,重启工作流
|
||||
自动重跑 1305 个失败点(收敛的 7911 点保留)。
|
||||
|
||||
### 5.2 改动清单(6 个文件)
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `crates/common/src/config.rs` | `GridConfig` 新增 `seed_chain: Vec<ChainStep>` 字段 |
|
||||
| `crates/common/src/models.rs` | `TaskSpec` 新增 `seed_chain_params` 字段 |
|
||||
| `crates/server/src/scheduler.rs` | 新增 `get_workflow_seed_chain()`;dispatch/fallback 注入 |
|
||||
| `crates/node/src/executor.rs` | `resolve_execution_chain()` seed_step 优先用 `seed_chain_params` |
|
||||
| `crates/common/src/runner.rs` | `default_seed_chain()` 兜底:seed_nc `orelax: Some(0.3)`,nl `orelax: Some(0.5)` |
|
||||
| `workflows/sdB_cno.yaml` | `tlusty_chain` nl 加 `orelax: 0.5`;新增 `seed_chain` 块 |
|
||||
|
||||
### 5.3 ORELAX 取值依据
|
||||
|
||||
- **nl 步 orelax=0.5**:nl 含谱线(ilvlin=100)扰动大,0.5 是保守的欠松弛值,
|
||||
阻尼 Newton 步长振荡但不至于过度阻尼导致谱线约束不足。
|
||||
- **seed_nc 步 orelax=0.3**:seed_nc 从已收敛邻居热启动,起点扰动小,可用更激进的 0.3。
|
||||
- **nc 步不加 orelax**:实测中温区 nc 用 default 即可收敛,不加更安全。
|
||||
|
||||
### 5.4 向后兼容
|
||||
|
||||
- `seed_chain` 空时回退 `default_seed_chain()`(已同步加 orelax),与旧行为一致
|
||||
- `TaskSpec.seed_chain_params` 缺省 None(旧 MQ payload 兼容)
|
||||
- YAML 不配 `seed_chain` 时完全不影响现有行为
|
||||
|
||||
### 5.5 验证
|
||||
|
||||
- `cargo build` ✅ 编译通过
|
||||
- `cargo test` ✅ **194 个测试全绿,0 失败**
|
||||
(含新增 `seed_step_uses_custom_seed_chain_when_provided` 测试)
|
||||
|
||||
---
|
||||
|
||||
## 6. 后续建议
|
||||
|
||||
### 6.1 重启工作流验证
|
||||
|
||||
当前 `skip_converged` 策略重启时自动重置 1305 个失败点回 pending。
|
||||
部署新二进制 + 新代码后重启,观察失败率下降情况。预计中温区(20-45kK)的
|
||||
~150 个失败点可被救回。
|
||||
|
||||
### 6.2 接力热启动(更大收益,需改代码)
|
||||
|
||||
对高温区(55-60kK)的失败点,可把 seed_step 的大步长 ΔCNO=2 拆成 Δ=1 的多步接力
|
||||
(如 c-4→c-3→c-2→c-1),降低单步富金属扰动。需改 `scheduler.rs` 的 fallback 逻辑
|
||||
支持多跳 seed。这是唯一可能救高温区的物理手段。
|
||||
|
||||
### 6.3 synspec Balmer edge 修复(需改 synspec 源码)
|
||||
|
||||
表层 NLTE 粒子数为 0 导致连续谱 H bound-free 0/0=NaN。需在 `synspec54.f` 的
|
||||
OPAC/连续谱计算加 NaN 守卫,或编译时启用 `-ffpe-trap=invalid,zero` 定位。
|
||||
|
||||
### 6.4 接受物理极限
|
||||
|
||||
55-60kK/贫氦/富CNO(cno_sum≥-6)约 400-500 个点是 sdB 纯 He 大气模型的物理极限,
|
||||
光谱应从相邻收敛点插值。
|
||||
|
||||
---
|
||||
|
||||
## 附:关键文件路径
|
||||
|
||||
- 数据库: `/home/fmq/下载/dcts.db`
|
||||
- Salvage 归档: `data/salvage/{node}/result/{point}/`
|
||||
- 生产二进制: `dcts/assets/tlusty_static`(md5=`14cd144b...`)
|
||||
- 开发二进制(勿用于测试): `tlusty/tlusty.exe`(md5=`9b249e00...`)
|
||||
- tlusty 源码: `tlusty/tlusty208.f`(IACC 强制改 7 在 :1928;ORELAX 默认在 :844)
|
||||
- synspec 源码: `synspec/synspec54.f`(NaN 源头 ST0=ET/CH 在 :12990)
|
||||
- seed_finder.rs: `crates/common/src/seed_finder.rs`(贫富方向逻辑 :14-40)
|
||||
@@ -156,7 +156,7 @@ pub fn apply_migrations(conn: &mut Connection) -> Result<()>;
|
||||
|
||||
服务端 `record_task_report`(db.rs:1920)已持有 `&TaskReport`——**无需改签名**,只在结算 UPDATE 补两列。
|
||||
|
||||
> **保真边界(审查修正)**:`ModelSummary`(models.rs:599-616)与 `StageSummary`(577-595)**不含 conv.json 的逐次 itek 迭代数组**——该细节在 Rust serde 往返中结构性丢失,仍仅存磁盘 conv.json。落库的 summary_json 是"阶段级摘要"(含 last_iter / worst_depth / n_depths 等部分迭代诊断),非原始全量。若需完整逐次迭代入库,须给 `StageSummary` 加 `#[serde(flatten)]` 保留字段(暂缓,先文档化此边界)。
|
||||
> **保真边界(审查修正,2026-08 更新)**:`ModelSummary`(models.rs:616-641)与 `StepSummary`(590-612)现已携带逐次 itek 迭代诊断数组(`StepSummary.itek_history: Vec<IterCheck>`,runner.rs:425 填充),随 summary_json 全量落库(见状态表「itek 全量保真 ✅ 已实施」),不再仅存磁盘 conv.json。落库的 summary_json 与磁盘 conv.json 同源于 runner 的 `StepSummary`,含 last_iter / worst_depth / n_depths 与完整收敛轨迹。
|
||||
|
||||
### 3.3 迁移 M1(tasks 表,均在线 ADD COLUMN)
|
||||
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ flowchart LR
|
||||
| **2. NLTE 连续谱 (nc)** | `tlusty.exe` | 切换到 NLTE,忽略束缚-束缚线跃迁(`ilvlin=0`),收敛基础电离平衡。 | `lte=F, ltgray=F, ilvlin=0, niter=10` | 10 次 |
|
||||
| **3. NLTE 完整谱线 (nl)** | `tlusty.exe` | 引入全部线跃迁(`ilvlin=100`),求解包含非平衡辐射场的大气结构;**要求收敛**(`require_converged=true`)。 | `lte=F, ltgray=F, ilvlin=100, niter=100` | 100 次 |
|
||||
| **3'. 种子热启动 (seed_nc)** | `tlusty.exe` | 以近邻收敛大气 `.7` 为 `fort.8` 初猜做 NLTE 连续谱收敛(跳过低温度灰大气阶段)。 | `lte=F, ltgray=F, ilvlin=0, niter=20, ichang=0` | 20 次 |
|
||||
| **4. 合成光谱 (synspec)** | `synspec.exe` | 基于收敛大气结构(`.7`),计算高分辨率合成光谱。 | 波长窗/展宽/截断由 `synspec:` 数值参数配置 | — |
|
||||
| **4. 合成光谱 (synspec)** | `synspec.exe` | 基于收敛大气结构(`.7`),计算高分辨率合成光谱。 | 波长窗/展宽/截断由 `synspec_input:`(fort.55 控制卡)配置 | — |
|
||||
|
||||
> 阶段标签由 workflow 配置保证唯一(`lte`/`nc`/`nl`/`seed_nc`),各阶段输入卡/日志/大气/收敛诊断
|
||||
> 以 `<name>.<label>.<后缀>` 快照落盘归档(见 [`tlusty_result_artifacts.md`](./tlusty_result_artifacts.md))。
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
# fort.14(`.emflux`)全 NaN 现象分析
|
||||
|
||||
> 调研日期:2026-08-08
|
||||
> 范围:TLUSTY 208 输出文件 fort.14(出射谱 λ-Fλ,runner 快照为 `.emflux`)中出现全 NaN 的物理与数值含义。
|
||||
> 核心问题:**`.emflux` 全 NaN 意味着什么?是否代表 TLUSTY 大气模型收敛失败?**
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR
|
||||
|
||||
**`.emflux` 全文件 NaN = TLUSTY 大气模型结构(TEMP/POPUL/ELEC)数值发散到不可用程度的可靠标志。** 源码机制与真实数据双重佐证:
|
||||
|
||||
- **源码层面**:fort.14 的流量 `FLUX` 由辐射转移求解器(RTEFR1)基于大气结构(TEMP/POPUL)计算。大气发散 → 不透明度计算产生 Inf/0 → 源函数 Inf/Inf=NaN → 整列 FLUX 全 NaN → OUTPRI 在耗尽 NITER 上限后写进 fort.14。全 NaN 只能由大气发散引起,**不存在"大气收敛良好但辐射转移单独失败"的路径**。
|
||||
- **数据层面**:162 个网格点中,**62/62(100%)的 emflux-NaN 点 `converged=false`**;`converged=true` 且 emflux 含 NaN 的点 = **0**。没有任何局部 NaN 样本。
|
||||
|
||||
**对校验设计的结论**:emflux bolometric 校验把"全 NaN 判失败"是**正确的**,语义应表述为"**大气收敛失败(辐射转移求解发散)**",而非仅"辐射转移失败"。它与 `atmosphere_has_nan` 同级、互为冗余,且能捕获后者的检测盲区。
|
||||
|
||||
---
|
||||
|
||||
## 1. fort.14 的写出机制
|
||||
|
||||
### 1.1 写出位置与格式
|
||||
|
||||
fort.14 由 `SUBROUTINE OUTPRI`(`tlusty208.f:14156`)写出,唯一活跃写出语句在 **`tlusty208.f:14188`**:
|
||||
|
||||
```fortran
|
||||
14181 DO IJ=1,NFREQ
|
||||
14182 IJP=IJ
|
||||
14183 IF(ispodf.eq.0) IJP=JIK(IJ)
|
||||
14184 IF(IJX(IJP).NE.-1) THEN
|
||||
14185 WRITE(13,602) FREQ(IJP),FLUX(IJP),FH(IJP)
|
||||
14186 TOTF=TOTF+FLUX(IJP)*W(IJP)
|
||||
14187 FLAM=FLUX(IJP)*FREQ(IJP)*FREQ(IJP)/2.997925E18
|
||||
14188 write(14,614) 2.997925e18/freq(ijp),slam
|
||||
14189 END IF
|
||||
14190 END DO
|
||||
```
|
||||
|
||||
- **FORMAT** `614 FORMAT(F15.3,1pe15.3)`(`tlusty208.f:14406`)—— 第一列波长 λ(Å),第二列 Fλ。
|
||||
- **写出变量**:`FLUX(IJP)`(COMMON/SURFAC/,`MODELQ.FOR:135`),不是局部量。`FLAM` 是从 Fν 到 Fλ 的换算(`Fλ = Fν · ν² / c`)。
|
||||
- **关键**:`FLUX` 是直接从 COMMON 块读已存的值,OUTPRI 本身**不重新求解辐射转移**。
|
||||
|
||||
### 1.2 写出时机
|
||||
|
||||
OUTPRI 仅在 `IF(LFIN)` 块内被调用(`tlusty208.f:3887-3889`):
|
||||
|
||||
```fortran
|
||||
3887: IF(LFIN) THEN
|
||||
3888: CALL OUTPRI
|
||||
3889: END IF
|
||||
```
|
||||
|
||||
`LFIN` 的判定(`tlusty208.f:47596`):
|
||||
|
||||
```fortran
|
||||
47596: LFIN=ABS(CHMX).LE.CHMAX.OR.ITER.GE.NITER
|
||||
```
|
||||
|
||||
即**最后一次迭代**(收敛 `CHMX≤CHMAX` 或耗尽迭代上限 `ITER≥NITER`)才写 fort.14。这意味着:fort.14 反映的是**最终大气状态**下的辐射转移解。
|
||||
|
||||
---
|
||||
|
||||
## 2. NaN 的产生链(源码追踪)
|
||||
|
||||
### 2.1 FLUX 的计算路径
|
||||
|
||||
`FLUX(IJ)` 的赋值有三处(都在辐射转移求解子程序中):
|
||||
|
||||
| 位置 | 子程序 | 语句 |
|
||||
|------|--------|------|
|
||||
| `tlusty208.f:31800` | `RTEFR1`(Feautrier 形式解) | `FLUX(IJ)=AHout*half` |
|
||||
| `tlusty208.f:32225` | `RTEDF2` | `FLUX(IJ)=AH` |
|
||||
| `tlusty208.f:32848` | `RTEDF1` | `FLUX(IJ)=FH(IJ)*RAD1(1)-half*hextrd(ij)` |
|
||||
|
||||
这些在每次迭代的频率循环中被调用(如 `tlusty208.f:4269-4271`):
|
||||
|
||||
```fortran
|
||||
4269: DO IJ=1,NFREQ
|
||||
4270: CALL OPACF1(IJ) ← 不透明度计算
|
||||
4271: CALL RTEFR1(IJ) ← 辐射转移求解
|
||||
```
|
||||
|
||||
### 2.2 NaN 的具体产生点
|
||||
|
||||
**第一步:不透明度计算 OPACF1(`tlusty208.f:4800`)依赖大气结构**
|
||||
|
||||
| 源码行 | 语句 | 发散时 |
|
||||
|--------|------|--------|
|
||||
| `tlusty208.f:4857` / `:45375` | `HKT1(ID)=HK/T`(T=TEMP(ID)) | TEMP→0 时 HKT1→Inf |
|
||||
| `tlusty208.f:4858` | `XKF(ID)=EXP(-HKT1(ID)*FR)` | HKT1·FR 极大时下溢到 0 |
|
||||
| `tlusty208.f:31444` / `:33455` | `ABTRA(ITR,ID)=POPUL(II,ID)` | POPUL 含 NaN → ABTRA 含 NaN |
|
||||
| `tlusty208.f:31446` | `EMTRA(ITR,ID)=POPUL(JJ,ID)*ANE*SBF(II)*...` | POPUL 极大值 × 其他 → Inf |
|
||||
|
||||
**第二步:辐射转移 RTEFR1(`tlusty208.f:32265`)源函数计算**
|
||||
|
||||
| 源码行 | 语句 | 发散时 |
|
||||
|--------|------|--------|
|
||||
| `tlusty208.f:32352` | `ST0(ID)=EMIS1(ID)/AB0(ID)` | **两个无穷大相除 = NaN**(典型源头) |
|
||||
| `tlusty208.f:32361` | `SS0(ID)=-SCAT1(ID)/AB0(ID)` | 同上 |
|
||||
|
||||
NaN 一旦进入 `RAD1`(辐射转移矩阵解),就通过 `AH`/`AHout` 传播到 `FLUX(IJ)`,然后波及整列所有频率点。
|
||||
|
||||
### 2.3 为什么发散后不会提前 STOP
|
||||
|
||||
TLUSTY 有一个发散护栏(`tlusty208.f:47587-47591`):
|
||||
|
||||
```fortran
|
||||
47587: IF(ITER.NE.1 .AND. ABS(CHMX).GT.1.D16) THEN
|
||||
47588: WRITE(6,610) ITER,CHMX
|
||||
47590: STOP
|
||||
47591: END IF
|
||||
```
|
||||
|
||||
但 **CHMX 是温度相对变化的最大值**(`tlusty208.f:47468` `CHT=CHANGT(ID)/TEMP(ID)`),当 TEMP 含 NaN 时 CHMX 也变 NaN。而 **NaN 与任何数比较都返回 `.FALSE.`**:
|
||||
- `ABS(NaN).GT.1.D16` 为假 → **STOP 不触发**(护栏对 NaN 失效)。
|
||||
- `ABS(NaN).LE.CHMAX` 为假 → `LFIN` 只能靠 `ITER.GE.NITER`(耗尽上限)成立。
|
||||
|
||||
因此程序**一路跑到 NITER,然后带着含 NaN 的 TEMP/POPUL 走完最后一次频率循环,把整列 FLUX 全写成 NaN,最后 OUTPRI 把全 NaN 的 FLUX 倒进 fort.14**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 两种 NaN 场景的区分
|
||||
|
||||
fort.14 的 NaN 理论上有两种可能来源:
|
||||
|
||||
| 场景 | 描述 | 实际是否存在 |
|
||||
|------|------|-------------|
|
||||
| **A. 大气模型发散** | TEMP/POPUL 含 NaN → FLUX 全 NaN | ✅ 存在(62 例) |
|
||||
| **B. 大气收敛良好,局部辐射转移溢出** | 某些波长点不透明度极值 → 局部 NaN | ❌ 数据中不存在 |
|
||||
|
||||
**源码判定**:场景 B 在机制上极不可能。FLUX 全 NaN 要求所有频率点的源函数都变为 NaN,这只能由大气结构整体发散引起。局部 NaN(少数频率点)需要大气结构有效但个别频率点溢出——但 TLUSTY 的频率循环对每个频率点用同一套 TEMP/POPUL,大气有效时不会让某个频率点单独产生 NaN(除非该频率点恰好触发了吸收系数的精确除零,但这在实测数据中从未出现)。
|
||||
|
||||
**数据判定**:在 162 个有 `.emflux` 的网格点中,**所有 62 个 emflux-NaN 点都是 100% 全 NaN**(如 t55000_g5.0:4514/4514 行全 NaN;t60000_g6.5:4420/4420 行全 NaN)。**没有任何一个点是局部/特定波长 NaN**。这直接排除了场景 B。
|
||||
|
||||
---
|
||||
|
||||
## 4. 真实数据验证
|
||||
|
||||
### 4.1 全量统计(162 个网格点)
|
||||
|
||||
| 情形 | 数量 | conv.json converged |
|
||||
|------|------|---------------------|
|
||||
| `.emflux` 全 NaN + `.7` 含 NaN | **53** | 全部 false |
|
||||
| `.emflux` 全 NaN + `.7` 检测无 NaN | **9** | 全部 false |
|
||||
| `.emflux` 正常 + `.7` 含 NaN | **0** | — |
|
||||
| 两者都正常(0 NaN) | **100** | 混合 |
|
||||
|
||||
**决定性事实**:
|
||||
1. **62/62(100%)的 emflux-NaN 点 `converged=false`**。
|
||||
2. `converged=true` 且 emflux 含 NaN 的点 = **0**。
|
||||
3. 全 NaN 点都是 100% 整文件 NaN,无局部 NaN。
|
||||
|
||||
### 4.2 典型全 NaN 点
|
||||
|
||||
`t55000_g5.0_he-2_c-1_n-4_o-4`:
|
||||
- `.7` 从第 11 行(depth 1 的 TEMP/ELEC/DENS/POPUL)开始全是 NaN(5200/5210 行 NaN)。
|
||||
- `conv.json`:`converged:false`、`atmosphere_has_nan:true`、`best_max_relc:39100`(seed_nc 第 3 次迭代发散)。
|
||||
- `.nl.err`:`IEEE_INVALID_FLAG IEEE_UNDERFLOW_FLAG IEEE_DENORMAL` 浮点异常。
|
||||
|
||||
### 4.3 检测器盲区:9 个"`.7` 检测无 NaN 但 emflux 全 NaN"的点
|
||||
|
||||
这 9 个点最关键——它们决定了 emflux 校验是否有独立价值:
|
||||
|
||||
- `.7` 不含字面 NaN/Inf(`atmosphere_has_nan` 检测器通过),但实际已发散成**巨大但有限的非物理值**:
|
||||
- TEMP ≈ 1.015e-15(趋近 0)
|
||||
- POPUL 达 5e12(极大)
|
||||
- `final_max_relc` 在 1e16 ~ 1e27(灾难性发散)
|
||||
- 这些值不是字面 NaN,但在 OPACF1 里 `HK/TEMP`→巨大、POPUL 乘积→Inf、`EMIS1/AB0`→Inf/Inf=NaN。
|
||||
- `synspec_error` 为 `"spec 含 NaN/Inf/溢出行 (共 303554 行)"` —— 发散大气喂给 SYNSPEC 后整谱也是 NaN/溢出。
|
||||
- **原因**:`atmosphere_has_nan`(`conv_check.rs:18`)的正则 `(?i)(\bnan\b|\binf(?:inity)?\b|\*{3,}|[eE]\+(?:3\d{2}|[4-9]\d{2,}))` 只匹配 `nan/inf/***/E+300+`,**抓不到"巨大但有限的发散值"**(如 5e12 POPUL、1e-15 TEMP)。
|
||||
|
||||
**这 9 个点靠 `converged=false`(max_relc 极大)+ emflux 全 NaN + synspec NaN 三重否决才被正确标记。emflux 全 NaN 校验在这类点上恰好补位了 `atmosphere_has_nan` 的盲区。**
|
||||
|
||||
---
|
||||
|
||||
## 5. 对校验设计的影响
|
||||
|
||||
### 5.1 emflux bolometric 校验的语义修正
|
||||
|
||||
现有 `check_emflux_bolometric`(`conv_check.rs`)把"全 NaN(占比>50%)"判失败。根据本调研:
|
||||
|
||||
- **判定正确**:全 NaN 是大气发散的充分(且几乎必要的)标志。
|
||||
- **语义需精确化**:错误描述从 ~~"辐射转移可能失败"~~ 改为 **"大气收敛失败(辐射转移求解发散)"**。
|
||||
|
||||
```rust
|
||||
// 修正后(conv_check.rs)
|
||||
error: Some(format!(
|
||||
"emflux NaN/不可解析占比 {:.0}% 过高(>{:.0}%),大气收敛失败(辐射转移求解发散)",
|
||||
nan_ratio * 100.0, 50.0
|
||||
)),
|
||||
```
|
||||
|
||||
### 5.2 与 `atmosphere_has_nan` 的关系
|
||||
|
||||
emflux 全 NaN 校验与 `atmosphere_has_nan` **同级、互为冗余**:
|
||||
|
||||
| 校验 | 覆盖的发散形态 | 盲区 |
|
||||
|------|---------------|------|
|
||||
| `atmosphere_has_nan` | `.7` 含字面 NaN/Inf/`***`/E+300+ | 巨大但有限的发散值(如 5e12 POPUL、1e-15 TEMP) |
|
||||
| `check_emflux_bolometric`(NaN 分支) | 大气发散导致的辐射转移全 NaN | 需 TLUSTY 启用且 fort.14 存在 |
|
||||
| `check_fort9`(max_relc) | max_relc 极大(收敛失败) | 无法区分真收敛/假收敛 |
|
||||
|
||||
三者交叉覆盖,任一独立即可拦截大部分发散点。emflux 全 NaN 校验的独特价值在于捕获那 9 个 `atmosphere_has_nan` 盲区点。
|
||||
|
||||
### 5.3 不影响的部分
|
||||
|
||||
- **局部 NaN(如 `.spec` 中 Balmer 线区域的 NaN)**:这是 SYNSPEC 处理 Balmer 线 Stark 加宽时的正常行为(与 `tests/synspec/hhe/results/hhe35nc.spec` 一致),不是收敛失败。emflux 校验不涉及 `.spec`,且阈值设在 50%(容忍局部 NaN,只拦全 NaN)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 结论
|
||||
|
||||
1. **`.emflux` 全 NaN 的物理含义**:TLUSTY 大气模型结构(TEMP/POPUL/ELEC)已数值发散到不可用程度,导致最后一次频率循环里 OPACF1→RTEFR1 把整列 FLUX 全部污染为 NaN,OUTPRI 在耗尽 NITER 上限后写入 fort.14。
|
||||
|
||||
2. **是否等于 TLUSTY 收敛失败?—— 是**。源码机制(CHMX 变 NaN 后 STOP 护栏与收敛判定双双失效,全 NaN 只能在大气发散时产生)+ 数据(62/62 全 NaN 点 converged=false,converged=true 点 0 个有 NaN)双重佐证。**全 NaN emflux 是大气收敛失败的充分(且几乎必要的)标志。**
|
||||
|
||||
3. **"`.7` 大气无 NaN(按检测器)且 converged=true,但 `.emflux` 全 NaN"是否可能?—— 不可能**。162 点中此组合为 0 例。出现"`.7` 检测无 NaN + emflux 全 NaN"的 9 例,都是 `.7` 实际已发散成巨大有限值/近零温度(检测器盲区),converged 全为 false。
|
||||
|
||||
4. **emflux 全 NaN 校验的独立价值**:能捕获 `atmosphere_has_nan` 正则盲区(巨大有限发散值),起到交叉验证补位作用。
|
||||
|
||||
---
|
||||
|
||||
## 附录:关键源码行号
|
||||
|
||||
均在 `/home/fmq/program/tlusty/tl208-s54/tlusty/tlusty208.f`:
|
||||
|
||||
| 功能 | 行号 |
|
||||
|------|------|
|
||||
| fort.14 写出(OUTPRI) | 14188 |
|
||||
| FORMAT 614 | 14406 |
|
||||
| FLUX 来源 COMMON | `MODELQ.FOR:135` |
|
||||
| FLUX 赋值(辐射转移解) | 31800, 32225, 32848 |
|
||||
| NaN 产生(不透明度 HK/TEMP) | 4857, 45375 |
|
||||
| NaN 产生(POPUL 布居) | 31444, 31446, 33455 |
|
||||
| NaN 传播(源函数 EMIS1/AB0) | 32352, 32361 |
|
||||
| 发散 STOP 护栏(对 NaN 失效) | 47587-47591 |
|
||||
| LFIN 收敛判定 | 47596 |
|
||||
| OUTPRI 调用时机(LFIN) | 3887-3889 |
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
> 调研日期:2026-08-04
|
||||
> 范围:TLUSTY 208 + SYNSPEC 54 计算的恒星大气与光谱的**物理正确性**保证方法。
|
||||
> 前置文档:`docs/task_failure_detection_analysis.md`(程序"是否崩溃/收敛"的判定,是本调研的下层)。
|
||||
> 前置文档:`docs/tlusty&synspec收敛性判断.md`(程序"是否崩溃/收敛"的判定,是本调研的下层;该文档 §4 记录了漏洞 1-5 的修复历程)。
|
||||
> 核心命题:**程序正常退出(rc=0)且 max_relc < chmax ≠ 光谱在物理上正确**。本文调研介于两者之间的静默错误来源与验证方法。
|
||||
> 方法:源码级静态分析 + 天体物理方法学综述 + 三方独立复核。
|
||||
>
|
||||
> **修订说明(2026-08-07)**:本调研初版(2026-08-04)基于修复前的代码状态撰写。经核查,漏洞 1(`.spec` 内容校验)、漏洞 2(fort.9 缺失分支虚构 best_max_relc)、漏洞 4(NaN 阈值)已在后续提交中修复。**fort.55 字段错位(§5)也已修复**——`SynspecInput` 重构为 9 个按行子结构体,与 SYNSPEC `READ(55,*)` 语句一一对应,IDSTD 从 50 修正为 0(自动 2ND/3)。本次修订同步了这些已修复项的现状,仅保留仍真实存在的缺口(`.6` 能量守恒解析、诊断产物未解析)。
|
||||
>
|
||||
> **修订说明(2026-08-08)**:经源码级二次核查,同步近期重构导致的引用漂移与数据更新:① 行号校正——`runner.rs:613→:598`、`runner.rs:660→:645`、`nst_writer.rs:16`(ND 默认已迁至 `config.rs:712 default_nd()`)、`synspec54.f:1157→:1230`(IBFAC 经 `READ(84,*)` 读出)、`hhe35nl.6:340→:341`;② 结构体名 `StageSummary` 更正为 `StepSummary`(`models.rs:590`);③ ND/ions 表述更正——二者已非硬编码,分别在 `config.rs:712/833` 作为可配默认值(经 YAML 覆盖,见 `workflows/sdB_cno.yaml:63-90`);④ §5.2 DOPSTD 公式补全为 `DSTD=SQRT(1.4E7*TSTD+VTS)` + `DOPSTD=1.E7/ALAM0*DSTD`;⑤ §1.3 fort.13 改述为"默认结构性排除";⑥ 数据统计刷新——网格点 284→361,`.iden` 格式分类 44/221→92 逐线表/243 频率网格/26 其他;⑦ EQW 断言更正——逐线表 EQW **并非全为 0**,约 43/92(≈47%)含非零值(强线可达上千 mA),但作为系统化 EW 来源仍不可靠,结论(改走 `.spec` 数值积分)不变。
|
||||
|
||||
---
|
||||
|
||||
@@ -13,9 +17,9 @@
|
||||
当前 pipeline 对光谱正确性的保证**基本停留在"数值收敛"层面**,完全没有做物理不变量校验。三方调研发现:
|
||||
|
||||
1. **最高 ROI 缺口**:TLUSTY 已经无条件地把**能量守恒诊断**(`TOTAL SURFACE FLUX`、逐深度 `RAD/TOT`、`CON/TOT`、`(RAD+CON)/TOT`)写进已归档的 `.6` 文件,**却从未被任何 Rust 代码解析**。这是 Hubeny 官方推荐的 1% 判据,零成本可得,是物理正确性的第一道硬门槛。
|
||||
2. **系统性的配方风险**:`fort55_writer.rs` 写的字段 `idrv/ifreq` 与 SYNSPEC 实际读取的 `IDSTD/IPRIN` 错位(已核实),导致所有光谱的标准深度参考点与输出详尽度偏离预期。
|
||||
2. **系统性的配方风险(已修复,见 §5)**:~~`fort55_writer.rs` 写的字段 `idrv/ifreq` 与 SYNSPEC 实际读取的 `IDSTD/IPRIN` 错位~~。已重构为 9 个按行子结构体与 SYNSPEC `READ(55,*)` 语句一一对应,IDSTD 修正为 0(自动 2ND/3),并补全了原漏生成的分子表行(避免 VTB 行被吞导致 vturb 错误覆盖)。
|
||||
3. **网格级错误传播**:通过 `converged && !nan` 门控上传的种子大气(`.7`),会把"伪收敛但物理未平衡"的点扩散给整个物理族——这是网格计算特有的静默放大机制,现有检查无法识别。
|
||||
4. **完整的方法学分层**(§6):硬门槛(线上每点)→ 质量监控(抽样)→ 深度核查(线下基准比对),覆盖辐射平衡、光谱自洽、网格一致性、外部基准四个维度。
|
||||
4. **完整的方法学分层**(§6):硬门槛(线上每点)→ 质量监控(抽样)→ 深度核查(线下基准比对),覆盖辐射平衡、光谱自洽、网格一致性、外部基准四个维度。**外部基准的特殊性**(§3.4):本网格是 CNO 全 NLTE 独立扫描,社区经典网格均不含 CNO NLTE,故外部基准对 CNO 维度失效——需用**网格内部 CNO 物理自洽验证**(路线 A)+ **同代码栈 Pacheco 2021**(路线 C)替代。
|
||||
|
||||
---
|
||||
|
||||
@@ -30,13 +34,13 @@ TLUSTY 在 `LFIN`(最终迭代)时由 `OUTPRI` 子程序无条件写出,
|
||||
```
|
||||
TOTAL SURFACE FLUX 6.77712339D+12 ← 实际积分通量 TOTF
|
||||
ID MASS TAUROSS TEMP NE DENS P_gas LOG(G_rad) RAD/TOT CON/TOT (RAD+CON)/TOT
|
||||
1 ... 8.913E-01 0.000E+00 8.91264E-01
|
||||
1 ... 1.001E+00 0.000E+00 1.00106E+00
|
||||
```
|
||||
|
||||
- `FLTT = SIG4P * TEFF**4`(预期通量 = σTeff⁴/4π,`SIG4P` 见 `BASICS.FOR:51`)
|
||||
- `RAD/TOT` = 辐射通量 / σTeff⁴
|
||||
- `(RAD+CON)/TOT` = 总通量(辐射+对流)/ σTeff⁴ —— **此列应≈1.0**
|
||||
- 实测样本(发散模型 max_relc≈1e21):`(RAD+CON)/TOT ≈ 0.891`,即 **11% 能量守恒违反**;收敛良好模型应≈1.0
|
||||
- 实测样本(收敛良好的 sdB 模型,28 次迭代,`hhe35nl.6:341`):`(RAD+CON)/TOT = 1.00106E+00`(逐深度恒定,能量守恒误差 0.1%)。**未收敛/发散模型**的此列会显著偏离 1(典型表现是表层偏离或逐深度不单调),是物理正确性的第一道硬门槛。
|
||||
|
||||
**关键事实**:`.6` 已在归档白名单(`STAGE_SNAPSHOT_SUFFIXES` 含 "6"),但 `crates/` 下无任何代码解析它。**数据已就位,只差一个 `.6` 解析器写入 conv.json**。
|
||||
|
||||
@@ -54,14 +58,14 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
| 辐射平衡逐深度 | `RECHCK` | 17→`.cont` | 无条件 | **被 SYNSPEC 覆盖丢失** |
|
||||
| 统计平衡残差 | `CHCKSE` | 16→fort.16 | `ICHCKP≠0`(nst 未设) | **未启用** |
|
||||
| 冷却/加热率 | `COOLRT` | 86/87/88 | `ICOOLP≠0`(nst 未设) | **未启用** |
|
||||
| 出射辐射场谱 | `OUTPRI` | 13→fort.13 | 无条件 | **白名单排除**(result_filter.rs:179) |
|
||||
| 出射辐射场谱 | `OUTPRI` | 13→fort.13 | 无条件 | **默认结构性排除**(非 `BARE_KEEPS`、不带 `<name>.` 前缀,`result_filter.rs:67-72`;与 fort.1/2/3 等中间单元同等处理,非专门列入排除名单) |
|
||||
| TLUSTY 出射谱 | `OUTPRI` | 14→`.emflux` | 无条件 | **已快照归档,未解析**(可做 bolometric 积分) |
|
||||
| b 因子(NLTE 偏离) | `OUTPRI` | 12→`.bfac` | 无条件 | **已快照归档,未解析** |
|
||||
| SYNSPEC 谱线统计 | START/OUTPRI | 6→`.log` | 无条件 | **已归档,未解析**(含 `LINES-TOTAL`、波长范围、等效宽度) |
|
||||
|
||||
### 1.4 conv.json 的现状
|
||||
|
||||
`ModelSummary` / `StageSummary` / `ConvCheckResult`(`models.rs`)记录的字段**全部是数值收敛指标**(max_relc, last_iter, worst_depth, synspec_rc 等),**没有任何物理正确性字段**(能量守恒、辐射平衡、统计平衡残差)。
|
||||
`ModelSummary` / `StepSummary` / `ConvCheckResult`(`models.rs:616/590/564`)记录的字段**全部是数值收敛指标**(max_relc, last_iter, worst_depth, synspec_rc 等),**没有任何物理正确性字段**(能量守恒、辐射平衡、统计平衡残差)。
|
||||
|
||||
---
|
||||
|
||||
@@ -75,32 +79,32 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
|---------|---------|---------|------------|--------|
|
||||
| **NITER 截断被当收敛** | 大气未达统计平衡,布居数错误 | `tlusty208.f:14728` `LFIN=ABS(CHMX).LE.CHMAX.OR.ITER.GE.NITER`,OR 短路:CHMX 再大也会因 `ITER≥NITER` 判 LFIN | 部分(`conv_check.rs` 重判 max_relc<chmax,但若截断时"刚好"<chmax 则通过) | 极高 |
|
||||
| **ACCEL2/Ng 加速伪收敛** | 加速外推后下次 CHMX 人为变小,提前判收敛但平衡未达成 | `tlusty208.f:29693-29797`,无加速后发散回退检查 | 未捕获(max_relc 变小与真收敛难区分) | 高 |
|
||||
| **模型截断但物理不充分** | 关键 H/He 跃迁被省略,NLTE 种群错误但程序正常 | `gen_input5.rs` 固定 H=9lev、HeI=14、HeII=14;远低于 MLEVEL=1134 上限 | 未捕获(需物理判断) | 高 |
|
||||
| **ND=50 深度网格偏稀** | 大气结构分辨率不足,谱合成输入质量差 | `nst_writer.rs:6` ND=50;`BASICS.FOR:9` MDEPTH=100 | 未捕获(需对比 ND=99) | 中-高 |
|
||||
| **模型截断但物理不充分** | 关键 H/He 跃迁被省略,NLTE 种群错误但程序正常 | 默认原子模型 `default_ions()`(`config.rs:833`,可经 YAML `tlusty_input.dot5.ions` 覆盖,`workflows/sdB_cno.yaml:63-90` 即显式列出完整 22 行表)H=9lev、HeI=14、HeII=14;远低于 MLEVEL=1134 上限 | 未捕获(需物理判断) | 高 |
|
||||
| **ND=50 深度网格偏稀** | 大气结构分辨率不足,谱合成输入质量差 | `default_nd()`(`config.rs:712`,经 `TlustyInput.nst.depth.nd` 可配)ND=50 默认;`BASICS.FOR:9` MDEPTH=100 | 未捕获(需对比 ND=99) | 中-高 |
|
||||
|
||||
### 2.2 第二层:SYNSPEC 谱合成错误
|
||||
|
||||
| 错误来源 | 物理后果 | 源码证据 | 当前是否捕获 | 严重度 |
|
||||
|---------|---------|---------|------------|--------|
|
||||
| **★fort.55 字段错位** | 标准深度取最深层(IDSTD=50=ND 而非自动 2ND/3≈33)、输出详尽度偏离、Lyman 线翼开关被误设 | `fort55_writer.rs:5` 写 `imode,idrv,ifreq`;SYNSPEC `:253` 读 `IMODE,IDSTD,IPRIN` —— **已核实错位** | 未捕获 | **高(系统性)** |
|
||||
| **★fort.55 字段错位(已修复,见 §5)** | ~~标准深度取最深层(IDSTD=50=ND)~~、输出详尽度偏离;IOPHLI 无论写何值都被 SYNSPEC 无条件置 0(见 §5.2) | 旧 `fort55_writer.rs:5` 写 `imode,idrv,ifreq` 与 SYNSPEC `synspec54.f:253` 读 `IMODE,IDSTD,IPRIN` 错位。**已重构为 9 子结构体按行一一对应**(`config.rs` Fort55Line1-6 + Fort55LineMol/Vtb/Ang) | 已修复(IDSTD 现 0=自动 2ND/3) | ~~高(系统性)~~ → 已消解 |
|
||||
| **NLTE 静默降级为 LTE** | 匹配不上的 NLTE 线被 set to LTE,线强度错误但程序继续 | `synspec54.f:9114-9176`,仅写 fort.11 计数 | 未捕获(DCTS 不解析 fort.11) | 高 |
|
||||
| **IBFAC=0 默认全程按 LTE 读种群** | fort.8 不含/不全含 NLTE 种群时种群保持 LTE 不替换 | `synspec54.f:1157` IBFAC=0 默认;`:11220` NUMP>IP 才替换 | 未捕获 | 高 |
|
||||
| **波长网格步长固定且窗口窄** | 窄线被漏掉,谱"正常但缺线" | `fort55_writer.rs:12` SPACE=0.01;默认窗口仅 1400-1410Å(`runner.rs:522`) | 未捕获 | 中-高 |
|
||||
| **线表缺失(只链 fort.19)** | 缺关键谱线,有计数但无"应有线却缺失"校验 | `runner.rs:540` symlink 单一线表;`:9157` NLIN0 计数 | 未捕获 | 高 |
|
||||
| **IBFAC=0 默认全程按 LTE 读种群** | fort.8 不含/不全含 NLTE 种群时种群保持 LTE 不替换 | `synspec54.f:1230` `READ(84,*)` 读出 IBFAC,默认 0;`:11220` NUMP>IP 才替换 | 未捕获 | 高 |
|
||||
| **波长网格步长固定且窗口窄** | 窄线被漏掉,谱"正常但缺线" | SPACE=0.01(`config.rs:1519` `default_space()`);默认窗口仅 1400-1410Å(`config.rs:1508,1511` `default_alam0/alast`),sdB_cno 工作流显式配 3000-7000Å | 未捕获 | 中-高 |
|
||||
| **线表缺失(只链 fort.19)** | 缺关键谱线,有计数但无"应有线却缺失"校验 | `runner.rs:598` symlink 单一 `runtime.linelist`;`synspec54.f:9157` NLIN0 计数 | 未捕获 | 高 |
|
||||
|
||||
### 2.3 第三层:数据传递与种子继承(网格级错误放大)
|
||||
|
||||
| 错误来源 | 物理后果 | 源码证据 | 当前是否捕获 | 严重度 |
|
||||
|---------|---------|---------|------------|--------|
|
||||
| **★种子继承的错误传播** | 伪收敛点的 `.7` 通过 `converged && !nan` 门控上传为种子,被相邻 SeedStep 点热启动,**错误沿网格拓扑传播**;`directed_cno_distance` 倾向选贫方向,一个坏种子可污染整个 Teff/logg 族 | `executor.rs:193` 种子门控;`seed_finder.rs` 最近邻选种 | **未捕获**(坏种子本身"通过"了所有检查) | **极高** |
|
||||
| **★种子继承的错误传播** | 伪收敛点的 `.7` 通过 `converged && !nan` 门控上传为种子,被相邻 SeedStep 点热启动,**错误沿网格拓扑传播**;`directed_cno_distance` 倾向选贫方向,一个坏种子可污染整个 Teff/logg 族 | `reporter.rs:122` 种子门控 `converged && !atmo_has_nan`;`executor.rs:221` 读取种子 bytes;`seed_finder.rs:26-40` 有向最近邻选种(RICH_PENALTY=4.0 > POOR_PENALTY=1.0,倾向贫方向) | **未捕获**(坏种子本身"通过"了所有检查) | **极高** |
|
||||
|
||||
### 2.4 第四层:编译/数值精度
|
||||
|
||||
| 错误来源 | 物理后果 | 源码证据 | 当前是否捕获 | 严重度 |
|
||||
|---------|---------|---------|------------|--------|
|
||||
| **生产编译无 -fcheck/-ffpe-trap** | 数组越界、IEEE 异常 → rc=0 + 静默写越界内存,产出无 NaN 标记但物理错误的大气 | `deployment.md:86,90` `gfortran -fno-automatic -O3` | 未捕获(详见 task_failure_detection_analysis.md §4 漏洞 3) | 高 |
|
||||
| **生产编译无 -fcheck/-ffpe-trap** | 数组越界、IEEE 异常 → rc=0 + 静默写越界内存,产出无 NaN 标记但物理错误的大气 | `deployment.md:86,90` `gfortran -fno-automatic -O3` | 未捕获(详见 `tlusty&synspec收敛性判断.md` §4 漏洞 3) | 高 |
|
||||
|
||||
> 详见 `task_failure_detection_analysis.md` 的 gfortran 运行时错误退出码实测表。
|
||||
> 详见 `tlusty&synspec收敛性判断.md` 的 gfortran 运行时错误退出码实测表。
|
||||
|
||||
---
|
||||
|
||||
@@ -115,11 +119,11 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
| **通量守恒** | 每深度 πF(rad+conv)=σTeff⁴ | `(RAD+CON)/TOT` 偏离 1 ≤1%(Hubeny 官方判据);任一深度 >5% 直接判坏 | `.6` 末表 | 低 | **线上** |
|
||||
| **max_relc 收敛门槛** | 状态向量相对修正已可忽略 | < chmax(默认 10⁻³,对 sdB/早型星合理;精密线分析可收紧到 10⁻⁴) | `*_chmax*.9` | 低(已实现) | **线上** |
|
||||
| **假收敛排查** | max_relc 单调下降 ≥3 个量级 | 初始/最终 max_relc ≥10³ | fort.9 全序列 | 中 | 抽样 |
|
||||
| **NaN/Inf 否决(收紧)** | 所有深度点温度/密度为有限物理值 | **任意 1 行命中即否决**(当前 10% 阈值过松) | `.7` | 低 | **线上** |
|
||||
| **NaN/Inf 否决(收紧)** | 所有深度点温度/密度为有限物理值 | **任意 1 行命中即否决**(已实现 0 行容忍,`conv_check.rs:264-265`) | `.7` | 低 | **线上** |
|
||||
| **温度结构边界** | T(τ) 落在物理合理范围 | 表层 T ≲ 2-3×Teff;idstd 处 T≈(2/3)Teff;总体 10¹-10⁸ K | `.7` | 中 | 抽样 |
|
||||
| **统计平衡残差(NLTE)** | 跃迁平衡 R_ij·n_i=R_ji·n_j、粒子数守恒 | b 因子无 NaN/Inf;关键能级 b∈10⁻³-10³ | `.bfac` | 中 | 抽样 |
|
||||
|
||||
**对 TLUSTY 的已知坑**:(a) fort.9 缺失分支无条件判收敛 + 虚构 `best_max_relc=0.0`(见 task_failure_detection_analysis.md 漏洞 2);(b) Kantorovich 加速的伪收敛风险;(c) 表层(worst_depth=1)最易出 NaN。
|
||||
**对 TLUSTY 的已知坑**:(a) fort.9 缺失分支虚构 `best_max_relc=0.0`(**已修复**:现按 `niter==0`/`niter>0` 区分——grey start 保留 converged=true 但 best_max_relc=None;异常返回 INFINITY,`runner.rs:448-477`、`conv_check.rs:88-98`);(b) Kantorovich 加速的伪收敛风险;(c) 表层(worst_depth=1)最易出 NaN。
|
||||
|
||||
### 3.2 光谱合成层面(SYNSPEC 产出)
|
||||
|
||||
@@ -147,15 +151,78 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
|
||||
### 3.4 外部基准比对(ground truth)
|
||||
|
||||
| 基准 | 验证什么 | 容差/判据 | 数据来源 | 难度 | 时机 |
|
||||
|------|---------|----------|---------|------|------|
|
||||
| **Lanz & Hubeny 2003/2007 网格** | 与社区标准 TLUSTY/SYNSPEC 网格逐点一致 | 连续谱 <1-2%,线轮廓 <5% | tlusty.oca.eu(OSTAR2002/BSTAR2006,免费) | 中 | 建库时 |
|
||||
| **NLTE-OBGRID** | Hubeny 更新版网格(更完整 H 原子) | 同上 | MAST archive.stsci.edu/hlsp/nlte-obgrid | 中 | 建库时 |
|
||||
| **PHOENIX / CASTELLI / ATLAS9** | 与独立代码(不同物理近似)合理一致 | 光学几个百分点;UV(400nm)可达 10-20% | STScI | 中-高 | 建库时 |
|
||||
| **CALSPEC 实测标准星** | 终极 ground truth | Bohlin+2020:TLUSTY 白矮星网格 1500Å-30μm 一致到 **1%** | MAST(GD153/GD71/G191B2B) | 高 | 深度核查 |
|
||||
| **Gaia FGK 基准星** | 非光谱学基本参数(干涉/角直径)校验 | gold standard | blancocuaresma.com/s/benchmarkstars | 高 | 深度核查 |
|
||||
> **核心矛盾与适用性说明(本网格特有)**:本网格是 6 维 sdB + H/He/**CNO 全 NLTE** 独立扫描(Teff 20-60kK、logg 5.0-6.5、logHe −4..2、logC/logN/logO 各 −4..−1,见 `workflows/sdB_cno.yaml`)。社区经典网格 OSTAR2002/BSTAR2006/NLTE-OBGRID **均不含 CNO 的 NLTE 同时建模**(CNO 按 LTE 或仅 H/He NLTE),PHOENIX/ATLAS9 更无。因此**逐点外部比对 CNO 维度几乎不可能**——任何外部基准最多只能验证 H/He 部分。正确破局:用**代码同源性**验证实现正确性,用**网格内部物理规律**验证 CNO NLTE 物理正确性。
|
||||
|
||||
**实测比对必须考虑的系统误差**:消光(RV=3.1)、视向速度、instrumental broadening(需 ROTINS 卷积)、距离/标度因子。
|
||||
#### 路线 A(最高 ROI):网格内部 CNO 物理自洽验证
|
||||
|
||||
这是本网格**独有的、外部基准反而做不到**的验证。CNO NLTE 有强可检验预测:**线强对丰度的单调响应 + CNO 互不耦合**。已有 361 个网格点数据(`data/result/` 实测,持续增长),零额外计算成本。
|
||||
|
||||
| 校验 | 物理不变量 | 判据 | 所需数据 | 难度 |
|
||||
|------|----------|------|---------|------|
|
||||
| **C 线 EW 随 logC 单调** | 固定 Teff/logg/logHe,C II/C III 线强随 C 丰度单调递增 | 同族 4 个 logC 点 EW 单调,无反转/平台 | 同族 `.spec` + 线表 | 中 |
|
||||
| **CNO 线互不耦合** | 固定 C/N,扫 O 时 C/N 线 EW 几乎不变(NLTE 交叉耦合弱) | C 线 EW 跨 logO 变化 <5% | 同族 `.spec` + 线表 | 中 |
|
||||
| **电离序列协同** | N III 4634-4641 与 He II 4686 协同增减(共依赖 ne/T) | 两线强度比在网格上平滑无突变 | `.spec` 序列 | 中 |
|
||||
|
||||
> **EW 数据来源的现实约束**:理想情况下从 `.iden`(SYNSPEC fort.12 逐线表)直接读 EQW 字段(`synspec54.f:9719` `WRITE(12,603) ...EQW...`)。但经核查,当前归档的 361 个 `.iden` 存在两个问题:(1) **EQW 字段(第 7 列)大面积缺失**——SYNSPEC 仅在线心强度权重 `WW1` 非零时才填 EQW(`:9705`),当前配置下多数逐线表行的 EQW 仍为 `0.0`,仅约 43/92(≈47%)的逐线表含非零 EQW(强线如 He II 3203Å 的 EQW 可达上千 mA),作为系统化 EW 来源并不可靠;(2) **格式不统一**——仅 92 个是逐线表格式,243 个是频率网格格式,另有 26 个其他形态(`data/result/` 实测)。因此 EW 实测需改走**对 `.spec` 归一化谱按线窗口数值积分**(`∫(1−F)dλ`),不能依赖 `.iden`。线波长定位参考线表(fort.19)。此为路线 A 脚本的实现前提。
|
||||
|
||||
#### 路线 B:TLUSTY 官方 hhe 样本的退化比对(端到端回归)
|
||||
|
||||
仓库已有 `hotsd/tlusty (副本)/hhe35nl.6` 等 TLUSTY 官方 H/He NLTE 标准样本(Teff=35000K,**不含 CNO**)。这是**同源码、同原子数据**的最干净回归测试。
|
||||
|
||||
- **操作**:用本 pipeline 跑一个 `logC=logN=logO=−10`(等效无 CNO)的点,配置与 hhe35nl 对齐。
|
||||
- **判据**:`.7` 大气结构(T(τ)、逐深度温度/密度/电子密度)与官方样本逐深度一致到 **<0.1%**——CNO 丰度趋零时物理上退化为纯 H/He,任何偏离都指向 pipeline 的输入封装/参数传递 bug。
|
||||
- **覆盖**:直接验证 §2.1(深度网格、模型截断)、§2.2(fort.55 字段错位的副作用)是否影响大气结构。投入低(跑 1 个点)。
|
||||
|
||||
#### 路线 C:同代码栈社区网格——Pacheco & Díaz 2021
|
||||
|
||||
唯一能做**近似逐点比对**的社区基准,因为与本网格用**完全相同的代码栈**:
|
||||
|
||||
| 维度 | 本网格 | Pacheco 2021(ApJS 256, 41) | 可比性 |
|
||||
|------|--------|------------------------------|--------|
|
||||
| 代码 | TLUSTY **v208** + SYNSPEC **54** | TLUSTY **v205/v208** + SYNSPEC **54** | ✅ 同版本,差异最小 |
|
||||
| Teff | 20k/30k/40k/50k/60k | 20k/25k/30k/35k/45k/65k | ✅ 重叠 20k/30k(余插值) |
|
||||
| logg | 5.0/5.5/6.0/6.5 | 4.5/5.5/6.5 | ✅ 重叠 5.5/6.5 |
|
||||
| logHe | −4..2 | He-poor/He-rich 两支(约 −3..+2) | ⚠️ 仅两端可比 |
|
||||
| CNO | logC/N/O 各 −4..−1 **独立扫描** | **整体 solar 或 [Fe/H]−1.5 两组固定比例** | ❌ **无法逐点比对 CNO 维度** |
|
||||
| 波长 | 3000-7000Å | 1000-10000Å @ 0.01Å | ✅ 本网格是其子集 |
|
||||
| 数据 | — | 公开:SpecModels / SVO / Vizier | ✅ 可下载 |
|
||||
|
||||
**比对策略**(绕开 CNO 丰度差异):
|
||||
1. **连续谱形状**(3200-3700Å、4400-4800Å 无强 CNO 线窗口):取本网格 CNO 最接近太阳(logC≈−3.6/N≈−4.2/O≈−3.3)的点,在相同 Teff/logg/logHe 下比对连续谱,容差 **1-2%**。
|
||||
2. **CNO 线波长定位**:线强因丰度不同而异,但 C II 4267、C III 4640-4650、N III 4630-4640 的**线心波长必须一致**(同套原子数据),漂移 = 线表加载错。
|
||||
3. **非重叠点插值**:Teff=40k/50k 用其 35k/45k/65k 插值曲线,本网格点应落 ±5% 内。
|
||||
|
||||
**数据获取**:`http://specmodels.iag.usp.br/` 或 `http://svo2.cab.inta-csic.es/theory//newov2/`。
|
||||
|
||||
#### 路线 D:实测光谱 ground truth——SDSS / LAMOST sdB 库
|
||||
|
||||
终极基准是真实恒星。大量 sdB 有实测光谱 + 已测定参数:
|
||||
|
||||
- **SDSS hotSubDwarf catalog**(Kepler et al. 2015-2019,~10⁴ 颗):每颗有 Teff/logg/logHe 及部分 C/N 丰度。
|
||||
- 选 5-10 颗参数落入本网格的 sdB,取最近邻网格点,卷积仪器轮廓(SDSS R≈2000)后比对。
|
||||
- **容差**:线轮廓吻合、Balmer/He EW 10-15%(含噪声与投影自转)。
|
||||
|
||||
#### 经典社区网格的适用范围(仅 H/He 部分)
|
||||
|
||||
下列网格**不含 CNO NLTE**,只能用于验证 H/He 连续谱与 Balmer/He 线,**不能验证 CNO 维度**:
|
||||
|
||||
| 基准 | CNO 处理 | 可验证 | 数据来源 |
|
||||
|------|---------|--------|---------|
|
||||
| OSTAR2002 / BSTAR2006(Lanz & Hubeny 2003/2007) | LTE | H/He 连续谱、线轮廓 | tlusty.oca.eu |
|
||||
| NLTE-OBGRID | LTE | H/He 连续谱(更完整 H 原子) | MAST archive.stsci.edu/hlsp/nlte-obgrid |
|
||||
| PHOENIX / ATLAS9 | LTE(独立代码) | 独立代码交叉(光学几个百分点;UV 可达 10-20%) | STScI |
|
||||
| CALSPEC 标准星(Bohlin+2020) | — | TLUSTY WD 网格 1500Å-30μm 1% 一致(**仅适用 WD,非 sdB**) | MAST(GD153/GD71) |
|
||||
|
||||
#### 优先级与实测系统误差
|
||||
|
||||
**务实排序(针对"无 CNO 外部基准"现实)**:
|
||||
|
||||
1. **P0 路线 A**(网格内部 CNO 单调性/解耦)——数据现成,CNO 独有验证,投入最低。
|
||||
2. **P0 路线 B**(hhe 退化比对)——同源码回归测试,最干净。
|
||||
3. **P1 路线 C**(Pacheco 2021)——唯一同版本同物理基准,连续谱/线位。
|
||||
4. **P2 路线 D**(SDSS 实测)——终极 ground truth,投入最高。
|
||||
|
||||
**实测比对(路线 D)必须考虑的系统误差**:消光(RV=3.1)、视向速度、instrumental broadening(需 ROTINS 卷积)、距离/标度因子。
|
||||
|
||||
---
|
||||
|
||||
@@ -178,25 +245,47 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
|
||||
---
|
||||
|
||||
## 5. fort.55 字段错位问题(已核实的系统性 bug)
|
||||
## 5. fort.55 字段错位问题(已修复)
|
||||
|
||||
这是一个影响**所有光谱**的系统性问题,值得单独记录。
|
||||
> **状态:已修复(2026-08-07)**。本节保留问题描述作为历史记录,处置见 §5.3。
|
||||
> 影响所有光谱的系统性 bug:Rust 端 `SynspecInput` 的字段命名与 SYNSPEC `READ(55,*)` 语句的变量名脱节,且大量字段硬编码不可配。修复方式是重构为 9 个按行分组的子结构体,字段名与 SYNSPEC 源码一一对应。
|
||||
|
||||
### 5.1 事实
|
||||
### 5.1 事实(修复前的错位)
|
||||
|
||||
- `fort55_writer.rs:5` 生成的第一行:`imode, idrv, ifreq`(值如 `0, 50, 1`)
|
||||
- SYNSPEC `synspec54.f:253` 实际读取:`IMODE, IDSTD, IPRIN`
|
||||
- 即 **`idrv=50` 被当作 `IDSTD=50`,`ifreq=1` 被当作 `IPRIN=1`**
|
||||
旧 `SynspecInput` 是扁平 7 字段结构体,与 SYNSPEC fort.55 的 9 行 30+ 字段规范脱节:
|
||||
|
||||
### 5.2 影响
|
||||
- **第 1 行错位**:`fort55_writer.rs:5`(旧)写 `imode, idrv, ifreq`;SYNSPEC `synspec54.f:253` 读 `IMODE, IDSTD, IPRIN`。即 **`idrv=50` 被当作 `IDSTD=50`,`ifreq=1` 被当作 `IPRIN=1`**。而真正的 `IFREQ`(频率网格开关)在第 4 行(`synspec54.f:2146`)。
|
||||
- **第 6 行错位**:旧字段 `abs_cutoff=0.01` 被写到 SPACE 位置(相邻频率点间距),而非任何"截止"参数。真正的截止参数 CUTOF0(行6第3列)被硬编码为 `10`、CUTOFS(连续谱间距,行6第4列)硬编码为 `0`,均不可配。
|
||||
- **第 2-5 行全硬编码**:INMOD/INTRPL/ICHANG/ICHEMC(行2)、IOPHLI/nun*(行3)、IFREQ/INLTE/ICONTL/INLIST/IFHE2(行4)、IHYDPR/IHE1PR/IHE2PR(行5)全部不可通过 YAML 配置。
|
||||
- **行 7/8 完全缺失**:VTB(湍流速度,行7)、NMU0/ANG0/IFLUX(角度/比强度,行8)不生成,靠 SYNSPEC 的 `err=/end=` 标号侥幸跳过——脆弱巧合。
|
||||
|
||||
- **IDSTD=50**(=ND,最深层):SYNSPEC 的标准深度(`synspec54.f:2155`)本应自动取 `2*ND/3≈33`,现被强制取最深层。标准深度是谱线多普勒宽度、Stark 加宽的参考层,取错层会让线翼轮廓参考错误深度。
|
||||
- **IPRIN=1**:SYNSPEC 的输出详尽度开关,影响 `.log` 里打印的诊断量多少。
|
||||
- **第 3 行 `IOPHLI=0`**(`fort55_writer.rs:7` 第一个 0):`synspec54.f:255` 读取,IOPHLI 控制 Lyman 线翼处理,0 可能关闭相关处理。
|
||||
### 5.2 影响(修复前的物理后果)
|
||||
|
||||
### 5.3 处置
|
||||
- **IDSTD=50**(=ND,最深层):SYNSPEC 的标准深度(`synspec54.f:2152-2155`,仅在 `IDSTD=0` 时自动取 `2*ND/3≈33`)本应自动取 `2*ND/3≈33`,现被强制取最深层。该层的 T/ne 用于谱线多普勒宽度(`DSTD=SQRT(1.4E7*TSTD+VTS)`,`synspec54.f:2265`;再 `DOPSTD=1.E7/ALAM0*DSTD`,`:8131/8728`)、参考丰度(AVAB)、波长网格步长的归一化基准。实测 8 个不同 Teff/logg/He/CNO 的收敛点,`sqrt(T50/T33)` 稳定在 **2.0-2.13**,即 DOPSTD 系统性偏大 ~100-113%(VTS 与 `1.E7/ALAM0` 系数对 IDSTD 错层带来的相对放大不改变此量级结论)。`AB0=EXP(...)/DOPSTD/AVAB`(`:8913`)直接影响每条谱线的线强归一化,且 `IF(AB0.LT.UN) GO TO 10`(`:8914`)还改变哪些谱线被保留。
|
||||
- **IPRIN=1**:SYNSPEC 的输出详尽度开关,仅影响 `.log` 里打印的诊断量多少,不影响光谱数值。
|
||||
- **IOPHLI 被 SYNSPEC 无条件置 0**:SYNSPEC `synspec54.f:255` 读取 IOPHLI 后,`:269` 紧接 `iophli=0`(注释 `disabling an old option`)**无条件覆盖**。即无论 fort.55 写什么,Lyman 线翼准分子卫星线处理都被**必然关闭**。这是 SYNSPEC 源码的既定行为,非 fort.55 错位所致。
|
||||
|
||||
这是配方层面的 bug,需要单独修复(修正 `fort55_writer.rs` 的字段语义,使 IDSTD=0 自动、IPRIN/IPRIND 按需设置),并重算受影响的光谱。本调研仅记录,不含修复实现。
|
||||
### 5.3 处置(已实施)
|
||||
|
||||
**修复方式**:重构 `SynspecInput`(`config.rs`)为 9 个按行分组的子结构体(`Fort55Line1`..`Fort55Line6` + `Fort55LineMol` + `Fort55LineVtb` + `Fort55LineAng`),字段名严格使用 SYNSPEC 源码变量名,行内字段顺序与 `READ(55,*)` 语句完全一致。每字段带文档注释(物理含义 + `synspec54.f` 行号)。`generate_fort55_content`(`fort55_writer.rs`)改为按子结构体逐行序列化。`SynspecInput::validate()`(`config.rs`)在配置加载时校验不触发不支持的条件读取路径(DM 数组/显式角度/风模式/分子 iunitm)。
|
||||
|
||||
**覆盖范围**:fort.55 全部 9 行 30+ 字段均可通过 YAML `synspec_input` 块按行配置,支持后续不同工作流(sdB/sdO/WD 等)的差异化参数。单元测试覆盖行数、字段对齐、VTB 默认负值、默认值关键字段、全自定义五个维度。
|
||||
|
||||
**行为变化**:
|
||||
|
||||
| 项 | 修复前 | 修复后 | 影响 |
|
||||
|----|--------|--------|------|
|
||||
| **IDSTD** | 50(最深层,错位) | **0(自动取 2ND/3≈33)** | **光谱线强归一化修正**,所有光谱需重算 |
|
||||
| IPRIN | 1(被 ifreq 错位占用) | 1(语义正确,显式可配) | 无行为变化 |
|
||||
| 真正的 IFREQ | 1(硬编码行4) | 1(显式可配) | 无行为变化 |
|
||||
| **行7 分子表** | 漏生成(VTB 行被吞) | 显式生成 `nmlist=0` | 修正行序错位(原靠 EOF 兜底) |
|
||||
| **行8 VTB** | 漏生成(end=30 跳过→vturb 保持 2) | 生成 `-1.0`(`vtb<0` 不覆盖→vturb 保持 2) | 无行为变化(均保持大气 vturb=2) |
|
||||
| 行9 NMU0 | 漏生成(end=100 跳过) | 生成 `0`(不算比强度) | 无行为变化 |
|
||||
| 其余字段 | 硬编码 | 可配,默认值与原硬编码一致 | 无行为变化 |
|
||||
|
||||
> **行7 分子表的关键性**:SYNSPEC 在 IMODE∈[-3,1](默认满足)时会额外读一行分子表(`synspec54.f:2208`)。gfortran list-directed READ 满足变量列表后消费整行剩余——若漏生成此行,VTB 行会被分子表 READ 吞掉,导致 `vtb` 读到 NMU0 行的首值,`if(vtb.ge.0.)` 触发 vturb 被覆盖为 0(审查 Critical-1)。显式生成 `nmlist=0` 行可保证行序对齐。VTB 默认 -1.0 是双重保险:即使行序错位,`vtb<0` 也不会触发覆盖。
|
||||
|
||||
**重算指引**:fort.55 只影响 SYNSPEC 阶段,TLUSTY 大气结构(`.7`)不受影响,种子库可继续用。重算只需把 `synspec_stage.policy` 临时改为 `force_recompute` 重跑 SYNSPEC 阶段,无需重算 TLUSTY。
|
||||
|
||||
---
|
||||
|
||||
@@ -208,9 +297,9 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
|
||||
| # | 校验项 | 现状 | 所需投入 | 依据 |
|
||||
|---|--------|------|---------|------|
|
||||
| 1 | SYNSPEC `.spec`/`.cont` 内容校验(NaN/Inf/负值/全零/行数) | **未做**(漏洞 1) | 低 | task_failure_detection_analysis.md P0 |
|
||||
| 2 | 大气 NaN 否决收紧到 0 行容忍 | 10% 阈值过松(漏洞 4) | 低 | conv_check.rs:212 |
|
||||
| 3 | max_relc < chmax + is_finite + fort.9 真实存在 | 已实现,但 fort.9 缺失分支有虚构值(漏洞 2) | 低 | runner.rs:417 |
|
||||
| 1 | SYNSPEC `.spec`/`.cont` 内容校验(NaN/Inf/负值/全零/行数) | **已修复(漏洞 1)**:`spec_is_valid`(`conv_check.rs:287-335`)在 `runner.rs:645` 调用,校验 NaN/Inf/溢出/行数不足/全零 | — | `tlusty&synspec收敛性判断.md` §4 漏洞 1(P0) |
|
||||
| 2 | 大气 NaN 否决收紧到 0 行容忍 | **已修复(漏洞 4)**:`atmosphere_has_nan` 现 0 行容忍(`conv_check.rs:264-265`),含 NaN/Inf/`***` 溢出/E+300 超高指数四类 | — | `conv_check.rs:264` |
|
||||
| 3 | max_relc < chmax + is_finite + fort.9 真实存在 | **已修复(漏洞 2 进阶)**:fort.9 缺失返回 INFINITY;`niter==0`(grey start)与 `niter>0`(异常)分流,best_max_relc=None 不再虚构 0.0 | — | `runner.rs:448-477`、`conv_check.rs:88-98` |
|
||||
| 4 | **通量守恒 `(RAD+CON)/TOT` 偏离 1 ≤1%**(从 `.6` 提取) | **未做(数据已就位)** | 低(写一个 .6 解析器) | §1.1,Hubeny 官方判据 |
|
||||
|
||||
### 第二层:质量监控(线上抽样 + 线下定期)
|
||||
@@ -226,14 +315,19 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
|
||||
### 第三层:深度核查(线下,建库时 + 每次配方变更时)
|
||||
|
||||
| # | 校验项 | 所需投入 |
|
||||
|---|--------|---------|
|
||||
| 11 | 与 Lanz & Hubeny 2003/2007 / NLTE-OBGRID 重叠点逐波长比对 | 中 |
|
||||
| 12 | 与 PHOENIX/CASTELLI 独立代码比对 | 中-高 |
|
||||
| 13 | 与 CALSPEC/SDSS 实测标准星比对 | 高 |
|
||||
| 14 | Balmer/He 线序列单调性与基准 | 中 |
|
||||
| 15 | 网格二阶差分离群点检测 | 中 |
|
||||
| 16 | 统计平衡残差 / b 因子合理性(`.bfac`) | 中 |
|
||||
> 外部基准的适用性见 §3.4:经典网格(OSTAR/BSTAR/NLTE-OBGRID)**不含 CNO NLTE**,仅能验证 H/He 部分;CNO 维度的外部比对首选 Pacheco 2021(同代码栈),其余靠网格内部物理规律(路线 A)。
|
||||
|
||||
| # | 校验项 | 所需投入 | 对应路线 |
|
||||
|---|--------|---------|---------|
|
||||
| 11 | **网格内部 CNO 单调性/解耦**(固定 Teff/logg/logHe 扫 logC/N/O) | 中 | §3.4 路线 A |
|
||||
| 12 | **hhe 官方样本退化比对**(CNO=−10 退化到纯 H/He,与 hhe35nl 逐深度对比) | 低 | §3.4 路线 B |
|
||||
| 13 | **Pacheco 2021 连续谱/线位比对**(同代码栈,重叠 Teff/logg/logHe 点) | 中 | §3.4 路线 C |
|
||||
| 14 | 与 OSTAR2002/BSTAR2006/NLTE-OBGRID 重叠点 **H/He 部分**比对(不含 CNO 验证) | 中 | §3.4 经典网格表 |
|
||||
| 15 | 与 PHOENIX/ATLAS9 独立代码比对(H/He 连续谱交叉) | 中-高 | §3.4 经典网格表 |
|
||||
| 16 | 与 SDSS/LAMOST 实测 sdB 比对(卷积仪器轮廓后) | 高 | §3.4 路线 D |
|
||||
| 17 | Balmer/He 线序列单调性与基准 | 中 | — |
|
||||
| 18 | 网格二阶差分离群点检测 | 中 | — |
|
||||
| 19 | 统计平衡残差 / b 因子合理性(`.bfac`) | 中 | — |
|
||||
|
||||
### 配方变更触发规则
|
||||
|
||||
@@ -247,11 +341,11 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
|
||||
**投入产出比排序**:
|
||||
|
||||
1. **P0:解析 `.6` 的能量守恒诊断**(§1.1)—— 数据已就位,Hubeny 官方 1% 判据,是物理正确性的第一道硬门槛。同时修 `.spec` 内容校验(§6 第一层)。
|
||||
2. **P1:fort.55 字段错位修复**(§5)—— 影响所有光谱的系统性 bug,需修正后重算。
|
||||
1. **P0:解析 `.6` 的能量守恒诊断**(§1.1)—— 数据已就位,Hubeny 官方 1% 判据,是物理正确性的第一道硬门槛。第一层硬门槛 #1/#2/#3 已在近期提交修复(`.spec` 内容校验、NaN 0 行容忍、fort.9 缺失分流),**此项 #4 是第一层唯一仍真实存在的缺口**。
|
||||
2. **~~P1:fort.55 字段错位修复~~(已完成,见 §5)**——`SynspecInput` 已重构为 9 子结构体与 SYNSPEC 一一对应,IDSTD 修正为 0。**待办:重算受影响光谱**(把 `synspec_stage.policy` 改 `force_recompute` 重跑 SYNSPEC 阶段,TLUSTY 大气不受影响)。
|
||||
3. **P1:网格级种子传播校验**(§2.3)—— 这是网格计算特有的静默放大机制,可通过"同一物理族下游点的 max_relc/通量守恒分布异常"间接检测。
|
||||
4. **P2:质量监控层**(§6 第二层)—— bolometric 积分、Planck 检验、连续谱自洽。
|
||||
5. **P3:建库时基准比对**(§6 第三层)—— 与 Lanz & Hubeny 网格逐点比对是最直接的同代码 ground truth。
|
||||
5. **P2:深度核查层**(§6 第三层,§3.4)—— 分两支:**(a) 网格内部 CNO 单调性 + hhe 退化比对**(路线 A/B,数据现成、CNO 独有,优先做);**(b) 外部基准**——同代码栈的 Pacheco 2021(路线 C)是唯一能比 CNO 连续谱/线位的社区网格;经典网格(OSTAR/BSTAR/NLTE-OBGRID)不含 CNO NLTE,仅能验证 H/He 部分。
|
||||
|
||||
---
|
||||
|
||||
@@ -263,20 +357,29 @@ TLUSTY `RECHCK` 子程序(`tlusty208.f:49854`)在 LFIN 时无条件调用,
|
||||
- TLUSTY User's Guide IV, Hubeny 2021(arXiv:2104.02829)—— CHMAX=10⁻⁴ 高精度例
|
||||
|
||||
**社区基准网格(ground truth)**
|
||||
- OSTAR2002 / BSTAR2006(tlusty.oca.eu)—— Lanz & Hubeny 2003/2007 社区标准网格
|
||||
- NLTE-OBGRID(MAST archive.stsci.edu/hlsp/nlte-obgrid)—— Hubeny 更新版
|
||||
- WD-GRID(MAST archive.stsci.edu/hlsp/wd-grid)—— Bohlin+2020 白矮星 NLTE 网格,1500Å-30μm 对 CALSPEC 1% 一致
|
||||
|
||||
> 适用性:以下经典网格**均不含 CNO NLTE 同时建模**(CNO 按 LTE),只能验证 H/He 部分(§3.4)。本网格(sdB + CNO 全 NLTE)的外部基准首选 Pacheco 2021(同代码栈、含 CNO NLTE)。
|
||||
|
||||
- **Pacheco & Díaz 2021**(ApJS 256, 41, DOI:10.3847/1538-4365/ac2508)—— **TLUSTY v208 + SYNSPEC 54**(与本网格同版本),sdB/sdO NLTE 含 C/N/O NLTE 线覆盖;Teff 10-65kK、logg 4.5-6.5、He-poor/rich 两支;1000-10000Å @ 0.01Å;数据公开(SpecModels `specmodels.iag.usp.br` / SVO `svo2.cab.inta-csic.es/theory//newov2` / Vizier)。**CNO 丰度为整体 solar 或 [Fe/H]−1.5 两组固定比例,非独立扫描**——不能逐点比对 CNO 维度,但可比对连续谱形状与线波长定位。
|
||||
- **Németh et al. 2014**(ASPC 481, 95;arXiv:1308.0252)—— Pacheco 2021 的前身,TLUSTY/SYNSPEC NLTE sdB/sdO 网格(3200-7200Å),覆盖 (He-)sdB/sdO 全参数空间。
|
||||
- **Latour et al. 2011**(ApJ 733, 100)—— sdO NLTE 网格(TLUSTY/SYNSPEC),含 H+He 及可选 C/N/O NLTE 模型。
|
||||
- OSTAR2002 / BSTAR2006(tlusty.oca.eu)—— Lanz & Hubeny 2003/2007 社区标准网格(**H/He NLTE,CNO LTE**)
|
||||
- NLTE-OBGRID(MAST archive.stsci.edu/hlsp/nlte-obgrid)—— Hubeny 更新版(**H/He NLTE,CNO LTE**)
|
||||
- WD-GRID(MAST archive.stsci.edu/hlsp/wd-grid)—— Bohlin+2020 白矮星 NLTE 网格,1500Å-30μm 对 CALSPEC 1% 一致(**仅适用 WD,非 sdB**)
|
||||
|
||||
**交叉代码比对**
|
||||
- Witzke+2021, MPS-ATLAS(A&A)—— 三代码流量差异:~400nm 最大,其他波段几个百分点
|
||||
- CASTELLI/Kurucz ATLAS9(STScI)
|
||||
|
||||
**本仓库内相关文档**
|
||||
- `docs/task_failure_detection_analysis.md` —— rc 不可靠性、fort.9/NaN 阈值漏洞、gfortran 退出码实测(第一层硬门槛的依据)
|
||||
- `docs/tlusty&synspec收敛性判断.md` —— rc 不可靠性、fort.9/NaN 阈值漏洞 1-5 修复历程、gfortran 退出码实测(第一层硬门槛的依据;注意该文档 §4 部分漏洞描述仍是修复前状态,本文档已同步现状)
|
||||
- `docs/tlusty_result_artifacts.md` —— fort 单元语义、`.emflux`/`.bfac` 快照、TLUSTY fort.14 与 SYNSPEC `.spec` 关系
|
||||
- `docs/PIPELINE.md` —— 阶段链、nst 配方(ND=50、默认 CHMAX=0.001)、物理极限区
|
||||
|
||||
**源码证据**
|
||||
- 能量守恒诊断:`tlusty208.f:14179-14191,14271-14273`(OUTPRI);`:49854`(RECHCK);实测 `hotsd/tlusty (副本)/hhe35nl.6:334-339`
|
||||
- fort.55 错位:`fort55_writer.rs:5` vs `synspec54.f:253`(已核实)
|
||||
- 种子传播:`executor.rs:193`、`seed_finder.rs`
|
||||
- 能量守恒诊断:`tlusty208.f:14179-14191,14271-14273`(OUTPRI);`:49854`(RECHCK);实测 `hotsd/tlusty (副本)/hhe35nl.6:341`(`(RAD+CON)/TOT=1.00106E+00`,收敛良好)
|
||||
- fort.55 错位(已修复):旧 `fort55_writer.rs:5` 写 `imode,idrv,ifreq` vs SYNSPEC `synspec54.f:253` 读 `IMODE,IDSTD,IPRIN`;现重构为 `config.rs` 的 `Fort55Line1`-`Fort55Line6` + `Fort55LineMol`/`Fort55LineVtb`/`Fort55LineAng` 9 子结构体按行对应。`synspec54.f:269` `iophli=0` 无条件覆盖(已核实,SYNSPEC 既定行为)。
|
||||
- 种子传播:`reporter.rs:122`(门控)、`executor.rs:221`(读种子 bytes)、`seed_finder.rs:26-40`(有向最近邻)
|
||||
- 漏洞 1 修复:`conv_check.rs:287-335`(spec_is_valid)、`runner.rs:645`(调用点)
|
||||
- 漏洞 4 修复:`conv_check.rs:264-265`(0 行容忍)
|
||||
- 漏洞 2 进阶修复:`runner.rs:448-477`、`conv_check.rs:88-98`
|
||||
|
||||
@@ -207,14 +207,14 @@ UI 布局草图如下:
|
||||
(`tasks` 表的 `*_policy` / `*_strategies` 列,见 `FallbackSnapshot`),与 §4.2「不修改原有
|
||||
policy,保持用户初始配置」语义一致。2026-08-04 起策略不再门控回退(回退只由策略链驱动),
|
||||
但 policy 仍随重试任务落库,供审计与下次启动时的策略重置逻辑消费。数值参数
|
||||
(`synspec: {wstart/wend/...}`、`timeout_sec`)仍取当前 YAML(用户编辑意图优先),形成
|
||||
(`synspec_input`(fort.55 控制卡)、`timeout_sec`)仍取当前 YAML(用户编辑意图优先),形成
|
||||
「旧链 / 旧 policy + 新数值参数」的混合口径——这是有意取舍:策略链与 policy 是任务级
|
||||
回退语义记录必须用派发时的,运行参数允许跟随最新配置。
|
||||
|
||||
### 7.3 完整 YAML 编辑器(数值参数编辑通道)已恢复
|
||||
|
||||
内嵌面板(§6.2)只编辑阶段三维配置(enabled / policy / strategies);`synspec:` 数值参数块
|
||||
(波长范围、展宽、截断等)不在面板编辑范围。**2026-08 补充**:引擎面板新增「YAML 配置」按钮,
|
||||
内嵌面板(§6.2)只编辑阶段三维配置(enabled / policy / strategies);`synspec_input`(fort.55
|
||||
控制卡)等数值参数块不在面板编辑范围。**2026-08 补充**:引擎面板新增「YAML 配置」按钮,
|
||||
恢复原 `yamlEditor.js` 的完整 YAML 查看/编辑弹窗(语法高亮 + 行号 + 脏状态守卫 + 运行中锁定 +
|
||||
服务端校验错误内联横幅),数值参数等完整配置均可在此查看与修改后直接保存(PUT
|
||||
/api/workflows/:name)。该弹窗与内嵌面板的保存互相独立、以各自编辑内容为准——二者维度正交:
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
| G1 | 无逐点 HTTP 端点 | 前端拿不到任何点级数据 | 新增 `/workflows/:name/points` |
|
||||
| G2 | 无按工作流的统计端点(`get_grid_summary_stats` 未暴露;`/api/status` 只有全局) | 卡片/详情无法显示每工作流进度 | 新增 `/workflows/:name/stats`;列表接口内联计数 |
|
||||
| G3 | `queued` 被并进 `pending`(`db.rs:1596,1612`) | 无法区分"排队中"与"未入队" | 新端点拆开统计 |
|
||||
| G4 | 单点耗时/迭代数未进库(`TaskReport.elapsed_sec` 收到即丢;`last_iter` 在 `StageSummary` 前被丢弃) | 列表无耗时列、无 ETA | 迁移补列 + runner 携带 `last_iter`(Phase 3) |
|
||||
| G4 | 单点耗时/迭代数未进库(`TaskReport.elapsed_sec` 收到即丢;`last_iter` 在 `StepSummary` 前被丢弃) | 列表无耗时列、无 ETA | 迁移补列 + runner 携带 `last_iter`(Phase 3) |
|
||||
| G5 | 无进度时间序列 | 画不出"进度-时间"曲线 | 新表 `workflow_progress_snapshots`(Phase 3) |
|
||||
| G6 | 失败点无法重试(start 只重置 `queued`,`db.rs:886-899`) | 失败点永久卡死 | 新增 retry 端点(复用 `reset_specific_grid_points_to_pending` 思路) |
|
||||
| G7 | conv.json 不提供下载/查看 | 逐阶段诊断无法展示 | 点详情端点读盘解析返回 |
|
||||
@@ -330,7 +330,7 @@ LIMIT ? LIMIT OFFSET ?;
|
||||
|
||||
1. `grid_points` 加列:`elapsed_sec REAL`(最近一次成功/失败尝试的墙钟耗时)。
|
||||
在 `record_task_report` 写入——`TaskReport.elapsed_sec` 本来就在手(`models.rs:318`),当前被丢弃。
|
||||
2. `StageSummary` 携带 `last_iter / worst_depth / n_depths`(`models.rs:364-373` 扩字段,
|
||||
2. `StepSummary` 携带 `last_iter / worst_depth / n_depths`(`models.rs:590-612` 扩字段,
|
||||
`runner.rs:302-317` 处 `ConvCheckResult` 已有值,只是没搬过去)→ conv.json 与点详情获得迭代数。
|
||||
3. 新表 `workflow_progress_snapshots(workflow_name TEXT, ts DATETIME, pending INT, queued INT,
|
||||
running INT, completed INT, failed INT)`:由 `main.rs:111-197` 的 30s 后台循环顺手写一行;
|
||||
@@ -394,7 +394,7 @@ LIMIT ? LIMIT OFFSET ?;
|
||||
- 验收:能回答"这个点为什么失败/被谁救回",能看到冷启动成功参数区,能一键重试失败点。
|
||||
|
||||
### Phase 3 — 时序与精算(可选增强)
|
||||
- 后端:迁移(`elapsed_sec` 列、`StageSummary.last_iter`)+ `workflow_progress_snapshots` + ETA 精算。
|
||||
- 后端:迁移(`elapsed_sec` 列、`StepSummary.last_iter`)+ `workflow_progress_snapshots` + ETA 精算。
|
||||
- 前端:概览 ETA 精确化 + 进度-时间 sparkline + 点表耗时列去"~"。
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user