Slice 27: round BS EN ISO 13370 floor U to 2 d.p. per RdSAP10 §5.12

Spec text (RdSAP 10 §5.12, p.46): "Unless provided by the assessor the
floor U-value is calculated according to BS EN ISO 13370 using its area
(A) and exposed perimeter (P) and rounded to two decimal places." Our
u_floor returned the raw formula output — that's a 0.0040 W/m²K precision
gap vs the PDF that was costing 0.03–0.13 W/K on §3 LINE_33 for 4 fixtures.

§3 LINE_33 residuals collapsed:
  000474: 0.0296 → 0.0032
  000477: 0.1246 → 0.0013
  000480: 0.0168 → 0.0075
  000490: 0.0282 → 0.0013
  000516: 0.0038 → 0.0038 (exposed floor, Table 20 — unaffected)
  000487: 37.88 (RR defect, slice 25)

+3 SapResult pin closures (000474/477/490 ECF now pass at abs=1e-4).
Pin counts: section_cascade 151/35 unchanged (residuals shrunk but still
> 1e-4); e2e SapResult 24→27 PASS.

Remaining LINE_33 0.001–0.0075 W/K is wall + party-wall area precision —
PDF stores 2-d.p.-rounded element areas (slice 27b).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-05-23 08:50:33 +00:00
parent af51be1780
commit 1821f3fef3
2 changed files with 49 additions and 30 deletions

View file

@ -159,24 +159,29 @@ pumps_fans_kwh_per_yr | ✓ | ✓ | ✓ | ✓ | ✓ |
is still off by sub-SAP-point amounts on every fixture — none of `sap_score_
continuous` is closed at abs=1e-4.
### B.3 §3 residuals after slice 24 (rooflight 27a for 000516)
### B.3 §3 residuals after slice 27 (floor U §5.12 rounding)
```
fixture | LINE_31 Δ | LINE_33 Δ | LINE_36 Δ | LINE_37 Δ
000474 | 0.0014 | 0.0296 | 0.0002 | 0.0294
000477 | 0.0004 | 0.1246 | ✓ | 0.1244
000480 | 0.0060 | 0.0168 | 0.0009 | 0.0177
000474 | 0.0014 | 0.0032 | 0.0002 | 0.0030
000477 | 0.0004 | 0.0013 | ✓ | 0.0013
000480 | 0.0060 | 0.0075 | 0.0009 | 0.0084
000487 | 8.82 | 37.88 | 1.32 | 39.21
000490 | 0.0010 | 0.0282 | 0.0002 | 0.0284
000490 | 0.0010 | 0.0013 | 0.0002 | 0.0014
000516 | 0.0025 | 0.0038 | 0.0004 | 0.0042
```
5 of 6 fixtures have §3 residuals under 0.2 W/K. 000516's 0.82 W/K rooflight
gap was closed by slice 24 — line (27a) is now in the cascade. The residual
0.0038 W/K on 000516 LINE_33 is the same pre-existing wall-perimeter +
per-window curtain precision that's biting 000474/477/480/490 (slice 27
territory). 000487's huge gaps are the RR fixture defect + the U=0.86
external-gable variant our `gable_wall` enum doesn't handle.
5 of 6 fixtures now have §3 LINE_33 residuals under 0.01 W/K. Slice 27
applied the §5.12 mandated 2-d.p. rounding to BS EN ISO 13370 floor
U-values, closing 9095% of the residual on 000474/477/490 (000516 is
exposed-floor only; 000487 still has the RR defect).
Remaining 0.00130.0075 W/K comes from wall + party-wall area precision
(my calc carries `36.4492 m²` where the PDF stores `36.4500` — clearly
2-d.p. rounded). The spec page for §3 element-area rounding hasn't been
read; if a "round to 0.01 m²" rule exists for §3 areas, applying it
would close these. 000487's huge gaps are the RR fixture defect + the
U=0.86 external-gable variant our `gable_wall` enum doesn't handle.
### B.4 §4 residuals
@ -193,6 +198,7 @@ fixture | section §4 pin status
### B.5 Recent slices (in reverse order — newest first)
```
Slice 27: BS EN ISO 13370 floor U rounded to 2 d.p. per RdSAP10 §5.12
Slice 24: rooflight (line 27a) — SapRoofWindow datatype + 000516 cascade closure
ac68cf88 Slice 23: 000516 detailed RR + exposed_floor + door_count fixture lodgement
6be8fdb7 Slice 22: per-window curtain resistance fix (mixed glazing)
@ -272,17 +278,30 @@ extraction commands (sample for §9a):
awk '/^9a\. Energy requirements/,/^10a\./' "sap worksheets/U985-0001-NNNNNN.txt"
```
### C.4 Slice 27 — Floor-U precision (≈0.04 W/m²K drift on 4 fixtures)
### C.4 Slice 27 — ~~Floor-U precision~~ DONE (mostly)
After slices 22 + 23 closed window + RR contributions, 4 fixtures
(000474/477/480/490) have residual ~0.03-0.13 W/K on LINE_33 traced to floor U
drift:
- 000477: calc 0.5261 vs PDF 0.5300 for suspended timber + age B + 31.26 m²
ground floor.
- Likely the BS EN ISO 13370 ground-contact formula vs PDF Table 19 lookup.
Done. The §5.12 spec mandates "rounded to two decimal places" for BS EN ISO
13370 floor U-values, which my calc was skipping. Applied `round(U, 2)` to
both suspended-timber and solid-floor branches in `u_floor` — closed
000474/477/490 from ~0.030.13 W/K residual to under 0.002 W/K on each.
Diagnose what the PDF uses for these (probably a tabulated value, not a
formula) and align the calc.
Remaining 0.00130.0075 W/K residual is wall + party-wall area precision —
PDF stores 2-d.p.-rounded element areas (e.g. `36.4500 m²` for a wall I
compute as `36.4492 m²`). Closing these needs the §3 area-rounding spec
rule — see slice 27b below.
### C.4b Slice 27b — Wall + party-wall area precision (PDF rounds to 2 d.p.)
The 0.00130.0075 W/K LINE_33 residual on 000474/477/480/490/516 is
consistently traceable to gross wall-area and party-wall-area values:
- 000474 Main wall area: my 36.4492 vs PDF 36.4500 (Δ × 1.5 U = 0.0012 W/K)
- 000516 wall area: my 45.3675 vs PDF 45.3700 (Δ × 1.5 U = 0.00375 W/K)
- per-window U_eff aggregation: my per-window curtain transform diverges
from PDF's aggregate by ~0.0001 per fixture (slice 22 trade-off)
If §3 mandates area rounding to 0.01 m² (or 4 d.p.) at the element level,
applying it would close LINE_33 to ≤ 1e-4. Need SAP 10.2 §3 page reference
from the user.
### C.5 Slice 28 — Continuous SAP / fuel cost / CO2 closure

View file

@ -681,10 +681,11 @@ def u_floor(
wall_thickness_mm: Optional[int],
description: Optional[str] = None,
) -> float:
"""RdSAP10 ground-floor U-value via BS EN ISO 13370 solid-floor branch.
Suspended-floor branch is approximated as solid since the difference at
the feature-engineering granularity is < 0.1 W/m^2K for typical UK floors.
"""RdSAP10 ground-floor U-value via BS EN ISO 13370 (suspended or solid
branch, per Table 19 footnote 1). Result is rounded to 2 d.p. per spec
§5.12 ("Unless provided by the assessor the floor U-value is calculated
according to BS EN ISO 13370 using its area (A) and exposed perimeter
(P) and rounded to two decimal places.").
`description` is the joined surveyor text from `floors[i].description`.
When it asserts retrofit insulation ("Solid, insulated (assumed)" /
@ -698,8 +699,7 @@ def u_floor(
Full-SAP assessments lodge a measured floor U-value directly in
the description ("Average thermal transmittance X W/m²K"); when
present this supersedes the BS EN ISO 13370 calculation per spec
§5.12 opening clause ("Unless provided by the assessor the floor
U-value is calculated according to BS EN ISO 13370").
§5.12 opening clause.
"""
measured = _measured_u_from_description(description)
if measured is not None:
@ -735,18 +735,18 @@ def u_floor(
else (construction is None and band_upper in _SUSPENDED_TIMBER_DEFAULT_BANDS)
)
if use_suspended_branch:
return _u_floor_suspended(
return round(_u_floor_suspended(
area_m2=area_m2,
perimeter_m=perimeter_m,
wall_thickness_mm=wall_thickness_mm,
insulation_thickness_mm=ins_mm or 0,
)
), 2)
r_f = ((ins_mm or 0) / 1000.0) / 0.035
d_t = w + soil_g * (r_si + r_f + r_se)
b = 2.0 * area_m2 / perimeter_m
if d_t < b:
return 2.0 * soil_g * log(pi * b / d_t + 1.0) / (pi * b + d_t)
return soil_g / (0.457 * b + d_t)
return round(2.0 * soil_g * log(pi * b / d_t + 1.0) / (pi * b + d_t), 2)
return round(soil_g / (0.457 * b + d_t), 2)
# ---------------------------------------------------------------------------