mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Extract shared reduced-field window synthesis core across the RdSAP family 🟪
ADR-0028's deferred extraction, triggered by 17.1 as the second instance: the inherited 20.0.0 coefficients (0.148 + band multipliers + 4-way split) now live in one `_synthesise_reduced_field_windows` core. The 20.0.0 / 18.0 / 17.1 seams keep their own names (so each can diverge) but collapse to glazing-type resolution (cascade + that spec's ND handling) plus a call to the core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0f3321e655
commit
fcc2e5d0f8
1 changed files with 47 additions and 81 deletions
|
|
@ -3061,19 +3061,26 @@ _RDSAP20_GLAZED_AREA_BAND_MULTIPLIER: dict[int, float] = {
|
|||
_RDSAP20_SYNTH_ORIENTATIONS: tuple[int, ...] = (1, 3, 5, 7)
|
||||
|
||||
|
||||
def _synthesise_20_0_0_sap_windows(schema: RdSapSchema20_0_0) -> List[SapWindow]:
|
||||
"""ADR-0027 Reduced-Field Synthesis of `sap_windows` for a 20.0.0 cert.
|
||||
def _synthesise_reduced_field_windows(
|
||||
glazed_area: int,
|
||||
total_floor_area: float,
|
||||
glazing_type: int,
|
||||
) -> List[SapWindow]:
|
||||
"""ADR-0028 shared core for pre-SAP10 reduced-field glazing synthesis.
|
||||
|
||||
993/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` (width x height is the only
|
||||
quantity the calculator reads, so height=1 makes width carry the area
|
||||
exactly — matching the existing Elmhurst precedent).
|
||||
Certs with no per-window array carry only a glazed_area band + floor area;
|
||||
synthesise total glazing as `ratio x TFA x band_multiplier`, split 4-way
|
||||
across N/E/S/W with `window_width = area/4, window_height = 1.0` (width x
|
||||
height is the only quantity the calculator reads, so height=1 makes width
|
||||
carry the area exactly — the Elmhurst precedent).
|
||||
|
||||
This is the single home of the inherited 20.0.0 coefficients across the
|
||||
pre-SAP10 RdSAP family (20.0.0 / 18.0 / 17.1). `glazing_type` is pre-resolved
|
||||
by each spec's seam (cascade + that spec's own "ND" handling), keeping spec
|
||||
vocabulary out of the numeric core.
|
||||
"""
|
||||
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
|
||||
)
|
||||
band_multiplier = _RDSAP20_GLAZED_AREA_BAND_MULTIPLIER.get(glazed_area, 1.0)
|
||||
total_area = _RDSAP20_GLAZING_RATIO * float(total_floor_area) * band_multiplier
|
||||
per_window_area = total_area / len(_RDSAP20_SYNTH_ORIENTATIONS)
|
||||
return [
|
||||
SapWindow(
|
||||
|
|
@ -3081,11 +3088,7 @@ def _synthesise_20_0_0_sap_windows(schema: RdSapSchema20_0_0) -> List[SapWindow]
|
|||
glazing_gap=0,
|
||||
orientation=orientation,
|
||||
window_type=0,
|
||||
# ADR-0027: 20.0.0 glazed_type codes 1-8+ND are identical to 21.0.1's,
|
||||
# so reuse the verified 21.0.1 cascade (fixes code 1 "DG pre-2002"
|
||||
# being mis-read as single). g⊥ comes from window_transmission_details
|
||||
# (slice 6), so glazing_type only feeds the daylight g_L lookup.
|
||||
glazing_type=_api_cascade_glazing_type(schema.multiple_glazing_type),
|
||||
glazing_type=glazing_type,
|
||||
window_width=per_window_area,
|
||||
window_height=1.0,
|
||||
draught_proofed=False,
|
||||
|
|
@ -3097,6 +3100,17 @@ def _synthesise_20_0_0_sap_windows(schema: RdSapSchema20_0_0) -> List[SapWindow]
|
|||
]
|
||||
|
||||
|
||||
def _synthesise_20_0_0_sap_windows(schema: RdSapSchema20_0_0) -> List[SapWindow]:
|
||||
"""ADR-0027/0028 seam: 20.0.0 glazed_type codes 1-8+ND are identical to
|
||||
21.0.1's, so route multiple_glazing_type through the verified cascade (fixes
|
||||
code 1 "DG pre-2002" read as single), then call the shared core."""
|
||||
return _synthesise_reduced_field_windows(
|
||||
schema.glazed_area,
|
||||
schema.total_floor_area,
|
||||
_api_cascade_glazing_type(schema.multiple_glazing_type),
|
||||
)
|
||||
|
||||
|
||||
# ADR-0028: multiple_glazing_type "ND" (Not Defined, 69/1000 18.0 certs) has no
|
||||
# cascade mapping — treat as the DG-modal default (cascade code 2 → daylight g_L
|
||||
# 0.80, matching the calculator's `_G_LIGHT_DEFAULT` for unknown glazing).
|
||||
|
|
@ -3104,46 +3118,21 @@ _RDSAP18_ND_GLAZING_TYPE: int = 2
|
|||
|
||||
|
||||
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)
|
||||
# ADR-0028: 18.0 glazed_type codes 1-8 are identical to 20.0.0's (verified
|
||||
# against epc_codes.csv), so reuse the verified cascade for integer codes;
|
||||
# the "ND" string falls back to the DG-modal default.
|
||||
"""ADR-0028 seam: reuses the inherited 20.0.0 coefficients via the shared
|
||||
core (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`).
|
||||
18.0 glazed_type codes 1-8 are identical to 20.0.0's (verified vs
|
||||
epc_codes.csv): route integer codes through the verified cascade; the "ND"
|
||||
string falls back to the DG-modal default. Own seam so 18.0 can diverge."""
|
||||
mgt = schema.multiple_glazing_type
|
||||
glazing_type = (
|
||||
_api_cascade_glazing_type(mgt)
|
||||
if isinstance(mgt, int)
|
||||
else _RDSAP18_ND_GLAZING_TYPE
|
||||
)
|
||||
return [
|
||||
SapWindow(
|
||||
frame_material=None,
|
||||
glazing_gap=0,
|
||||
orientation=orientation,
|
||||
window_type=0,
|
||||
glazing_type=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
|
||||
]
|
||||
return _synthesise_reduced_field_windows(
|
||||
schema.glazed_area, schema.total_floor_area, glazing_type
|
||||
)
|
||||
|
||||
|
||||
# ADR-0028: multiple_glazing_type "ND" (Not Defined, 56/1000 17.1 certs) → the
|
||||
|
|
@ -3152,43 +3141,20 @@ _RDSAP17_1_ND_GLAZING_TYPE: int = 2
|
|||
|
||||
|
||||
def _synthesise_17_1_sap_windows(schema: RdSapSchema17_1) -> List[SapWindow]:
|
||||
"""ADR-0028 Reduced-Field Synthesis of `sap_windows` for a 17.1 cert.
|
||||
|
||||
A separate seam from the 18.0/20.0.0 helpers so 17.1 can diverge, but it
|
||||
reuses the inherited 20.0.0 coefficients unchanged (ADR-0028: 969/1000 band-1
|
||||
with no measured band-1 windows; its band-4 rich certs reproduce the model).
|
||||
990/1000 certs carry no per-window array — synthesise total glazing as
|
||||
`ratio x TFA`, split 4-way across N/E/S/W with 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)
|
||||
# ADR-0028: 17.1 glazed_type codes 1-8 are identical to 20.0.0's (verified
|
||||
# against epc_codes.csv); the "ND" string falls back to the DG-modal default.
|
||||
"""ADR-0028 seam: reuses the inherited 20.0.0 coefficients via the shared
|
||||
core (969/1000 band-1 with no measured band-1 windows; band-4 rich certs
|
||||
reproduce the model). 17.1 glazed_type codes 1-8 are identical to 20.0.0's:
|
||||
route integer codes through the verified cascade; the "ND" string falls back
|
||||
to the DG-modal default. Own seam so 17.1 can diverge."""
|
||||
mgt = schema.multiple_glazing_type
|
||||
glazing_type = (
|
||||
_api_cascade_glazing_type(mgt)
|
||||
if isinstance(mgt, int)
|
||||
else _RDSAP17_1_ND_GLAZING_TYPE
|
||||
)
|
||||
return [
|
||||
SapWindow(
|
||||
frame_material=None,
|
||||
glazing_gap=0,
|
||||
orientation=orientation,
|
||||
window_type=0,
|
||||
glazing_type=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
|
||||
]
|
||||
return _synthesise_reduced_field_windows(
|
||||
schema.glazed_area, schema.total_floor_area, glazing_type
|
||||
)
|
||||
|
||||
|
||||
# GOV.UK API `glazing_type` integer → (u_value W/m²K, g_perpendicular,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue