Add model embeddings for Z-factor DoF, separate from SaturatedController.
Some checks failed
CI / check (push) Has been cancelled

Fixed/Free Probe calibration now emits embeddings[] (unknown + equation) instead of controls[], keeping SaturatedController for physical regulation only.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-19 22:42:31 +02:00
parent 3808e0f11b
commit 5425685a48
22 changed files with 1189 additions and 485 deletions

View File

@@ -1,12 +1,6 @@
//! Calibration redesign (HARD RULE — Probe for ALL measurements) — integration
//! test: a `Probe` node measuring SDT on the condenser refrigerant inlet edge
//! drives the plain inverse embedding for `z_ua`. The Probe is the measurement
//! source (`control.measure.component` names the Probe); the freed z-factor
//! lives on the BPHX condenser (`control.actuator.component`). The two are
//! linked 1:1 (+1 residual on Probe SDT target, +1 unknown z_ua).
//!
//! This is the Probe-based variant of `calibration_sdt.rs`. It proves the
//! end-to-end path the UI emits after the calibration redesign.
//! test: a `Probe` measuring SDT + free `cond/z_ua` via model `embeddings[]`
//! (Modelica unknown + equation — not SaturatedController / controls[]).
use entropyk_cli::run::{run_simulation, SimulationResult, SimulationStatus};
use tempfile::tempdir;
@@ -139,22 +133,21 @@ fn probe_based_sdt_calibration_converges_and_solves_z_ua() {
"tolerance": 1e-06,
"timeout_ms": 60000
},
"controls": [
"embeddings": [
{
"type": "SaturatedController",
"id": "probe_sdt_calib",
"measure": {
"component": "cond_sdt_probe",
"output": "saturationTemperature"
},
"actuator": {
"id": "emb_cond_z_ua",
"unknown": {
"component": "cond",
"factor": "z_ua",
"initial": 0.3,
"start": 0.3,
"min": 0.05,
"max": 2.0
},
"target": 315.0
"equation": {
"component": "cond_sdt_probe",
"output": "saturationTemperature",
"value": 315.0
}
}
]
}
@@ -185,6 +178,22 @@ fn probe_based_sdt_calibration_converges_and_solves_z_ua() {
}
}
}
let mut sdt_c = None;
if let Some(state) = result.state.as_ref() {
for e in state.iter() {
if e.target.as_deref() == Some("cond") || e.source.as_deref() == Some("cond_sdt_probe")
{
if let Some(ts) = e.saturation_temperature_c {
sdt_c = Some(ts);
}
}
}
}
let sdt_k = sdt_c.expect("must read SDT near condenser inlet probe") + 273.15;
assert!(
(sdt_k - 315.0).abs() < 0.5,
"SDT must hit target 315.0 K within 0.5 K, got {sdt_k}"
);
assert!(
solved.value > 0.05 && solved.value < 2.0,
"z_ua must solve within bounds, got {}",