mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 89: PS pitched-sloping-ceiling roof area uses inclined surface
RdSAP 10 §3.8 "Roof area" spec:
"Roof area is the greatest of the floor areas on each level...
In the case of a pitched roof with a sloping ceiling, divide the
area so obtained by cos(30°)."
The cascade previously used `top_floor_area_m2` (horizontal projection)
verbatim for the roof area calculation — correct for flat roofs and
pitched-with-loft (where assessors measure on the horizontal), but
~15% under-area for PS pitched-sloping-ceiling roofs (1/cos(30°) =
1.1547). For cert 001479 Ext1 + Ext2 (both PS sloping ceiling):
Ext1: cascade 5.37 m² × 0.15 = 0.81 W/K
worksheet 6.20 m² × 0.15 = 0.93 W/K (delta -0.12)
Ext2: cascade 1.92 m² × 2.30 = 4.42 W/K
worksheet 2.22 m² × 2.30 = 5.11 W/K (delta -0.69)
Total roof W/K shortfall: -0.81
Fix: detect PS pitched-sloping-ceiling roofs via `bp.roof_construction
_type` (string lodgement from the Summary §8 "Roof Type" line) and
apply the 1/cos(30°) inclination factor before rounding the gross
roof area.
Schema addition: `SapBuildingPart.roof_construction_type: Optional[
str] = None` mirrors the existing `floor_construction_type`. Mapper
populates it via `_strip_code(roof.roof_type)` for both Main and
Extension bps — the Elmhurst Summary lodges the roof type
explicitly (e.g. "PS Pitched, sloping ceiling" / "PA Pitched (slates
/tiles), access to loft" / "Flat").
**Result: cert 001479 Summary → mapper → cascade now lands at SAP
69.0094 EXACT (delta -0.0000) — Layer 2 GREEN at 1e-4.** Full fabric
breakdown matches the worksheet exactly:
fabric_heat_loss = 139.4957 W/K ✓
walls = 39.7652 ✓ party = 17.0700 ✓
roof = 10.3438 ✓ floor = 23.1705 ✓
windows = 43.5962 ✓ doors = 5.5500 ✓
Layer 2 status across the 7 cert chain tests:
000477 GREEN (was GREEN)
000516 GREEN (was GREEN)
001479 GREEN (new — was +1.19 before Slice 87)
000474 RED -0.7524 (Elmhurst (12) non-spec — orthogonal)
000480 RED -1.0273 (Elmhurst (12) non-spec — orthogonal)
000487 RED +0.4834 (Elmhurst (12) non-spec — orthogonal)
000490 RED -1.1042 (Elmhurst (12) non-spec — orthogonal)
Cohort cascade pins remain GREEN (66 of 66) — hand-built fixtures
have roof_construction_type=None (default) so the new code path is
inert for them; their roofs use RR detailed_surfaces with explicit
areas already.
Pyright net-zero on every touched file (heat_transmission 13 → 13,
mapper 35 → 35, epc_property_data 0 → 0).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
c40679d1e1
commit
006e9842c9
3 changed files with 16 additions and 5 deletions
|
|
@ -427,6 +427,7 @@ class SapBuildingPart:
|
|||
floor_u_value_known: Optional[bool] = None
|
||||
|
||||
roof_construction: Optional[int] = None
|
||||
roof_construction_type: Optional[str] = None # str from site notes e.g. "PS Pitched, sloping ceiling"
|
||||
roof_insulation_location: Optional[Union[int, str]] = (
|
||||
None # TODO: make enum/mapping?
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2178,6 +2178,7 @@ def _map_elmhurst_building_part(
|
|||
party_wall_construction=_elmhurst_party_wall_construction_int(walls.party_wall_type),
|
||||
sap_floor_dimensions=floor_dims,
|
||||
wall_thickness_mm=walls.thickness_mm,
|
||||
roof_construction_type=_strip_code(roof.roof_type),
|
||||
roof_insulation_location=_strip_code(roof.insulation),
|
||||
roof_insulation_thickness=_resolve_sloping_ceiling_thickness(
|
||||
roof, age_code,
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ from domain.ml.rdsap_uvalues import (
|
|||
u_wall,
|
||||
u_window,
|
||||
)
|
||||
from math import floor, sqrt
|
||||
from math import cos, floor, radians, sqrt
|
||||
|
||||
|
||||
def _round_half_up(value: float, dp: int) -> float:
|
||||
|
|
@ -96,6 +96,10 @@ _WINDOW_CURTAIN_RESISTANCE_M2K_PER_W: Final[float] = 0.04
|
|||
# rounding policy — applied to gross wall / roof / floor / party / window
|
||||
# / door / alt-wall / RR sub-area inputs to the §3 cascade.
|
||||
_AREA_ROUND_DP: Final[int] = 2
|
||||
# RdSAP 10 §3.8 "Roof area" — pitched-sloping-ceiling roofs use the
|
||||
# inclined surface area (floor area divided by cos(30°)) rather than
|
||||
# the horizontal projection.
|
||||
_COS_30_DEG: Final[float] = cos(radians(30.0))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -538,10 +542,15 @@ def heat_transmission_from_cert(
|
|||
rw_area_part = (
|
||||
_round_half_up(roof_windows_area_total, _AREA_ROUND_DP) if i == 0 else 0.0
|
||||
)
|
||||
gross_roof_area = _round_half_up(
|
||||
geom["top_floor_area_m2"] if exposure.has_exposed_roof else 0.0,
|
||||
_AREA_ROUND_DP,
|
||||
)
|
||||
# RdSAP 10 §3.8 "Roof area": roof area is the greatest of the
|
||||
# floor areas on each level. For a pitched roof with a sloping
|
||||
# ceiling, divide that area by cos(30°) — the worksheet enters
|
||||
# the inclined surface area, not the horizontal projection.
|
||||
top_floor_area = geom["top_floor_area_m2"] if exposure.has_exposed_roof else 0.0
|
||||
roof_type = (part.roof_construction_type or "").lower()
|
||||
if "sloping ceiling" in roof_type:
|
||||
top_floor_area = top_floor_area / _COS_30_DEG
|
||||
gross_roof_area = _round_half_up(top_floor_area, _AREA_ROUND_DP)
|
||||
roof_area = max(0.0, gross_roof_area - rw_area_part)
|
||||
floor_area_total = _round_half_up(
|
||||
geom["ground_floor_area_m2"] if exposure.has_exposed_floor else 0.0,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue