Add diagram workbench UI with Modelica DoF coaching and ISO glyphs.

Ship the Next.js cycle editor with CAD chrome, technical HX symbols, Fixed/Free boundary guidance, and secondary water/air pressure drop support in the solver stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-17 22:46:46 +02:00
parent 62efea0646
commit 3358b74342
275 changed files with 70187 additions and 5230 deletions

View File

@@ -7,11 +7,10 @@
//! - AC #4: Backward compatibility — no freezing by default
use approx::assert_relative_eq;
use entropyk_components::{
Component, ComponentError, JacobianBuilder, ResidualVector, StateSlice,
};
use entropyk_components::{Component, ComponentError, JacobianBuilder, ResidualVector, StateSlice};
use entropyk_solver::{
solver::{JacobianFreezingConfig, NewtonConfig, Solver},
system::DEFAULT_MASS_FLOW_SEED_KG_S,
System,
};
@@ -37,8 +36,10 @@ impl Component for LinearTargetSystem {
state: &StateSlice,
residuals: &mut ResidualVector,
) -> Result<(), ComponentError> {
// CM1.2: per-edge state is (ṁ, P, h); skip ṁ at index 0 so equation i
// targets global state index i+1 (P, h, …).
for (i, &t) in self.targets.iter().enumerate() {
residuals[i] = state[i] - t;
residuals[i] = state[i + 1] - t;
}
Ok(())
}
@@ -49,7 +50,7 @@ impl Component for LinearTargetSystem {
jacobian: &mut JacobianBuilder,
) -> Result<(), ComponentError> {
for i in 0..self.targets.len() {
jacobian.add_entry(i, i, 1.0);
jacobian.add_entry(i, i + 1, 1.0);
}
Ok(())
}
@@ -82,8 +83,9 @@ impl Component for CubicTargetSystem {
state: &StateSlice,
residuals: &mut ResidualVector,
) -> Result<(), ComponentError> {
// CM1.2: skip ṁ at index 0; equation i targets global state index i+1.
for (i, &t) in self.targets.iter().enumerate() {
let d = state[i] - t;
let d = state[i + 1] - t;
residuals[i] = d * d * d;
}
Ok(())
@@ -95,10 +97,10 @@ impl Component for CubicTargetSystem {
jacobian: &mut JacobianBuilder,
) -> Result<(), ComponentError> {
for (i, &t) in self.targets.iter().enumerate() {
let d = state[i] - t;
let d = state[i + 1] - t;
let entry = 3.0 * d * d;
// Guard against zero diagonal (would make Jacobian singular at solution)
jacobian.add_entry(i, i, if entry.abs() < 1e-15 { 1.0 } else { entry });
jacobian.add_entry(i, i + 1, if entry.abs() < 1e-15 { 1.0 } else { entry });
}
Ok(())
}
@@ -366,7 +368,7 @@ fn test_jacobian_freezing_already_converged_at_initial_state() {
let mut sys = build_system_with_linear_targets(targets.clone());
let mut solver = NewtonConfig::default()
.with_initial_state(targets.clone())
.with_initial_state(vec![DEFAULT_MASS_FLOW_SEED_KG_S, targets[0], targets[1]])
.with_jacobian_freezing(JacobianFreezingConfig::default());
let result = solver.solve(&mut sys);