Files
Entropyk/docs/audits/jacobian-killing-pattern-audit.md
sepehr 88620790d6 Complete Story 0.2 phantom-gradient regularization standard.
Add analytic derivatives and C1 tests for smooth_max/min/clamp, document epsilon/width guidance, and declare flow_regularization as the HX reference pattern without changing component physics.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-18 16:33:48 +02:00

8.5 KiB
Raw Blame History

Jacobian-Killing Pattern Audit

Date: 2026-07-18
Story: 0-1-jacobian-killing-pattern-audit-fd-test-harness (Epic 0)
Scope: Inventory of clamp / max / min (and related hard gates) on Newton residual/Jacobian paths in entropyk-components. Physics fixes are owned by Stories 0.20.4.

Failure-mode legend

Mode Meaning
zero-gradient Analytic ∂r/∂x (or helper derivative) hard-zeros in a region where physics should still inform Newton
discontinuity Hard clamp/step on a Newton unknown → C⁰ or worse residual; FD and analytic disagree near the edge
benign Floor/guard for division-by-zero, config validation, or non-Newton parameter — not a solver killer
already-regularized C¹ / phantom pattern already present (may still need unification)
Severity Meaning
P0 Confirmed FR19 killer; blocks convergence at domain edges
P1 Likely Newton-relevant clamp on live unknowns / quality
P2 Secondary / correlation-local; watch when that path is active
info Documented debt or missing API; not a clamp site

FR19 known-bad sites (confirmed 2026-07-18)

Site Failure mode Severity Fix owner
crates/components/src/valve_flow.rs:105-107valve_mass_flow_dp_up returns Ok(0.0) when dp <= 0 || m <= 0 zero-gradient P0 Story 0.3
crates/components/src/isenthalpic_expansion_valve.rs:233state[i].clamp(0.0, 1.0) on orifice opening (residual path) discontinuity P0 Story 0.3
crates/components/src/isenthalpic_expansion_valve.rs:449_state[i].clamp(0.0, 1.0) on orifice opening (Jacobian path) discontinuity P0 Story 0.3
crates/components/src/heat_exchanger/two_phase_dp.rs:107quality.clamp(0.0, 1.0) in homogeneous_density discontinuity P0 Story 0.4
crates/components/src/heat_exchanger/two_phase_dp.rs:150quality.clamp(0.0, 1.0) in friedel_multiplier discontinuity P0 Story 0.4
crates/components/src/heat_exchanger/condenser.rs:492state[idx].clamp(0.0, 1.5) fan actuator φ discontinuity P0 Story 0.4
crates/components/src/heat_exchanger/condenser.rs:752state[idx].clamp(0.0, 0.98) flooded level λ discontinuity P0 Story 0.4

Evidence notes

  • Valve ΔP≤0: valve_mass_flow already forces dp = ΔP.max(0), so ṁ=0 and valve_mass_flow_dp_up returns a hard zero — Newton gets no direction to restore positive ΔP.
  • EXV opening clamps: same hard clamp appears in both residual helper and Jacobian branch; Story 0.2 smooth_clamp is the intended replacement (entropyk_core::smoothing already exists).
  • EXV phantom ΔP: DP_FLOOR (~L396445) is a partial already-regularized mitigation for ΔP≤0 on the Jacobian only; residual still early-outs. Opening clamps remain P0.
  • Missing API: Epic Story 0.3 text mentions valve_mass_flow_dp_downonly valve_mass_flow_dp_up exists today (lib.rs re-export). Catalogue gap severity info; inventing dp_down is out of scope for Story 0.1.

Broader catalogue (residual / Jacobian call graph)

Classified from a ripgrep sweep of .clamp( / .max( / .min( under crates/components/src. Not every hit is listed — config setters, test helpers, and pure numerical floors are collapsed into summary rows.

P0 / P1 — Newton-relevant

Location Pattern Mode Sev Notes / owner
valve_flow.rs:71,75,78,99 dp.max(0), opening.clamp, m.max(0) zero-gradient / discontinuity P0 Feeds EXV/orifice; Story 0.3
valve_flow.rs:105-107 hard Ok(0.0) on ∂ṁ/∂P_up zero-gradient P0 FR19
isenthalpic_expansion_valve.rs:233,449 opening clamp on state discontinuity P0 FR19 → 0.3
isenthalpic_expansion_valve.rs:396-445 DP_FLOOR phantom already-regularized info Keep; unify with toolkit in 0.2/0.3
two_phase_dp.rs:107,150 quality clamp discontinuity P0 FR19 → 0.4
two_phase_dp.rs:118-131 hard x<=0 / x>=1 branches in zivi_void_fraction discontinuity P1 Story 0.4 (MSH/dome)
two_phase_dp.rs:255-294 Hermite blend near x→1 (msh_gradient) already-regularized info Partial MSH fix; 0.4 completes
condenser.rs:492,752 fan / flood actuator clamps discontinuity P0 FR19 → 0.4
evaporator.rs analogous secondary / property floors benign / P2 P2 Re-check when evaporator actuators mirror condenser
heat_exchanger/flow_regularization.rs smooth mass / activity already-regularized info Reference for Story 0.2 unification
heat_exchanger/sat_domain.rs:81 p_pa.clamp(p_min, p_max) discontinuity P1 Intermediate iterate domain; Story 0.4 / sat path
screw_economizer_compressor.rs:486 eco fraction .clamp(0.0, 0.4) discontinuity P1 If eco fraction is a Newton unknown
screw_economizer_compressor.rs:291,343 slide valve clamp discontinuity P1 Config vs state — verify call site
bphx_correlation.rs:588+ quality / p_reduced clamps discontinuity P2 Correlation internals on quality

Benign / info (not Epic-0 P0)

Pattern class Examples Mode Sev
Divisor floors .max(1e-9), .max(1e-12) on ρ, μ, D benign info
Builder / config clamps with_orifice_fixed opening clamp, UA scale .max(0) benign info
Registry JSON parsing registry.rs quality/RH clamps benign info
Air humidity RH clamp air_boundary.rs benign info
FD step sizing in tests (x.abs()*1e-6).max(1e-3) benign info
Item Issue Sev
heat_exchanger/exchanger.rs:854-897 4-port Jacobian is 100% central FD in production info (NFR9)
screw_economizer_compressor.rs Incomplete analytic Jacobian rows info
python_components.rs Empty jacobian_entries stubs info
Category-B solver tests (jacobian_freezing, inverse calibration) Deferred per plans/audit-2026-07-remediation-plan.md info

Harness usage (entropyk_components::jacobian_fd)

Central FD vs analytic at one state point:

use entropyk_components::jacobian_fd::{
    check_jacobian_health, JacobianFdConfig,
};

let report = check_jacobian_health(&component, &state, JacobianFdConfig::default())?;
assert!(
    report.has_zero_gradient_killers(),
    "expected clamp/zero-gradient signature, got clean report"
);
// After Stories 0.20.4 fixes, Story 0.5 flips the gate to:
// assert!(report.is_clean());

Defaults (aligned with condenser FD tests)

Constant Default
rel_epsilon 1e-6
h_floor 1e-3
rel_tol 1e-4
analytic_atol 1e-12
fd_informative 1e-8

Do not call JacobianMatrix::numerical (solver) from this harness — that API is forward difference for Newton fallback.

Integration smoke tests: crates/components/tests/jacobian_health_sweep.rs.


Story 0.5 CI gate (future)

  1. Reuse check_jacobian_health over per-component physical-domain grids (ΔP≤0, quality 0/1, opening 0/1, dome edges, actuator clamps).
  2. Fail CI on new zero_gradient_killers or mismatches outside an allow-list that shrinks to empty after 0.20.4.
  3. Commit a per-component / per-region health report artifact (extend this file or generate JSON beside it).

Deferred work (explicit)

Work Owner
Standardize / document smooth_* ε guidance; declare flow_regularization as HX reference Story 0.2landed (docs/components/phantom-gradient-regularization.md; derivatives in crates/core/src/smoothing.rs)
Valve ΔP≤0 phantom + EXV opening smooth_clamp Story 0.3
Quality / condenserevaporator state clamps + MSH singularity Story 0.4
Full-envelope CI Jacobian health gate Story 0.5
Replace production FD Jacobian in generic 4-port HX Separate NFR9 / remediation track

Severity ranking summary

  1. P0 (fix in 0.30.4): valve ΔP zero-gradient; EXV opening clamps; two-phase quality clamps; condenser fan/flood clamps.
  2. P1: sat-domain pressure clamp; screw eco/slide clamps when unknowns; zivi hard edges.
  3. P2 / info: correlation floors, config clamps, NFR9 FD-in-production debt.