mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice S0380.76: Combined-cylinder H12/H13 routing closes solar Q_s to <2 kWh/yr
Per SAP 10.2 spec p.75 (Effective solar volume section): "in the case of a combined cylinder (such as arrangement b) in Figure H2, [(H14) =] the volume of the dedicated solar storage plus 0.3 times the volume of the remainder of the cylinder." The spec leaves the dedicated solar storage volume (H12) itself to the surveyor / certified-software convention for combined-cylinder setups. Empirical pattern across 4 Elmhurst worksheets establishes the combined-cylinder default H12 = 1/3 × cylinder volume (H13): cert 000565 worksheet: H12 = 53 L, H13 = 160 L (ratio 0.331) cert A worksheet: H12 = 37 L, H13 = 110 L (ratio 0.336) cert B worksheet: H12 = 37 L, H13 = 110 L (ratio 0.336) cert C worksheet: H12 = 37 L, H13 = 110 L (ratio 0.336) This matches the broader f-chart literature convention for "solar pre-heat zone" sizing in stratified tanks: roughly the lower third of the cylinder is reserved for solar pre-heat, the upper two-thirds for boiler-heated water. The Elmhurst-certified rounding is to the nearest integer litre (53.33 → 53; 36.67 → 37). `_solar_hw_monthly_override` now derives H12/H13 from `epc.has_hot_water_cylinder + sap_heating.cylinder_size` via the existing `_CYLINDER_SIZE_CODE_TO_LITRES` Table 28 lookup (RdSAP 10 §10.5). When no cylinder is lodged (instantaneous + solar HW shape, hypothetical), fall back to Table 29's 75 L separate pre-heat tank. Cert 000565 closure: - Orchestrator solar Q_s annual: 268 → 283 kWh/yr (worksheet 281.35, Δ +1.73, 0.6% error). Within the same precision band as appendix_h_solar's per-month <1e-3 kWh pin. - HW pin: −69 → −86 kWh/yr (slight regression due to compounding with three independent demand-cascade bugs not yet wired: (45)m over by 903, primary_loss (59) under by 1175 (cylinder HP routing missing), storage_loss (56) over by 98 + missing (57)m solar adjustment). These were previously masked by the +357 kWh "no solar credit" over-count; now visible as the residual. Solar Q_s closure is the load-bearing achievement here — the Appendix H orchestrator is now spec-pinned through to the cert's own lodged geometry. HW pin closure follows once the demand-path gaps land in follow-on slices. Test baseline: 547 pass + 9 expected `test_sap_result_pin[000565-*]` fails (unchanged). Cohort-2 + ASHP cohort + golden fixtures untouched — no other cert lodges solar HW. Pyright net-zero (34 errors baseline → 34 errors post-change). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
a9143d0921
commit
a532f75db0
1 changed files with 43 additions and 2 deletions
|
|
@ -3008,6 +3008,20 @@ _TABLE_29_DEDICATED_SOLAR_STORAGE_L: Final[float] = 75.0
|
|||
_TABLE_29_DEFAULT_ORIENTATION: Final[Orientation] = Orientation.S
|
||||
_TABLE_29_DEFAULT_PITCH_DEG: Final[float] = 30.0
|
||||
|
||||
# Combined-cylinder default: when solar HW shares the cert's HW
|
||||
# cylinder (single vessel split into solar pre-heat + boiler-heated
|
||||
# zones), the dedicated solar storage volume (H12) defaults to 1/3
|
||||
# of the total cylinder volume (H13). Empirically verified across 4
|
||||
# Elmhurst worksheets — cert 000565 (H13=160, H12=53 ≈ 160/3),
|
||||
# cert A/B/C (H13=110, H12=37 ≈ 110/3) — rounded to the nearest
|
||||
# integer litre. The SAP 10.2 spec p.75 only states the effective-
|
||||
# volume formula `H14 = H12 + 0.3·(H13 − H12)` for combined
|
||||
# cylinders, leaving H12 itself to the surveyor / certified
|
||||
# software convention. The 1/3 rule matches Elmhurst's certified
|
||||
# behaviour and the broader f-chart literature convention for
|
||||
# "pre-heat zone" sizing in stratified tanks.
|
||||
_COMBINED_CYLINDER_SOLAR_PREHEAT_FRACTION: Final[float] = 1.0 / 3.0
|
||||
|
||||
# SAP 10.2 Table H2 (p.78) — overshading factor (H8). RdSAP uses the
|
||||
# string lodgement on Summary §16.0 ("None Or Little" / "Modest" /
|
||||
# "Significant" / "Heavy") and maps to the numeric factor here.
|
||||
|
|
@ -3059,6 +3073,20 @@ def _solar_hw_monthly_override(
|
|||
epc.solar_hw_overshading or "Modest",
|
||||
_TABLE_H2_OVERSHADING_FACTOR["Modest"],
|
||||
)
|
||||
# (H12) / (H13) routing: when the cert lodges a HW cylinder, the
|
||||
# solar pre-heat shares that vessel (combined cylinder) with H12
|
||||
# defaulting to 1/3 of the cylinder volume per the f-chart
|
||||
# stratification convention. When no cylinder is lodged, fall back
|
||||
# to Table 29's 75 L separate pre-heat tank.
|
||||
cylinder_volume_l = _hot_water_cylinder_volume_l(epc)
|
||||
if cylinder_volume_l is not None:
|
||||
dedicated_solar_storage_l = round(
|
||||
cylinder_volume_l * _COMBINED_CYLINDER_SOLAR_PREHEAT_FRACTION
|
||||
)
|
||||
combined_cylinder_l: Optional[float] = cylinder_volume_l
|
||||
else:
|
||||
dedicated_solar_storage_l = _TABLE_29_DEDICATED_SOLAR_STORAGE_L
|
||||
combined_cylinder_l = None
|
||||
h24_kwh_positive = solar_water_heating_input_monthly_kwh(
|
||||
collector_orientation=orientation,
|
||||
collector_pitch_deg=pitch_deg,
|
||||
|
|
@ -3070,8 +3098,8 @@ def _solar_hw_monthly_override(
|
|||
loop_efficiency=_TABLE_29_LOOP_EFF,
|
||||
incidence_angle_modifier=_TABLE_29_IAM_FLAT_PLATE,
|
||||
overshading_factor=overshading,
|
||||
dedicated_solar_storage_volume_l=_TABLE_29_DEDICATED_SOLAR_STORAGE_L,
|
||||
combined_cylinder_total_volume_l=None,
|
||||
dedicated_solar_storage_volume_l=dedicated_solar_storage_l,
|
||||
combined_cylinder_total_volume_l=combined_cylinder_l,
|
||||
hot_water_demand_monthly_kwh=hw_demand_monthly_kwh,
|
||||
wwhrs_monthly_kwh=(0.0,) * 12,
|
||||
cold_water_temperatures_monthly_c=TABLE_J1_TCOLD_FROM_MAINS_C,
|
||||
|
|
@ -3113,6 +3141,19 @@ def _orientation_from_summary_string(raw: Optional[str]) -> Optional[Orientation
|
|||
return _SUMMARY_ORIENTATION_BY_STRING.get(raw)
|
||||
|
||||
|
||||
def _hot_water_cylinder_volume_l(epc: EpcPropertyData) -> Optional[float]:
|
||||
"""Resolve the HW cylinder volume (litres) from the cert's
|
||||
`cylinder_size` code via RdSAP 10 §10.5 Table 28. Returns None
|
||||
when no cylinder is lodged or the size code falls outside the
|
||||
cohort-observed range (codes 2-4 → Normal / Medium / Large)."""
|
||||
if not epc.has_hot_water_cylinder:
|
||||
return None
|
||||
size_code = _int_or_none(epc.sap_heating.cylinder_size)
|
||||
if size_code is None:
|
||||
return None
|
||||
return _CYLINDER_SIZE_CODE_TO_LITRES.get(size_code)
|
||||
|
||||
|
||||
def _table_3a_combi_loss_default_applies(main: Optional[MainHeatingDetail]) -> bool:
|
||||
"""Gate for the Table 3a keep-hot 600 kWh/yr fall-through per SAP 10.2
|
||||
§4 line 7702. Returns True only when the main heating system is in the
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue