mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
§6 slice 4: 6 Elmhurst fixtures conform on (83) + (84) to ≤5e-3 W
Adds SECTION_6_VERTICAL_WINDOWS, SECTION_6_ROOF_WINDOWS,
SECTION_6_ROOFLIGHTS, LINE_83_M_TOTAL_SOLAR_W, LINE_84_M_TOTAL_GAINS_W
to each of the 6 _elmhurst_worksheet_*.py fixtures, plus an
ALL_FIXTURES-parametrised end-to-end test in test_solar_gains.py.
144 assertions GREEN (12 months × 2 lines × 6 fixtures) at abs=5e-3 W:
- (83) total solar gains via solar_gains_from_cert
- (84) = §5 LINE_73_M_TOTAL_INTERNAL_GAINS_W + (83) — cross-checks
§5 conformance and §6 orchestrator in one go.
000516 exercises the roof window path (1.18 m² NE at 45° pitch, Z=1.0).
000474/000477/000487 carry mixed glazing types (g⊥=0.72 + g⊥=0.76 within
the same fixture) — verifies _g_perpendicular respects per-window
manufacturer-declared values.
`_build_section_6_epc(fixture)` is local to the test (handover §11):
fixture build_epc()s stay untouched. make_window gains a convenience
`solar_transmittance` shortcut so fixture literals stay readable.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d56fef4d62
commit
377caea20a
8 changed files with 234 additions and 8 deletions
|
|
@ -164,8 +164,20 @@ def make_window(
|
|||
permanent_shutters_present: Union[bool, str] = False,
|
||||
frame_material: Optional[str] = "PVC",
|
||||
window_transmission_details: Optional[WindowTransmissionDetails] = None,
|
||||
solar_transmittance: Optional[float] = None,
|
||||
u_value: float = 2.8,
|
||||
) -> SapWindow:
|
||||
"""Build a SapWindow with SAP10 defaults; override the fields the test cares about."""
|
||||
"""Build a SapWindow with SAP10 defaults; override the fields the test cares about.
|
||||
|
||||
`solar_transmittance` is a shortcut that builds a
|
||||
`WindowTransmissionDetails(u_value, data_source=1, solar_transmittance)`
|
||||
when `window_transmission_details` isn't supplied directly — keeps
|
||||
Elmhurst §6 fixture literals tight.
|
||||
"""
|
||||
if window_transmission_details is None and solar_transmittance is not None:
|
||||
window_transmission_details = WindowTransmissionDetails(
|
||||
u_value=u_value, data_source=1, solar_transmittance=solar_transmittance,
|
||||
)
|
||||
return SapWindow(
|
||||
frame_material=frame_material,
|
||||
glazing_gap=glazing_gap,
|
||||
|
|
|
|||
|
|
@ -26,8 +26,10 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
EpcPropertyData,
|
||||
SapBuildingPart,
|
||||
SapFloorDimension,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
|
|
@ -275,3 +277,29 @@ LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = (
|
|||
446.3699, 444.6484, 431.0234, 405.3696, 380.1328, 350.1810,
|
||||
336.5702, 339.0214, 352.0014, 382.2118, 410.3693, 434.9896,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# §6 Solar gains — cert-derived inputs + expected outputs
|
||||
# ============================================================================
|
||||
# 5 wall windows across 2 glazing types (Manufacturer-declared g⊥):
|
||||
# "Double between 2002" g=0.72: E 3.74, SE 0.50, NW 1.98
|
||||
# "Double with unknown" g=0.76: E 3.74, NW 1.76
|
||||
# All PVC frame. No roof windows, no rooflights.
|
||||
SECTION_6_VERTICAL_WINDOWS: tuple[SapWindow, ...] = (
|
||||
make_window(orientation=3, width=3.74, height=1.0, solar_transmittance=0.72),
|
||||
make_window(orientation=4, width=0.50, height=1.0, solar_transmittance=0.72),
|
||||
make_window(orientation=8, width=1.98, height=1.0, solar_transmittance=0.72),
|
||||
make_window(orientation=3, width=3.74, height=1.0, solar_transmittance=0.76),
|
||||
make_window(orientation=8, width=1.76, height=1.0, solar_transmittance=0.76),
|
||||
)
|
||||
SECTION_6_ROOF_WINDOWS: tuple[RoofWindowInput, ...] = ()
|
||||
SECTION_6_ROOFLIGHTS: tuple[RooflightInput, ...] = ()
|
||||
|
||||
LINE_83_M_TOTAL_SOLAR_W: tuple[float, ...] = (
|
||||
74.2861, 144.8943, 240.3371, 357.4289, 446.8944, 462.0286,
|
||||
437.9571, 369.7981, 281.3970, 172.1314, 92.4825, 61.2179,
|
||||
)
|
||||
LINE_84_M_TOTAL_GAINS_W: tuple[float, ...] = (
|
||||
520.6560, 589.5427, 671.3605, 762.7985, 827.0272, 812.2096,
|
||||
774.5274, 708.8195, 633.3985, 554.3432, 502.8518, 496.2075,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
SapBuildingPart,
|
||||
SapFloorDimension,
|
||||
SapRoomInRoof,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
|
|
@ -212,3 +214,27 @@ LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = (
|
|||
537.2284, 535.1563, 517.9364, 486.2482, 454.3577, 418.6634,
|
||||
401.8987, 404.7492, 421.3137, 457.4575, 492.3897, 522.7868,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# §6 Solar gains — cert-derived inputs + expected outputs
|
||||
# ============================================================================
|
||||
# 3 wall windows across 2 glazing types (Manufacturer-declared g⊥):
|
||||
# "Double between 2002" g=0.72: E 1.28, W 6.76
|
||||
# "Double with unknown" g=0.76: W 1.17
|
||||
# All PVC frame. No roof windows, no rooflights.
|
||||
SECTION_6_VERTICAL_WINDOWS: tuple[SapWindow, ...] = (
|
||||
make_window(orientation=3, width=1.28, height=1.0, solar_transmittance=0.72),
|
||||
make_window(orientation=7, width=6.76, height=1.0, solar_transmittance=0.72),
|
||||
make_window(orientation=7, width=1.17, height=1.0, solar_transmittance=0.76),
|
||||
)
|
||||
SECTION_6_ROOF_WINDOWS: tuple[RoofWindowInput, ...] = ()
|
||||
SECTION_6_ROOFLIGHTS: tuple[RooflightInput, ...] = ()
|
||||
|
||||
LINE_83_M_TOTAL_SOLAR_W: tuple[float, ...] = (
|
||||
63.6246, 124.4632, 204.9732, 298.9411, 366.3636, 375.0383,
|
||||
357.0518, 306.7022, 238.3923, 147.6861, 79.3324, 52.3218,
|
||||
)
|
||||
LINE_84_M_TOTAL_GAINS_W: tuple[float, ...] = (
|
||||
600.8530, 659.6195, 722.9096, 785.1894, 820.7212, 793.7017,
|
||||
758.9505, 711.4514, 659.7060, 605.1436, 571.7221, 575.1085,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
SapBuildingPart,
|
||||
SapFloorDimension,
|
||||
SapRoomInRoof,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
|
|
@ -244,3 +246,24 @@ LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = (
|
|||
574.7298, 572.6278, 554.4769, 521.1014, 487.3662, 450.0743,
|
||||
432.3496, 435.2074, 452.6494, 490.4110, 527.2724, 559.3723,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# §6 Solar gains — cert-derived inputs + expected outputs
|
||||
# ============================================================================
|
||||
# 2 wall windows, both Manufacturer g⊥=0.76 / PVC: NE 8.74, SW 1.80.
|
||||
# No roof windows, no rooflights.
|
||||
SECTION_6_VERTICAL_WINDOWS: tuple[SapWindow, ...] = (
|
||||
make_window(orientation=2, width=8.74, height=1.0, solar_transmittance=0.76),
|
||||
make_window(orientation=6, width=1.80, height=1.0, solar_transmittance=0.76),
|
||||
)
|
||||
SECTION_6_ROOF_WINDOWS: tuple[RoofWindowInput, ...] = ()
|
||||
SECTION_6_ROOFLIGHTS: tuple[RooflightInput, ...] = ()
|
||||
|
||||
LINE_83_M_TOTAL_SOLAR_W: tuple[float, ...] = (
|
||||
60.7732, 115.5953, 190.2388, 289.4797, 373.3152, 392.2015,
|
||||
369.1410, 303.2958, 224.0851, 136.4060, 74.9915, 50.5862,
|
||||
)
|
||||
LINE_84_M_TOTAL_GAINS_W: tuple[float, ...] = (
|
||||
635.5030, 688.2231, 744.7156, 810.5810, 860.6814, 842.2758,
|
||||
801.4906, 738.5032, 676.7345, 626.8170, 602.2639, 609.9585,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
SapBuildingPart,
|
||||
SapFloorDimension,
|
||||
SapRoomInRoof,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
# RdSAP wall_construction code seen in the cert→worksheet mapping. The
|
||||
|
|
@ -259,3 +261,26 @@ LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = (
|
|||
548.8580, 546.8173, 529.7869, 499.9224, 468.4056, 433.5905,
|
||||
417.8752, 420.2523, 436.8085, 472.1723, 505.4898, 534.4324,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# §6 Solar gains — cert-derived inputs + expected outputs
|
||||
# ============================================================================
|
||||
# 2 wall windows, both South-facing, 2 glazing types (Manufacturer g⊥):
|
||||
# "Double with unknown" g=0.76: S 0.77
|
||||
# "Double between 2002" g=0.72: S 6.69
|
||||
# All PVC frame. No roof windows, no rooflights.
|
||||
SECTION_6_VERTICAL_WINDOWS: tuple[SapWindow, ...] = (
|
||||
make_window(orientation=5, width=0.77, height=1.0, solar_transmittance=0.76),
|
||||
make_window(orientation=5, width=6.69, height=1.0, solar_transmittance=0.72),
|
||||
)
|
||||
SECTION_6_ROOF_WINDOWS: tuple[RoofWindowInput, ...] = ()
|
||||
SECTION_6_ROOFLIGHTS: tuple[RooflightInput, ...] = ()
|
||||
|
||||
LINE_83_M_TOTAL_SOLAR_W: tuple[float, ...] = (
|
||||
122.5143, 200.6468, 255.5884, 288.8704, 301.0208, 289.6916,
|
||||
283.0463, 274.8772, 266.9923, 216.4164, 145.2212, 105.8636,
|
||||
)
|
||||
LINE_84_M_TOTAL_GAINS_W: tuple[float, ...] = (
|
||||
671.3723, 747.4641, 785.3754, 788.7928, 769.4264, 723.2821,
|
||||
700.9215, 695.1295, 703.8008, 688.5887, 650.7110, 640.2960,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,8 +28,10 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
EpcPropertyData,
|
||||
SapBuildingPart,
|
||||
SapFloorDimension,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
|
|
@ -255,3 +257,25 @@ LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = (
|
|||
505.8067, 503.7906, 488.2293, 459.2325, 430.5641, 397.6412,
|
||||
382.3822, 385.1801, 400.0449, 433.4120, 465.2242, 492.9448,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# §6 Solar gains — cert-derived inputs + expected outputs
|
||||
# ============================================================================
|
||||
# 3 wall windows: NE 0.81, SE 5.52, NW 2.70 — all DG pre-2002 / PVC / 12 mm
|
||||
# with manufacturer g⊥=0.76. No roof windows, no rooflights.
|
||||
SECTION_6_VERTICAL_WINDOWS: tuple[SapWindow, ...] = (
|
||||
make_window(orientation=2, width=0.81, height=1.0, solar_transmittance=0.76),
|
||||
make_window(orientation=4, width=5.52, height=1.0, solar_transmittance=0.76),
|
||||
make_window(orientation=8, width=2.70, height=1.0, solar_transmittance=0.76),
|
||||
)
|
||||
SECTION_6_ROOF_WINDOWS: tuple[RoofWindowInput, ...] = ()
|
||||
SECTION_6_ROOFLIGHTS: tuple[RooflightInput, ...] = ()
|
||||
|
||||
LINE_83_M_TOTAL_SOLAR_W: tuple[float, ...] = (
|
||||
89.4795, 157.2665, 228.0608, 304.1703, 360.4042, 366.4669,
|
||||
349.7056, 306.4273, 254.2093, 177.2863, 108.0591, 76.0043,
|
||||
)
|
||||
LINE_84_M_TOTAL_GAINS_W: tuple[float, ...] = (
|
||||
595.2863, 661.0571, 716.2901, 763.4028, 790.9684, 764.1081,
|
||||
732.0878, 691.6074, 654.2542, 610.6983, 573.2833, 568.9492,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
SapBuildingPart,
|
||||
SapFloorDimension,
|
||||
SapRoomInRoof,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.solar_gains import Orientation, RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
|
|
@ -222,3 +224,30 @@ LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = (
|
|||
600.1430, 597.7026, 578.1360, 542.0517, 505.9694, 466.5482,
|
||||
447.5794, 450.9272, 469.7096, 509.7947, 549.4610, 583.9178,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# §6 Solar gains — cert-derived inputs + expected outputs
|
||||
# ============================================================================
|
||||
# 2 wall windows (Manufacturer g⊥=0.76 / PVC): NE 3.88, SW 4.43.
|
||||
# 1 roof window (NE 1.18, Manufacturer g⊥=0.76, pitch 45° per RdSAP10
|
||||
# Table 24 default, Wood frame FF=0.70). No rooflights.
|
||||
SECTION_6_VERTICAL_WINDOWS: tuple[SapWindow, ...] = (
|
||||
make_window(orientation=2, width=3.88, height=1.0, solar_transmittance=0.76),
|
||||
make_window(orientation=6, width=4.43, height=1.0, solar_transmittance=0.76),
|
||||
)
|
||||
SECTION_6_ROOF_WINDOWS: tuple[RoofWindowInput, ...] = (
|
||||
RoofWindowInput(
|
||||
area_m2=1.18, orientation=Orientation.NE,
|
||||
g_perpendicular=0.76, frame_factor=0.70,
|
||||
),
|
||||
)
|
||||
SECTION_6_ROOFLIGHTS: tuple[RooflightInput, ...] = ()
|
||||
|
||||
LINE_83_M_TOTAL_SOLAR_W: tuple[float, ...] = (
|
||||
85.4797, 154.2449, 234.3467, 329.9943, 406.2034, 419.4674,
|
||||
397.6609, 338.2558, 267.0293, 176.7227, 103.9502, 72.1445,
|
||||
)
|
||||
LINE_84_M_TOTAL_GAINS_W: tuple[float, ...] = (
|
||||
685.6226, 751.9476, 812.4827, 872.0460, 912.1729, 886.0156,
|
||||
845.2404, 789.1829, 736.7389, 686.5174, 653.4111, 656.0623,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,9 +9,15 @@ Appendix U §U3.2 (pages 127-129), Table U4 (latitudes), Table U5
|
|||
(k1-k9 constants per orientation).
|
||||
"""
|
||||
|
||||
from types import ModuleType
|
||||
|
||||
import pytest
|
||||
|
||||
from datatypes.epc.domain.epc_property_data import SapWindow, WindowTransmissionDetails
|
||||
from datatypes.epc.domain.epc_property_data import (
|
||||
EpcPropertyData,
|
||||
SapWindow,
|
||||
WindowTransmissionDetails,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.internal_gains import OvershadingCategory
|
||||
from domain.sap.worksheet.solar_gains import (
|
||||
|
|
@ -23,6 +29,7 @@ from domain.sap.worksheet.solar_gains import (
|
|||
window_solar_gain_w,
|
||||
z_solar_for_overshading,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
|
||||
|
||||
# Worksheet U985-0001-000490 reference (UK-avg weather, region 0):
|
||||
|
|
@ -240,3 +247,55 @@ def test_out_of_range_region_raises_value_error() -> None:
|
|||
surface_solar_flux_w_per_m2(
|
||||
orientation=Orientation.S, pitch_deg=90.0, region=22, month=7,
|
||||
)
|
||||
|
||||
|
||||
def _build_section_6_epc(fixture: ModuleType) -> EpcPropertyData:
|
||||
"""Wrap a fixture's base build_epc() with the §6-relevant fields it
|
||||
doesn't yet carry: per-orientation vertical wall windows lodged on the
|
||||
epc. Roof windows + rooflights pass through as orchestrator args
|
||||
because RdSAP cert summaries don't lodge them distinctly. Kept local
|
||||
to the §6 test so legacy build_epc()s stay pinned for §1-§4 +
|
||||
e2e SAP-score regressions (handover §11)."""
|
||||
base = fixture.build_epc()
|
||||
base.sap_windows = list(fixture.SECTION_6_VERTICAL_WINDOWS)
|
||||
return base
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fixture", ALL_FIXTURES, ids=[fixture_id(f) for f in ALL_FIXTURES])
|
||||
def test_solar_gains_from_cert_matches_elmhurst_worksheet_all_fixtures(
|
||||
fixture: ModuleType,
|
||||
) -> None:
|
||||
"""End-to-end §6 orchestrator against every Elmhurst conformance fixture.
|
||||
|
||||
Each fixture pins its own §6 input constants
|
||||
(SECTION_6_VERTICAL_WINDOWS, SECTION_6_ROOF_WINDOWS,
|
||||
SECTION_6_ROOFLIGHTS) + worksheet outputs (LINE_83_M, LINE_84_M). The
|
||||
test composes an EPC from the cert-shape windows and drives
|
||||
solar_gains_from_cert at region 0 (UK-avg, the SAP rating pass).
|
||||
(84) reconciles via the §5 LINE_73_M total internal gains plus our
|
||||
new (83), checking both halves of the worksheet sum.
|
||||
"""
|
||||
# Arrange
|
||||
epc = _build_section_6_epc(fixture)
|
||||
|
||||
# Act
|
||||
result = solar_gains_from_cert(
|
||||
epc=epc,
|
||||
region=0,
|
||||
overshading=OvershadingCategory.AVERAGE,
|
||||
roof_windows=fixture.SECTION_6_ROOF_WINDOWS,
|
||||
rooflights=fixture.SECTION_6_ROOFLIGHTS,
|
||||
)
|
||||
|
||||
# Assert
|
||||
for m in range(12):
|
||||
assert result.total_solar_gains_monthly_w[m] == pytest.approx(
|
||||
fixture.LINE_83_M_TOTAL_SOLAR_W[m], abs=5e-3
|
||||
), f"(83) month {m+1}"
|
||||
line_84 = (
|
||||
fixture.LINE_73_M_TOTAL_INTERNAL_GAINS_W[m]
|
||||
+ result.total_solar_gains_monthly_w[m]
|
||||
)
|
||||
assert line_84 == pytest.approx(
|
||||
fixture.LINE_84_M_TOTAL_GAINS_W[m], abs=5e-3
|
||||
), f"(84) month {m+1}"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue