mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
§6 slice 5: CalculatorInputs.solar_gains_monthly_w + per-month index lookup
Adds the §6 (83)m output as a required 12-tuple field on CalculatorInputs; _solve_month indexes into it directly instead of recomputing solar each month via _solar_gains_w(windows, region, month). Test (test_calculator_consumes_solar_gains_monthly_w_field_for_per_month_solar) pins the read path: an explicit non-zero monthly tuple flows through calculate_sap_from_inputs unchanged. cert_to_inputs preserves identical behaviour during the migration by computing the new field via the legacy _solar_gains_w leaf per month. Slice 6 swaps that for solar_gains_from_cert; slice 7 deletes the legacy leaf + WindowInput + windows field. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
377caea20a
commit
376cdb6bc3
4 changed files with 45 additions and 2 deletions
|
|
@ -105,6 +105,10 @@ class CalculatorInputs:
|
|||
# zero out in summer per Table 5a. Produced by §5 orchestrator
|
||||
# `internal_gains_from_cert` (called from cert_to_inputs).
|
||||
internal_gains_monthly_w: tuple[float, ...]
|
||||
# SAP10.2 (83)m — total solar gains W per month (Jan..Dec). Produced
|
||||
# by §6 orchestrator `solar_gains_from_cert` upstream; the calculator
|
||||
# only indexes into it per month, no recomputation here.
|
||||
solar_gains_monthly_w: tuple[float, ...]
|
||||
region: int
|
||||
windows: tuple[WindowInput, ...]
|
||||
control_type: int
|
||||
|
|
@ -218,7 +222,7 @@ def _solve_month(
|
|||
) -> MonthlyEntry:
|
||||
t_ext = external_temperature_c(inputs.region, month)
|
||||
g_int = inputs.internal_gains_monthly_w[month - 1]
|
||||
g_sol = _solar_gains_w(windows=inputs.windows, region=inputs.region, month=month)
|
||||
g_sol = inputs.solar_gains_monthly_w[month - 1]
|
||||
g_total = g_int + g_sol
|
||||
|
||||
# SAP 10.3 §7.3: two-pass iteration. Seed η = 1, compute T_internal,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ from domain.ml.sap_efficiencies import (
|
|||
seasonal_efficiency,
|
||||
water_heating_efficiency as _legacy_water_heating_efficiency,
|
||||
)
|
||||
from domain.sap.calculator import CalculatorInputs, WindowInput
|
||||
from domain.sap.calculator import CalculatorInputs, WindowInput, _solar_gains_w
|
||||
from domain.sap.tables.table_12 import (
|
||||
co2_factor_kg_per_kwh,
|
||||
primary_energy_factor,
|
||||
|
|
@ -998,6 +998,18 @@ def cert_to_inputs(
|
|||
# SAP10.2 line (73)m — total internal gains W/month from §5
|
||||
# orchestrator (composed above).
|
||||
internal_gains_monthly_w=internal_gains_monthly_w,
|
||||
# SAP10.2 line (83)m — total solar gains W/month. Computed here via
|
||||
# the legacy `_solar_gains_w` per-month leaf to preserve identical
|
||||
# behaviour during the §6 wiring migration; the next slice swaps
|
||||
# this for the §6 orchestrator `solar_gains_from_cert`.
|
||||
solar_gains_monthly_w=tuple(
|
||||
_solar_gains_w(
|
||||
windows=_window_inputs(epc.sap_windows),
|
||||
region=_region_index(epc.region_code),
|
||||
month=m,
|
||||
)
|
||||
for m in range(1, 13)
|
||||
),
|
||||
region=_region_index(epc.region_code),
|
||||
windows=_window_inputs(epc.sap_windows),
|
||||
control_type=_control_type(main),
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import pytest
|
|||
from domain.sap.calculator import (
|
||||
CalculatorInputs,
|
||||
WindowInput,
|
||||
_solar_gains_w,
|
||||
calculate_sap_from_inputs,
|
||||
)
|
||||
from domain.sap.worksheet.dimensions import Dimensions
|
||||
|
|
@ -85,6 +86,9 @@ def _baseline_dwelling() -> CalculatorInputs:
|
|||
heat_transmission=ht,
|
||||
monthly_infiltration_ach=(0.7,) * 12,
|
||||
internal_gains_monthly_w=(450.0,) * 12,
|
||||
solar_gains_monthly_w=tuple(
|
||||
_solar_gains_w(windows=windows, region=0, month=m) for m in range(1, 13)
|
||||
),
|
||||
region=0,
|
||||
windows=windows,
|
||||
control_type=2,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from domain.sap.calculator import (
|
|||
CalculatorInputs,
|
||||
SapResult,
|
||||
WindowInput,
|
||||
_solar_gains_w,
|
||||
calculate_sap_from_inputs,
|
||||
)
|
||||
from domain.sap.worksheet.dimensions import Dimensions
|
||||
|
|
@ -84,6 +85,9 @@ def _baseline_inputs() -> CalculatorInputs:
|
|||
# per-month variation lives in §5 orchestrator output; tracer
|
||||
# tests don't need the modulation to verify the SAP loop.
|
||||
internal_gains_monthly_w=(450.0,) * 12,
|
||||
solar_gains_monthly_w=tuple(
|
||||
_solar_gains_w(windows=windows, region=0, month=m) for m in range(1, 13)
|
||||
),
|
||||
region=0,
|
||||
windows=windows,
|
||||
control_type=2,
|
||||
|
|
@ -102,6 +106,25 @@ def _baseline_inputs() -> CalculatorInputs:
|
|||
)
|
||||
|
||||
|
||||
def test_calculator_consumes_solar_gains_monthly_w_field_for_per_month_solar() -> None:
|
||||
# Arrange — replace the baseline inputs' solar with an explicit known
|
||||
# 12-tuple. The §6 orchestrator produces this upstream; the calculator
|
||||
# must just look it up, not recompute from the legacy `windows` field.
|
||||
# 100 W constant solar everywhere — distinct enough that any leftover
|
||||
# _solar_gains_w(windows, ...) recomputation would land elsewhere.
|
||||
explicit_solar = (100.0,) * 12
|
||||
inputs = replace(
|
||||
_baseline_inputs(), solar_gains_monthly_w=explicit_solar,
|
||||
)
|
||||
|
||||
# Act
|
||||
result = calculate_sap_from_inputs(inputs)
|
||||
|
||||
# Assert
|
||||
for monthly in result.monthly:
|
||||
assert monthly.solar_gains_w == 100.0
|
||||
|
||||
|
||||
def test_calculator_returns_twelve_month_breakdown_and_plausible_sap_score() -> None:
|
||||
# Arrange — baseline 100 m² gas-boiler dwelling in UK-average climate.
|
||||
inputs = _baseline_inputs()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue