---
story_key: 0-2-phantom-gradient-regularization-standard
epic: 0
story: 2
source: _bmad-output/planning-artifacts/epics-solver-hp.md
baseline_commit: f88cd7f7d83c8c25bf8d4262742fb9736c3bf7c7
depends_on: 0-1-jacobian-killing-pattern-audit-fd-test-harness
---

# Story 0.2: Phantom-Gradient Regularization Standard

Status: done

<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
<!-- Ultimate context engine analysis completed - comprehensive developer guide created -->

## 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

1. **Core C¹ toolkit is complete, tested, and derivative-safe.**
   - **Given** the `entropyk-core` crate
   - **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 existing `smooth_abs`, with **derivative-continuity unit tests**
   - **And** ε / width selection guidance is documented (physical scale of the blended region)

2. **`flow_regularization.rs` relationship 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**)

## Tasks / Subtasks

- [x] **Task 1: Inventory what already exists — do not reinvent** (AC: #1, #2)
  - [x] 1.1 Confirm primitives in `crates/core/src/smoothing.rs`: `smooth_max`, `smooth_min`, `smooth_abs` (+ derivative), `smooth_clamp`, plus `smoothstep` / `cubic_blend` family.
  - [x] 1.2 Confirm HX-specific adapters in `flow_regularization.rs` already call `smooth_abs` — keep them in `entropyk-components` (domain-specific, not core).
  - [x] 1.3 Gap check (expected at story creation):
    | Gap | Expected action |
    |---|---|
    | No public `smooth_clamp_derivative` | **Add** analytic ∂/∂x (+ FD unit test) — required by Stories 0.3/0.4 Jacobian paths |
    | `smooth_max` / `smooth_min` derivatives only inline in tests | **Add** public `smooth_max_da` / `smooth_min_da` (or named `*_derivative` w.r.t. first arg) + FD tests |
    | No dedicated C¹ join tests for `smooth_clamp` | **Add** 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_regularization` not formally declared “reference” | **Document** — do **not** move into core |

- [x] **Task 2: Complete the toolkit APIs in `entropyk-core`** (AC: #1)
  - [x] 2.1 Add analytic derivatives needed by consumers:
    - `smooth_clamp_derivative(x, lo, hi, width) -> f64`
    - `smooth_max` / `smooth_min` first-arg (or both-arg) derivatives — match existing formulas already verified in unit tests
  - [x] 2.2 Keep APIs pure `f64`, `#[inline]`, zero-alloc, no new crate deps.
  - [x] 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 existing `normalize` zero-width handling.
  - [x] 2.4 Re-export path unchanged: `entropyk_core::smoothing::*` via existing `pub mod smoothing` in `crates/core/src/lib.rs`. No facade/`entropyk` re-export required unless already present.

- [x] **Task 3: Document the Phantom-Gradient Regularization Standard** (AC: #1, #2)
  - [x] 3.1 Add a focused doc (preferred path): `docs/components/phantom-gradient-regularization.md` **OR** expand `docs/components/flow-regularization.md` with a top-level “Standard” section that links to `smoothing.rs`. Prefer a **dedicated** short doc if the flow doc stays HX-specific.
  - [x] 3.2 Document **ε / width selection guidance** with physical meaning:

    | Primitive | Parameter | Meaning | Rule of thumb |
    |---|---|---|---|
    | `smooth_abs(x, eps)` | `eps` | value at `x=0`; blend half-width | ~1% of characteristic \|x\| scale, or smaller if kink is far from operating region |
    | `smooth_max` / `smooth_min` | `k` | overshoot at equality = `k/2` | `k` ≪ \|a−b\| in the region you care about; never so large it shifts equilibrium |
    | `smooth_clamp` | `width` | transition 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 width `1e-3`…`1e-2` for [0,1]) |
    | `flow_activity` | `m_eps` | mass-flow activity scale | keep `DEFAULT_M_EPS_KG_S = 1e-4` as HX reference; do not change the constant in this story |

  - [x] 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)` / early `Ok(0.0)` that zeros informative derivatives (fixes deferred to 0.3/0.4)
  - [x] 3.4 Cross-link from `docs/audits/jacobian-killing-pattern-audit.md` deferred table: mark Story 0.2 standard as landed when done (update the “deferred” row only — do not re-audit).
  - [x] 3.5 English technical docs; keep code comments English.

- [x] **Task 4: Derivative-continuity unit tests** (AC: #1)
  - [x] 4.1 Extend `crates/core/src/smoothing.rs` `#[cfg(test)]`:
    - FD match for `smooth_clamp_derivative` across interior, both ramps, and far exterior.
    - Slope continuity of `smooth_clamp` across junctions (sample FD slope just inside/outside joins; assert small jump).
    - FD match for new `smooth_max` / `smooth_min` derivative helpers.
  - [x] 4.2 Do **not** require `check_jacobian_health` for this story — that harness is component-level (Story 0.1). Core unit FD helpers already in `smoothing.rs` tests are the right layer.
  - [x] 4.3 Keep using relative/absolute tolerances consistent with existing tests (`~1e-5` FD match for analytic derivatives).

- [x] **Task 5: Hard scope boundary — toolkit only** (AC: #2)
  - [x] 5.1 **DO NOT** modify production residual/Jacobian physics in:
    - `valve_flow.rs`
    - `isenthalpic_expansion_valve.rs`
    - `heat_exchanger/two_phase_dp.rs`
    - `heat_exchanger/condenser.rs`
    - any other component clamp site
  - [x] 5.2 **DO NOT** change `DEFAULT_M_EPS_KG_S` / `DEFAULT_M_SCALE_KG_S` or HX residual formulas.
  - [x] 5.3 **DO NOT** change `n_equations()` anywhere.
  - [x] 5.4 Allowed touch list is essentially: `smoothing.rs` (+ tests), docs under `docs/components/` and a small audit cross-link, optional rustdoc polish on `flow_regularization.rs` module docs pointing to the standard.

- [x] **Task 6: Validation** (AC: #1, #2)
  - [x] 6.1 `cargo test -p entropyk-core` green (includes new smoothing tests / doctests).
  - [x] 6.2 `cargo test -p entropyk-components` green — proves no accidental physics regressions from doc-only / non-touch policy.
  - [x] 6.3 Diff review: production component residual files should show **zero** physics diffs attributable to this story.

### Review Findings

- [x] [Review][Patch] smooth_clamp overlapping bounds validation [crates/core/src/smoothing.rs:458]
- [x] [Review][Patch] normalize panic on NaN inputs [crates/core/src/smoothing.rs:69]
- [x] [Review][Patch] smooth_clamp panic on NaN inputs [crates/core/src/smoothing.rs:458]
- [x] [Review][Patch] smooth_max_derivative and smooth_min_derivative NaN division [crates/core/src/smoothing.rs:310]
- [x] [Review][Patch] smooth_abs_derivative NaN division and overflow handling [crates/core/src/smoothing.rs:418]
- [x] [Review][Patch] NaN propagation in derivatives under degenerate/fallback paths [crates/core/src/smoothing.rs:310]
- [x] [Review][Defer] smooth_max and smooth_min overflow for extremely large inputs [crates/core/src/smoothing.rs:293] — deferred, pre-existing
- [x] [Review][Defer] smooth_abs overflow for extremely large inputs [crates/core/src/smoothing.rs:396] — deferred, pre-existing
- [x] [Review][Defer] slope amplification for very small widths in step derivatives [crates/core/src/smoothing.rs:123] — deferred, pre-existing
- [x] [Review][Defer] cubic_blend and quintic_blend bound swapping [crates/core/src/smoothing.rs:205] — deferred, pre-existing

## 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_abs` to 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

```text
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` / `regularization` module under `components/`
- Copy-pasting `tanh` / `LogSumExp` soft-max when `smooth_max` already exists
- “Unifying” by moving `flow_activity` into 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-core` must not depend on `entropyk-components`.

### Technical Requirements

- **Language:** Rust edition 2021, crate `entropyk-core` (+ docs only elsewhere).
- **Deps:** no new crates. Existing workspace/`approx` patterns for tests as already used in core.
- **`smooth_clamp` construction (already implemented — preserve semantics):**
  ```rust
  // 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)
  ```
  Derivative must follow the chain rule through both ramps; unit-test against central FD.
- **`smooth_max` analytic ∂/∂a (already proven in tests):**
  ```text
  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` (keep `width ≤ (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 local `fd` helper).
- Must prove:
  1. Analytic derivatives match central FD.
  2. `smooth_clamp` slope is continuous at bound joins (C¹).
  3. Degenerate parameters do not panic.
- Run:
  ```bash
  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_clamp` as 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_up` exists (no `dp_down`).
- Numbering collision: `0-1-fix-failing-tests` is a **different**, already-done DoF story — ignore for this work.

### Git Intelligence

- Recent commits are FMI/docs-heavy (`f88cd7f` baseline). Smoothing + `flow_regularization` landed earlier (`3358b74` lineage) — brownfield completion, not invention.
- No commit yet closes the ε-standard doc or `smooth_clamp_derivative` gap.

### 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.rs` module docs (+4 lines); no clamp→smooth physics changes

### Completion Notes List

- Added public `smooth_max_derivative`, `smooth_min_derivative`, `smooth_clamp_derivative` with chain-rule / analytic formulas; FD-matched in unit tests.
- Documented ε/width guidance in module rustdoc and `docs/components/phantom-gradient-regularization.md`.
- Declared `flow_regularization` as 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`
