feat(all): NaN 伪收敛否决与 nl_tight 能量回退、emflux 检验 4π 修正、nst 行宽与种子边界修复、gfATO 谱线表接通与网格加密 9216 点、阶段分项统计与跳板机部署
物理修复(400 失败点归因,见 docs/failed400_nan_pseudo_convergence_2026_08_17.md): - runner: 阶段 converged 后复查 fort.7,含 NaN/Inf 即否决(fort.9 全零伪收敛, 曾致 261 点误跳过 nl_direct 回退);否决阶段不产出种子,阻断污染传播 - runner: nl_tight 回退——仅能量边际失败时以 CHMAX 收紧 10× 从自身模型续迭代, 残差降幅 ~10×;execute_tlusty_stage 抽取供主链与回退共用 - conv_check: fort.14 为 Eddington 通量 Hλ,积分需乘 4π 再比 σTeff⁴ (旧版 ratio 稳定 0.0796=1/4π,全点系统性假阳性)+ 回归测试 - nst_writer: 单行超 80 字符被 TLUSTY 静默截断,IFALI/JALI/TRAD 等从未生效; 按 75 字符自动换行 - seed_finder: Teff 容忍度改含边界 <=,相邻 5000K 档恢复互为种子 + 回归测试 谱线表与网格: - 默认线表 gfVIS99 → gfATO(全波段 18-23000Å),TaskSpec.linelist 支持工作流 级覆盖,节点按需下载(进程互斥锁防并发重复下载 238MB) - sdB_cno Teff 加密至 5000K 步长,432 → 9216 点;tlusty/synspec 静态二进制更新 统计与部署: - grid 汇总改按 tlusty_status/synspec_status 分项计数,新增 tlusty_failed/ synspec_failed/synspec_pending,前端详情页双视图适配 - deploy/fetch_results 支持跳板机 ProxyJump 与 SSH 主连接复用,fetch 新增 --force; - Docker 构建支持 CARGO_MIRROR/USE_MIRRORS 国内镜像参数;移除 tools/ 拷贝 - 新增 tlusty-synspec-test skill 与 6 篇根因分析/验证文档
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
check_temperature.py — 对 fort.7/{stage}.7 做温度结构物理判定。
|
||||
|
||||
精确复刻 DCTS 权威判据 check_temperature_structure(conv_check.rs:463)。
|
||||
退出码:0=物理(三项全过)/1=不物理 /2=文件缺失或无法解析(跳过,不判失败)。
|
||||
|
||||
判据(valid = 三项全过):
|
||||
1. 表层 T < max_factor × Teff (默认 max_factor=3.0)
|
||||
2. 每个深度 T ∈ [temp_floor, temp_ceiling] (默认 [10, 1e8] K)
|
||||
3. 无 NaN/Inf
|
||||
|
||||
用法:
|
||||
python3 check_temperature.py outputs/nl.7 --teff 60000
|
||||
python3 check_temperature.py outputs/nc.7 --teff 55000 --max-factor 3.0
|
||||
"""
|
||||
import argparse
|
||||
import math
|
||||
import re
|
||||
import sys
|
||||
|
||||
# 复刻 conv_check.rs:44 parse_fortran_float
|
||||
# Fortran 指数≥100 时挤掉 E,如 -1.35E+118 → -1.35+118
|
||||
_NO_E_EXP_RE = re.compile(r"^([+-]?[\d.]+)([+-]\d+)$")
|
||||
|
||||
|
||||
def parse_fortran_float(s):
|
||||
"""返回 float(可能为 inf/-inf);无法解析返回 None。"""
|
||||
s = s.strip()
|
||||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
pass
|
||||
m = _NO_E_EXP_RE.match(s)
|
||||
if m:
|
||||
try:
|
||||
return float(f"{m.group(1)}E{m.group(2)}") # 含 inf/-inf(溢出)
|
||||
except ValueError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def check_temperature_structure(path, teff, max_factor=3.0, temp_floor=10.0, temp_ceiling=1e8):
|
||||
"""复刻 conv_check.rs:463。返回 (temps, violations) 或 (None, None)=无法解析。"""
|
||||
try:
|
||||
with open(path, "r", errors="replace") as f:
|
||||
lines = f.readlines()
|
||||
except OSError:
|
||||
return None, None
|
||||
|
||||
if not lines:
|
||||
return None, None
|
||||
|
||||
# 1. 第1行 → nd, numpar
|
||||
tokens = lines[0].split()
|
||||
if len(tokens) < 2:
|
||||
return None, None
|
||||
try:
|
||||
nd = int(float(tokens[0]))
|
||||
numpar = int(float(tokens[1]))
|
||||
except ValueError:
|
||||
return None, None
|
||||
if nd <= 0 or numpar <= 0:
|
||||
return None, None
|
||||
|
||||
# 2. 行数计算
|
||||
dm_lines = (nd + 5) // 6 # FORMAT 502: 6/行
|
||||
block_lines = (numpar + 4) // 5 # FORMAT 503: 5/行
|
||||
|
||||
# 3. 跳过 DM 列阵
|
||||
idx = 1 # 第1行已读
|
||||
for _ in range(dm_lines):
|
||||
if idx >= len(lines):
|
||||
return None, None # 截断 → 跳过
|
||||
idx += 1
|
||||
|
||||
# 4. nd 个深度块,每块第一行第一token = TEMP
|
||||
temps = []
|
||||
for _ in range(nd):
|
||||
if idx >= len(lines):
|
||||
break # 截断:已解析的深度保留
|
||||
first_line_tokens = lines[idx].split()
|
||||
if not first_line_tokens:
|
||||
idx += block_lines
|
||||
continue
|
||||
t = parse_fortran_float(first_line_tokens[0])
|
||||
temps.append(t if t is not None else float("nan"))
|
||||
idx += block_lines # 跳到下一块(含本块剩余 block_lines-1 行)
|
||||
|
||||
if not temps:
|
||||
return None, None
|
||||
|
||||
# 5. 三项判据
|
||||
violations = []
|
||||
surface_ratio = temps[0] / teff if teff > 0 else float("inf")
|
||||
if surface_ratio > max_factor:
|
||||
violations.append(
|
||||
f"表层 T={temps[0]:.4g} > {max_factor}×Teff({teff}),ratio={surface_ratio:.4g}"
|
||||
)
|
||||
for i, t in enumerate(temps):
|
||||
if not math.isfinite(t):
|
||||
violations.append(f"深度 {i+1} T={t}(非有限,NaN/Inf)")
|
||||
break
|
||||
if t < temp_floor or t > temp_ceiling:
|
||||
violations.append(
|
||||
f"深度 {i+1} T={t:.4g} 越界 [{temp_floor}, {temp_ceiling}]"
|
||||
)
|
||||
break
|
||||
return temps, violations
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="fort.7 温度结构物理判定(DCTS 权威判据)")
|
||||
ap.add_argument("fort7", help="fort.7 或 {stage}.7 路径")
|
||||
ap.add_argument("--teff", type=float, required=True, help="Teff (K)")
|
||||
ap.add_argument("--max-factor", type=float, default=3.0, help="表层/Teff 上限(默认 3.0)")
|
||||
ap.add_argument("--floor", type=float, default=10.0, dest="temp_floor", help="温度下限 K(默认 10)")
|
||||
ap.add_argument("--ceiling", type=float, default=1e8, dest="temp_ceiling", help="温度上限 K(默认 1e8)")
|
||||
args = ap.parse_args()
|
||||
|
||||
import os
|
||||
if not os.path.exists(args.fort7):
|
||||
print(f"⚠ 文件不存在: {args.fort7}(跳过,不判失败)")
|
||||
return 2
|
||||
|
||||
temps, violations = check_temperature_structure(
|
||||
args.fort7, args.teff, args.max_factor, args.temp_floor, args.temp_ceiling
|
||||
)
|
||||
if temps is None:
|
||||
print(f"⚠ 无法解析 {args.fort7}(缺失/截断/格式错)—— 跳过,不判失败")
|
||||
return 2
|
||||
|
||||
surface_ratio = temps[0] / args.teff if args.teff > 0 else float("inf")
|
||||
print(f"=== 温度结构判定: {args.fort7} (Teff={args.teff}) ===")
|
||||
print(f" 深度数: {len(temps)}")
|
||||
print(f" 表层 T: {temps[0]:.4g} K ({surface_ratio:.4g}×Teff,Eddington 参考 0.84)")
|
||||
print(f" 底层 T: {temps[-1]:.4g} K")
|
||||
nan_count = sum(1 for t in temps if not math.isfinite(t))
|
||||
print(f" NaN/Inf 深度: {nan_count}")
|
||||
|
||||
if not violations:
|
||||
print(f"✅ 物理(三项全过:表层<{args.max_factor}×Teff、各深度∈[{args.temp_floor},{args.temp_ceiling}]、无 NaN)")
|
||||
return 0
|
||||
print("❌ 不物理 —— 违规:")
|
||||
for v in violations:
|
||||
print(f" - {v}")
|
||||
print("\n参考 references/physics-checks.md 的判据说明与修正历史。")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user