feat(all): 任务引擎双阶段解耦、僵尸涡旋修复、动态 CPU 配额与前端详情页重构

将 TLUSTY/SYNSPEC 拆为各自独立的 enabled/policy/strategies 阶段,
以策略链自动弹栈取代单级 seed_step 布尔回退;定向修复 2026-08-02
僵尸任务涡旋事故;新增节点并发配额热调;前端详情页从 1412 行巨型
视图拆为薄控制器 + detail 子模块,并补齐工具层与单测。

引擎与调度(task_engine_decoupling_design.md)
- models.rs: 新增 StagePolicy / EngineStageConfig / TaskSpec 阶段字段、
  normalize_compat() 校正旧版在途消息策略链、failed_stage 归因
- scheduler.rs: resolve_dispatchable_chain 派发门控、
  trigger_strategy_fallback 按 failed_stage 精确弹栈;启动期
  force_recompute/skip_converged(默认)/skip_failed 三策略
- db.rs: tasks 表 +7 列持久化阶段配置;终态守卫
  (mark_grid_point_running 仅 pending/queued→running;
  record_task_report 拒绝迟到失败翻黑 converged);策略弹栈快照

僵尸涡旋修复(runbook-20260802-zombie-vortex-fix.md)
- 全链路跨库活性交叉校验:派发/claim/孤儿回收/回退统一查 MQ 队列活性,
  活则放行、死则清僵尸,结构性消除"每点重复派发"
- stop/重启卫生:清队列同步 delete_tasks_by_ids,杜绝遗留 pending 行
- report_task: 幂等吸收 + 409 区分迟到冗余结果,仅 state_changed 时回退
- MQ: NULL workflow_name 回填 __legacy__、requeue 后迟到上报被 403 竞态修复

动态 CPU 配额(dynamic_cpu_slots_design.md)
- admin.rs: POST /admin/nodes/:id/quota(Option<Option<i32>> 区分
  缺字段/显式 null);nodes 表 +admin_max_slots
- worker.rs: effective_max_slots = min(admin, physical),心跳下发原子生效

科学产物保全(tlusty_result_artifacts.md)
- runner.rs: SYNSPEC 启动前快照 fort.12/fort.14 → .bfac/.emflux 防覆盖
- 半失败点(大气收敛+光谱失败)改判 Failed 并写入 note;仅 SYNSPEC
  场景不再恒判失败;撤销归档 LRU 200 上限改为永久保留
- executor.rs: 透传 synspec_params 数值参数(此前固定 None)

前端(dashboard/)
- workflowDetail.js 1412→328 行,拆出 views/detail/{ctx,overview,
  pointsTable,parSets,pointPanel}.js,AbortController 治理监听/请求生命周期
- 删除 wfActions.js,新增 wfEnginePanel.js(双阶段三维配置编辑面板)
- 新增 utils/{errors,format,icons,polling,yamlStage}.js 纯函数模块
- 路由级动态 import 代码分割;节点配额三点菜单 + Modal 管理
- 首次引入 node:test 单测(format/polling/yamlStage/psCache,644 行)
- 系统性补齐 a11y:skip-link、ARIA、Tab 键盘漫游、toast 关闭、退出动画

文档与工具
- 新增 6 篇设计/调研:引擎解耦、动态配额、涡旋 runbook、
  光谱正确性分析、收敛判断、产物归档
