mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 25d: 000487 §4 LINE_65 closure — derive LINE_64A from cert (App J step 8)
Closes the final §4 cascade fail. SAP10.2 Appendix J step 8 (p.82)
specifies the electric-shower kWh formula:
N_ES = N_shower / N_outlets (eq J16)
EES,j,m = N_ES × f_beh × P_ES,j × 0.1 × n_m (eq J17)
EES,m = Σ EES,j,m (eq J18)
where P_ES,j defaults to Table J4 (p.83) row "Instantaneous electric
shower" = 9.3 kW for assessments of existing dwellings, and 0.1 = the
6-minute shower duration in hours.
For 000487 (N=2.492, has_bath, 1 electric shower, 0 mixer outlets):
N_shower = 0.45 × 2.492 + 0.65 = 1.7714
N_outlets = 1 (just the electric)
N_ES = 1.7714 / 1 = 1.7714
Jan: 1.7714 × 1.035 × 9.3 × 0.1 × 31 = 52.86 kWh ≈ PDF LINE_64A[1] = 52.8566 ✓
LINE_65 (heat gains from water heating) was undercounting by 25% of
the missing LINE_64A (the recovery factor for instantaneous electric
showers per the heat-gains formula); deriving LINE_64A from cert
closes it.
Changes:
- water_heating.py: new `electric_shower_monthly_kwh` function +
`electric_shower_count` parameter to `water_heating_from_cert`.
When count > 0 and no override, derives LINE_64A from N_outlets +
Table J4 default P_ES.
- cert_to_inputs.py: `_electric_shower_count_from_cert` helper +
plumb through both the §4 section helper and internal cascade.
Per-fixture cluster status (was/now):
§3 24/24 → 24/24 ✓ all 6 fixtures
§4 53/54 → 54/54 ✓ all 6 fixtures
§5 52/54 → 54/54 ✓ all 6 fixtures
§6 11/12 → 12/12 ✓ all 6 fixtures
§7 45/60 → 52/60 (000487 cascade closed; LINE_92/93 marginal on
000474/477/480/490 remains)
Scoreboard:
section_cascade_pins: 293 → 304 PASS (+11; 97.4% closure)
e2e SapResult: 32 → 33 PASS (+1, water_heating closure cascades)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
8520a52ee9
commit
147da90a5a
3 changed files with 79 additions and 11 deletions
|
|
@ -133,12 +133,12 @@ Two test files contain the strict pins:
|
|||
Total: **169 PASS / 83 FAIL** across the strict pins. 4 of 6 fixtures fully
|
||||
close §1+§2+§4. 000487 is the worst (RR fixture defect propagates everywhere).
|
||||
|
||||
(Post-slice-25c: section_cascade_pins 293 PASS / 19 FAIL, e2e SapResult
|
||||
32 PASS / 40 FAIL. §3 closes 24/24. §4 closes 53/54 (only 000487
|
||||
LINE_65 remains — Appendix J step 8 electric-shower kWh derivation,
|
||||
slice 25d). §5 52/54, §6 11/12 — only 000487 cascade. 000477's §4
|
||||
combi-loss cluster was closed by fixing the SAP10.2 Table 3c (p.162)
|
||||
M+L lower bound: my DVF used `V < 100.0` where spec says `V < 100.2`.)
|
||||
(Post-slice-25d: section_cascade_pins 304 PASS / 8 FAIL, e2e SapResult
|
||||
33 PASS / 39 FAIL. §3 + §4 + §5 + §6 ALL fully close for all 6
|
||||
fixtures. §7 closes 52/60 — only LINE_92/93 marginal ~0.0001 K
|
||||
residual on 000474/477/480/490 remains (the precision artefact
|
||||
discovered in slice 26c, no spec-grounded fix identified). Cascade
|
||||
total closure: 312 tests, 304 PASS = 97.4%.)
|
||||
|
||||
### B.2 SapResult pin matrix (post-slice-22/23)
|
||||
|
||||
|
|
@ -199,6 +199,7 @@ fixture | section §4 pin status
|
|||
### B.5 Recent slices (in reverse order — newest first)
|
||||
|
||||
```
|
||||
Slice 25d: 000487 §4 LINE_65 closure — derive LINE_64A electric-shower kWh from cert (Appendix J step 8, p.82)
|
||||
Slice 25c: 000477 §4/§5/§6 closure — SAP10.2 Table 3c (p.162) M+L lower bound 100.0 → 100.2
|
||||
Slice 25b: 000487 §4 closure (7/8) — has_electric_shower + mixer/electric counts on SapHeating, Appendix J step 2a fix
|
||||
Slice 25a: 000487 §3 closure — detailed RR + gable_wall_external + Ext1 alt U=1.9 + §3.8 max-floor roof + half-up rounding
|
||||
|
|
|
|||
|
|
@ -719,6 +719,7 @@ def water_heating_section_from_cert(
|
|||
else None
|
||||
)
|
||||
has_electric_shower = _has_electric_shower_from_cert(epc)
|
||||
electric_shower_count = _electric_shower_count_from_cert(epc)
|
||||
bootstrap = water_heating_from_cert(
|
||||
epc=epc,
|
||||
mixer_shower_flow_rates_l_per_min=_mixer_shower_flow_rates_from_cert(epc),
|
||||
|
|
@ -726,6 +727,7 @@ def water_heating_section_from_cert(
|
|||
cold_water_temps_c=TABLE_J1_TCOLD_FROM_MAINS_C,
|
||||
low_water_use=False,
|
||||
has_electric_shower=has_electric_shower,
|
||||
electric_shower_count=electric_shower_count,
|
||||
)
|
||||
combi_loss_override = pcdb_combi_loss_override(
|
||||
pcdb_main,
|
||||
|
|
@ -740,6 +742,7 @@ def water_heating_section_from_cert(
|
|||
low_water_use=False,
|
||||
combi_loss_monthly_kwh_override=combi_loss_override,
|
||||
has_electric_shower=has_electric_shower,
|
||||
electric_shower_count=electric_shower_count,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -979,6 +982,14 @@ def _has_electric_shower_from_cert(epc: EpcPropertyData) -> bool:
|
|||
return (n or 0) >= 1
|
||||
|
||||
|
||||
def _electric_shower_count_from_cert(epc: EpcPropertyData) -> int:
|
||||
"""Cert-lodged count of instantaneous electric showers. Drives the
|
||||
LINE_64A energy derivation in `water_heating_from_cert` per SAP10.2
|
||||
Appendix J (p.82) step 8."""
|
||||
n = epc.sap_heating.electric_shower_count if epc.sap_heating is not None else None
|
||||
return max(0, n or 0)
|
||||
|
||||
|
||||
def _has_bath_from_cert(epc: EpcPropertyData) -> bool:
|
||||
"""True iff cert lodges ≥ 1 bath. `number_baths is None` is treated as
|
||||
bath present (modal UK lodging — bathless dwellings are rare and
|
||||
|
|
@ -1070,6 +1081,7 @@ def _water_heating_worksheet_and_gains(
|
|||
if epc.total_floor_area_m2 is None:
|
||||
return None, zero_monthly
|
||||
has_electric_shower = _has_electric_shower_from_cert(epc)
|
||||
electric_shower_count = _electric_shower_count_from_cert(epc)
|
||||
bootstrap = water_heating_from_cert(
|
||||
epc=epc,
|
||||
mixer_shower_flow_rates_l_per_min=_mixer_shower_flow_rates_from_cert(epc),
|
||||
|
|
@ -1077,6 +1089,7 @@ def _water_heating_worksheet_and_gains(
|
|||
cold_water_temps_c=TABLE_J1_TCOLD_FROM_MAINS_C,
|
||||
low_water_use=False,
|
||||
has_electric_shower=has_electric_shower,
|
||||
electric_shower_count=electric_shower_count,
|
||||
)
|
||||
combi_loss_override = pcdb_combi_loss_override(
|
||||
pcdb_record,
|
||||
|
|
@ -1091,6 +1104,7 @@ def _water_heating_worksheet_and_gains(
|
|||
low_water_use=False,
|
||||
combi_loss_monthly_kwh_override=combi_loss_override,
|
||||
has_electric_shower=has_electric_shower,
|
||||
electric_shower_count=electric_shower_count,
|
||||
)
|
||||
return wh_result, wh_result.heat_gains_monthly_kwh
|
||||
|
||||
|
|
|
|||
|
|
@ -586,6 +586,49 @@ def hot_water_other_uses_monthly_l_per_day(
|
|||
return tuple(annual_average * f for f in _TABLE_J2_MONTHLY_FACTORS)
|
||||
|
||||
|
||||
_ELECTRIC_SHOWER_DEFAULT_KW: Final[float] = 9.3
|
||||
_ELECTRIC_SHOWER_DURATION_HOURS: Final[float] = 0.1
|
||||
|
||||
|
||||
def electric_shower_monthly_kwh(
|
||||
*,
|
||||
n_occupants: float,
|
||||
has_bath: bool,
|
||||
n_outlets: int,
|
||||
n_electric_showers: int,
|
||||
rated_power_kw: float = _ELECTRIC_SHOWER_DEFAULT_KW,
|
||||
) -> tuple[float, ...]:
|
||||
"""SAP 10.2 §4 line (64a)m via Appendix J (p.82) step 8.
|
||||
|
||||
Per outlet for each month:
|
||||
N_ES = N_shower / N_outlets (eq J16)
|
||||
E_ES,j,m = N_ES × f_beh × P_ES,j × 0.1 × n_m (eq J17)
|
||||
Summed across electric-shower outlets j (eq J18).
|
||||
|
||||
N_shower from step 1c (same branch as `hot_water_mixer_showers_monthly_
|
||||
l_per_day`); N_outlets is the cert-lodged total of mixer + electric
|
||||
outlets. P_ES,j defaults to Table J4 row "Instantaneous electric
|
||||
shower" = 9.3 kW for assessments of existing dwellings.
|
||||
|
||||
Returns 12-tuple of zeros when there are no electric showers."""
|
||||
if n_electric_showers <= 0 or n_outlets <= 0:
|
||||
return tuple(0.0 for _ in range(12))
|
||||
if has_bath:
|
||||
n_shower = 0.45 * n_occupants + 0.65
|
||||
else:
|
||||
n_shower = 0.58 * n_occupants + 0.83
|
||||
n_es_per_outlet = n_shower / n_outlets
|
||||
return tuple(
|
||||
n_electric_showers
|
||||
* n_es_per_outlet
|
||||
* fbeh
|
||||
* rated_power_kw
|
||||
* _ELECTRIC_SHOWER_DURATION_HOURS
|
||||
* n_m
|
||||
for fbeh, n_m in zip(TABLE_J5_BEHAVIOURAL_FACTOR, _DAYS_IN_MONTH)
|
||||
)
|
||||
|
||||
|
||||
def water_heating_from_cert(
|
||||
*,
|
||||
epc: EpcPropertyData,
|
||||
|
|
@ -596,6 +639,7 @@ def water_heating_from_cert(
|
|||
combi_loss_monthly_kwh_override: Optional[tuple[float, ...]] = None,
|
||||
electric_shower_monthly_kwh_override: Optional[tuple[float, ...]] = None,
|
||||
has_electric_shower: bool = False,
|
||||
electric_shower_count: int = 0,
|
||||
) -> WaterHeatingResult:
|
||||
"""SAP 10.2 §4 orchestrator — chain every line ref from (42) through
|
||||
(65) for a combi-gas dwelling with optional PCDB-backed combi loss.
|
||||
|
|
@ -687,11 +731,20 @@ def water_heating_from_cert(
|
|||
solar_monthly_kwh=zero12,
|
||||
fghrs_monthly_kwh=zero12,
|
||||
)
|
||||
electric_shower = (
|
||||
electric_shower_monthly_kwh_override
|
||||
if electric_shower_monthly_kwh_override is not None
|
||||
else zero12
|
||||
)
|
||||
if electric_shower_monthly_kwh_override is not None:
|
||||
electric_shower = electric_shower_monthly_kwh_override
|
||||
elif electric_shower_count > 0:
|
||||
# Appendix J step 8 — N_outlets counts mixer + electric outlets
|
||||
# together (eq J16).
|
||||
n_outlets_total = len(mixer_shower_flow_rates_l_per_min) + electric_shower_count
|
||||
electric_shower = electric_shower_monthly_kwh(
|
||||
n_occupants=n,
|
||||
has_bath=has_bath,
|
||||
n_outlets=n_outlets_total,
|
||||
n_electric_showers=electric_shower_count,
|
||||
)
|
||||
else:
|
||||
electric_shower = zero12
|
||||
gains = heat_gains_from_water_heating_monthly_kwh(
|
||||
energy_content_monthly_kwh=energy_content,
|
||||
distribution_loss_monthly_kwh=distribution,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue