mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Appendix L slice 1: annual_lighting_kwh extraction
Surfaces the SAP10.2 Appendix L L1-L12 annual lighting kWh as a public free fn alongside lighting_monthly_w. Refactors lighting_monthly_w to compose it. One source of truth shared by the §5 gains side and the forthcoming cost side (inputs.lighting_kwh_per_yr) — slice 2 wires internal_gains_from_cert + cert_to_inputs. Synthetic L1-L12 test pins a hand-computed dwelling (TFA=100, N=2.0, C_L=10000, ε=100, D=1.0) at 189.152079 kWh, abs=1e-3. 6-fixture LINE_67 conformance tests (Elmhurst 000474..000516) act as a regression check on the monthly cosine + 0.85 internal-fraction composition — all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
95086f957e
commit
f4352587f7
2 changed files with 85 additions and 19 deletions
|
|
@ -205,6 +205,47 @@ def appliances_monthly_w(
|
|||
return tuple(monthly)
|
||||
|
||||
|
||||
def annual_lighting_kwh(
|
||||
*,
|
||||
total_floor_area_m2: float,
|
||||
n_occupants: float,
|
||||
fixed_lighting_capacity_lm: float,
|
||||
fixed_lighting_efficacy_lm_per_w: float,
|
||||
daylight_factor: float,
|
||||
) -> float:
|
||||
"""SAP 10.2 Appendix L L1-L12 — annual lighting energy in kWh/yr.
|
||||
|
||||
The scalar leaf shared by the §5 gains side (composed into
|
||||
`lighting_monthly_w` via the seasonal cosine modulation) and the
|
||||
cost side (`inputs.lighting_kwh_per_yr`). Surfacing it via this
|
||||
public free fn lets the cert→inputs precompute reuse the same
|
||||
derivation that drives (67)m — one source of truth.
|
||||
|
||||
See `lighting_monthly_w` for the per-kwarg semantics + RdSAP §12-1
|
||||
lamp-type / L5b / L8c / L2a/L2b fallback rules.
|
||||
"""
|
||||
lambda_b = (
|
||||
_LIGHTING_LAMBDA_B_COEFF
|
||||
* (total_floor_area_m2 * n_occupants) ** _LIGHTING_LAMBDA_B_EXPONENT
|
||||
)
|
||||
lambda_req = (2.0 / 3.0) * lambda_b * daylight_factor
|
||||
c_l_ref = _LIGHTING_C_L_REF_PER_M2 * total_floor_area_m2
|
||||
lambda_prov = (
|
||||
lambda_req * fixed_lighting_capacity_lm / c_l_ref if c_l_ref > 0 else 0.0
|
||||
)
|
||||
lambda_topup = max(0.0, lambda_req / 3.0 - lambda_prov)
|
||||
e_l_fixed = (
|
||||
max(lambda_req, lambda_prov) / fixed_lighting_efficacy_lm_per_w
|
||||
if fixed_lighting_efficacy_lm_per_w > 0
|
||||
else 0.0
|
||||
)
|
||||
e_l_topup = lambda_topup / _LIGHTING_TOPUP_EFFICACY_LM_PER_W
|
||||
e_l_portable = (
|
||||
(1.0 / 3.0) * lambda_b * daylight_factor / _LIGHTING_PORTABLE_EFFICACY_LM_PER_W
|
||||
)
|
||||
return e_l_fixed + e_l_topup + e_l_portable
|
||||
|
||||
|
||||
def lighting_monthly_w(
|
||||
*,
|
||||
total_floor_area_m2: float,
|
||||
|
|
@ -231,26 +272,13 @@ def lighting_monthly_w(
|
|||
|
||||
L12 reduced-gain branch (L12a, used for new-build DPER/TPER) is deferred.
|
||||
"""
|
||||
lambda_b = (
|
||||
_LIGHTING_LAMBDA_B_COEFF
|
||||
* (total_floor_area_m2 * n_occupants) ** _LIGHTING_LAMBDA_B_EXPONENT
|
||||
e_l_annual_kwh = annual_lighting_kwh(
|
||||
total_floor_area_m2=total_floor_area_m2,
|
||||
n_occupants=n_occupants,
|
||||
fixed_lighting_capacity_lm=fixed_lighting_capacity_lm,
|
||||
fixed_lighting_efficacy_lm_per_w=fixed_lighting_efficacy_lm_per_w,
|
||||
daylight_factor=daylight_factor,
|
||||
)
|
||||
lambda_req = (2.0 / 3.0) * lambda_b * daylight_factor
|
||||
c_l_ref = _LIGHTING_C_L_REF_PER_M2 * total_floor_area_m2
|
||||
lambda_prov = (
|
||||
lambda_req * fixed_lighting_capacity_lm / c_l_ref if c_l_ref > 0 else 0.0
|
||||
)
|
||||
lambda_topup = max(0.0, lambda_req / 3.0 - lambda_prov)
|
||||
e_l_fixed = (
|
||||
max(lambda_req, lambda_prov) / fixed_lighting_efficacy_lm_per_w
|
||||
if fixed_lighting_efficacy_lm_per_w > 0
|
||||
else 0.0
|
||||
)
|
||||
e_l_topup = lambda_topup / _LIGHTING_TOPUP_EFFICACY_LM_PER_W
|
||||
e_l_portable = (
|
||||
(1.0 / 3.0) * lambda_b * daylight_factor / _LIGHTING_PORTABLE_EFFICACY_LM_PER_W
|
||||
)
|
||||
e_l_annual_kwh = e_l_fixed + e_l_topup + e_l_portable
|
||||
monthly: list[float] = []
|
||||
for m_idx, days in enumerate(_DAYS_PER_MONTH):
|
||||
m = m_idx + 1
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from domain.sap.worksheet.internal_gains import (
|
|||
InternalGainsResult,
|
||||
OvershadingCategory,
|
||||
PumpDateCategory,
|
||||
annual_lighting_kwh,
|
||||
appliances_monthly_w,
|
||||
balanced_mv_no_hr_fan_w,
|
||||
central_heating_pump_w,
|
||||
|
|
@ -231,6 +232,43 @@ def test_lighting_gains_match_appendix_l1_l12_for_000490() -> None:
|
|||
assert actual == pytest.approx(exp, abs=5e-3), f"month {m+1}"
|
||||
|
||||
|
||||
def test_annual_lighting_kwh_matches_hand_computed_appendix_l_cascade() -> None:
|
||||
"""Synthetic L1-L12 cascade on a clean dwelling. Hand-derived via the
|
||||
SAP 10.2 Appendix L formulas:
|
||||
|
||||
TFA=100 m², N=2.0, C_L,fixed=10000 lm, ε_fixed=100 lm/W, D=1.0
|
||||
|
||||
λ_b = 11.2 × 59.73 × (200)^0.4714 = 8130.477969
|
||||
λ_req = (2/3) × λ_b × D = 5420.318646
|
||||
C_L_ref = 330 × TFA = 33000.0
|
||||
λ_prov = λ_req × C_L_fixed / C_L_ref = 1642.520802
|
||||
λ_topup = max(0, λ_req/3 - λ_prov) = 164.252080
|
||||
E_L_fixed = max(λ_req, λ_prov) / ε_fixed = 54.203186
|
||||
E_L_topup = λ_topup / 21.3 = 7.711365
|
||||
E_L_portable = (1/3) × λ_b × D / 21.3 = 127.237527
|
||||
|
||||
e_l_annual_kwh = 189.152079
|
||||
|
||||
Pins the new public leaf `annual_lighting_kwh` directly so the cost
|
||||
side (`inputs.lighting_kwh_per_yr`) and the gains side (§5 (67) via
|
||||
`lighting_monthly_w`) share one source of truth.
|
||||
"""
|
||||
# Arrange
|
||||
expected_kwh = 189.152079
|
||||
|
||||
# Act
|
||||
actual_kwh = annual_lighting_kwh(
|
||||
total_floor_area_m2=100.0,
|
||||
n_occupants=2.0,
|
||||
fixed_lighting_capacity_lm=10000.0,
|
||||
fixed_lighting_efficacy_lm_per_w=100.0,
|
||||
daylight_factor=1.0,
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert actual_kwh == pytest.approx(expected_kwh, abs=1e-3)
|
||||
|
||||
|
||||
def test_pumps_fans_seasonal_mask_zeroes_summer_months_jun_to_sep() -> None:
|
||||
"""SAP10.2 Table 5a footnote a/b: pumps and warm-air fans are "Set to
|
||||
zero in summer months". Year-round contributions (PIV, balanced MV
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue