Model/datatypes
Khalim Conn-Kowlessar 637df557bb Slice S0380.113: H=0 gable lodgement deducts per RdSAP 10 §3.9.2 step (b)
RdSAP 10 §3.9.2 step (b) (PDF p.23) verbatim:

  "Software calculates the area of each gable or adjacent wall by
  using the equation:
         A_RR_gable = L_gable × (0.25 + H_gable) − [(H_gable − H_common_1)² / 2
                                                   + (H_gable − H_common_2)² / 2]"

Step (d):
  A_RR_final = A_RR_wall − (Σ A_common + Σ A_gable + Σ A_party
                            + Σ A_sheltered + Σ A_connected)

The spec equation is signed and applies for all L > 0 — including
H_gable = 0. When the gable is shorter than the common walls the
correction term `(H_gable − H_common)² / 2` exceeds the
L × (0.25 + H_gable) term, producing a negative A_RR_gable.
Elmhurst's worksheet evaluates the equation literally; the negative
value adjusts A_RR_final upward via step (d) without billing a
physical wall area.

Cert 000565 §8.1 lodges Ext3's RR (Simplified Type 2) with an
absent Gable Wall 2:

  Gable Wall 1   L=9.00  H=7.00   Exposed     U=0.45
  Gable Wall 2   L=4.00  H=0.00               U=0.00   ← lodged but H=0
  Common Wall 1  L=5.00  H=1.50               U=0.45
  Common Wall 2  L=7.50  H=0.30               U=0.45

Spec equation for Gable Wall 2:
  A_gable_2 = 4 × (0.25 + 0) − (0 − 1.5)²/2 − (0 − 0.30)²/2
            = 1.0 − 1.125 − 0.045 = −0.17 m²

Worksheet (30) Ext3 residual = 17.35 m² back-solves exactly:
  A_RR_shell = 12.5 × √(32.0 / 1.5)                = 57.7350
  Σ walls (incl. -0.17 absent gable)               = 40.3850
  residual = shell − walls                         = 17.3500  ✓ 4 d.p.

Pre-slice the mapper had two clamps that together dropped the
spec-computed −0.17 m² adjustment:

  mapper.py:3350  `if length_m <= 0 or height_m <= 0: return None`
                  → filtered out any H=0 surface
  mapper.py:3443  `area_m2 = max(0.0, length_m * (0.25 + H) − correction)`
                  → clamped negative gable areas at 0

Combined the cascade computed residual = 17.18 m² (cascade UNDER
by 0.17). Plus a related secondary `if height_m > h` filter on the
correction sum that masked the all-common-walls-taller case.

3-layer fix:

1. `datatypes/epc/domain/mapper.py` `_map_elmhurst_rir_surface`:
   - Split the early-return filter: drop only when L<=0 (no wall),
     OR when H<=0 AND not (Simplified Type 2 with common walls).
   - Apply the spec gable-area formula to BOTH `gable_wall` (party
     default) and `gable_wall_external` kinds in Simplified Type 2
     (the U-value routing differs by kind, but the area equation
     is the same).
   - Remove `max(0.0, ...)` clamp so the signed result reaches the
     cascade.
   - Remove `if height_m > h` correction-sum filter (spec applies
     the full square unconditionally).

2. `domain/sap10_calculator/worksheet/heat_transmission.py` per-
   surface loop:
   - `gable_wall` branch: skip `party += 0.25 × area` when area < 0
     (wall doesn't exist physically) but still add the signed area
     to `rr_walls_in_a_rr_area` so the residual deduction in step (d)
     grows by |area|.
   - `gable_wall_external` branch: same skip pattern for `walls +=
     u × area` and `rr_detailed_area += area`.

Cohort safety: only cert 000565 Ext3 hits this in the corpus. All
other cohort certs are Type 1 RR (no common walls, formula gives
the same answer) or have all gables H > 0. The cascade's per-element
test pins (Ext1's Connected gable + Exposed gable, Ext4's Detailed
RR) unchanged.

Cert 000565 cascade snapshot (HEAD a461b70d → this):
  roof_w_per_k         51.3185 → 51.3768  ✓ EXACT (Δ -0.06 → -0.003)
  total_external_area 857.46  → 857.6323  ✓ EXACT (Δ -0.18 → -0.008)
  thermal_bridging    128.62  → 128.6448  ✓ EXACT (Δ -0.03 → -0.005)
  total_w_per_k       936.97  → 937.0563  ✓ EXACT (Δ -0.09 → -0.004)

  sap_score (int)         29 ✓ EXACT (preserved)
  sap_score_continuous 28.5027 → 28.5007 (Δ -0.0060 → -0.0080)
  ecf                   5.3877 →  5.3876
  total_fuel_cost_gbp  4681.01 → 4680.97
  co2_kg_per_yr        6448.59 → 6448.53
  space_heating_kwh   59019.21 → 59018.52
  main_heating_fuel   34715.31 → 34716.78

**Cert 000565 fabric cascade now essentially exact** (HTC −0.004 W/K
total residual across all 8 fabric components). The remaining
continuous SAP -0.0080 / cost +£0.71 / SH +10 kWh residuals come
from non-fabric upstream (likely ventilation or appliances) —
candidates for a future audit.

Pyright net-zero (57 → 57 errors across touched files).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 16:28:47 +00:00
..
epc Slice S0380.113: H=0 gable lodgement deducts per RdSAP 10 §3.9.2 step (b) 2026-06-01 16:28:47 +00:00
magicplan typing and renaming 🟪 2026-05-07 13:26:49 +00:00
__init__.py further breaking up code 2023-07-20 12:24:34 +01:00
datatypes.py further breaking up code 2023-07-20 12:24:34 +01:00
enums.py completed build of new demo portfolio - some fixed required still 2023-11-20 14:42:16 +00:00