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>
17 KiB
story_key, epic, story, source, baseline_commit, depends_on
| story_key | epic | story | source | baseline_commit | depends_on |
|---|---|---|---|---|---|
| 0-2-phantom-gradient-regularization-standard | 0 | 2 | _bmad-output/planning-artifacts/epics-solver-hp.md | f88cd7f7d8 |
0-1-jacobian-killing-pattern-audit-fd-test-harness |
Story 0.2: Phantom-Gradient Regularization Standard
Status: review
Story
As a component developer,
I want a standardized C¹ regularization toolkit (smooth max/min/clamp with documented ε) generalizing the existing flow_regularization.rs pattern,
so that fixes are uniform and derivative-safe instead of ad-hoc.
Acceptance Criteria
-
Core C¹ toolkit is complete, tested, and derivative-safe.
- Given the
entropyk-corecrate - When the toolkit lands (or is completed if already present)
- Then
smooth_max/smooth_min/smooth_clamp(C¹ or smoother, parameterized sharpness/width) are available alongside existingsmooth_abs, with derivative-continuity unit tests - And ε / width selection guidance is documented (physical scale of the blended region)
- Given the
-
flow_regularization.rsrelationship is resolved without physics churn.- Given the existing
crates/components/src/heat_exchanger/flow_regularization.rs - When reviewed
- Then it is either unified into the toolkit or documented as the reference implementation for mass-flow activity / zero-flow blending
- And no existing component behavior changes in this story (toolkit + docs only)
- Given the existing
Tasks / Subtasks
-
Task 1: Inventory what already exists — do not reinvent (AC: #1, #2)
- 1.1 Confirm primitives in
crates/core/src/smoothing.rs:smooth_max,smooth_min,smooth_abs(+ derivative),smooth_clamp, plussmoothstep/cubic_blendfamily. - 1.2 Confirm HX-specific adapters in
flow_regularization.rsalready callsmooth_abs— keep them inentropyk-components(domain-specific, not core). - 1.3 Gap check (expected at story creation):
Gap Expected action No public smooth_clamp_derivativeAdd analytic ∂/∂x (+ FD unit test) — required by Stories 0.3/0.4 Jacobian paths smooth_max/smooth_minderivatives only inline in testsAdd public smooth_max_da/smooth_min_da(or named*_derivativew.r.t. first arg) + FD testsNo dedicated C¹ join tests for smooth_clampAdd FD slope continuity at the four junctions ( lo,lo+width,hi-width,hi)No general ε/width guidance doc Add rustdoc module section + short markdown standard (see Task 3) flow_regularizationnot formally declared “reference”Document — do not move into core
- 1.1 Confirm primitives in
-
Task 2: Complete the toolkit APIs in
entropyk-core(AC: #1)- 2.1 Add analytic derivatives needed by consumers:
smooth_clamp_derivative(x, lo, hi, width) -> f64smooth_max/smooth_minfirst-arg (or both-arg) derivatives — match existing formulas already verified in unit tests
- 2.2 Keep APIs pure
f64,#[inline], zero-alloc, no new crate deps. - 2.3 Guard invalid parameters without panic:
width <= 0,hi < lo,k <= 0,eps <= 0— document degenerate behavior (hard clamp / hard max / hard abs) and cover with tests. Prefer non-panicking fallbacks consistent with existingnormalizezero-width handling. - 2.4 Re-export path unchanged:
entropyk_core::smoothing::*via existingpub mod smoothingincrates/core/src/lib.rs. No facade/entropykre-export required unless already present.
- 2.1 Add analytic derivatives needed by consumers:
-
Task 3: Document the Phantom-Gradient Regularization Standard (AC: #1, #2)
-
3.1 Add a focused doc (preferred path):
docs/components/phantom-gradient-regularization.mdOR expanddocs/components/flow-regularization.mdwith a top-level “Standard” section that links tosmoothing.rs. Prefer a dedicated short doc if the flow doc stays HX-specific. -
3.2 Document ε / width selection guidance with physical meaning:
Primitive Parameter Meaning Rule of thumb smooth_abs(x, eps)epsvalue at x=0; blend half-width~1% of characteristic |x| scale, or smaller if kink is far from operating region smooth_max/smooth_minkovershoot at equality = k/2k≪ |a−b| in the region you care about; never so large it shifts equilibriumsmooth_clampwidthtransition length at each bound width ≤ (hi−lo)/2; pick so Newton steps near bound still see ∂/∂x ∈ (0,1) over a physically small band (e.g. opening width1e-3…1e-2for [0,1])flow_activitym_epsmass-flow activity scale keep DEFAULT_M_EPS_KG_S = 1e-4as HX reference; do not change the constant in this story -
3.3 Explicit decision (write it into the doc):
- Canonical general toolkit:
entropyk_core::smoothing - Reference specialized pattern:
heat_exchanger::flow_regularization(activity / duty / transport blend) — not duplicated into core - Banned in residual/Jacobian paths on Newton unknowns: bare
.clamp(/ hard.max(0)/ earlyOk(0.0)that zeros informative derivatives (fixes deferred to 0.3/0.4)
- Canonical general toolkit:
-
3.4 Cross-link from
docs/audits/jacobian-killing-pattern-audit.mddeferred table: mark Story 0.2 standard as landed when done (update the “deferred” row only — do not re-audit). -
3.5 English technical docs; keep code comments English.
-
-
Task 4: Derivative-continuity unit tests (AC: #1)
- 4.1 Extend
crates/core/src/smoothing.rs#[cfg(test)]:- FD match for
smooth_clamp_derivativeacross interior, both ramps, and far exterior. - Slope continuity of
smooth_clampacross junctions (sample FD slope just inside/outside joins; assert small jump). - FD match for new
smooth_max/smooth_minderivative helpers.
- FD match for
- 4.2 Do not require
check_jacobian_healthfor this story — that harness is component-level (Story 0.1). Core unit FD helpers already insmoothing.rstests are the right layer. - 4.3 Keep using relative/absolute tolerances consistent with existing tests (
~1e-5FD match for analytic derivatives).
- 4.1 Extend
-
Task 5: Hard scope boundary — toolkit only (AC: #2)
- 5.1 DO NOT modify production residual/Jacobian physics in:
valve_flow.rsisenthalpic_expansion_valve.rsheat_exchanger/two_phase_dp.rsheat_exchanger/condenser.rs- any other component clamp site
- 5.2 DO NOT change
DEFAULT_M_EPS_KG_S/DEFAULT_M_SCALE_KG_Sor HX residual formulas. - 5.3 DO NOT change
n_equations()anywhere. - 5.4 Allowed touch list is essentially:
smoothing.rs(+ tests), docs underdocs/components/and a small audit cross-link, optional rustdoc polish onflow_regularization.rsmodule docs pointing to the standard.
- 5.1 DO NOT modify production residual/Jacobian physics in:
-
Task 6: Validation (AC: #1, #2)
- 6.1
cargo test -p entropyk-coregreen (includes new smoothing tests / doctests). - 6.2
cargo test -p entropyk-componentsgreen — proves no accidental physics regressions from doc-only / non-touch policy. - 6.3 Diff review: production component residual files should show zero physics diffs attributable to this story.
- 6.1
Dev Notes
Context & Positioning
- Epic 0 — Physical Regularization & Jacobian Health. Prerequisite for primary KPI NFR11 (≥95% first-try convergence). Robustness before speed.
- FR19 — informative non-zero C¹ Jacobians across the full physical domain via standardized phantom-gradient regularization.
- FR20 — FD harness (already delivered in Story 0.1); CI gate is Story 0.5.
- This story owns the standard + toolkit completeness. Stories 0.3 (valve/EXV) and 0.4 (HX quality/actuators/MSH) consume
smooth_clamp/smooth_max/smooth_absto fix P0 killers. - Source of truth for solver-HP scope:
_bmad-output/planning-artifacts/epics-solver-hp.md(supersedes legacy PRD/architecture for this epic).
CRITICAL: Toolkit already largely exists — complete, don't recreate
CANONICAL LOCATION (reuse):
crates/core/src/smoothing.rs
smooth_max(a, b, k)
smooth_min(a, b, k)
smooth_abs(x, eps) + smooth_abs_derivative
smooth_clamp(x, lo, hi, width) // value exists; derivative API missing
smoothstep / smootherstep / cubic_blend / quintic_blend (+ derivatives)
HX REFERENCE PATTERN (keep in components — do not move to core):
crates/components/src/heat_exchanger/flow_regularization.rs
flow_activity / effective_duty / blend_transport_residual
uses entropyk_core::smoothing::smooth_abs
VERIFICATION HARNESS (Story 0.1 — do not reimplement):
crates/components/src/jacobian_fd.rs
docs/audits/jacobian-killing-pattern-audit.md
Anti-patterns to prevent:
- Creating a second
smoothing/regularizationmodule undercomponents/ - Copy-pasting
tanh/LogSumExpsoft-max whensmooth_maxalready exists - “Unifying” by moving
flow_activityinto core (wrong layer — HX residual units / m_scale) - Replacing component clamps in this story (that is 0.3/0.4)
- Calling
JacobianMatrix::numerical(forward FD Newton fallback) for anything here
Architecture Compliance
- Exact analytic Jacobians (
project-context.md/ AGENTS.md): production assembly stays analytic; FD only in tests. - C¹ residual policy: any clamp/branch on a Newton unknown must use toolkit primitives so ∂R/∂x stays informative and continuous.
- DoF invariant: regularization never changes
n_equations(). - Zero panic in library code; public APIs need rustdoc + examples.
- Hot path: pure functions, no allocation, no dynamic dispatch.
- Layering:
entropyk-coremust not depend onentropyk-components.
Technical Requirements
- Language: Rust edition 2021, crate
entropyk-core(+ docs only elsewhere). - Deps: no new crates. Existing workspace/
approxpatterns for tests as already used in core. smooth_clampconstruction (already implemented — preserve semantics):Derivative must follow the chain rule through both ramps; unit-test against central FD.// Lower ramp then upper ramp via smoothstep — output always in [lo, hi], C¹ joins. let lower = lo + (x - lo) * smoothstep(lo, lo + width, x); hi + (lower - hi) * smoothstep(hi, hi - width, lower)smooth_maxanalytic ∂/∂a (already proven in tests):0.5 * (1 + (a-b) / sqrt((a-b)² + k²))- Prefer exporting named derivative functions over forcing callers to re-derive formulas (Story 0.3 will clamp EXV opening in both residual and Jacobian).
Current toolkit gaps (verified 2026-07-18)
| Item | Status |
|---|---|
smooth_max / smooth_min / smooth_abs / smooth_clamp values |
Present |
smooth_abs_derivative |
Present |
Public smooth_clamp_derivative |
Missing — add |
Public smooth_max/smooth_min derivatives |
Missing — add (formulas exist in tests) |
FD C¹ junction tests for smooth_clamp |
Missing — add (current test only saturates/bounds) |
| General ε/width selection doc | Missing — add (flow-regularization.md covers HX m_eps only) |
Formal “reference vs unify” decision for flow_regularization |
Missing — document as reference |
Downstream contract (write APIs for these consumers)
| Future story | Will need |
|---|---|
| 0.3 Valve / EXV | smooth_clamp + derivative for opening ∈ [0,1]; smooth ΔP blend (may use smooth_max/smooth_abs) |
| 0.4 HX | smooth_clamp for quality / fan / flood; reuse flow_regularization patterns |
| 0.5 CI gate | Assumes toolkit stable; no API churn after this story |
Recommended default widths for docs/examples (not applied to components here):
- Opening / quality ∈ [0,1]:
width = 1e-3…1e-2 - Fan actuator ∈ [0, 1.5]:
width ≈ 1e-2 - Flood level ∈ [0, 0.98]:
width ≈ 1e-2(keepwidth ≤ (hi−lo)/2)
File Structure Requirements
| File | Action |
|---|---|
crates/core/src/smoothing.rs |
UPDATE — add missing derivative APIs + C¹/FD tests; expand module rustdoc with ε guidance |
crates/core/src/lib.rs |
UPDATE only if export changes needed (likely unchanged) |
docs/components/phantom-gradient-regularization.md |
CREATE (preferred) — standard + ε table + reference decision |
docs/components/flow-regularization.md |
UPDATE — link to standard; declare HX module as specialized reference |
crates/components/src/heat_exchanger/flow_regularization.rs |
UPDATE docs only (optional module-doc pointer) — no formula changes |
docs/audits/jacobian-killing-pattern-audit.md |
UPDATE — mark 0.2 standard row as done / link to new doc |
valve_flow.rs / EXV / two_phase_dp.rs / condenser.rs |
DO NOT CHANGE physics |
Testing Requirements
- Unit tests live next to primitives in
smoothing.rs(existing pattern with localfdhelper). - Must prove:
- Analytic derivatives match central FD.
smooth_clampslope is continuous at bound joins (C¹).- Degenerate parameters do not panic.
- Run:
cargo test -p entropyk-core cargo test -p entropyk-components - Do not flip Story 0.1 detection tests to “must be clean” — killers remain until 0.3/0.4.
Previous Story Intelligence
From 0-1-jacobian-killing-pattern-audit-fd-test-harness (status: review):
- Shared harness:
jacobian_fd::check_jacobian_health— central FD, flags analytic≈0 while FD informative. - Audit catalogue confirms P0 killers; maps EXV opening clamps → Story 0.2
smooth_clampas the intended replacement API, applied later in 0.3. - Explicitly noted:
smooth_*already in core — Story 0.2 is unify/document/ε-guidance, not greenfield. - Scope discipline that worked: observe/fix tooling without changing production physics. Reuse that discipline here.
- Epic typo note still valid for 0.3: only
valve_mass_flow_dp_upexists (nodp_down). - Numbering collision:
0-1-fix-failing-testsis a different, already-done DoF story — ignore for this work.
Git Intelligence
- Recent commits are FMI/docs-heavy (
f88cd7fbaseline). Smoothing +flow_regularizationlanded earlier (3358b74lineage) — brownfield completion, not invention. - No commit yet closes the ε-standard doc or
smooth_clamp_derivativegap.
Project Context Reference
project-context.md/AGENTS.md: exact Jacobians; Component trait; English technical docs; zero panic.- Epic/FRs:
_bmad-output/planning-artifacts/epics-solver-hp.md— Epic 0, Story 0.2, FR19, FR20, NFR11. - Supporting:
docs/audits/jacobian-killing-pattern-audit.md,docs/components/flow-regularization.md,docs/adr/ADR-0001-multi-circuit-heat-exchanger-architecture.md(smooth_abs / no hard zero-flow branch).
References
- [Source:
_bmad-output/planning-artifacts/epics-solver-hp.md— Epic 0, Story 0.2, FR19, NFR11] - [Source:
crates/core/src/smoothing.rs— existing C¹/C∞ toolkit] - [Source:
crates/components/src/heat_exchanger/flow_regularization.rs— HX reference pattern] - [Source:
docs/components/flow-regularization.md— m_eps defaults, DoF rule] - [Source:
docs/audits/jacobian-killing-pattern-audit.md— deferred 0.2 work + P0 map] - [Source:
_bmad-output/implementation-artifacts/0-1-jacobian-killing-pattern-audit-fd-test-harness.md— prior story learnings] - [Source:
crates/components/src/jacobian_fd.rs— verification harness (do not reimplement)]
Dev Agent Record
Agent Model Used
Cursor Grok 4.5 (bmad-dev-story)
Debug Log References
cargo test -p entropyk-core— 164 lib + 41 doctests passed (incl. new smoothing derivative / C¹ / degenerate tests)cargo test -p entropyk-components— lib 910 passed; jacobian_health_sweep 8 passed; doctests 70 passed- Story-owned production touch limited to
flow_regularization.rsmodule docs (+4 lines); no clamp→smooth physics changes
Completion Notes List
- Added public
smooth_max_derivative,smooth_min_derivative,smooth_clamp_derivativewith chain-rule / analytic formulas; FD-matched in unit tests. - Documented ε/width guidance in module rustdoc and
docs/components/phantom-gradient-regularization.md. - Declared
flow_regularizationas HX reference pattern (not moved to core); linked from flow-regularization.md + audit deferred table (0.2 landed). - Degenerate parameters (
k/eps/width <= 0, inverted bounds) fall back to hard max/min/abs/clamp without panic. - Scope held: valve/EXV/HX clamp physics untouched for Stories 0.3/0.4.
File List
crates/core/src/smoothing.rs(UPDATE)crates/components/src/heat_exchanger/flow_regularization.rs(UPDATE — module docs only)docs/components/phantom-gradient-regularization.md(NEW)docs/components/flow-regularization.md(UPDATE)docs/audits/jacobian-killing-pattern-audit.md(UPDATE — deferred table)_bmad-output/implementation-artifacts/sprint-status.yaml(UPDATE — story status)_bmad-output/implementation-artifacts/0-2-phantom-gradient-regularization-standard.md(UPDATE — this file)
Change Log
- 2026-07-18: Completed phantom-gradient regularization standard — derivative APIs, C¹ tests, ε docs, flow_regularization reference decision; status → review
Story Completion Status
- Status: review
- Note: All tasks complete; ACs satisfied; ready for
code-review