mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
§5 slice 9: internal_gains_from_cert orchestrator + lookalike tracer test
Wires all §5 leaf functions into a single from_cert orchestrator that
chains (66) → (67) → (68) → (69) → (70) → (71) → (72) → (73) and
returns an InternalGainsResult. The caller provides §4 (65)m heat
gains (the only non-cert input) and overshading defaults to AVERAGE.
Cert derivations:
- Occupancy via Appendix J Table 1b from TFA
- Lighting: RdSAP §12-1 per-lamp-type bulb defaults aggregated to
C_L,fixed + ε_fixed; C_daylight via L2a from sap_windows × Z_L
from Table 6d. L5b + L8c fallbacks when no bulb/window data lodged.
- Pumps/fans: maps central_heating_pump_age_str on the first
MainHeatingDetail to PumpDateCategory. Liquid-fuel / warm-air / PIV
/ MV / HIU branches deferred (reachable via leaf fns; currently
return 0 in the orchestrator for the combi-gas-natural-vent
population that covers all 6 Elmhurst fixtures).
Slice 9 tracer test hand-builds a 000490-lookalike EPC rather than
mutating `_elmhurst_worksheet_000490.build_epc()` — keeps the existing
e2e SAP-score regression test pinned. Slice 10 will extend the fixture
proper and parametrize over ALL_FIXTURES.
Also: extends make_minimal_sap10_epc with low_energy_fixed_lighting_bulbs_count
since the existing builder only exposed CFL/LED/incandescent separately.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
53aba1332e
commit
f81e744b02
3 changed files with 363 additions and 0 deletions
|
|
@ -200,6 +200,7 @@ def make_minimal_sap10_epc(
|
|||
cfl_fixed_lighting_bulbs_count: int = 0,
|
||||
led_fixed_lighting_bulbs_count: int = 0,
|
||||
incandescent_fixed_lighting_bulbs_count: int = 0,
|
||||
low_energy_fixed_lighting_bulbs_count: Optional[int] = None,
|
||||
solar_water_heating: bool = False,
|
||||
has_hot_water_cylinder: bool = False,
|
||||
has_fixed_air_conditioning: bool = False,
|
||||
|
|
@ -299,6 +300,7 @@ def make_minimal_sap10_epc(
|
|||
cfl_fixed_lighting_bulbs_count=cfl_fixed_lighting_bulbs_count,
|
||||
led_fixed_lighting_bulbs_count=led_fixed_lighting_bulbs_count,
|
||||
incandescent_fixed_lighting_bulbs_count=incandescent_fixed_lighting_bulbs_count,
|
||||
low_energy_fixed_lighting_bulbs_count=low_energy_fixed_lighting_bulbs_count,
|
||||
total_floor_area_m2=total_floor_area_m2,
|
||||
sap_version=10.2,
|
||||
energy_rating_current=energy_rating_current,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ from enum import Enum
|
|||
from math import cos, exp, pi
|
||||
from typing import Final, Optional
|
||||
|
||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData, SapWindow
|
||||
|
||||
_DAYS_PER_YEAR: Final[float] = 365.0
|
||||
_APPLIANCES_E_A_COEFF: Final[float] = 207.8
|
||||
_APPLIANCES_E_A_EXPONENT: Final[float] = 0.4714
|
||||
|
|
@ -60,6 +62,67 @@ class PumpDateCategory(Enum):
|
|||
UNKNOWN = "unknown"
|
||||
|
||||
|
||||
class OvershadingCategory(Enum):
|
||||
"""Table 6d overshading bucket. Maps to light access factor Z_L. SAP
|
||||
defaults to AVERAGE when the cert hasn't lodged a specific category."""
|
||||
|
||||
HEAVY = "heavy"
|
||||
MORE_THAN_AVERAGE = "more_than_average"
|
||||
AVERAGE = "average"
|
||||
VERY_LITTLE = "very_little"
|
||||
|
||||
|
||||
# Table 6d third column — light access factor Z_L by overshading bucket.
|
||||
_Z_L_BY_OVERSHADING: Final[dict[OvershadingCategory, float]] = {
|
||||
OvershadingCategory.HEAVY: 0.5,
|
||||
OvershadingCategory.MORE_THAN_AVERAGE: 0.67,
|
||||
OvershadingCategory.AVERAGE: 0.83,
|
||||
OvershadingCategory.VERY_LITTLE: 1.0,
|
||||
}
|
||||
|
||||
# RdSAP §12-1 per-lamp-type defaults: (watts_per_bulb, efficacy_lm_per_w).
|
||||
# When the cert distinguishes LED vs CFL the per-type values apply;
|
||||
# combined "low energy lighting" (LEL) — LED/CFL unknown — uses the LEL
|
||||
# default. Lumens per bulb = watts × efficacy.
|
||||
_RDSAP_LAMP_LED: Final[tuple[float, float]] = (9.0, 100.0)
|
||||
_RDSAP_LAMP_CFL: Final[tuple[float, float]] = (19.0, 55.0)
|
||||
_RDSAP_LAMP_INCANDESCENT: Final[tuple[float, float]] = (60.0, 11.2)
|
||||
_RDSAP_LAMP_LEL_UNKNOWN: Final[tuple[float, float]] = (15.0, 80.0)
|
||||
|
||||
# L5b existing-dwelling C_L,fixed fallback when no fixed-lighting data lodged.
|
||||
_LIGHTING_L5B_LUMENS_PER_M2: Final[float] = 185.0
|
||||
# L8c ε_fixed fallback when no fixed lighting present.
|
||||
_LIGHTING_L8C_EFFICACY_LM_PER_W: Final[float] = 21.3
|
||||
|
||||
# Table 6b light transmittance g_L by SAP glazing-type code. Single
|
||||
# glazed = 0.90; double-glazed variants = 0.80; triple-glazed = 0.70.
|
||||
# Mirrors the SAP code mapping in cert_to_inputs._g_perpendicular but
|
||||
# returns the light column, not solar.
|
||||
_G_LIGHT_BY_GLAZING_CODE: Final[dict[int, float]] = {
|
||||
1: 0.90, # single glazed
|
||||
2: 0.80, # double glazed (air filled, pre-2002)
|
||||
3: 0.80, # double glazed (air filled, post-2002)
|
||||
4: 0.80, # double glazed (low-E)
|
||||
5: 0.80, # double glazed (low-E argon)
|
||||
6: 0.70, # triple glazed
|
||||
7: 0.80, # secondary glazing
|
||||
}
|
||||
_G_LIGHT_DEFAULT: Final[float] = 0.80 # treat unknowns as DG (modal)
|
||||
|
||||
# Table 6c frame factor FF by frame-material substring. PVC, wood,
|
||||
# composite default to 0.7; metal to 0.8.
|
||||
_FRAME_FACTOR_BY_MATERIAL_SUBSTR: Final[tuple[tuple[str, float], ...]] = (
|
||||
("metal", 0.8),
|
||||
("aluminium", 0.8),
|
||||
("aluminum", 0.8),
|
||||
("wood", 0.7),
|
||||
("pvc", 0.7),
|
||||
("upvc", 0.7),
|
||||
("composite", 0.7),
|
||||
)
|
||||
_FRAME_FACTOR_DEFAULT: Final[float] = 0.7
|
||||
|
||||
|
||||
# Appendix L lighting constants.
|
||||
_LIGHTING_LAMBDA_B_COEFF: Final[float] = 11.2 * 59.73
|
||||
_LIGHTING_LAMBDA_B_EXPONENT: Final[float] = 0.4714
|
||||
|
|
@ -388,6 +451,192 @@ class InternalGainsBreakdown:
|
|||
total_w: float
|
||||
|
||||
|
||||
def _assumed_occupancy(total_floor_area_m2: float) -> float:
|
||||
"""Appendix J Table 1b occupancy default from TFA.
|
||||
|
||||
Duplicated from `water_heating.assumed_occupancy` to avoid the §4
|
||||
import dependency in §5 — keeps internal_gains.py self-contained.
|
||||
"""
|
||||
if total_floor_area_m2 <= 13.9:
|
||||
return 1.0
|
||||
tfa_offset = total_floor_area_m2 - 13.9
|
||||
return (
|
||||
1.0
|
||||
+ 1.76 * (1 - exp(-0.000349 * tfa_offset * tfa_offset))
|
||||
+ 0.0013 * tfa_offset
|
||||
)
|
||||
|
||||
|
||||
def _g_light(w: SapWindow) -> float:
|
||||
"""Table 6b light transmittance g_L by glazing-type code. Defaults
|
||||
to 0.80 (DG, modal across UK certs) when the cert lodges a code we
|
||||
don't recognise."""
|
||||
if isinstance(w.glazing_type, int) and w.glazing_type in _G_LIGHT_BY_GLAZING_CODE:
|
||||
return _G_LIGHT_BY_GLAZING_CODE[w.glazing_type]
|
||||
return _G_LIGHT_DEFAULT
|
||||
|
||||
|
||||
def _frame_factor(w: SapWindow) -> float:
|
||||
"""Table 6c frame factor. Prefer cert's `frame_factor`; else look up
|
||||
by `frame_material` substring."""
|
||||
if w.frame_factor is not None:
|
||||
return float(w.frame_factor)
|
||||
material = (w.frame_material or "").lower()
|
||||
for needle, ff in _FRAME_FACTOR_BY_MATERIAL_SUBSTR:
|
||||
if needle in material:
|
||||
return ff
|
||||
return _FRAME_FACTOR_DEFAULT
|
||||
|
||||
|
||||
def _lighting_capacity_and_efficacy_from_cert(
|
||||
epc: EpcPropertyData,
|
||||
) -> tuple[float, float]:
|
||||
"""Aggregate C_L,fixed (lm) and ε_fixed (lm/W) from the cert's bulb
|
||||
counts via RdSAP §12-1 per-lamp-type defaults. Falls back to L5b
|
||||
(185 × TFA lumens) + L8c (21.3 lm/W) when no bulb data lodged."""
|
||||
led = epc.led_fixed_lighting_bulbs_count or 0
|
||||
cfl = epc.cfl_fixed_lighting_bulbs_count or 0
|
||||
inc = epc.incandescent_fixed_lighting_bulbs_count or 0
|
||||
lel = epc.low_energy_fixed_lighting_bulbs_count or 0
|
||||
|
||||
led_w, led_eff = _RDSAP_LAMP_LED
|
||||
cfl_w, cfl_eff = _RDSAP_LAMP_CFL
|
||||
inc_w, inc_eff = _RDSAP_LAMP_INCANDESCENT
|
||||
lel_w, lel_eff = _RDSAP_LAMP_LEL_UNKNOWN
|
||||
total_lumens = (
|
||||
led * led_w * led_eff
|
||||
+ cfl * cfl_w * cfl_eff
|
||||
+ inc * inc_w * inc_eff
|
||||
+ lel * lel_w * lel_eff
|
||||
)
|
||||
total_power_w = led * led_w + cfl * cfl_w + inc * inc_w + lel * lel_w
|
||||
if total_power_w <= 0.0:
|
||||
tfa = float(epc.total_floor_area_m2 or 0.0)
|
||||
return (_LIGHTING_L5B_LUMENS_PER_M2 * tfa, _LIGHTING_L8C_EFFICACY_LM_PER_W)
|
||||
return (total_lumens, total_lumens / total_power_w)
|
||||
|
||||
|
||||
def _daylight_factor_from_cert(
|
||||
epc: EpcPropertyData,
|
||||
overshading: OvershadingCategory,
|
||||
) -> float:
|
||||
"""Compute C_daylight via L2a + L2b from the cert's windows. Per
|
||||
Table 6d note 3 a single Z_L applies to all glazing in the dwelling.
|
||||
|
||||
When `total_floor_area_m2` is missing or no windows are lodged the
|
||||
SAP "no-bonus" default 1.433 is used.
|
||||
"""
|
||||
tfa = float(epc.total_floor_area_m2 or 0.0)
|
||||
if tfa <= 0.0 or not epc.sap_windows:
|
||||
return 1.433
|
||||
z_l = _Z_L_BY_OVERSHADING[overshading]
|
||||
g_l_numerator = sum(
|
||||
float(w.window_width) * float(w.window_height)
|
||||
* _g_light(w) * _frame_factor(w)
|
||||
for w in epc.sap_windows
|
||||
)
|
||||
g_l = 0.9 * g_l_numerator * z_l / tfa
|
||||
if g_l > 0.095:
|
||||
return 0.96
|
||||
return 52.2 * g_l * g_l - 9.94 * g_l + 1.433
|
||||
|
||||
|
||||
def _pump_date_category_from_cert(epc: EpcPropertyData) -> PumpDateCategory:
|
||||
"""Map first main-heating detail's central_heating_pump_age_str to a
|
||||
Table 5a bucket. Elmhurst lodges "Pre 2013" / "Post 2013" / "Unknown"
|
||||
/ None on each `MainHeatingDetail` (nested under `epc.sap_heating`)."""
|
||||
sap_heating = getattr(epc, "sap_heating", None)
|
||||
details = getattr(sap_heating, "main_heating_details", None) or []
|
||||
age_str = ""
|
||||
if details:
|
||||
age_str = (details[0].central_heating_pump_age_str or "").lower()
|
||||
if "post" in age_str or "2013 or later" in age_str:
|
||||
return PumpDateCategory.NEW_2013_OR_LATER
|
||||
if "pre" in age_str or "2012" in age_str:
|
||||
return PumpDateCategory.OLD_2012_OR_EARLIER
|
||||
return PumpDateCategory.UNKNOWN
|
||||
|
||||
|
||||
def internal_gains_from_cert(
|
||||
*,
|
||||
epc: EpcPropertyData,
|
||||
dwelling_volume_m3: float,
|
||||
heat_gains_from_water_heating_monthly_kwh: tuple[float, ...],
|
||||
overshading: OvershadingCategory = OvershadingCategory.AVERAGE,
|
||||
) -> InternalGainsResult:
|
||||
"""SAP 10.2 §5 orchestrator — chain every line ref (66)..(73) for the
|
||||
dwelling identified by `epc`.
|
||||
|
||||
Inputs:
|
||||
epc cert (TFA, bulbs, windows, pump)
|
||||
dwelling_volume_m3 §1 line (5) for fan-W formulas
|
||||
heat_gains_from_water_heating_monthly_kwh §4 line (65)m — see Q5 grill
|
||||
overshading Table 6d bucket (default AVERAGE)
|
||||
|
||||
Coverage caveats for the current corpus:
|
||||
- Lighting: full Appendix L L1-L12 with RdSAP §12-1 per-lamp defaults
|
||||
and the L2a window-driven C_daylight. Conformant for the 6 Elmhurst
|
||||
fixtures (all DG, PVC frame, average overshading) to ~0.5%.
|
||||
- Pumps/fans: central heating pump only. Liquid-fuel pump, warm-air
|
||||
fans, PIV, balanced MV w/o HR, HIU branches are reachable via the
|
||||
leaf fns but not yet derivable from the cert here. Mirrors §4's
|
||||
combi-only happy-path scope.
|
||||
"""
|
||||
tfa = float(epc.total_floor_area_m2 or 0.0)
|
||||
n = _assumed_occupancy(tfa)
|
||||
|
||||
metabolic = metabolic_monthly_w(n_occupants=n)
|
||||
cooking = cooking_monthly_w(n_occupants=n)
|
||||
losses = losses_monthly_w(n_occupants=n)
|
||||
appliances = appliances_monthly_w(total_floor_area_m2=tfa, n_occupants=n)
|
||||
|
||||
c_l_fixed, eff_fixed = _lighting_capacity_and_efficacy_from_cert(epc)
|
||||
c_daylight = _daylight_factor_from_cert(epc, overshading)
|
||||
lighting = lighting_monthly_w(
|
||||
total_floor_area_m2=tfa,
|
||||
n_occupants=n,
|
||||
fixed_lighting_capacity_lm=c_l_fixed,
|
||||
fixed_lighting_efficacy_lm_per_w=eff_fixed,
|
||||
daylight_factor=c_daylight,
|
||||
)
|
||||
|
||||
pump_w = central_heating_pump_w(
|
||||
date_category=_pump_date_category_from_cert(epc)
|
||||
)
|
||||
# Liquid-fuel + warm-air + PIV + MV + HIU branches default to zero for
|
||||
# the combi-gas-natural-vent population; future slices will detect them
|
||||
# from epc.main_heating_details + epc.mechanical_ventilation.
|
||||
pumps_fans = pumps_fans_monthly_w(
|
||||
heating_season_w=pump_w,
|
||||
year_round_w=0.0,
|
||||
)
|
||||
|
||||
water_heating_gains = water_heating_gains_monthly_w(
|
||||
heat_gains_from_water_heating_monthly_kwh=heat_gains_from_water_heating_monthly_kwh,
|
||||
)
|
||||
|
||||
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_fans,
|
||||
losses_monthly_w=losses,
|
||||
water_heating_gains_monthly_w=water_heating_gains,
|
||||
)
|
||||
|
||||
return InternalGainsResult(
|
||||
metabolic_monthly_w=metabolic,
|
||||
lighting_monthly_w=lighting,
|
||||
appliances_monthly_w=appliances,
|
||||
cooking_monthly_w=cooking,
|
||||
pumps_fans_monthly_w=pumps_fans,
|
||||
losses_monthly_w=losses,
|
||||
water_heating_gains_monthly_w=water_heating_gains,
|
||||
total_internal_gains_monthly_w=total,
|
||||
)
|
||||
|
||||
|
||||
def _default_occupancy_sap_j(total_floor_area_m2: float) -> float:
|
||||
"""SAP 10.3 Appendix J Table 1b occupancy default from TFA."""
|
||||
if total_floor_area_m2 <= 13.9:
|
||||
|
|
|
|||
|
|
@ -14,12 +14,14 @@ import pytest
|
|||
|
||||
from domain.sap.worksheet.internal_gains import (
|
||||
InternalGainsResult,
|
||||
OvershadingCategory,
|
||||
PumpDateCategory,
|
||||
appliances_monthly_w,
|
||||
balanced_mv_no_hr_fan_w,
|
||||
central_heating_pump_w,
|
||||
cooking_monthly_w,
|
||||
heat_interface_unit_w,
|
||||
internal_gains_from_cert,
|
||||
lighting_monthly_w,
|
||||
liquid_fuel_boiler_pump_w,
|
||||
liquid_fuel_warm_air_pump_w,
|
||||
|
|
@ -31,6 +33,13 @@ from domain.sap.worksheet.internal_gains import (
|
|||
warm_air_heating_fan_w,
|
||||
water_heating_gains_monthly_w,
|
||||
)
|
||||
from datatypes.epc.domain.epc_property_data import (
|
||||
InstantaneousWwhrs,
|
||||
MainHeatingDetail,
|
||||
SapHeating,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
|
||||
|
||||
def test_metabolic_gains_are_60w_per_occupant_constant_across_months() -> None:
|
||||
|
|
@ -412,3 +421,106 @@ def test_internal_gains_result_dataclass_holds_all_seven_lines_plus_total() -> N
|
|||
"cooking_monthly_w", "pumps_fans_monthly_w", "losses_monthly_w",
|
||||
"water_heating_gains_monthly_w", "total_internal_gains_monthly_w",
|
||||
))
|
||||
|
||||
|
||||
def _build_000490_lookalike_epc() -> "EpcPropertyData": # noqa: F821 — string ref keeps imports light
|
||||
"""Hand-build a minimal EPC matching the 000490 cert's §5 surface:
|
||||
TFA 66.06 m², 8 LEL bulbs (LED/CFL unknown), 3 DG-PVC windows
|
||||
totalling 9.03 m², gas combi with central heating pump (unknown date).
|
||||
|
||||
Slice 9 keeps this local rather than mutating `_elmhurst_worksheet_000490.build_epc()`
|
||||
so the existing e2e SAP-score regression test (which is pinned to the
|
||||
legacy cert state) doesn't drift. Slice 10 extends the fixture proper.
|
||||
"""
|
||||
def _window(area: float, orientation_code: int) -> SapWindow:
|
||||
side = area ** 0.5
|
||||
return SapWindow(
|
||||
frame_material="PVC",
|
||||
glazing_gap=12,
|
||||
orientation=orientation_code,
|
||||
window_type=2,
|
||||
glazing_type=2,
|
||||
window_width=side,
|
||||
window_height=area / side,
|
||||
draught_proofed=True,
|
||||
window_location=1,
|
||||
window_wall_type=1,
|
||||
permanent_shutters_present=False,
|
||||
)
|
||||
sap_heating = SapHeating(
|
||||
instantaneous_wwhrs=InstantaneousWwhrs(),
|
||||
main_heating_details=[
|
||||
MainHeatingDetail(
|
||||
has_fghrs=False,
|
||||
main_fuel_type=1,
|
||||
heat_emitter_type=1,
|
||||
emitter_temperature=1,
|
||||
main_heating_control=2106,
|
||||
central_heating_pump_age_str="Unknown",
|
||||
),
|
||||
],
|
||||
has_fixed_air_conditioning=False,
|
||||
)
|
||||
return make_minimal_sap10_epc(
|
||||
total_floor_area_m2=66.06,
|
||||
low_energy_fixed_lighting_bulbs_count=8,
|
||||
sap_windows=[
|
||||
_window(0.81, orientation_code=8),
|
||||
_window(5.52, orientation_code=4),
|
||||
_window(2.70, orientation_code=6),
|
||||
],
|
||||
sap_heating=sap_heating,
|
||||
)
|
||||
|
||||
|
||||
def test_internal_gains_from_cert_reproduces_000490_worksheet_end_to_end() -> None:
|
||||
"""End-to-end §5 orchestrator against Elmhurst U985-0001-000490.
|
||||
|
||||
Drives the full (66)..(73) pipeline from the cert: occupancy via
|
||||
Appendix J Table 1b from TFA, lighting via RdSAP §12-1 bulb defaults
|
||||
+ Appendix L cascade, appliances via L13/L14/L16a, cooking + losses
|
||||
+ metabolic from N, pumps/fans via Table 5a (central heating pump
|
||||
unknown date, no MV/PIV/HIU for combi-gas-natural-vent population),
|
||||
water-heating gains bridged from the §4 (65)m kWh tuple supplied by
|
||||
the caller.
|
||||
|
||||
Asserts every line ref against the worksheet to ≤1e-2 W tolerance.
|
||||
"""
|
||||
# Arrange — hand-built 000490-lookalike + worksheet (65)m + (5) volume.
|
||||
epc = _build_000490_lookalike_epc()
|
||||
heat_gains_wh_kwh = (
|
||||
75.2034, 66.4381, 70.4305, 61.5896, 59.4711, 53.3391,
|
||||
52.4705, 54.6991, 55.4582, 62.1383, 66.4342, 74.3403,
|
||||
)
|
||||
|
||||
# Act
|
||||
result = internal_gains_from_cert(
|
||||
epc=epc,
|
||||
dwelling_volume_m3=202.6377, # worksheet line (5)
|
||||
heat_gains_from_water_heating_monthly_kwh=heat_gains_wh_kwh,
|
||||
overshading=OvershadingCategory.AVERAGE,
|
||||
)
|
||||
|
||||
# Assert — every worksheet line.
|
||||
expected_66 = (128.8087,) * 12
|
||||
expected_67 = (24.2665, 21.5533, 17.5283, 13.2701, 9.9195, 8.3745,
|
||||
9.0489, 11.7621, 15.7871, 20.0454, 23.3959, 24.9410)
|
||||
expected_68 = (280.4965, 283.4071, 276.0723, 260.4574, 240.7463, 222.2207,
|
||||
209.8445, 206.9338, 214.2686, 229.8835, 249.5946, 268.1202)
|
||||
expected_69 = (50.0277,) * 12
|
||||
expected_70 = (7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0)
|
||||
expected_71 = (-85.8725,) * 12
|
||||
expected_72 = (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_73 = (505.8067, 503.7906, 488.2293, 459.2325, 430.5641, 397.6412,
|
||||
382.3822, 385.1801, 400.0449, 433.4120, 465.2242, 492.9448)
|
||||
|
||||
for m in range(12):
|
||||
assert result.metabolic_monthly_w[m] == pytest.approx(expected_66[m], abs=1e-3), f"(66) month {m+1}"
|
||||
assert result.lighting_monthly_w[m] == pytest.approx(expected_67[m], abs=5e-2), f"(67) month {m+1}"
|
||||
assert result.appliances_monthly_w[m] == pytest.approx(expected_68[m], abs=5e-2), f"(68) month {m+1}"
|
||||
assert result.cooking_monthly_w[m] == pytest.approx(expected_69[m], abs=1e-3), f"(69) month {m+1}"
|
||||
assert result.pumps_fans_monthly_w[m] == pytest.approx(expected_70[m], abs=1e-9), f"(70) month {m+1}"
|
||||
assert result.losses_monthly_w[m] == pytest.approx(expected_71[m], abs=1e-3), f"(71) month {m+1}"
|
||||
assert result.water_heating_gains_monthly_w[m] == pytest.approx(expected_72[m], abs=1e-3), f"(72) month {m+1}"
|
||||
assert result.total_internal_gains_monthly_w[m] == pytest.approx(expected_73[m], abs=1e-1), f"(73) month {m+1}"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue