mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Root cause: SAP 10.2 has an internal unit-convention ambiguity for (H7)m between page 75 (Equation H1 implies W/m² 24-hour-average flux) and page 76 (verbatim "Monthly solar radiation per m² from U3.3 in Appendix U", i.e. kWh/m²/month monthly integrated). Page 77 (H23) formula's `× hours / 1000` term double-converts when (H7) is W/m². The cascade's `surface_solar_flux_w_per_m2` returns the §U3.2 24h-avg flux in W/m² (verified bit-exact vs Elmhurst worksheet line 295: SE 90° Jan region 0 = 36.7938 W/m²). The (H9) helper was using this directly without applying the U3.3 conversion that page 76's "from U3.3" cross-reference calls for. Elmhurst-certified software follows the U3.3 reading. SAP 10.2 spec p.76 line (H7): "Monthly solar radiation per m² from U3.3 in Appendix U". Appendix U §U3.3 (p.130) defines the conversion S_monthly = 0.024 × n_m × S(orient,p,m), where S(orient,p,m) is the §U3.2 24-hour-average flux in W/m². Therefore: (H7)m_U3.3 [kWh/m²/month] = flux_U3.2 [W/m²] × hours / 1000 Option A fix (per ChatGPT-mediated research): apply the U3.3 conversion inside the (H9) helper, so (H9) is in kWh/month rather than W. Spec p.77 (H23) formula then carries the conversion's dimensional residue correctly without double-counting. Diagnostic that closed the trap: back-solving poly(X_cas, Y_eff) = ws_H24/H17 at fixed X across 24 worksheet-positive observations from 4 cert fixtures (000565 + new A/B/C at sap worksheets/Solar PV tests/) revealed Y_eff/Y_cascade took ONLY two distinct values: - 0.7200 (exact) for every 30-day month observation - 0.7440 (exact) for every 31-day month observation i.e. exactly days × 24 / 1000. No utilizability function, no missing constant — a per-month unit-conversion factor that the polynomial non-linearity had been masking. Closure metrics (HEAD post-fix): - 000565 (W-30, modest): annual Δ −0.0000 kWh (every month exact) - A-baseline (S-30, modest): annual Δ +0.0001 kWh - B-highY (S-30, none): annual Δ −0.0000 kWh (incl Oct 10.5905) - C-lowY (N-60, signif): annual Δ −4.36 kWh (polynomial zero-clamp boundary; worksheet poly = 0.0024 → 0.41 kWh, cascade poly = −0.04 → 0) 47/48 month-observations pin at <1e-4 kWh. Test baseline: 547 pass + 9 expected `test_sap_result_pin[000565-*]` cascade-gap fails (unchanged — orchestrator still NOT integrated into water_heating.py:943; that's the follow-on slice that closes cert 000565's HW pin +272 → ~0). Pyright net-zero on both touched files. Files: - domain/sap10_calculator/worksheet/appendix_h_solar.py: rename `monthly_solar_energy_available_h9_w` → `_h9_kwh_per_month`, add `hours_in_month` param, apply U3.3 conversion. Y23 param renamed accordingly. Orchestrator updated. - domain/sap10_calculator/worksheet/tests/test_appendix_h_solar.py: add cert 000565 (H24)m monthly magnitude pin at abs < 1e-3 kWh; update H9 + Y23 unit tests for new kWh/month units. - BRIEF_APPENDIX_H_EN_15316_RESEARCH.md: new "Closure" section with the days-in-month diagnostic, root cause, and lessons. - HANDOVER_POST_4_CERT_EMPIRICAL.md: NEW — closure handover. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
522 lines
19 KiB
Python
522 lines
19 KiB
Python
"""SAP 10.2 Appendix H — Solar thermal contribution to water heating.
|
||
|
||
Implements line refs (H1)..(H24) for the hot-water solar path. The
|
||
space-heating contribution (H25)..(H29) is deferred until a fixture
|
||
exercises it (cert 000565 lodges solar HW only, H29=0 across all
|
||
months per the worksheet).
|
||
|
||
The procedure follows SAP 10.2 specification §Appendix H (p.74-78),
|
||
which is an implementation of the EN 15316-4-3:2017 monthly method.
|
||
The collector + system parameters feed a polynomial fit (Equation H1
|
||
with Table H3 correlation factors) over the dimensionless `X` and `Y`
|
||
ratios of monthly demand-weighted heat loss / heat gain to monthly
|
||
demand, yielding the kWh of solar heat actually delivered to the hot-
|
||
water cylinder per month.
|
||
|
||
Spec reference: SAP 10.2 specification (14-03-2025), Appendix H pages
|
||
74-78. Equation H1 is on p.75; Table H3 (correlation factors) on p.78.
|
||
|
||
Scope of this module:
|
||
- Pure math: takes inputs as primitives + 12-tuples, returns 12-tuples.
|
||
- HW path only (H25-H29 SH path deferred).
|
||
- No cascade integration (`cert_to_inputs.py` wires this into
|
||
`water_heating_from_cert.solar_monthly_kwh` in a follow-on slice).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Final, Union
|
||
|
||
from domain.sap10_calculator.tables.pcdb.postcode_weather import PostcodeClimate
|
||
from domain.sap10_calculator.worksheet.solar_gains import (
|
||
Orientation,
|
||
surface_solar_flux_w_per_m2,
|
||
)
|
||
|
||
|
||
# SAP 10.2 Table H3 (p.78) — correlation factors of Equation H1. These
|
||
# are the Cx coefficients of the polynomial:
|
||
# Qs = ((Ca·Y) + (Cb·X) + (Cc·Y²) + (Cd·X²) + (Ce·Y³) + (Cf·X³)) · Dm
|
||
_CA: Final[float] = 1.029
|
||
_CB: Final[float] = -0.065
|
||
_CC: Final[float] = -0.245
|
||
_CD: Final[float] = 0.0018
|
||
_CE: Final[float] = 0.0215
|
||
_CF: Final[float] = 0.0
|
||
|
||
|
||
# SAP 10.2 Appendix U Table U1 footnote (used by H20) — number of hours
|
||
# in each calendar month (line ref (41)m on the main worksheet).
|
||
_HOURS_IN_MONTH: Final[tuple[int, ...]] = (
|
||
31 * 24, 28 * 24, 31 * 24, 30 * 24, 31 * 24, 30 * 24,
|
||
31 * 24, 31 * 24, 30 * 24, 31 * 24, 30 * 24, 31 * 24,
|
||
)
|
||
|
||
|
||
def overall_heat_loss_coefficient_h10(
|
||
aperture_area_m2: float,
|
||
from_test_certificate: float | None = None,
|
||
) -> float:
|
||
"""SAP 10.2 (H10) — overall heat loss coefficient of solar system
|
||
(W/K). When test data is available, use the lodged value; otherwise
|
||
the spec default per p.76:
|
||
|
||
(H10) = 5 + 0.5 × (H1)
|
||
"""
|
||
if from_test_certificate is not None:
|
||
return from_test_certificate
|
||
return 5.0 + 0.5 * aperture_area_m2
|
||
|
||
|
||
def loop_heat_loss_coefficient_h11(
|
||
*,
|
||
linear_heat_loss_a1: float, # (H3)
|
||
second_order_heat_loss_a2: float, # (H4)
|
||
overall_heat_loss_h10: float, # (H10)
|
||
aperture_area_m2: float, # (H1)
|
||
) -> float:
|
||
"""SAP 10.2 (H11) — loop heat loss coefficient `U_loop` (W/m²K).
|
||
|
||
(H11) = (H3) + [(H4) × 40] + [(H10) ÷ (H1)]
|
||
"""
|
||
return (
|
||
linear_heat_loss_a1
|
||
+ second_order_heat_loss_a2 * 40.0
|
||
+ overall_heat_loss_h10 / aperture_area_m2
|
||
)
|
||
|
||
|
||
def effective_solar_volume_h14(
|
||
*,
|
||
dedicated_solar_storage_volume_l: float, # (H12)
|
||
combined_cylinder_total_volume_l: float | None, # (H13)
|
||
) -> float:
|
||
"""SAP 10.2 (H14) — effective solar storage volume `V_eff` (litres).
|
||
|
||
Separate pre-heat solar storage:
|
||
(H14) = (H12)
|
||
Combined cylinder (single vessel split into solar pre-heat + boiler-
|
||
heated zones):
|
||
(H14) = (H12) + 0.3 × [(H13) - (H12)]
|
||
"""
|
||
if combined_cylinder_total_volume_l is None:
|
||
return dedicated_solar_storage_volume_l
|
||
return (
|
||
dedicated_solar_storage_volume_l
|
||
+ 0.3 * (
|
||
combined_cylinder_total_volume_l - dedicated_solar_storage_volume_l
|
||
)
|
||
)
|
||
|
||
|
||
def reference_volume_h15(aperture_area_m2: float) -> float:
|
||
"""SAP 10.2 (H15) — reference volume (litres) = 75 × (H1)."""
|
||
return 75.0 * aperture_area_m2
|
||
|
||
|
||
def storage_tank_correction_coefficient_h16(
|
||
*,
|
||
reference_volume_h15_l: float,
|
||
effective_solar_volume_h14_l: float,
|
||
) -> float:
|
||
"""SAP 10.2 (H16) — storage tank correction `f_st`.
|
||
|
||
(H16) = [(H15) ÷ (H14)]^0.25
|
||
"""
|
||
return (reference_volume_h15_l / effective_solar_volume_h14_l) ** 0.25
|
||
|
||
|
||
def hot_water_demand_monthly_h17_kwh(
|
||
*,
|
||
hot_water_demand_monthly_kwh: tuple[float, ...], # (62)m
|
||
wwhrs_monthly_kwh: tuple[float, ...], # (63a)m
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H17)m — HW demand seen by solar = (62)m − (63a)m.
|
||
|
||
Per spec footnote 20 (p.77): PV diverters are ignored here when
|
||
solar water heating is present, so they do not enter (H17)m.
|
||
"""
|
||
return tuple(
|
||
d - w for d, w in zip(hot_water_demand_monthly_kwh, wwhrs_monthly_kwh)
|
||
)
|
||
|
||
|
||
def proportion_solar_to_hot_water_monthly_h18(
|
||
*,
|
||
hw_demand_seen_by_solar_monthly_kwh: tuple[float, ...], # (H17)m
|
||
space_heating_demand_monthly_kwh: tuple[float, ...], # (98a)m
|
||
solar_hot_water_only: bool,
|
||
solar_space_heating_only: bool,
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H18)m — proportion of solar input to HW.
|
||
|
||
Spec p.77:
|
||
- HW-only system: (H18)m = 1.0
|
||
- SH-only system: (H18)m = 0.0
|
||
- else: (H18)m = (H17)m ÷ [(H17)m + (98a)m]
|
||
"""
|
||
if solar_hot_water_only:
|
||
return (1.0,) * 12
|
||
if solar_space_heating_only:
|
||
return (0.0,) * 12
|
||
return tuple(
|
||
h / (h + s) if (h + s) > 0.0 else 0.0
|
||
for h, s in zip(
|
||
hw_demand_seen_by_solar_monthly_kwh,
|
||
space_heating_demand_monthly_kwh,
|
||
)
|
||
)
|
||
|
||
|
||
def hot_water_reference_temperature_h20_c(
|
||
*,
|
||
cold_water_temperatures_monthly_c: tuple[float, ...], # T_cold from Table J1
|
||
external_temperatures_monthly_c: tuple[float, ...], # (96)m
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H20)m — HW reference temperature (°C).
|
||
|
||
(H20)m = 55 + 3.86 × T_cold,m − 1.32 × (96)m
|
||
"""
|
||
return tuple(
|
||
55.0 + 3.86 * tc - 1.32 * te
|
||
for tc, te in zip(
|
||
cold_water_temperatures_monthly_c,
|
||
external_temperatures_monthly_c,
|
||
)
|
||
)
|
||
|
||
|
||
def hot_water_reference_temperature_difference_h21_c(
|
||
*,
|
||
hw_reference_temperature_monthly_c: tuple[float, ...], # (H20)m
|
||
external_temperatures_monthly_c: tuple[float, ...], # (96)m
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H21)m — HW reference temperature difference (K).
|
||
|
||
(H21)m = (H20)m − (96)m
|
||
"""
|
||
return tuple(
|
||
h20 - te
|
||
for h20, te in zip(
|
||
hw_reference_temperature_monthly_c,
|
||
external_temperatures_monthly_c,
|
||
)
|
||
)
|
||
|
||
|
||
def hot_water_factor_x_monthly_h22(
|
||
*,
|
||
proportion_solar_to_hw_h18: tuple[float, ...], # (H18)m
|
||
aperture_area_m2: float, # (H1)
|
||
loop_heat_loss_h11: float, # (H11)
|
||
loop_efficiency: float, # (H5)
|
||
hw_reference_temp_diff_h21: tuple[float, ...], # (H21)m
|
||
storage_tank_correction_h16: float, # (H16)
|
||
hours_in_month: tuple[int, ...], # (41)m
|
||
hw_demand_seen_by_solar_h17: tuple[float, ...], # (H17)m
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H22)m — HW factor X.
|
||
|
||
X_HW = [(H18)m × (H1) × (H11) × (H5) × (H21)m × (H16) ×
|
||
((41)m × 24)] ÷ [1000 × (H17)m]
|
||
|
||
Clamped to the range [0, 18] per spec p.76 (`if X < 0, enter zero;
|
||
if X > 18, enter 18`).
|
||
|
||
NB: The spec writes `(41)m × 24` for hours-in-month — this is a
|
||
typo (`(41)m` IS already hours-in-month per Appendix U Table U1
|
||
footnote). Implemented as hours-in-month directly to match the
|
||
worksheet's per-month accounting.
|
||
"""
|
||
out: list[float] = []
|
||
for m in range(12):
|
||
h17 = hw_demand_seen_by_solar_h17[m]
|
||
if h17 <= 0.0:
|
||
out.append(0.0)
|
||
continue
|
||
numerator = (
|
||
proportion_solar_to_hw_h18[m]
|
||
* aperture_area_m2
|
||
* loop_heat_loss_h11
|
||
* loop_efficiency
|
||
* hw_reference_temp_diff_h21[m]
|
||
* storage_tank_correction_h16
|
||
* hours_in_month[m]
|
||
)
|
||
x = numerator / (1000.0 * h17)
|
||
if x < 0.0:
|
||
out.append(0.0)
|
||
elif x > 18.0:
|
||
out.append(18.0)
|
||
else:
|
||
out.append(x)
|
||
return tuple(out)
|
||
|
||
|
||
def monthly_solar_energy_available_h9_kwh_per_month(
|
||
*,
|
||
aperture_area_m2: float, # (H1)
|
||
zero_loss_efficiency: float, # (H2)
|
||
monthly_solar_flux_w_per_m2: tuple[float, ...], # U3.2 flux in W/m² (24h avg)
|
||
hours_in_month: tuple[int, ...], # (41)m × 24
|
||
overshading_factor: float, # (H8)
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H9)m — solar energy available on collector aperture
|
||
in **kWh/month** (NOT W).
|
||
|
||
Spec p.76 line for (H9): "Solar energy available, (H1) × (H2) ×
|
||
(H7)m × (H8)". Spec p.76 line for (H7): "Monthly solar radiation
|
||
per m² from U3.3 in Appendix U" — i.e. the integrated monthly
|
||
irradiation `0.024 × n_m × S(orient,p,m)` in kWh/m²/month, NOT
|
||
the §U3.2 24-hour-average flux S(orient,p,m) in W/m².
|
||
|
||
The cascade's `surface_solar_flux_w_per_m2` returns the §U3.2
|
||
flux in W/m² (verified bit-exact against Elmhurst worksheet line
|
||
295: SE 90° Jan region 0 = 36.7938 W/m²). To reach the §U3.3
|
||
integrated value the SAP spec calls for, multiply by
|
||
`hours_in_month / 1000` (W·h → kWh):
|
||
|
||
(H7)m_U3.3 [kWh/m²/month] = flux_U3.2 [W/m²] × hours / 1000
|
||
|
||
(H9)m therefore lands in kWh/month:
|
||
|
||
(H9)m = (H1) × (H2) × (H7)m_U3.3 × (H8)
|
||
|
||
Reading the spec page 77 (H23) formula `H18·H6·H5·H9·hours /
|
||
(1000·H17)` with (H9) in W instead of kWh/month over-counts Y
|
||
by exactly `hours/1000` (= 0.720 for 30-day months, 0.744 for
|
||
31-day months) — the long-running 1.81× cascade-vs-worksheet
|
||
gap on cert 000565 closes to <1e-3 kWh/month across 4 fixtures
|
||
once (H9) carries the U3.3 conversion.
|
||
"""
|
||
return tuple(
|
||
aperture_area_m2
|
||
* zero_loss_efficiency
|
||
* (flux * hours / 1000.0)
|
||
* overshading_factor
|
||
for flux, hours in zip(monthly_solar_flux_w_per_m2, hours_in_month)
|
||
)
|
||
|
||
|
||
def hot_water_factor_y_monthly_h23(
|
||
*,
|
||
proportion_solar_to_hw_h18: tuple[float, ...], # (H18)m
|
||
incidence_angle_modifier: float, # (H6)
|
||
loop_efficiency: float, # (H5)
|
||
monthly_solar_energy_available_h9_kwh_per_month: tuple[float, ...], # (H9)m kWh/month
|
||
hours_in_month: tuple[int, ...], # (41)m × 24
|
||
hw_demand_seen_by_solar_h17: tuple[float, ...], # (H17)m
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H23)m — HW factor Y.
|
||
|
||
Y_HW = [(H18)m × (H6) × (H5) × (H9)m × ((41)m × 24)] ÷
|
||
[1000 × (H17)m]
|
||
|
||
Clamped to a lower bound of 0 per spec p.76 (`if Y < 0, enter zero`).
|
||
(H9)m is in kWh/month (per `monthly_solar_energy_available_h9_
|
||
kwh_per_month` — the §U3.3 monthly-integrated convention SAP p.76
|
||
references). The `× hours / 1000` term then carries the
|
||
dimensional residue inherent to SAP's stepwise units.
|
||
"""
|
||
out: list[float] = []
|
||
for m in range(12):
|
||
h17 = hw_demand_seen_by_solar_h17[m]
|
||
if h17 <= 0.0:
|
||
out.append(0.0)
|
||
continue
|
||
numerator = (
|
||
proportion_solar_to_hw_h18[m]
|
||
* incidence_angle_modifier
|
||
* loop_efficiency
|
||
* monthly_solar_energy_available_h9_kwh_per_month[m]
|
||
* hours_in_month[m]
|
||
)
|
||
y = numerator / (1000.0 * h17)
|
||
out.append(max(0.0, y))
|
||
return tuple(out)
|
||
|
||
|
||
def monthly_collector_solar_flux_w_per_m2(
|
||
*,
|
||
orientation: Orientation,
|
||
pitch_deg: float,
|
||
region: Union[int, PostcodeClimate],
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H7)m — monthly solar flux on the collector aperture
|
||
(W/m²).
|
||
|
||
Thin 12-month wrapper around `solar_gains.surface_solar_flux_w_per
|
||
_m2`, which implements the Appendix U §U3.3 polynomial conversion
|
||
from the horizontal irradiance in Table U3 to any orientation /
|
||
tilt combination. Cert 000565's collector is W-facing, 30° pitch,
|
||
Thames Valley (region 1)."""
|
||
return tuple(
|
||
surface_solar_flux_w_per_m2(
|
||
orientation=orientation, pitch_deg=pitch_deg,
|
||
region=region, month=m,
|
||
)
|
||
for m in range(1, 13)
|
||
)
|
||
|
||
|
||
def heat_delivered_to_hot_water_monthly_h24_kwh(
|
||
*,
|
||
factor_x_h22: tuple[float, ...], # (H22)m
|
||
factor_y_h23: tuple[float, ...], # (H23)m
|
||
hw_demand_seen_by_solar_h17: tuple[float, ...], # (H17)m
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 (H24)m — Equation H1 applied to HW (Q_s,w).
|
||
|
||
Q_s,w = [Ca·Y + Cb·X + Cc·Y² + Cd·X² + Ce·Y³ + Cf·X³] × (H17)m
|
||
|
||
Clamped per spec p.76:
|
||
- if Q_s,w > (H17)m, enter (H17)m (cannot deliver more than demand)
|
||
- if Q_s,w < 0, enter zero
|
||
"""
|
||
out: list[float] = []
|
||
for m in range(12):
|
||
x = factor_x_h22[m]
|
||
y = factor_y_h23[m]
|
||
h17 = hw_demand_seen_by_solar_h17[m]
|
||
poly = (
|
||
_CA * y
|
||
+ _CB * x
|
||
+ _CC * y * y
|
||
+ _CD * x * x
|
||
+ _CE * y * y * y
|
||
+ _CF * x * x * x
|
||
)
|
||
q = poly * h17
|
||
if q < 0.0:
|
||
out.append(0.0)
|
||
elif q > h17:
|
||
out.append(h17)
|
||
else:
|
||
out.append(q)
|
||
return tuple(out)
|
||
|
||
|
||
def solar_water_heating_input_monthly_kwh(
|
||
*,
|
||
# Collector geometry + region (drives Appendix U §U3.3 lookup for H7m)
|
||
collector_orientation: Orientation,
|
||
collector_pitch_deg: float,
|
||
region: Union[int, PostcodeClimate],
|
||
# Collector params lodged by cert or backed by Table H1 default
|
||
aperture_area_m2: float, # (H1)
|
||
zero_loss_efficiency: float, # (H2)
|
||
linear_heat_loss_a1: float, # (H3)
|
||
second_order_heat_loss_a2: float, # (H4)
|
||
loop_efficiency: float, # (H5)
|
||
incidence_angle_modifier: float, # (H6)
|
||
overshading_factor: float, # (H8) Table H2
|
||
overall_heat_loss_coefficient_from_test: float | None = None, # (H10) override
|
||
# Cylinder / storage volume inputs
|
||
dedicated_solar_storage_volume_l: float, # (H12)
|
||
combined_cylinder_total_volume_l: float | None, # (H13)
|
||
# Monthly demand + climate inputs
|
||
hot_water_demand_monthly_kwh: tuple[float, ...], # (62)m
|
||
wwhrs_monthly_kwh: tuple[float, ...], # (63a)m
|
||
cold_water_temperatures_monthly_c: tuple[float, ...], # Table J1 Tcold,m
|
||
external_temperatures_monthly_c: tuple[float, ...], # (96)m Appendix U §U3.1
|
||
# Solar contribution routing (cert 000565 lodges HW-only)
|
||
space_heating_demand_monthly_kwh: tuple[float, ...] = (0.0,) * 12,
|
||
solar_hot_water_only: bool = True,
|
||
solar_space_heating_only: bool = False,
|
||
) -> tuple[float, ...]:
|
||
"""SAP 10.2 Appendix H top-level orchestrator — returns (H24)m kWh
|
||
of solar heat delivered to the hot-water cylinder per month.
|
||
|
||
Chains the per-line helpers in spec order (p.75-77):
|
||
|
||
(H7)m Appendix U §U3.3 flux on collector aperture
|
||
(H9)m = (H1) × (H2) × (H7)m × (H8)
|
||
(H10) = 5 + 0.5 × (H1) [or from test certificate]
|
||
(H11) = (H3) + 40·(H4) + (H10)/(H1)
|
||
(H14) = (H12) [separate] OR (H12) + 0.3·((H13)−(H12)) [combined]
|
||
(H15) = 75 × (H1)
|
||
(H16) = ((H15)/(H14))^0.25
|
||
(H17)m = (62)m − (63a)m
|
||
(H18)m HW-share of demand (1 / 0 / blend per `solar_*_only` flags)
|
||
(H20)m = 55 + 3.86·Tcold,m − 1.32·(96)m
|
||
(H21)m = (H20)m − (96)m
|
||
(H22)m X factor (clamp [0, 18])
|
||
(H23)m Y factor (clamp ≥ 0)
|
||
(H24)m Equation H1 polynomial (clamp [0, (H17)m])
|
||
|
||
Space-heating contribution (H25)..(H29) is NOT computed here. Pass
|
||
`solar_hot_water_only=True` (default) for the cert 000565 shape;
|
||
other shapes will need an SH orchestrator in a follow-on slice.
|
||
"""
|
||
h7 = monthly_collector_solar_flux_w_per_m2(
|
||
orientation=collector_orientation,
|
||
pitch_deg=collector_pitch_deg,
|
||
region=region,
|
||
)
|
||
h9 = monthly_solar_energy_available_h9_kwh_per_month(
|
||
aperture_area_m2=aperture_area_m2,
|
||
zero_loss_efficiency=zero_loss_efficiency,
|
||
monthly_solar_flux_w_per_m2=h7,
|
||
hours_in_month=_HOURS_IN_MONTH,
|
||
overshading_factor=overshading_factor,
|
||
)
|
||
h10 = overall_heat_loss_coefficient_h10(
|
||
aperture_area_m2=aperture_area_m2,
|
||
from_test_certificate=overall_heat_loss_coefficient_from_test,
|
||
)
|
||
h11 = loop_heat_loss_coefficient_h11(
|
||
linear_heat_loss_a1=linear_heat_loss_a1,
|
||
second_order_heat_loss_a2=second_order_heat_loss_a2,
|
||
overall_heat_loss_h10=h10,
|
||
aperture_area_m2=aperture_area_m2,
|
||
)
|
||
h14 = effective_solar_volume_h14(
|
||
dedicated_solar_storage_volume_l=dedicated_solar_storage_volume_l,
|
||
combined_cylinder_total_volume_l=combined_cylinder_total_volume_l,
|
||
)
|
||
h15 = reference_volume_h15(aperture_area_m2)
|
||
h16 = storage_tank_correction_coefficient_h16(
|
||
reference_volume_h15_l=h15,
|
||
effective_solar_volume_h14_l=h14,
|
||
)
|
||
h17 = hot_water_demand_monthly_h17_kwh(
|
||
hot_water_demand_monthly_kwh=hot_water_demand_monthly_kwh,
|
||
wwhrs_monthly_kwh=wwhrs_monthly_kwh,
|
||
)
|
||
h18 = proportion_solar_to_hot_water_monthly_h18(
|
||
hw_demand_seen_by_solar_monthly_kwh=h17,
|
||
space_heating_demand_monthly_kwh=space_heating_demand_monthly_kwh,
|
||
solar_hot_water_only=solar_hot_water_only,
|
||
solar_space_heating_only=solar_space_heating_only,
|
||
)
|
||
h20 = hot_water_reference_temperature_h20_c(
|
||
cold_water_temperatures_monthly_c=cold_water_temperatures_monthly_c,
|
||
external_temperatures_monthly_c=external_temperatures_monthly_c,
|
||
)
|
||
h21 = hot_water_reference_temperature_difference_h21_c(
|
||
hw_reference_temperature_monthly_c=h20,
|
||
external_temperatures_monthly_c=external_temperatures_monthly_c,
|
||
)
|
||
h22 = hot_water_factor_x_monthly_h22(
|
||
proportion_solar_to_hw_h18=h18,
|
||
aperture_area_m2=aperture_area_m2,
|
||
loop_heat_loss_h11=h11,
|
||
loop_efficiency=loop_efficiency,
|
||
hw_reference_temp_diff_h21=h21,
|
||
storage_tank_correction_h16=h16,
|
||
hours_in_month=_HOURS_IN_MONTH,
|
||
hw_demand_seen_by_solar_h17=h17,
|
||
)
|
||
h23 = hot_water_factor_y_monthly_h23(
|
||
proportion_solar_to_hw_h18=h18,
|
||
incidence_angle_modifier=incidence_angle_modifier,
|
||
loop_efficiency=loop_efficiency,
|
||
monthly_solar_energy_available_h9_kwh_per_month=h9,
|
||
hours_in_month=_HOURS_IN_MONTH,
|
||
hw_demand_seen_by_solar_h17=h17,
|
||
)
|
||
return heat_delivered_to_hot_water_monthly_h24_kwh(
|
||
factor_x_h22=h22,
|
||
factor_y_h23=h23,
|
||
hw_demand_seen_by_solar_h17=h17,
|
||
)
|