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:
@@ -261,7 +261,7 @@ impl GridScheduler {
|
||||
|
||||
/// 读取指定工作流的 TLUSTY 物理迭代步进链(`config::GridConfig.tlusty_chain`),
|
||||
/// 序列化为 JSON Value 供 TaskSpec 携带。节点 executor 反序列化为 `Vec<ChainStep>`
|
||||
/// 后透传给 runner 的 custom_chain 参数,使用户在 YAML 配置的 niter/chmax/metals
|
||||
/// 后透传给 runner 的 custom_chain 参数,使用户在 YAML 配置的 niter/chmax
|
||||
/// 等阶段参数真正生效(此前 executor 硬编码用 default 链,忽略用户配置)。
|
||||
/// 工作流未配置 tlusty_chain(空数组)→ None(executor 用 default 链兜底)。
|
||||
async fn get_workflow_tlusty_chain(&self, workflow_name: &str) -> Option<serde_json::Value> {
|
||||
@@ -273,6 +273,19 @@ impl GridScheduler {
|
||||
serde_json::to_value(&cfg.tlusty_chain).ok()
|
||||
}
|
||||
|
||||
/// 读取指定工作流的种子热启动链(`config::GridConfig.seed_chain`),序列化为 JSON
|
||||
/// Value 供 TaskSpec 携带。仅 seed_step 策略下由 executor 读取。
|
||||
/// 与 `get_workflow_tlusty_chain` 对称。工作流未配置 seed_chain(空数组)→ None
|
||||
///(executor 用 `default_seed_chain()` 兜底)。
|
||||
async fn get_workflow_seed_chain(&self, workflow_name: &str) -> Option<serde_json::Value> {
|
||||
let wf = self.db.get_workflow(workflow_name).await.ok()??;
|
||||
let cfg = parse_grid_config_or_warn(&wf.config_yaml, workflow_name, "seed_chain")?;
|
||||
if cfg.seed_chain.is_empty() {
|
||||
return None;
|
||||
}
|
||||
serde_json::to_value(&cfg.seed_chain).ok()
|
||||
}
|
||||
|
||||
/// 读取指定工作流的 TLUSTY 输入文件全局参数(`config::GridConfig.tlusty_input`),
|
||||
/// 序列化为 JSON Value 供 TaskSpec 携带。包含 NFREAD 频率网格、ions 能级表、
|
||||
/// nst extra_keys 等不随阶段变化的参数。节点 executor 反序列化为 `TlustyInput`
|
||||
@@ -286,6 +299,38 @@ impl GridScheduler {
|
||||
.and_then(|t| serde_json::to_value(t).ok())
|
||||
}
|
||||
|
||||
/// 一次性读取工作流的全部物理校验阈值(能量守恒 / 温度结构 / emflux)。
|
||||
/// 统一读取避免对同一 YAML 多次解析。返回 8 元组,对应 TaskSpec 的 8 个标量字段:
|
||||
/// (energy_tolerance, temp_max_factor, temp_floor, temp_ceiling, emflux_tolerance,
|
||||
/// convergence_min_ratio, bfac_max, bfac_min)。
|
||||
async fn get_workflow_validation_thresholds(
|
||||
&self,
|
||||
workflow_name: &str,
|
||||
) -> Option<(
|
||||
Option<f64>,
|
||||
Option<f64>,
|
||||
Option<f64>,
|
||||
Option<f64>,
|
||||
Option<f64>,
|
||||
Option<f64>,
|
||||
Option<f64>,
|
||||
Option<f64>,
|
||||
)> {
|
||||
let wf = self.db.get_workflow(workflow_name).await.ok()??;
|
||||
let cfg = parse_grid_config_or_warn(&wf.config_yaml, workflow_name, "validation_thresholds")?;
|
||||
Some((
|
||||
cfg.energy_tolerance,
|
||||
cfg.temp_max_factor,
|
||||
cfg.temp_floor,
|
||||
cfg.temp_ceiling,
|
||||
cfg.emflux_tolerance,
|
||||
cfg.convergence_min_ratio,
|
||||
cfg.bfac_max,
|
||||
cfg.bfac_min,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
/// 从策略链解析出「首个可派发」的顺位(见 docs/task_engine_decoupling_design.md §4.2)。
|
||||
///
|
||||
/// 判定:
|
||||
@@ -404,7 +449,21 @@ impl GridScheduler {
|
||||
let (tlusty_cfg, synspec_cfg) = self.get_workflow_stage_configs(workflow_name).await;
|
||||
let synspec_params = self.get_workflow_synspec_params(workflow_name).await;
|
||||
let tlusty_chain = self.get_workflow_tlusty_chain(workflow_name).await;
|
||||
let seed_chain = self.get_workflow_seed_chain(workflow_name).await;
|
||||
let tlusty_input = self.get_workflow_tlusty_input(workflow_name).await;
|
||||
let (
|
||||
energy_tolerance,
|
||||
temp_max_factor,
|
||||
temp_floor,
|
||||
temp_ceiling,
|
||||
emflux_tolerance,
|
||||
convergence_min_ratio,
|
||||
bfac_max,
|
||||
bfac_min,
|
||||
) = self
|
||||
.get_workflow_validation_thresholds(workflow_name)
|
||||
.await
|
||||
.unwrap_or((None, None, None, None, None, None, None, None));
|
||||
|
||||
// 双阶段全关是退化配置(save_workflow 已拦截,此处兜底防御):无可执行阶段,
|
||||
// 整工作流跳过派发(修复审查 #5)。
|
||||
@@ -538,6 +597,7 @@ impl GridScheduler {
|
||||
synspec_config: synspec_cfg.clone(),
|
||||
synspec_params: synspec_params.clone(),
|
||||
tlusty_chain_params: tlusty_chain.clone(),
|
||||
seed_chain_params: seed_chain.clone(),
|
||||
tlusty_input_params: tlusty_input.clone(),
|
||||
// 显式绑定大气来源(设计 §5.2,修复审查 #3):仅 SYNSPEC-only(TLUSTY 关闭)
|
||||
// 场景需要外部大气——节点凭 atmosphere_ref(或 point_name 兜底)从本地归档
|
||||
@@ -547,6 +607,14 @@ impl GridScheduler {
|
||||
} else {
|
||||
Some(name.clone())
|
||||
},
|
||||
energy_tolerance,
|
||||
temp_max_factor,
|
||||
temp_floor,
|
||||
temp_ceiling,
|
||||
emflux_tolerance,
|
||||
convergence_min_ratio,
|
||||
bfac_max,
|
||||
bfac_min,
|
||||
};
|
||||
|
||||
self.db.insert_task(&task_spec).await?;
|
||||
@@ -834,7 +902,21 @@ impl GridScheduler {
|
||||
let timeout_sec = self.get_workflow_timeout_sec(workflow_name).await;
|
||||
let synspec_params = self.get_workflow_synspec_params(workflow_name).await;
|
||||
let tlusty_chain = self.get_workflow_tlusty_chain(workflow_name).await;
|
||||
let seed_chain = self.get_workflow_seed_chain(workflow_name).await;
|
||||
let tlusty_input = self.get_workflow_tlusty_input(workflow_name).await;
|
||||
let (
|
||||
energy_tolerance,
|
||||
temp_max_factor,
|
||||
temp_floor,
|
||||
temp_ceiling,
|
||||
emflux_tolerance,
|
||||
convergence_min_ratio,
|
||||
bfac_max,
|
||||
bfac_min,
|
||||
) = self
|
||||
.get_workflow_validation_thresholds(workflow_name)
|
||||
.await
|
||||
.unwrap_or((None, None, None, None, None, None, None, None));
|
||||
|
||||
// SYNSPEC 链回退:重试光谱合成。无邻居种子门控(大气来自目标点自身既有产物,
|
||||
// 见 docs/task_engine_decoupling_design.md §5)——旧实现把 synspec 失败误归因到
|
||||
@@ -874,8 +956,18 @@ impl GridScheduler {
|
||||
synspec_params,
|
||||
// TLUSTY 已关闭(半失败重试只重跑光谱),不执行 chain/input → None。
|
||||
tlusty_chain_params: None,
|
||||
seed_chain_params: None,
|
||||
tlusty_input_params: None,
|
||||
atmosphere_ref: Some(name.to_string()),
|
||||
// 不重算大气 → 不做物理正确性校验。
|
||||
energy_tolerance: None,
|
||||
temp_max_factor: None,
|
||||
temp_floor: None,
|
||||
temp_ceiling: None,
|
||||
emflux_tolerance: None,
|
||||
convergence_min_ratio: None,
|
||||
bfac_max: None,
|
||||
bfac_min: None,
|
||||
};
|
||||
self.db.insert_task(&task_spec).await?;
|
||||
self.db
|
||||
@@ -959,8 +1051,17 @@ impl GridScheduler {
|
||||
synspec_config: synspec_cfg.clone(),
|
||||
synspec_params,
|
||||
tlusty_chain_params: tlusty_chain.clone(),
|
||||
seed_chain_params: seed_chain.clone(),
|
||||
tlusty_input_params: tlusty_input.clone(),
|
||||
atmosphere_ref: None,
|
||||
energy_tolerance,
|
||||
temp_max_factor,
|
||||
temp_floor,
|
||||
temp_ceiling,
|
||||
emflux_tolerance,
|
||||
convergence_min_ratio,
|
||||
bfac_max,
|
||||
bfac_min,
|
||||
};
|
||||
|
||||
self.db.insert_task(&task_spec).await?;
|
||||
@@ -1018,6 +1119,7 @@ mod tests {
|
||||
logo: vec![(-2.0).into()],
|
||||
},
|
||||
tlusty_chain: vec![],
|
||||
seed_chain: vec![],
|
||||
tlusty_input: None,
|
||||
synspec_input: None,
|
||||
nworkers: 4,
|
||||
@@ -1030,6 +1132,14 @@ mod tests {
|
||||
linelist: None,
|
||||
tlusty_stage: None,
|
||||
synspec_stage: None,
|
||||
energy_tolerance: None,
|
||||
temp_max_factor: None,
|
||||
temp_floor: None,
|
||||
temp_ceiling: None,
|
||||
emflux_tolerance: None,
|
||||
convergence_min_ratio: None,
|
||||
bfac_max: None,
|
||||
bfac_min: None,
|
||||
};
|
||||
|
||||
scheduler.initialize_grid(&cfg, "test_wf").await.unwrap();
|
||||
@@ -1075,6 +1185,7 @@ mod tests {
|
||||
logo: vec![(-2.0).into()],
|
||||
},
|
||||
tlusty_chain: vec![],
|
||||
seed_chain: vec![],
|
||||
tlusty_input: None,
|
||||
synspec_input: None,
|
||||
nworkers: 4,
|
||||
@@ -1087,6 +1198,14 @@ mod tests {
|
||||
linelist: None,
|
||||
tlusty_stage: None,
|
||||
synspec_stage: None,
|
||||
energy_tolerance: None,
|
||||
temp_max_factor: None,
|
||||
temp_floor: None,
|
||||
temp_ceiling: None,
|
||||
emflux_tolerance: None,
|
||||
convergence_min_ratio: None,
|
||||
bfac_max: None,
|
||||
bfac_min: None,
|
||||
};
|
||||
|
||||
// wf_a 初始化并推入队列
|
||||
@@ -1195,6 +1314,7 @@ mod tests {
|
||||
logo: vec![(-2.0).into()],
|
||||
},
|
||||
tlusty_chain: vec![],
|
||||
seed_chain: vec![],
|
||||
tlusty_input: None,
|
||||
synspec_input: None,
|
||||
nworkers: 4,
|
||||
@@ -1207,6 +1327,14 @@ mod tests {
|
||||
linelist: None,
|
||||
tlusty_stage: None,
|
||||
synspec_stage: None,
|
||||
energy_tolerance: None,
|
||||
temp_max_factor: None,
|
||||
temp_floor: None,
|
||||
temp_ceiling: None,
|
||||
emflux_tolerance: None,
|
||||
convergence_min_ratio: None,
|
||||
bfac_max: None,
|
||||
bfac_min: None,
|
||||
};
|
||||
cfg
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user