From 99e5c2cd44e0935e6c566e2603ae4f12f6f0997c Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 20 May 2026 19:06:34 +0000 Subject: [PATCH] =?UTF-8?q?=C2=A75=20slice=2010:=20extract=20LINE=5F66..LI?= =?UTF-8?q?NE=5F73=20+=20ALL=5FFIXTURES=20e2e=20conformance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds SECTION_5_BULB_COUNT_LEL, SECTION_5_WINDOW_AREAS_M2, SECTION_5_PUMP_AGE_STR and LINE_66..LINE_73 expected outputs to every Elmhurst fixture (000474, 000477, 000480, 000487, 000490, 000516). Constants extracted from the U985-0001-NNNNNN worksheets supplied 2026-05-20. All six fixtures share the same shape: all-LEL bulb lighting, gas combi pump with unknown install date, average overshading. Adds an ALL_FIXTURES-parametrized test in test_internal_gains.py that composes a §5 EPC from the fixture's constants and drives internal_gains_from_cert. Tolerances: ≤1e-3 W on the linear-in-N rows (66/69/71), ≤2e-1 W on (67) lighting (worksheet-rounded N + rooflight Z_L=1.0 approximated by AVERAGE Z_L=0.83), ≤5e-2 W on (68) appliances, ≤3e-1 W on (73) sum. Result: 26 tests pass; six fixtures conform to ≤0.6% lighting bias end-to-end. The fixture's base build_epc() is unchanged — §5 EPC composition lives in a test helper so the existing e2e SAP-score regression (000490, 000474) remains pinned for the upcoming calc.py wiring slice. Co-Authored-By: Claude Opus 4.7 --- .../tests/_elmhurst_worksheet_000474.py | 36 ++++++ .../tests/_elmhurst_worksheet_000477.py | 31 ++++++ .../tests/_elmhurst_worksheet_000480.py | 31 ++++++ .../tests/_elmhurst_worksheet_000487.py | 31 ++++++ .../tests/_elmhurst_worksheet_000490.py | 31 ++++++ .../tests/_elmhurst_worksheet_000516.py | 31 ++++++ .../worksheet/tests/test_internal_gains.py | 105 ++++++++++++++++++ 7 files changed, 296 insertions(+) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py index e8129a02..78377857 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py @@ -238,3 +238,39 @@ LINE_65_M_HEAT_GAINS_FROM_WH_KWH: tuple[float, ...] = ( ) COMBI_LOSS_OVERRIDE: Optional[tuple[float, ...]] = LINE_61_M_COMBI_LOSS_KWH + +# ============================================================================ +# §5 Internal gains — cert-derived inputs + expected outputs +# ============================================================================ +# Lighting: 8 low-energy bulbs (LED/CFL undistinguished on cert) → RdSAP +# §12-1 default 80 lm/W × 15 W = 1200 lm each. +SECTION_5_BULB_COUNT_LEL: int = 8 +# Window areas per worksheet §6 (5 windows: East 3.74 ×2, NW 1.76 + 1.98, +# SE 0.5). All DG air-filled (g_L=0.80) on PVC frames (FF=0.7). +SECTION_5_WINDOW_AREAS_M2: tuple[float, ...] = (3.74, 3.74, 1.76, 1.98, 0.5) +# Vaillant ecoTEC pro 28 combi, pump in heated space, unknown install date +# → Table 5a 7 W heating-season-only row. +SECTION_5_PUMP_AGE_STR: str = "Unknown" + +LINE_66_M_METABOLIC_W: tuple[float, ...] = (113.3748,) * 12 +LINE_67_M_LIGHTING_W: tuple[float, ...] = ( + 19.8107, 17.5957, 14.3098, 10.8334, 8.0981, 6.8368, + 7.3874, 9.6024, 12.8883, 16.3646, 19.1000, 20.3613, +) +LINE_68_M_APPLIANCES_W: tuple[float, ...] = ( + 245.9478, 248.5000, 242.0686, 228.3770, 211.0937, 194.8499, + 183.9980, 181.4459, 187.8772, 201.5689, 218.8522, 235.0960, +) +LINE_69_M_COOKING_W: tuple[float, ...] = (48.2271,) * 12 +LINE_70_M_PUMPS_FANS_W: tuple[float, ...] = ( + 7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0, +) +LINE_71_M_LOSSES_W: tuple[float, ...] = (-75.5832,) * 12 +LINE_72_M_WATER_HEATING_GAINS_W: tuple[float, ...] = ( + 87.5927, 85.5340, 81.6263, 73.1405, 67.9223, 62.4757, + 59.1662, 61.9545, 65.2172, 71.2596, 79.3985, 86.5137, +) +LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = ( + 446.3699, 444.6484, 431.0234, 405.3696, 380.1328, 350.1810, + 336.5702, 339.0214, 352.0014, 382.2118, 410.3693, 434.9896, +) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000477.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000477.py index 11c597d1..045ec96b 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000477.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000477.py @@ -180,3 +180,34 @@ LINE_65_M_HEAT_GAINS_FROM_WH_KWH: tuple[float, ...] = ( ) COMBI_LOSS_OVERRIDE: Optional[tuple[float, ...]] = LINE_61_M_COMBI_LOSS_KWH + +# ============================================================================ +# §5 Internal gains — cert-derived inputs + expected outputs +# ============================================================================ +SECTION_5_BULB_COUNT_LEL: int = 9 +# 3 windows: East 1.28, West 1.17 + 6.76. All DG / PVC. +SECTION_5_WINDOW_AREAS_M2: tuple[float, ...] = (1.28, 1.17, 6.76) +SECTION_5_PUMP_AGE_STR: str = "Unknown" + +LINE_66_M_METABOLIC_W: tuple[float, ...] = (144.9204,) * 12 +LINE_67_M_LIGHTING_W: tuple[float, ...] = ( + 28.5492, 25.3572, 20.6218, 15.6121, 11.6702, 9.8525, + 10.6459, 13.8380, 18.5733, 23.5831, 27.5250, 29.3427, +) +LINE_68_M_APPLIANCES_W: tuple[float, ...] = ( + 319.8635, 323.1826, 314.8184, 297.0120, 274.5345, 253.4089, + 239.2956, 235.9765, 244.3407, 262.1471, 284.6246, 305.7502, +) +LINE_69_M_COOKING_W: tuple[float, ...] = (51.9074,) * 12 +LINE_70_M_PUMPS_FANS_W: tuple[float, ...] = ( + 7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0, +) +LINE_71_M_LOSSES_W: tuple[float, ...] = (-96.6136,) * 12 +LINE_72_M_WATER_HEATING_GAINS_W: tuple[float, ...] = ( + 81.6015, 79.4024, 75.2820, 66.4100, 60.9388, 55.1879, + 51.7430, 54.7206, 58.1855, 64.5131, 73.0260, 80.4796, +) +LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = ( + 537.2284, 535.1563, 517.9364, 486.2482, 454.3577, 418.6634, + 401.8987, 404.7492, 421.3137, 457.4575, 492.3897, 522.7868, +) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000480.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000480.py index d1b1597b..08246823 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000480.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000480.py @@ -212,3 +212,34 @@ LINE_65_M_HEAT_GAINS_FROM_WH_KWH: tuple[float, ...] = ( ) COMBI_LOSS_OVERRIDE: Optional[tuple[float, ...]] = LINE_61_M_COMBI_LOSS_KWH + +# ============================================================================ +# §5 Internal gains — cert-derived inputs + expected outputs +# ============================================================================ +SECTION_5_BULB_COUNT_LEL: int = 10 +# 2 windows: NE 8.74, SW 1.8. All DG / PVC. +SECTION_5_WINDOW_AREAS_M2: tuple[float, ...] = (8.74, 1.8) +SECTION_5_PUMP_AGE_STR: str = "Unknown" + +LINE_66_M_METABOLIC_W: tuple[float, ...] = (152.4740,) * 12 +LINE_67_M_LIGHTING_W: tuple[float, ...] = ( + 30.0891, 26.7249, 21.7341, 16.4541, 12.2997, 10.3839, + 11.2202, 14.5844, 19.5751, 24.8551, 29.0096, 30.9254, +) +LINE_68_M_APPLIANCES_W: tuple[float, ...] = ( + 340.9107, 344.4483, 335.5337, 316.5556, 292.5991, 270.0834, + 255.0415, 251.5039, 260.4185, 279.3966, 303.3531, 325.8689, +) +LINE_69_M_COOKING_W: tuple[float, ...] = (52.7886,) * 12 +LINE_70_M_PUMPS_FANS_W: tuple[float, ...] = ( + 7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0, +) +LINE_71_M_LOSSES_W: tuple[float, ...] = (-101.6493,) * 12 +LINE_72_M_WATER_HEATING_GAINS_W: tuple[float, ...] = ( + 93.1167, 90.8413, 86.5957, 77.4784, 71.8542, 65.9937, + 62.4747, 65.5058, 69.0425, 75.5460, 84.2964, 91.9648, +) +LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = ( + 574.7298, 572.6278, 554.4769, 521.1014, 487.3662, 450.0743, + 432.3496, 435.2074, 452.6494, 490.4110, 527.2724, 559.3723, +) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000487.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000487.py index db4235e7..f3789d83 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000487.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000487.py @@ -227,3 +227,34 @@ LINE_65_M_HEAT_GAINS_FROM_WH_KWH: tuple[float, ...] = ( COMBI_LOSS_OVERRIDE: Optional[tuple[float, ...]] = LINE_61_M_COMBI_LOSS_KWH ELECTRIC_SHOWER_OVERRIDE: Optional[tuple[float, ...]] = LINE_64A_M_ELECTRIC_SHOWER_KWH + +# ============================================================================ +# §5 Internal gains — cert-derived inputs + expected outputs +# ============================================================================ +SECTION_5_BULB_COUNT_LEL: int = 7 +# 2 windows: South 0.77, South 6.69. All DG / PVC. +SECTION_5_WINDOW_AREAS_M2: tuple[float, ...] = (0.77, 6.69) +SECTION_5_PUMP_AGE_STR: str = "Unknown" + +LINE_66_M_METABOLIC_W: tuple[float, ...] = (149.5185,) * 12 +LINE_67_M_LIGHTING_W: tuple[float, ...] = ( + 32.2313, 28.6276, 23.2815, 17.6256, 13.1753, 11.1232, + 12.0190, 15.6227, 20.9688, 26.6247, 31.0750, 33.1271, +) +LINE_68_M_APPLIANCES_W: tuple[float, ...] = ( + 332.3739, 335.8229, 327.1315, 308.6286, 285.2720, 263.3201, + 248.6549, 245.2059, 253.8973, 272.4002, 295.7567, 317.7086, +) +LINE_69_M_COOKING_W: tuple[float, ...] = (52.4438,) * 12 +LINE_70_M_PUMPS_FANS_W: tuple[float, ...] = ( + 7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0, +) +LINE_71_M_LOSSES_W: tuple[float, ...] = (-99.6790,) * 12 +LINE_72_M_WATER_HEATING_GAINS_W: tuple[float, ...] = ( + 74.9695, 73.0835, 70.0906, 64.3849, 60.6749, 56.8639, + 54.9180, 57.1404, 59.6591, 63.8641, 69.3748, 74.3133, +) +LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = ( + 548.8580, 546.8173, 529.7869, 499.9224, 468.4056, 433.5905, + 417.8752, 420.2523, 436.8085, 472.1723, 505.4898, 534.4324, +) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py index 68569e7a..be98b226 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py @@ -223,3 +223,34 @@ LINE_65_M_HEAT_GAINS_FROM_WH_KWH: tuple[float, ...] = ( 75.2034, 66.4381, 70.4305, 61.5896, 59.4711, 53.3391, 52.4705, 54.6991, 55.4582, 62.1383, 66.4342, 74.3403, ) + +# ============================================================================ +# §5 Internal gains — cert-derived inputs + expected outputs +# ============================================================================ +SECTION_5_BULB_COUNT_LEL: int = 8 +# 3 windows: NE 0.81, NW 2.7, SE 5.52. All DG / PVC. +SECTION_5_WINDOW_AREAS_M2: tuple[float, ...] = (0.81, 2.7, 5.52) +SECTION_5_PUMP_AGE_STR: str = "Unknown" + +LINE_66_M_METABOLIC_W: tuple[float, ...] = (128.8087,) * 12 +LINE_67_M_LIGHTING_W: tuple[float, ...] = ( + 24.2665, 21.5533, 17.5283, 13.2701, 9.9195, 8.3745, + 9.0489, 11.7621, 15.7871, 20.0454, 23.3959, 24.9410, +) +LINE_68_M_APPLIANCES_W: tuple[float, ...] = ( + 280.4965, 283.4071, 276.0723, 260.4574, 240.7463, 222.2207, + 209.8445, 206.9338, 214.2686, 229.8835, 249.5946, 268.1202, +) +LINE_69_M_COOKING_W: tuple[float, ...] = (50.0277,) * 12 +LINE_70_M_PUMPS_FANS_W: tuple[float, ...] = ( + 7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0, +) +LINE_71_M_LOSSES_W: tuple[float, ...] = (-85.8725,) * 12 +LINE_72_M_WATER_HEATING_GAINS_W: tuple[float, ...] = ( + 101.0798, 98.8663, 94.6647, 85.5412, 79.9343, 74.0821, + 70.5249, 73.5203, 77.0253, 83.5192, 92.2698, 99.9197, +) +LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = ( + 505.8067, 503.7906, 488.2293, 459.2325, 430.5641, 397.6412, + 382.3822, 385.1801, 400.0449, 433.4120, 465.2242, 492.9448, +) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py index 6fa8d3c4..d9a8d1b3 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py @@ -189,3 +189,34 @@ LINE_65_M_HEAT_GAINS_FROM_WH_KWH: tuple[float, ...] = ( ) COMBI_LOSS_OVERRIDE: Optional[tuple[float, ...]] = LINE_61_M_COMBI_LOSS_KWH + +# ============================================================================ +# §5 Internal gains — cert-derived inputs + expected outputs +# ============================================================================ +SECTION_5_BULB_COUNT_LEL: int = 9 +# 3 windows: NE 1.18 + 3.88, SW 4.43. All DG / PVC. +SECTION_5_WINDOW_AREAS_M2: tuple[float, ...] = (1.18, 3.88, 4.43) +SECTION_5_PUMP_AGE_STR: str = "Unknown" + +LINE_66_M_METABOLIC_W: tuple[float, ...] = (157.9824,) * 12 +LINE_67_M_LIGHTING_W: tuple[float, ...] = ( + 32.6842, 29.0298, 23.6086, 17.8733, 13.3605, 11.2795, + 12.1879, 15.8423, 21.2634, 26.9988, 31.5116, 33.5926, +) +LINE_68_M_APPLIANCES_W: tuple[float, ...] = ( + 358.3100, 362.0281, 352.6585, 332.7118, 307.5326, 283.8678, + 268.0582, 264.3400, 273.7096, 293.6564, 318.8355, 342.5004, +) +LINE_69_M_COOKING_W: tuple[float, ...] = (53.4313,) * 12 +LINE_70_M_PUMPS_FANS_W: tuple[float, ...] = ( + 7.0, 7.0, 7.0, 7.0, 7.0, 0.0, 0.0, 0.0, 0.0, 7.0, 7.0, 7.0, +) +LINE_71_M_LOSSES_W: tuple[float, ...] = (-105.3216,) * 12 +LINE_72_M_WATER_HEATING_GAINS_W: tuple[float, ...] = ( + 96.0567, 93.5526, 88.7767, 78.3746, 71.9842, 65.3089, + 61.2413, 64.6528, 68.6444, 76.0475, 86.0218, 94.7327, +) +LINE_73_M_TOTAL_INTERNAL_GAINS_W: tuple[float, ...] = ( + 600.1430, 597.7026, 578.1360, 542.0517, 505.9694, 466.5482, + 447.5794, 450.9272, 469.7096, 509.7947, 549.4610, 583.9178, +) diff --git a/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py b/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py index 7af7a32e..0e00cac6 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py +++ b/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py @@ -33,13 +33,17 @@ from domain.sap.worksheet.internal_gains import ( warm_air_heating_fan_w, water_heating_gains_monthly_w, ) +from types import ModuleType + from datatypes.epc.domain.epc_property_data import ( + EpcPropertyData, InstantaneousWwhrs, MainHeatingDetail, SapHeating, SapWindow, ) from domain.ml.tests._fixtures import make_minimal_sap10_epc +from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id def test_metabolic_gains_are_60w_per_occupant_constant_across_months() -> None: @@ -524,3 +528,104 @@ def test_internal_gains_from_cert_reproduces_000490_worksheet_end_to_end() -> No 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}" + + +def _build_section_5_epc(fixture: ModuleType) -> EpcPropertyData: + """Wrap a fixture's base `build_epc()` with the §5-relevant fields it + doesn't yet carry: sap_windows (DG air-filled / PVC), low-energy bulb + count, and a MainHeatingDetail with the recorded pump age. Kept in + test scope so the legacy fixture build_epc()s stay pinned for §1-§4 + conformance + the e2e SAP-score regression.""" + def _window(area: float) -> SapWindow: + side = area ** 0.5 + return SapWindow( + frame_material="PVC", + glazing_gap=12, + orientation=1, # arbitrary — orientation doesn't affect §5 + window_type=2, + glazing_type=2, # DG air-filled + 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=fixture.SECTION_5_PUMP_AGE_STR, + ), + ], + has_fixed_air_conditioning=False, + ) + return make_minimal_sap10_epc( + total_floor_area_m2=fixture.LINE_4_TFA_M2, + low_energy_fixed_lighting_bulbs_count=fixture.SECTION_5_BULB_COUNT_LEL, + sap_windows=[_window(a) for a in fixture.SECTION_5_WINDOW_AREAS_M2], + sap_heating=sap_heating, + ) + + +@pytest.mark.parametrize("fixture", ALL_FIXTURES, ids=[fixture_id(f) for f in ALL_FIXTURES]) +def test_internal_gains_from_cert_matches_elmhurst_worksheet_all_fixtures( + fixture: ModuleType, +) -> None: + """End-to-end §5 orchestrator against every Elmhurst conformance + fixture in `_elmhurst_fixtures.ALL_FIXTURES`. + + Each fixture pins its own §5 input constants (SECTION_5_BULB_COUNT_LEL, + SECTION_5_WINDOW_AREAS_M2, SECTION_5_PUMP_AGE_STR) + worksheet outputs + (LINE_66..LINE_73). The test composes an EPC from these constants and + drives `internal_gains_from_cert`. (65)m comes from each fixture's + §4 LINE_65_M_HEAT_GAINS_FROM_WH_KWH. + + Tolerances are generous on (67) and (68) because their (TFA × N)^0.4714 + + cosine modulation propagate display rounding from the worksheet's + 4-d.p. N value, plus rooflight Z_L=1.0 (Table 6d note 2) is approximated + by a single AVERAGE Z_L=0.83 here. (66), (69), (71) close to <1e-3 W. + (73) accumulates the other tolerances → ≤3e-1 W. + """ + # Arrange + epc = _build_section_5_epc(fixture) + + # Act + result = internal_gains_from_cert( + epc=epc, + dwelling_volume_m3=fixture.LINE_5_VOLUME_M3, + heat_gains_from_water_heating_monthly_kwh=fixture.LINE_65_M_HEAT_GAINS_FROM_WH_KWH, + overshading=OvershadingCategory.AVERAGE, + ) + + # Assert + for m in range(12): + assert result.metabolic_monthly_w[m] == pytest.approx( + fixture.LINE_66_M_METABOLIC_W[m], abs=1e-3 + ), f"(66) month {m+1}" + assert result.lighting_monthly_w[m] == pytest.approx( + fixture.LINE_67_M_LIGHTING_W[m], abs=2e-1 + ), f"(67) month {m+1}" + assert result.appliances_monthly_w[m] == pytest.approx( + fixture.LINE_68_M_APPLIANCES_W[m], abs=5e-2 + ), f"(68) month {m+1}" + assert result.cooking_monthly_w[m] == pytest.approx( + fixture.LINE_69_M_COOKING_W[m], abs=1e-3 + ), f"(69) month {m+1}" + assert result.pumps_fans_monthly_w[m] == pytest.approx( + fixture.LINE_70_M_PUMPS_FANS_W[m], abs=1e-9 + ), f"(70) month {m+1}" + assert result.losses_monthly_w[m] == pytest.approx( + fixture.LINE_71_M_LOSSES_W[m], abs=1e-3 + ), f"(71) month {m+1}" + assert result.water_heating_gains_monthly_w[m] == pytest.approx( + fixture.LINE_72_M_WATER_HEATING_GAINS_W[m], abs=1e-3 + ), f"(72) month {m+1}" + assert result.total_internal_gains_monthly_w[m] == pytest.approx( + fixture.LINE_73_M_TOTAL_INTERNAL_GAINS_W[m], abs=3e-1 + ), f"(73) month {m+1}"