From e0ff02b1ac15ee983e9ffc59811649ba2382d68e Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 11 Jun 2026 11:49:38 +0000 Subject: [PATCH] =?UTF-8?q?Synthesise=20glazing=20from=20the=20glazed=5Far?= =?UTF-8?q?ea=20band=20for=20windowless=2018.0=20certs=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- datatypes/epc/domain/mapper.py | 40 +++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index e2c295c2..688e5ebe 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -852,7 +852,9 @@ class EpcPropertyDataMapper: permanent_shutters_present=False, ) for w in schema.sap_windows - ], + ] + if schema.sap_windows + else _synthesise_18_0_sap_windows(schema), sap_energy_source=SapEnergySource( mains_gas=es.mains_gas == "Y", meter_type=str(es.meter_type), @@ -3007,6 +3009,42 @@ def _synthesise_20_0_0_sap_windows(schema: RdSapSchema20_0_0) -> List[SapWindow] ] +def _synthesise_18_0_sap_windows(schema: RdSapSchema18_0) -> List[SapWindow]: + """ADR-0028 Reduced-Field Synthesis of `sap_windows` for an 18.0 cert. + + A separate seam from the 20.0.0 helper so 18.0 can diverge as we learn more, + but it reuses the inherited 20.0.0 coefficients unchanged (ADR-0028: the + 18.0 corpus can't self-fit — 958/1000 band-1 with no measured band-1 windows + — and its band-4 rich certs reproduce `0.148 x 1.51 = 0.223`). 990/1000 certs + carry no per-window array, only a glazed_area band + floor area; synthesise + total glazing as `ratio x TFA`, split 4-way across N/E/S/W with + `window_width = area/4, window_height = 1.0`. + """ + band_multiplier = _RDSAP20_GLAZED_AREA_BAND_MULTIPLIER.get(schema.glazed_area, 1.0) + total_area = ( + _RDSAP20_GLAZING_RATIO * float(schema.total_floor_area) * band_multiplier + ) + per_window_area = total_area / len(_RDSAP20_SYNTH_ORIENTATIONS) + return [ + SapWindow( + frame_material=None, + glazing_gap=0, + orientation=orientation, + window_type=0, + # ADR-0028: 18.0 glazed_type codes 1-8+ND are identical to 20.0.0's + # (verified against epc_codes.csv), so reuse the verified cascade. + glazing_type=_api_cascade_glazing_type(schema.multiple_glazing_type), + window_width=per_window_area, + window_height=1.0, + draught_proofed=False, + window_location=0, + window_wall_type=0, + permanent_shutters_present=False, + ) + for orientation in _RDSAP20_SYNTH_ORIENTATIONS + ] + + # GOV.UK API `glazing_type` integer → (u_value W/m²K, g_perpendicular, # frame_factor) lookup the cascade reads via `window_transmission_ # details` for per-window cascade fidelity. The cascade defaults to a