mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
§5 slice 8: (73) total_internal_gains_monthly_w + InternalGainsResult
Closes the §5 leaf-function surface:
- total_internal_gains_monthly_w sums (66) + (67) + (68) + (69)
+ (70) + (71) + (72) element-wise. (71) carries negative sign so the
losses term subtracts.
- InternalGainsResult frozen dataclass bundles all 7 line refs plus the
total as 12-tuples — the typed payload returned by the orchestrator.
Verified against Elmhurst U985-0001-000490 (73)m to ≤1e-2 W/month.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f77229e4b4
commit
53aba1332e
2 changed files with 119 additions and 0 deletions
|
|
@ -297,6 +297,57 @@ def pumps_fans_monthly_w(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def total_internal_gains_monthly_w(
|
||||||
|
*,
|
||||||
|
metabolic_monthly_w: tuple[float, ...],
|
||||||
|
lighting_monthly_w: tuple[float, ...],
|
||||||
|
appliances_monthly_w: tuple[float, ...],
|
||||||
|
cooking_monthly_w: tuple[float, ...],
|
||||||
|
pumps_fans_monthly_w: tuple[float, ...],
|
||||||
|
losses_monthly_w: tuple[float, ...],
|
||||||
|
water_heating_gains_monthly_w: tuple[float, ...],
|
||||||
|
) -> tuple[float, ...]:
|
||||||
|
"""SAP10.2 §5 line (73) — total internal gains.
|
||||||
|
|
||||||
|
(73)m = (66)m + (67)m + (68)m + (69)m + (70)m + (71)m + (72)m
|
||||||
|
|
||||||
|
Pure element-wise sum across the seven gain streams. (71)m carries a
|
||||||
|
negative sign (losses) so the contribution is a subtraction.
|
||||||
|
"""
|
||||||
|
return tuple(
|
||||||
|
m + l + a + c + p + s + w
|
||||||
|
for m, l, a, c, p, s, w in zip(
|
||||||
|
metabolic_monthly_w,
|
||||||
|
lighting_monthly_w,
|
||||||
|
appliances_monthly_w,
|
||||||
|
cooking_monthly_w,
|
||||||
|
pumps_fans_monthly_w,
|
||||||
|
losses_monthly_w,
|
||||||
|
water_heating_gains_monthly_w,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class InternalGainsResult:
|
||||||
|
"""SAP10.2 §5 line refs (66)..(73), each a 12-tuple of watts per month.
|
||||||
|
|
||||||
|
Returned by `internal_gains_from_cert`. Downstream §6/§7/§9 calculators
|
||||||
|
consume `total_internal_gains_monthly_w` directly; the per-line tuples
|
||||||
|
are exposed for worksheet conformance + audit. Field names mirror the
|
||||||
|
SAP10.2 line refs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
metabolic_monthly_w: tuple[float, ...] # line (66)
|
||||||
|
lighting_monthly_w: tuple[float, ...] # line (67)
|
||||||
|
appliances_monthly_w: tuple[float, ...] # line (68)
|
||||||
|
cooking_monthly_w: tuple[float, ...] # line (69)
|
||||||
|
pumps_fans_monthly_w: tuple[float, ...] # line (70)
|
||||||
|
losses_monthly_w: tuple[float, ...] # line (71)
|
||||||
|
water_heating_gains_monthly_w: tuple[float, ...] # line (72)
|
||||||
|
total_internal_gains_monthly_w: tuple[float, ...] # line (73)
|
||||||
|
|
||||||
|
|
||||||
def water_heating_gains_monthly_w(
|
def water_heating_gains_monthly_w(
|
||||||
*,
|
*,
|
||||||
heat_gains_from_water_heating_monthly_kwh: tuple[float, ...],
|
heat_gains_from_water_heating_monthly_kwh: tuple[float, ...],
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ Table 5a + Appendix L (lighting/appliances/cooking) + Appendix J Table 1b
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from domain.sap.worksheet.internal_gains import (
|
from domain.sap.worksheet.internal_gains import (
|
||||||
|
InternalGainsResult,
|
||||||
PumpDateCategory,
|
PumpDateCategory,
|
||||||
appliances_monthly_w,
|
appliances_monthly_w,
|
||||||
balanced_mv_no_hr_fan_w,
|
balanced_mv_no_hr_fan_w,
|
||||||
|
|
@ -26,6 +27,7 @@ from domain.sap.worksheet.internal_gains import (
|
||||||
metabolic_monthly_w,
|
metabolic_monthly_w,
|
||||||
piv_fan_w,
|
piv_fan_w,
|
||||||
pumps_fans_monthly_w,
|
pumps_fans_monthly_w,
|
||||||
|
total_internal_gains_monthly_w,
|
||||||
warm_air_heating_fan_w,
|
warm_air_heating_fan_w,
|
||||||
water_heating_gains_monthly_w,
|
water_heating_gains_monthly_w,
|
||||||
)
|
)
|
||||||
|
|
@ -344,3 +346,69 @@ def test_heat_interface_unit_converts_kwh_per_day_to_constant_watts() -> None:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert w == pytest.approx(20.0, abs=1e-9)
|
assert w == pytest.approx(20.0, abs=1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
def test_total_internal_gains_sums_seven_components_per_month_for_000490() -> None:
|
||||||
|
"""SAP10.2 §5 line (73): G_total,m = G_M + G_L + G_A + G_C + G_pumps
|
||||||
|
+ G_losses + G_WH per month. (71) is negative — net subtraction.
|
||||||
|
|
||||||
|
Verified against Elmhurst U985-0001-000490 worksheet (73)m row.
|
||||||
|
"""
|
||||||
|
# Arrange — component tuples from the 000490 worksheet rows (66)..(72)
|
||||||
|
metabolic = (128.8087,) * 12
|
||||||
|
lighting = (24.2665, 21.5533, 17.5283, 13.2701, 9.9195, 8.3745,
|
||||||
|
9.0489, 11.7621, 15.7871, 20.0454, 23.3959, 24.9410)
|
||||||
|
appliances = (280.4965, 283.4071, 276.0723, 260.4574, 240.7463, 222.2207,
|
||||||
|
209.8445, 206.9338, 214.2686, 229.8835, 249.5946, 268.1202)
|
||||||
|
cooking = (50.0277,) * 12
|
||||||
|
pumps = (7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0)
|
||||||
|
losses = (-85.8725,) * 12
|
||||||
|
water_heating = (101.0798, 98.8663, 94.6647, 85.5412, 79.9343, 74.0821,
|
||||||
|
70.5249, 73.5203, 77.0253, 83.5192, 92.2698, 99.9197)
|
||||||
|
expected_total = (
|
||||||
|
505.8067, 503.7906, 488.2293, 459.2325, 430.5641, 397.6412,
|
||||||
|
382.3822, 385.1801, 400.0449, 433.4120, 465.2242, 492.9448,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
total = total_internal_gains_monthly_w(
|
||||||
|
metabolic_monthly_w=metabolic,
|
||||||
|
lighting_monthly_w=lighting,
|
||||||
|
appliances_monthly_w=appliances,
|
||||||
|
cooking_monthly_w=cooking,
|
||||||
|
pumps_fans_monthly_w=pumps,
|
||||||
|
losses_monthly_w=losses,
|
||||||
|
water_heating_gains_monthly_w=water_heating,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert len(total) == 12
|
||||||
|
for m, (actual, exp) in enumerate(zip(total, expected_total)):
|
||||||
|
assert actual == pytest.approx(exp, abs=1e-2), f"month {m+1}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_internal_gains_result_dataclass_holds_all_seven_lines_plus_total() -> None:
|
||||||
|
"""InternalGainsResult bundles every line (66)..(73) as a 12-tuple so
|
||||||
|
downstream §6/§7/§9 callers receive a single typed payload from the
|
||||||
|
orchestrator. Field names mirror the worksheet line refs."""
|
||||||
|
# Arrange
|
||||||
|
zeros = (0.0,) * 12
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = InternalGainsResult(
|
||||||
|
metabolic_monthly_w=zeros,
|
||||||
|
lighting_monthly_w=zeros,
|
||||||
|
appliances_monthly_w=zeros,
|
||||||
|
cooking_monthly_w=zeros,
|
||||||
|
pumps_fans_monthly_w=zeros,
|
||||||
|
losses_monthly_w=zeros,
|
||||||
|
water_heating_gains_monthly_w=zeros,
|
||||||
|
total_internal_gains_monthly_w=zeros,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert — every field is a 12-tuple
|
||||||
|
assert all(len(getattr(result, f)) == 12 for f in (
|
||||||
|
"metabolic_monthly_w", "lighting_monthly_w", "appliances_monthly_w",
|
||||||
|
"cooking_monthly_w", "pumps_fans_monthly_w", "losses_monthly_w",
|
||||||
|
"water_heating_gains_monthly_w", "total_internal_gains_monthly_w",
|
||||||
|
))
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue