# Findings log One entry per mismatch between the draft contract and an existing model, or between the two reference codebases. Append, never silently resolve. Contract changes must appear here. **Verdicts:** `contract flaw` (change the contract), `model flaw` (fix the model), `family-specific` (legitimate difference; keep in the family namespace; test: does it matter for cross-family comparison? If not, it stays family-specific). **Format:** `F-NNN | area | what differs | verdict | action | status` ## Entries Seeded by the Stage A side-by-side diff (2026-10-11). Sources compared: - **HB** = the finned-tube code prototype (not public), at `d391da0`. - **PB** = packed bed, `encapsulated_ltes` in [encapsulated-LTES](https://github.com/mmsg-warwick/encapsulated-LTES) at `023bf58` (clean). Models: `FullModel` (resolved capsule at every `x`) and `ReducedModel` (one x-averaged capsule). | ID | Area | What differs | Verdict | Action | Status | |---|---|---|---|---|---| | F-001 | Coordinates | Flow coordinate is `z` in HB and `x` in PB (both on domain `"pipe"`). Storage: HB `y` on `"PCM vertical"` (cylindrical) and, in the mush model, `x` on `"PCM horizontal"`; PB `r` on `"capsule"` (spherical). PB's `x` clashes with HB's fin-gap `x`. | Flow coordinate: model flaw (PB). Storage domains: family-specific. | PB renames `x` to `z` (D12). Storage domain names are settled with the open naming item. | closed (F-039): the bed uses `z` on "flow path" (F-038) and `r` on "capsule" | | F-002 | Parameter names | PCM properties: HB `"PCM solid phase conductivity [W.m-1.K-1]"`, `"PCM latent heat [J.kg-1]"`, `"PCM melting temperature [K]"`; PB has the same names without `"PCM "`. Flow: HB `"Inlet flow rate [m3.s-1]"` (volumetric, per pipe); PB `"Inlet velocity [m.s-1]"` (interstitial). `"Pipe length [m]"` is the bed length in PB. HB scales to the device with `"Number of pipes"`; PB has no cross-section, so everything is per unit bed area. | Contract flaw (no shared namespace yet) and model flaw in both (neither uses mass flow rate, D8). | Contract defines the material namespace and the device-level control (mass flow rate). PB needs a bed cross-section parameter so totals are in J and W. | closed (F-039): shared "PCM ..." names; mass flow rate control; "Bed cross-sectional area [m2]" | | F-003 | Material classes | Not shared, despite DESIGN §1. HB: `PhaseChangeMaterial` with `PureMaterial` and `CompositeMaterial`, corners smoothed by `smooth_min`/`smooth_max` over `"PCM melting range [K]"` (default 0.5 K), `liquid_fraction(H)`, `k` linear in liquid fraction, `T = T_m` taken as solid, `H` scaled by `rho_L`. PB: `H2T`, `T2H`, `k` are methods on the parameter class, sharp `pybamm.minimum`/`maximum`, `k` piecewise with indicator functions, no liquid-fraction function. PB `T2H` gives `H = 0` at exactly `T = T_m` (both indicators false), and its guard `if self.T_m == T` compares symbols, so it never fires. Same underlying physics: `rho_L = rho_s L`, liquid sensible heat `rho_l c_p_l`. | Model flaw (PB); DESIGN §1 inaccurate. | Use HB's classes as the base (Stage B merge). Correct DESIGN §1 wording. | closed: one copy of the material classes (pytes.materials), used by both families | | F-004 | Energy conservation | HB: exact, using the ledger. Sharp front uses the integral of `dH/dt`. Mush uses the integral of `dH/dt` between PCM and fin, and the same `h(T_c - T_f)` expression on both sides at the wall. Test tolerance 1e-9 of released energy. PB: the fluid source is `q = -k(H_surf) * BoundaryGradient(T_c)`, a separately discretised flux. The stored-energy ODE uses the extrapolated `T_out` (F-006) and the inflow is wrong (F-005). **Measured** with default `"Raul2018"`, 1 h, PyBaMM 26.7.1: max error in energy conservation / final stored energy = 3.7 % (`FullModel`), 4.3 % (`ReducedModel`). | Model flaw (PB). | Stage B: ledger coupling across the capsule surface (D6). | closed (F-039): the bed uses the ledger; conservation exact (4e-15) where PB was out by up to 2.9 % | | F-005 | Inlet condition | HB works around the `pybamm.upwind` ghost node: `T_bc = (T_in + T_1)/2`, with constant extrapolation on `"pipe"`. PB uses a plain Dirichlet `T_in`, so the inflow is `2 T_in - T_1`. | Model flaw (PB). | Move the HB workaround into `compat` (tagged), and use it for both families via the shared fluid submodel. | closed: `pytes.compat.upwind` in the shared fluid, both families | | F-006 | Outlet temperature | HB: value in the last cell (constant extrapolation), which is the value upwinding advects out. PB: default FiniteVolume extrapolation (linear), which is not the advected value. | Model flaw (PB). | Outlet from the fluid submodel only. Ties to the open question of deriving `T_out` from `h_out`. | closed: outlet is the advected last-cell enthalpy, `T(h_out)` (F-032), both families | | F-007 | Power: definition, sign, units | HB `"Power [W]" = N rho_f c_p_f Q_f (T_out - T_in)`, positive on discharge, whole device. PB has no power variable. `"Stored energy per unit area [J.m-2]"` integrates `eps rho_f c_p_f u (T_in - T_out)`, which is positive on charge and per unit area. Both use `c_p dT`, not enthalpy. | Model flaw (both). | Stage A: power from `mdot (h_out - h_in)` via the fluid-property object (D8). PB gets this from the shared fluid in Stage B. | closed: power from enthalpy in the shared device energy, both families | | F-008 | Energy variables | HB `"Thermal energy [J]"` = PCM + fins + **fluid**, relative to 0 K; `"Phase-change material thermal energy [J]"` includes fins; `"Released energy [J]"` is a solver state. PB `"Total enthalpy per unit area [J.m-2]"` = capsules + fluid, relative to 0 K; `"Variation in total enthalpy..."` relative to `T_0`. Neither uses the `T_low` reference (D14). Note: checklist item 7 says the finned pipe has no fluid inventory. That is outdated: the HB working tree includes it. | Model flaw (both). | Stage A: storage and total thermal energy with the zero at uniform `T_low`; HB "Released energy" becomes "Discharged energy". Update checklist item 7. | closed: storage and total thermal energy, zero at the reference temperature (F-030), both families | | F-009 | Heat transfer coefficient | Both use the constant `"Heat transfer coefficient [W.m-2.K-1]"`. HB applies it at the pipe wall; the mush model puts a pitch's worth of wall heat through the fin root (`d_m/d_c` factor). PB applies it at the capsule surface, with specific area `3(1 - eps)/R`. Both define `"Heat transfer fluid conductivity"` and never use it. | Source: model flaw (both, D8). Where it applies: family-specific (coupling slot). | HTC comes from the fluid-property object (a constant for now). Keep `k_f` (maintainer, 2026-10-11): reserved for axial diffusion and the correlation hook. | closed: the coefficient is in the surface flux law (`NewtonCooling`, F-032), both families | | F-010 | SOC and liquid fraction | HB `"State of charge"` = `"Average liquid fraction"`, a scalar. Sharp front: weighted by the composite annulus volume. Mush: weighted by the PCM gap volume, excluding fins. PB `"State of charge"` is a **field over `x`**: the capsule volume fraction with liquid fraction ≥ 1/2 (a phase indicator, not a liquid fraction). `"X-averaged state of charge"` thresholds the x-averaged `H`, which is not the average of the indicator. PB has no average liquid fraction. | Model flaw (both, D13). | Stage A: energy-based SOC on total energy (since removed, F-030). Contract fixes the definition of average liquid fraction: PCM-volume-weighted, fins and fluid excluded. | closed: SOC removed (F-030); average liquid fraction PCM-volume-weighted in both families | | F-011 | Domain structure | Same pattern: `"pipe"` primary for the fluid; storage domain primary, with `"pipe"` secondary (tertiary for the mush model's PCM). | Family-specific (compatible). | None. Fits the D5 geometry switch. | closed | | F-012 | Fluid equation | Both: state `T_f`, first-order upwind, no axial conduction (PB has it commented out). HB: `A_flow = pi R_f^2`, wall area per fluid volume `2/R_f`. PB: divides by `eps`, specific area `3(1 - eps)/R` per bed volume, `u` interstitial. Both match D5 with `A_flow` = pipe area or `eps` × bed area. | Family-specific. | Shared fluid submodel takes `A_flow` and the wetted perimeter from the family. | closed: one `AdvectedFluid` with `FlowPath` (flow area, number of paths) from each family | | F-013 | Cut-off and operating mode | HB: `operating_mode` and `"Cut-off temperature [K]"`. PB: no events. | Model flaw (PB). | No longer applies: models have no cut-off events (F-031); PB needs none. | closed | | F-014 | Storage temperature as a state | PB carries `T_c` as an **algebraic** variable (`T_c - H2T(H) = 0`), which makes the model a DAE with twice the storage unknowns. HB uses `T = H2T(H)` as an expression. | Model flaw (PB) under D7 (state list `[H]`). | Stage B: use the expression. | closed (F-039): temperature is the expression `H2T(H)`; the bed model is an ODE system | | F-015 | Celsius variables | HB: shared helper `set_temperature_variables`. PB: written by hand for each variable. | Model flaw (PB). | Shared helper (D12). | closed: shared helper in both families | | F-016 | Initial state | Both: uniform `"Initial temperature [K]"` mapped through `T2H`. | Model flaw (both, D14: initialise by SOC). | Stage A for HB. Initial temperature kept as an alternative (F-029). | closed: initial state by "Initial temperature [K]" (F-030), both families | | F-017 | Defaults | Mesh: HB 40×40 (sharp front) and 20³ (mush); PB `r` 40, `x` 80. Solver: HB `IDAKLUSolver(rtol=1e-5, atol=1e-8)`, tuned and documented; PB default `IDAKLUSolver()`. Fluid extrapolation: see F-006. Quick-plot variables differ. Temperature scale 200 K, reference `T_m`, in both. Enthalpy scale: HB `rho_L` about mid-band; PB `T2H(T_0)` with no reference. | Mesh: family-specific. Fluid spatial method: contract (owned by the fluid submodel). Plot variables: D15. Solver and scaling: model flaw (PB). | Fluid spatial method set by the fluid submodel. Shared default plot variables. | closed (F-039): fluid method from `compat`; shared plot variables; bed mesh and solver chosen in F-039 | | F-018 | Experiment runner | `step.py` control residuals recompute power as `C Q (T_out - T_in)` instead of reading `"Power [W]"`. The runner reads `"Inlet flow rate [m3.s-1]"` and `"Inlet temperature [K]"` directly, and removes the event by name. | Model flaw (HB runner). | Stage A: residuals use the model's power variable and the declared control set. Approved by the maintainer (2026-10-11). | closed (Stage A) | | F-019 | Test coverage | HB: 72 tests (smoke, conservation, materials, experiments). PB: a version check and the template's entry-point tests, with no physics tests. PB has things HB lacks: experimental data (Nallusamy 2007), a mesh-refinement study, and a lower-fidelity `ReducedModel`. | Family-specific (coverage gap in PB). | Conformance suite covers PB in Stage B. `ReducedModel` is part of the PB hierarchy (maintainer, 2026-10-11); renamed `AverageCapsuleModel` on porting. | closed (F-041): the bed in the conformance suite (F-039); `ReducedModel` is the bed with `"storage": "averaged"` | | F-020 | Naming and sign | PB `"Flux into phase-change material [W.m-2]"` is `-k dT/dr` at the surface, which is the flux **out of** the capsule. | Model flaw (PB). | Name by direction in the contract. | closed: the storage boundary flux is named by direction, into the storage ("