Files
DCTS/docs/tlusty_result_artifacts.md
fmq cd370d88e7 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 笔误
2026-08-04 23:40:52 +08:00

10 KiB
Raw Permalink Blame History

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.rscrates/common/src/runner.rscrates/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.12b 因子)、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.84NATOMS 崩溃缓存)、.tmp 残留
  • 符号链接(datafort.19 等共享 runtime 资源)、子目录
  • 不以 <name>. 开头且不在裸名列表的文件、未知后缀/未知阶段标签
  • fort.12 / fort.17:被 runner 消费(见 §4.1)后删除

3. TLUSTY fort 单元语义(源码分析)

TLUSTY 每次运行到达最终迭代(LFIN=.TRUE.)时,经 OUTPRItlusty208.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.specfort.17.contfort.12.iden,随后删除 fort.12/fort.17

结论:归档里的 .iden 保存的是 SYNSPEC 的谱线证认表(覆盖后的 fort.12), TLUSTY 的 b 因子与出射谱在覆盖前无任何快照,会被静默丢失 —— 这是真实的数据损失点。

4.2 快照决策

  • fort.69:纯计时日志,不保存(无科学内容)。
  • fort.12b 因子)与 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.rsSCIENCE_SUFFIXESbfac/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)*CASCAS=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.20b 因子)、fort.22(绝对 b 因子)同属 b 因子家族;如需保留可在 TLUSTY 阶段结束后一并快照,但 fort.12 已覆盖相对 b 因子,绝对 b 因子可由 POPUL/POPLTE 重算。