- PIPELINE/design/api/database 等协同重写为分布式 C/S 架构口径
- scripts/fetch_results.sh 跨节点产物备份;import_results 按 cno 升序导入
- workflows/sdB_cno.yaml: 新增 tlusty/synspec_stage 配置块,修正 wstart 笔误
This commit is contained in:
fmq
2026-08-04 23:40:52 +08:00
parent c8fd24b120
commit cd370d88e7
74 changed files with 13386 additions and 3033 deletions
+195
View File
@@ -0,0 +1,195 @@
# TLUSTY 结果产物分析:Node 归档白名单与 fort 单元快照
> 本文档记录对 TLUSTY/SYNSPEC 源码的单元号(fort.N)语义分析,以及据此确定的
> Node 端结果归档(`result_dir`)白名单与 TLUSTY b 因子/出射谱快照的设计依据。
> 源码路径:TLUSTY 位于 `/home/fmq/program/SpectraRust/tlusty/``tlusty208.f`),
> SYNSPEC 位于 `/home/fmq/program/SpectraRust/synspec/``synspec54.f`)。
> 归档逻辑实现于 `crates/node/src/executor.rs`、`crates/common/src/runner.rs`、
> `crates/common/src/result_filter.rs`。
## 1. 背景与问题
Node 每个任务算完后,把沙盒 `work_dir/task_<id>/<name>/` 内的产物拷贝到持久归档
`result_dir/<name>/`。归档采用**白名单**策略(`is_result_worthy`),只保留有语义价值
的产物,丢弃 Tlusty/Synspec 运行时的中间工作单元(旧版 catch-all 会把它们一并搬进
归档,每个模型浪费约 2MB / 4.8MB)。
需要回答的核心问题:
1. 当前归档到底保留了哪些文件?
2. TLUSTY 的 `fort.12`b 因子)、`fort.14`(出射谱)、`fort.69`(计时日志)是否有
保存价值?—— 需以 TLUSTY 源码为准。
3. TLUSTY 出射谱(fort.14)与 SYNSPEC 输出的 `.spec` 有什么区别,是否冗余?
## 2. Node 结果归档机制(现状)
- **归档路径**`result_dir/<name>/``result_dir` 由 node 配置指定,默认为 `data/result/`)。
- **触发时机**:上报成功后(上报失败也归档,用于排错),随后清理沙盒。
- **原子写入**:先拷为 `.result.tmp.<uuid>` 再 rename,防止半截文件。
- **无 LRU 上限**:所有已算网格点产物永久保留(2026-08-02 撤销此前 200 目录上限)。
- **白名单判定**`common::result_filter::is_result_worthy(fname, model_name)`
### 2.1 保留的文件(白名单)
`model_name` 为网格点权威名,如 `t20000_g5.0_he-2_c-4_n-4_o-4`
**裸名保留**(不以 model_name 为前缀,有独立语义):
| 文件 | 语义 |
|------|------|
| `conv.json` | 收敛摘要 |
| `fort.8` | SYNSPEC 输入大气 |
| `fort.55` | SYNSPEC 控制卡 |
**科学核心** `<name>.<后缀>`
| 后缀 | 来源 | 语义 |
|------|------|------|
| `.7` | TLUSTY/SYNSPEC | 最终大气 |
| `.spec` | SYNSPEC fort.7 | 合成光谱(高分辨率) |
| `.cont` | SYNSPEC fort.17 | 连续谱 |
| `.iden` | SYNSPEC fort.12 | 谱线证认表 |
| `.log` | SYNSPEC | SYNSPEC 日志 |
| `.bfac` | TLUSTY fort.12 快照 | 最终模型 b 因子(非 LTE 偏离因子) |
| `.emflux` | TLUSTY fort.14 快照 | 最终模型出射谱(波长 Å + Fλ) |
**阶段快照** `<name>.<label>.<后缀>`label ∈ `lte`/`nc`/`nl`/`seed_nc`):
- `.5`(输入卡)、`.6`(输出日志)、`.err`(错误日志)、`.nst`(控制卡)、`.7`(该阶段大气)
**收敛诊断** `<name>.<label>_chmax*.9`**唯一保留的 .9 形态**(裸 `<name>.<label>.9`
`_chmax*.9` 内容重复,runner 源头已停止写出)。
### 2.2 丢弃的内容
- 所有 Tlusty 中间工作单元:`fort.1/2/3/10/11/13/14/18/22/42/44/50/57/69/82/95`
- `fort.84`NATOMS 崩溃缓存)、`.tmp` 残留
- 符号链接(`data``fort.19` 等共享 runtime 资源)、子目录
- 不以 `<name>.` 开头且不在裸名列表的文件、未知后缀/未知阶段标签
- `fort.12` / `fort.17`:被 runner 消费(见 §4.1)后删除
## 3. TLUSTY fort 单元语义(源码分析)
TLUSTY 每次运行到达最终迭代(`LFIN=.TRUE.`)时,经 `OUTPRI`tlusty208.f:14156,调用点
:3889)一次性写出多个单元。各单元语义与行号:
| 单元 | 内容 | 源码位置 | 判定 |
|------|------|----------|------|
| `fort.12` | **b 因子(非 LTE 偏离因子表)**:头 2I5 + 每深度 TEMP/ELEC/DENS/BFAC,格式 701/702/703 | :14344 | 科学价值高,需快照 |
| `fort.13` | 出射辐射场(FREQ, FLUX=H(ν), FH=Eddington 因子),格式 602 | :14185 | 与 fort.14 同源,未快照 |
| `fort.14` | **出射谱(波长 Å + Fλ)**,格式 614 `F15.3,1pe15.3` | :14188 | 科学价值高,需快照 |
| `fort.17` | 每深度布居数 POPUL(格式 503 | :14124 | SYNSPEC 覆盖后转存 `.cont` |
| `fort.20` | b 因子 BFAC(另一分支) | :14119 | 未保留 |
| `fort.22` | 绝对 b 因子 BFAB | :14346 | 未保留 |
| `fort.69` | **迭代计时日志**`TIMING` 子程序写 IP/MOD/TIME/DT/ROUTFORMAL SOLUTION / LINEARIZATION),格式 600 | :29937 | 纯性能诊断,丢弃 |
> 注意:`OUTPRI` 仅在最终迭代调用,故每个单元内保存的是**最后一次迭代**的最终值。
> 收敛链上最后一次 TLUSTY 运行即最终模型。
## 4. SYNSPEC 对 unit 12/14 的覆盖与丢失风险
### 4.1 单元号复用导致覆盖
TLUSTY 与 SYNSPEC **同目录串行执行**,且两个程序复用相同的单元号:
| 单元 | TLUSTY 写 | SYNSPEC 写 |
|------|-----------|-----------|
| 12 | b 因子 | 谱线证认表(synspec54.f:9719,格式 603 |
| 14 | 出射谱 | 谱线数据(synspec54.f:9852,格式 601 |
由于 TLUSTY 先跑、SYNSPEC 后跑,SYNSPEC 会把 `fort.12`/`fort.14` 覆盖。现有 runner
流程:
1. TLUSTY 写 b 因子 + 出射谱 → **被 SYNSPEC 覆盖**
2. SYNSPEC 写 `fort.7`(谱)、`fort.17`(连续谱)、`fort.12`(谱线证认表)
3. runner 将 `fort.7``.spec``fort.17``.cont``fort.12``.iden`,随后删除 `fort.12`/`fort.17`
**结论**:归档里的 `.iden` 保存的是 SYNSPEC 的谱线证认表(覆盖后的 fort.12),
**TLUSTY 的 b 因子与出射谱在覆盖前无任何快照,会被静默丢失** —— 这是真实的数据损失点。
### 4.2 快照决策
- `fort.69`:纯计时日志,**不保存**(无科学内容)。
- `fort.12`b 因子)与 `fort.14`(出射谱):**需快照**。
## 5. 实现:快照 b 因子 + 出射谱
### 5.1 快照命名与白名单
| 源文件 | 快照名 | 白名单后缀 |
|--------|--------|-----------|
| `fort.12` | `<name>.bfac` | `bfac`(加入 `SCIENCE_SUFFIXES` |
| `fort.14` | `<name>.emflux` | `emflux`(加入 `SCIENCE_SUFFIXES` |
### 5.2 插入点
`runner::run_model_with_timeout`:**收敛链循环结束后、SYNSPEC 启动前**调用
`snapshot_tlusty_outputs(&model_dir, name)`。此时 fort.12/14 是最后一次 TLUSTY 运行
(最终模型)的产物,且尚未被 SYNSPEC 覆盖。仅 SYNSPEC 场景(tlusty 未运行)下
fort.12/14 不存在,函数按文件存在性静默跳过。
### 5.3 变更文件
- `crates/common/src/runner.rs`:新增 `snapshot_tlusty_outputs()` + 调用 + 2 个测试
- `crates/common/src/result_filter.rs``SCIENCE_SUFFIXES``bfac`/`emflux` + 文档 + 测试
- `crates/node/src/executor.rs`:归档文档、`test_save_result_artifacts` 同步 + 修正
`fort.13` 注释(原误标为"NLTE 跃迁频率网格",实为出射辐射场 FREQ/FLUX/FH
## 6. TLUSTY 出射谱(fort.14vs SYNSPEC `.spec`
两者**转换公式完全相同、物理量一致**,差异在网格密度、线不透明度处理、覆盖范围与用途。
### 6.1 相同点
- 公式一字不差:`Fλ = H(ν) × ν² / c`
- TLUSTY:14188):`FLAM=FLUX(IJP)*FREQ(IJP)*FREQ(IJP)/2.997925E18`
- SYNSPEC:3365):`FLAM=FLUX(IJ)*FREQ(IJ)*FREQ(IJ)*CAS``CAS=1./2.997925D18`
- `FLUX` 都是表面出射 Eddington 通量 H(ν)erg/cm²/s/sterad/HzOUTPRI 注释明确
"precisely the second moment H(freq) at the surface")。
- 同一波长点的连续谱水平应当一致。
### 6.2 差异点
| 维度 | TLUSTY fort.14 | SYNSPEC `.spec` |
|------|---------------|-----------------|
| 网格密度 | 模型自身频率网格(NFREQ 个点,数百~数千,非均匀) | 以每条谱线为中心的细网格(`FR0=FREQ0(IL0)`SPACE/CUTOFF/DOPSTD 采样线轮廓,密度高几个量级) |
| 线不透明度 | 模型求解时的粗/近似线处理(不透明度采样/ODF/超能级),只体现最强线 | 完整线表(fort.19)逐细频点重解辐射转移,得到分辨开的真实线轮廓 |
| 波长覆盖 | 整个模型宽带频率范围 | 仅 fort.55 指定窗口(dcts 默认 14001410 Å) |
| FLUX 来源 | 模型迭代过程的自洽辐射场解(顺带用于通量守恒检查 TOTF) | 用已收敛大气在细网格重新解 RT |
| 用途/下游 | 模型副带品,快速看谱/校验 | 科学产品,注释明说 "serves as input to ROTINS"(转动/仪器展宽卷积),用于与观测对比 |
### 6.3 实际含义
两者是**同一物理量(表面 Fλ)在不同分辨率和线处理下的版本**,非两种东西。叠画时
连续谱重合,`.spec` 显示被分辨开的精细谱线轮廓,fort.14 中弱线基本不可见。归档同时
保留二者不构成冗余:`.emflux` 代表模型自身辐射场(宽带、粗,可独立校验),`.spec`
代表最终高分辨率合成谱(窄窗、细,与观测对比的产品)。
## 7. 完整归档文件清单(快照实施后)
```
result_dir/<name>/
├── conv.json
├── fort.8 # SYNSPEC 输入大气(裸名保留)
├── fort.55 # SYNSPEC 控制卡(裸名保留)
├── <name>.7 # 最终大气
├── <name>.spec # SYNSPEC 合成光谱
├── <name>.cont # SYNSPEC 连续谱
├── <name>.iden # SYNSPEC 谱线证认表
├── <name>.log # SYNSPEC 日志
├── <name>.bfac # TLUSTY 最终 b 因子(快照 fort.12
├── <name>.emflux # TLUSTY 最终出射谱(快照 fort.14
├── <name>.<label>.5 # 阶段输入卡快照
├── <name>.<label>.6 # 阶段输出日志快照
├── <name>.<label>.err # 阶段错误日志快照
├── <name>.<label>.nst # 阶段控制卡快照
├── <name>.<label>.7 # 阶段大气快照
└── <name>.<label>_chmax*.9 # 阶段收敛诊断(唯一保留的 .9)
```
## 8. 后续可选优化
- `fort.13`(出射辐射场,含 Eddington 因子 FH)未快照;如需保留,在
`snapshot_tlusty_outputs` 中加 `("fort.13", "emrad")` 并在白名单加 `emrad` 后缀即可。
- `fort.20`b 因子)、`fort.22`(绝对 b 因子)同属 b 因子家族;如需保留可在 TLUSTY
阶段结束后一并快照,但 `fort.12` 已覆盖相对 b 因子,绝对 b 因子可由 POPUL/POPLTE 重算。