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:
+57
-14
@@ -166,7 +166,7 @@ pub async fn execute_task(
|
||||
// 执行链来源(优先级):
|
||||
// 1. TaskSpec.tlusty_chain_params(用户在 YAML `tlusty_chain:` 配置的多阶段 ChainStep
|
||||
// 数组,由 scheduler 序列化注入)——非空时优先使用,使用户能细粒度控制 niter/chmax/
|
||||
// metals 等阶段参数。
|
||||
// ilvlin 等阶段参数。
|
||||
// 2. default_chain_for_strategy(current_strategy) 兜底——按策略名(cold_run/seed_step)
|
||||
// 选预设默认链(runner.rs 的 default_cold_chain / default_seed_chain)。
|
||||
// 历史:Phase 6 起仅用 default 链(用户 config.chain 被忽略,是死字段);本次接通后
|
||||
@@ -175,6 +175,7 @@ pub async fn execute_task(
|
||||
let chain = resolve_execution_chain(
|
||||
current_strategy,
|
||||
&task.tlusty_chain_params,
|
||||
&task.seed_chain_params,
|
||||
&task.task_id.to_string(),
|
||||
);
|
||||
// TLUSTY 输入文件全局参数(NFREAD/ions 表/nst extra_keys 等)。
|
||||
@@ -208,6 +209,14 @@ pub async fn execute_task(
|
||||
task.timeout_sec,
|
||||
shutdown,
|
||||
tlusty_input.as_ref(),
|
||||
task.energy_tolerance,
|
||||
task.temp_max_factor,
|
||||
task.temp_floor,
|
||||
task.temp_ceiling,
|
||||
task.emflux_tolerance,
|
||||
task.convergence_min_ratio,
|
||||
task.bfac_max,
|
||||
task.bfac_min,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -450,21 +459,35 @@ pub async fn cleanup_seed_cache(seed_dir: &Path) {
|
||||
|
||||
/// 解析任务要执行的大气链(冷启动链 / 种子热启动链)。
|
||||
///
|
||||
/// 优先级(修复回归):
|
||||
/// - `current_strategy == "seed_step"` → 强制 `default_seed_chain()`(seed_nc→nl)。
|
||||
/// 自定义 `tlusty_chain` 是冷启动链:首步 lte 的 `ltgray=T` 会删除 fort.8、丢弃已下载的
|
||||
/// 热启动种子(runner.rs 阶段 fort.8 准备逻辑)。scheduler 在派发与回退两条路径都注入
|
||||
/// 同一个 `tlusty_chain`,若 seed_step 也沿用自定义链,会把种子回退退化成本地冷启动,
|
||||
/// 丢失热启动语义。故种子链固定走内置默认,仅在 cold_run 等冷策略下信任用户自定义链。
|
||||
/// 优先级:
|
||||
/// - `current_strategy == "seed_step"` → 优先 TaskSpec.seed_chain_params(用户 YAML
|
||||
/// `seed_chain:` 配置),非空即用;为空/反序列化失败 → `default_seed_chain()` 兜底。
|
||||
/// 不复用 `tlusty_chain`:冷启动链首步 lte 的 `ltgray=T` 会删除 fort.8、丢弃已下载的
|
||||
/// 热启动种子(runner.rs 阶段 fort.8 准备逻辑)。seed_chain 专用于种子热启动,
|
||||
/// 首步 seed_nc 的 `ltgray=F` 保留 fort.8 种子。
|
||||
/// - 其余策略 → 优先 TaskSpec.tlusty_chain_params(用户 YAML `tlusty_chain:` 配置),非空即用;
|
||||
/// 为空/反序列化失败 → `default_chain_for_strategy(current_strategy)` 兜底。
|
||||
fn resolve_execution_chain(
|
||||
current_strategy: &str,
|
||||
tlusty_chain_params: &Option<serde_json::Value>,
|
||||
seed_chain_params: &Option<serde_json::Value>,
|
||||
task_id: &str,
|
||||
) -> Vec<ChainStep> {
|
||||
if current_strategy == "seed_step" {
|
||||
common::runner::default_seed_chain()
|
||||
seed_chain_params
|
||||
.as_ref()
|
||||
.and_then(|v| match serde_json::from_value::<Vec<ChainStep>>(v.clone()) {
|
||||
Ok(c) => Some(c),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"任务 {} 的 seed_chain_params 反序列化失败,回退 default_seed_chain: {}",
|
||||
task_id, e
|
||||
);
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|c| !c.is_empty())
|
||||
.unwrap_or_else(common::runner::default_seed_chain)
|
||||
} else {
|
||||
tlusty_chain_params
|
||||
.as_ref()
|
||||
@@ -512,6 +535,10 @@ mod tests {
|
||||
synspec_error: None,
|
||||
synspec_sec: Some(10.0),
|
||||
elapsed_sec: 120.0,
|
||||
energy_check: None,
|
||||
temp_check: None,
|
||||
emflux_check: None,
|
||||
bfac_check: None,
|
||||
note: None,
|
||||
}
|
||||
}
|
||||
@@ -520,22 +547,38 @@ mod tests {
|
||||
chain.iter().map(|s| s.label.clone()).collect()
|
||||
}
|
||||
|
||||
/// P1 回归防护:seed_step 即使注入自定义冷启动链,也必须强制走种子热启动默认链
|
||||
/// (seed_nc→nl),否则首步 lte(ltgray=T) 会删 fort.8、丢弃已下载种子,回退退化成本地冷启动。
|
||||
/// P1 回归防护:seed_step 忽略 tlusty_chain_params(冷启动链),只认 seed_chain_params。
|
||||
/// 否则首步 lte(ltgray=T) 会删 fort.8、丢弃已下载种子,回退退化成本地冷启动。
|
||||
#[test]
|
||||
fn seed_step_ignores_custom_cold_chain() {
|
||||
let custom = serde_json::json!([
|
||||
let custom_cold = serde_json::json!([
|
||||
{"label": "lte", "lte": "T", "ltgray": "T", "ilvlin": 0, "niter": 0},
|
||||
{"label": "nc", "lte": "F", "ltgray": "F", "ilvlin": 0, "niter": 10},
|
||||
{"label": "nl", "lte": "F", "ltgray": "F", "ilvlin": 100, "niter": 100},
|
||||
]);
|
||||
|
||||
let chain = resolve_execution_chain("seed_step", &Some(custom), "t1");
|
||||
// seed_chain_params=None → 走 default_seed_chain;tlusty_chain_params 被忽略。
|
||||
let chain = resolve_execution_chain("seed_step", &Some(custom_cold), &None, "t1");
|
||||
assert_eq!(labels(&chain), vec!["seed_nc", "nl"]);
|
||||
// 首步必须是非灰 LTE(ltgray=F),否则会删 fort.8 丢弃种子。
|
||||
assert_eq!(chain[0].ltgray, "F");
|
||||
}
|
||||
|
||||
/// seed_step 使用 seed_chain_params(用户 YAML seed_chain 配置)时生效。
|
||||
#[test]
|
||||
fn seed_step_uses_custom_seed_chain_when_provided() {
|
||||
let custom_seed = serde_json::json!([
|
||||
{"label": "seed_nc", "lte": "F", "ltgray": "F", "ilvlin": 0, "niter": 20, "orelax": 0.3},
|
||||
{"label": "nl", "lte": "F", "ltgray": "F", "ilvlin": 100, "niter": 100, "orelax": 0.5},
|
||||
]);
|
||||
|
||||
let chain = resolve_execution_chain("seed_step", &None, &Some(custom_seed), "t1b");
|
||||
assert_eq!(labels(&chain), vec!["seed_nc", "nl"]);
|
||||
// 用户配置的 orelax 生效。
|
||||
assert_eq!(chain[0].orelax, Some(0.3));
|
||||
assert_eq!(chain[1].orelax, Some(0.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cold_run_uses_custom_chain_when_provided() {
|
||||
let custom = serde_json::json!([
|
||||
@@ -544,13 +587,13 @@ mod tests {
|
||||
{"label": "nl", "lte": "F", "ltgray": "F", "ilvlin": 100, "niter": 100},
|
||||
]);
|
||||
|
||||
let chain = resolve_execution_chain("cold_run", &Some(custom), "t2");
|
||||
let chain = resolve_execution_chain("cold_run", &Some(custom), &None, "t2");
|
||||
assert_eq!(labels(&chain), vec!["lte", "nc", "nl"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cold_run_falls_back_to_default_when_no_custom_chain() {
|
||||
let chain = resolve_execution_chain("cold_run", &None, "t3");
|
||||
let chain = resolve_execution_chain("cold_run", &None, &None, "t3");
|
||||
assert_eq!(labels(&chain), vec!["lte", "nc", "nl"]);
|
||||
}
|
||||
|
||||
|
||||
@@ -235,6 +235,10 @@ mod tests {
|
||||
synspec_error: synspec_error.map(|s| s.to_string()),
|
||||
synspec_sec: None,
|
||||
elapsed_sec: 10.0,
|
||||
energy_check: None,
|
||||
temp_check: None,
|
||||
emflux_check: None,
|
||||
bfac_check: None,
|
||||
note: None,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user