mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 29: §9a energy requirements cascade pin (72/72)
Adds `energy_requirements_section_from_cert(epc)` to the cert→inputs cascade. Composes §8 (98c)m + Table 11 secondary fraction + per-system efficiencies into (201)..(221) line refs via the existing `space_heating_fuel_monthly_kwh` orchestrator. Extracts `_main_heating_efficiency(epc)` as a shared helper — same eff derivation as the inline `cert_to_inputs` flow (PCDB winter override → Table 4a/4b seasonal → heat-network 1/DLF override). Single source of truth for §4 and §9a. Worksheet display convention: when no secondary system is lodged the PDF displays (208) = 0 (not the fallback 100% electric efficiency). The per-system fuel formula already collapses to 0 via fraction_201 = 0, so this is presentation-only; the helper zeros (208) when `secondary_fraction == 0`. 000474 (no secondary) now matches exactly. Adds §9a LINE_ constants to all 6 fixtures — (201), (202), (206), (207), (208), (211)m, (211), (213)m, (213), (215)m, (215), (221). Extracted from `sap worksheets/U985-0001-NNNNNN.txt` PDF blocks. Cascade scoreboard: 396/396 → 468/468 (§7..§9a closed). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
13719e010a
commit
049694e1e6
8 changed files with 279 additions and 12 deletions
|
|
@ -411,6 +411,35 @@ def _first_main_heating(epc: EpcPropertyData) -> Optional[MainHeatingDetail]:
|
|||
return details[0] if details else None
|
||||
|
||||
|
||||
def _main_heating_efficiency(epc: EpcPropertyData) -> float:
|
||||
"""SAP 10.2 (206) main system 1 efficiency as a 0..1 fraction.
|
||||
|
||||
Resolves PCDB Table 105 winter efficiency override → Table 4a/4b
|
||||
seasonal efficiency → heat-network 1/DLF override. Used by §4 (water
|
||||
heating cascade) and §9a (per-system fuel kWh) — both must see the
|
||||
same value, so this single helper is the single source of truth."""
|
||||
main = _first_main_heating(epc)
|
||||
main_code = main.sap_main_heating_code if main is not None else None
|
||||
main_category = main.main_heating_category if main is not None else None
|
||||
main_fuel = _main_fuel_code(main)
|
||||
pcdb_main = (
|
||||
gas_oil_boiler_record(main.main_heating_index_number)
|
||||
if main is not None and main.main_heating_index_number is not None
|
||||
else None
|
||||
)
|
||||
if pcdb_main is not None and pcdb_main.winter_efficiency_pct is not None:
|
||||
eff = pcdb_main.winter_efficiency_pct / 100.0
|
||||
else:
|
||||
eff = seasonal_efficiency(main_code, main_category, main_fuel)
|
||||
if _is_heat_network_main(main):
|
||||
primary_age = (
|
||||
epc.sap_building_parts[0].construction_age_band
|
||||
if epc.sap_building_parts else None
|
||||
)
|
||||
eff = 1.0 / _heat_network_dlf(primary_age)
|
||||
return eff
|
||||
|
||||
|
||||
def _control_type(main: Optional[MainHeatingDetail]) -> int:
|
||||
"""SAP 10.2 §7.1 / Table 9 control type 1/2/3 from the
|
||||
`main_heating_control` code on `MainHeatingDetail`. Defaults to 2
|
||||
|
|
@ -1013,6 +1042,42 @@ def fabric_energy_efficiency_from_cert(epc: EpcPropertyData) -> Optional[float]:
|
|||
)
|
||||
|
||||
|
||||
def energy_requirements_section_from_cert(
|
||||
epc: EpcPropertyData,
|
||||
) -> Optional[EnergyRequirementsResult]:
|
||||
"""SAP 10.2 §9a cert→inputs cascade for `space_heating_fuel_monthly_kwh`.
|
||||
|
||||
Composes §8 (98c)m + Table 11 secondary fraction + per-system
|
||||
efficiencies into the (201)..(221) line refs. Single-main scope A
|
||||
(no (203)/(207)/(213)/(209)/(221)). Returns None when TFA missing.
|
||||
"""
|
||||
if epc.total_floor_area_m2 is None:
|
||||
return None
|
||||
sh = space_heating_section_from_cert(epc)
|
||||
assert sh is not None, "space_heating None despite TFA present"
|
||||
main = _first_main_heating(epc)
|
||||
main_code = main.sap_main_heating_code if main is not None else None
|
||||
main_category = main.main_heating_category if main is not None else None
|
||||
main_fuel = _main_fuel_code(main)
|
||||
secondary_fraction_value = _secondary_fraction(
|
||||
main, epc.sap_heating.secondary_heating_type if epc.sap_heating else None
|
||||
)
|
||||
# When no secondary system is lodged the worksheet displays (208) = 0;
|
||||
# the per-system fuel formula already collapses to 0 via fraction_201 = 0
|
||||
# so this is presentation-only.
|
||||
secondary_efficiency_value = (
|
||||
_secondary_efficiency(epc.sap_heating, main_code, main_fuel)
|
||||
if secondary_fraction_value > 0.0 else 0.0
|
||||
)
|
||||
eff = _main_heating_efficiency(epc)
|
||||
return space_heating_fuel_monthly_kwh(
|
||||
space_heating_monthly_kwh=sh.total_space_heating_monthly_kwh,
|
||||
secondary_heating_fraction=secondary_fraction_value,
|
||||
main_heating_efficiency_pct=eff * 100.0,
|
||||
secondary_heating_efficiency_pct=secondary_efficiency_value * 100.0,
|
||||
)
|
||||
|
||||
|
||||
def solar_gains_section_from_cert(epc: EpcPropertyData) -> SolarGainsResult:
|
||||
"""SAP 10.2 §6 cert→inputs cascade for `solar_gains_from_cert`.
|
||||
|
||||
|
|
@ -1450,18 +1515,10 @@ def cert_to_inputs(
|
|||
if main is not None and main.main_heating_index_number is not None
|
||||
else None
|
||||
)
|
||||
if pcdb_main is not None and pcdb_main.winter_efficiency_pct is not None:
|
||||
eff = pcdb_main.winter_efficiency_pct / 100.0
|
||||
else:
|
||||
eff = seasonal_efficiency(main_code, main_category, main_fuel)
|
||||
if _is_heat_network_main(main):
|
||||
# SAP 10.2 Table 12 note (k): heat-network unit prices are per
|
||||
# kWh of heat GENERATED (before distribution losses), not per
|
||||
# kWh of fuel consumed. Setting efficiency = 1/DLF makes the
|
||||
# calculator's `main_fuel_kwh = q_useful / (1/DLF) = q_useful
|
||||
# × DLF = q_generated`, so cost = q_generated × unit_price as
|
||||
# the spec requires.
|
||||
eff = 1.0 / _heat_network_dlf(primary_age)
|
||||
# Heat-network override (Table 12 note (k)) sets efficiency = 1/DLF so
|
||||
# `main_fuel_kwh = q_useful × DLF = q_generated`, matching the spec's
|
||||
# "unit prices per kWh of heat generated" convention.
|
||||
eff = _main_heating_efficiency(epc)
|
||||
if pcdb_main is not None and pcdb_main.summer_efficiency_pct is not None:
|
||||
water_eff = pcdb_main.summer_efficiency_pct / 100.0
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -431,3 +431,22 @@ LINE_108_PER_M2_KWH: float = 0.0
|
|||
# solar space heating = 0 → Σ(98a) = Σ(98c) → (98a)/TFA = LINE_99_PER_M2_KWH;
|
||||
# (108) = LINE_108_PER_M2_KWH = 0 (no AC). So LINE_109 = LINE_99 exactly.
|
||||
LINE_109_FEE_KWH_PER_M2: float = 186.879
|
||||
|
||||
# ============================================================================
|
||||
# §9a Energy requirements — Individual heating systems
|
||||
# ============================================================================
|
||||
LINE_201_SECONDARY_FRACTION: float = 0.0000
|
||||
LINE_202_MAIN_TOTAL_FRACTION: float = 1.0000
|
||||
LINE_206_MAIN_1_EFFICIENCY_PCT: float = 88.7000
|
||||
LINE_207_MAIN_2_EFFICIENCY_PCT: float = 0.0000
|
||||
LINE_208_SECONDARY_EFFICIENCY_PCT: float = 0.0000
|
||||
LINE_211_M_MAIN_1_FUEL_KWH: tuple[float, ...] = (
|
||||
2055.0528, 1734.1671, 1623.6845, 1157.0979, 750.5403, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 1027.8952, 1549.2667, 2067.1879,
|
||||
)
|
||||
LINE_211_ANNUAL_KWH: float = 11964.8924
|
||||
LINE_213_M_MAIN_2_FUEL_KWH: tuple[float, ...] = (0.0,) * 12
|
||||
LINE_213_ANNUAL_KWH: float = 0.0000
|
||||
LINE_215_M_SECONDARY_FUEL_KWH: tuple[float, ...] = (0.0,) * 12
|
||||
LINE_215_ANNUAL_KWH: float = 0.0000
|
||||
LINE_221_COOLING_FUEL_KWH: float = 0.0000
|
||||
|
|
|
|||
|
|
@ -397,3 +397,25 @@ LINE_108_PER_M2_KWH: float = 0.0
|
|||
# solar space heating = 0 → Σ(98a) = Σ(98c) → (98a)/TFA = LINE_99_PER_M2_KWH;
|
||||
# (108) = LINE_108_PER_M2_KWH = 0 (no AC). So LINE_109 = LINE_99 exactly.
|
||||
LINE_109_FEE_KWH_PER_M2: float = 130.3326
|
||||
|
||||
# ============================================================================
|
||||
# §9a Energy requirements — Individual heating systems
|
||||
# ============================================================================
|
||||
LINE_201_SECONDARY_FRACTION: float = 0.1000
|
||||
LINE_202_MAIN_TOTAL_FRACTION: float = 0.9000
|
||||
LINE_206_MAIN_1_EFFICIENCY_PCT: float = 88.6000
|
||||
LINE_207_MAIN_2_EFFICIENCY_PCT: float = 0.0000
|
||||
LINE_208_SECONDARY_EFFICIENCY_PCT: float = 100.0000
|
||||
LINE_211_M_MAIN_1_FUEL_KWH: tuple[float, ...] = (
|
||||
1790.1174, 1504.3490, 1401.3456, 978.5134, 626.0756, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 865.2002, 1323.5731, 1781.7982,
|
||||
)
|
||||
LINE_211_ANNUAL_KWH: float = 10270.9726
|
||||
LINE_213_M_MAIN_2_FUEL_KWH: tuple[float, ...] = (0.0,) * 12
|
||||
LINE_213_ANNUAL_KWH: float = 0.0000
|
||||
LINE_215_M_SECONDARY_FUEL_KWH: tuple[float, ...] = (
|
||||
176.2271, 148.0948, 137.9547, 96.3292, 61.6337, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 85.1741, 130.2984, 175.4081,
|
||||
)
|
||||
LINE_215_ANNUAL_KWH: float = 1011.1202
|
||||
LINE_221_COOLING_FUEL_KWH: float = 0.0000
|
||||
|
|
|
|||
|
|
@ -439,3 +439,25 @@ LINE_108_PER_M2_KWH: float = 0.0
|
|||
# solar space heating = 0 → Σ(98a) = Σ(98c) → (98a)/TFA = LINE_99_PER_M2_KWH;
|
||||
# (108) = LINE_108_PER_M2_KWH = 0 (no AC). So LINE_109 = LINE_99 exactly.
|
||||
LINE_109_FEE_KWH_PER_M2: float = 146.8852
|
||||
|
||||
# ============================================================================
|
||||
# §9a Energy requirements — Individual heating systems
|
||||
# ============================================================================
|
||||
LINE_201_SECONDARY_FRACTION: float = 0.1000
|
||||
LINE_202_MAIN_TOTAL_FRACTION: float = 0.9000
|
||||
LINE_206_MAIN_1_EFFICIENCY_PCT: float = 88.7000
|
||||
LINE_207_MAIN_2_EFFICIENCY_PCT: float = 0.0000
|
||||
LINE_208_SECONDARY_EFFICIENCY_PCT: float = 100.0000
|
||||
LINE_211_M_MAIN_1_FUEL_KWH: tuple[float, ...] = (
|
||||
2148.2772, 1820.4838, 1716.4018, 1232.1415, 804.0422, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 1082.1336, 1619.0882, 2157.7252,
|
||||
)
|
||||
LINE_211_ANNUAL_KWH: float = 12580.2936
|
||||
LINE_213_M_MAIN_2_FUEL_KWH: tuple[float, ...] = (0.0,) * 12
|
||||
LINE_213_ANNUAL_KWH: float = 0.0000
|
||||
LINE_215_M_SECONDARY_FUEL_KWH: tuple[float, ...] = (
|
||||
211.7247, 179.4188, 169.1609, 121.4344, 79.2428, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 106.6503, 159.5701, 212.6558,
|
||||
)
|
||||
LINE_215_ANNUAL_KWH: float = 1239.8578
|
||||
LINE_221_COOLING_FUEL_KWH: float = 0.0000
|
||||
|
|
|
|||
|
|
@ -462,3 +462,25 @@ LINE_108_PER_M2_KWH: float = 0.0
|
|||
# solar space heating = 0 → Σ(98a) = Σ(98c) → (98a)/TFA = LINE_99_PER_M2_KWH;
|
||||
# (108) = LINE_108_PER_M2_KWH = 0 (no AC). So LINE_109 = LINE_99 exactly.
|
||||
LINE_109_FEE_KWH_PER_M2: float = 132.828
|
||||
|
||||
# ============================================================================
|
||||
# §9a Energy requirements — Individual heating systems
|
||||
# ============================================================================
|
||||
LINE_201_SECONDARY_FRACTION: float = 0.1000
|
||||
LINE_202_MAIN_TOTAL_FRACTION: float = 0.9000
|
||||
LINE_206_MAIN_1_EFFICIENCY_PCT: float = 88.5000
|
||||
LINE_207_MAIN_2_EFFICIENCY_PCT: float = 0.0000
|
||||
LINE_208_SECONDARY_EFFICIENCY_PCT: float = 100.0000
|
||||
LINE_211_M_MAIN_1_FUEL_KWH: tuple[float, ...] = (
|
||||
1895.6271, 1588.7007, 1496.1538, 1083.2060, 729.6397, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 920.5077, 1406.7319, 1897.8511,
|
||||
)
|
||||
LINE_211_ANNUAL_KWH: float = 11018.4181
|
||||
LINE_213_M_MAIN_2_FUEL_KWH: tuple[float, ...] = (0.0,) * 12
|
||||
LINE_213_ANNUAL_KWH: float = 0.0000
|
||||
LINE_215_M_SECONDARY_FUEL_KWH: tuple[float, ...] = (
|
||||
186.4033, 156.2222, 147.1218, 106.5153, 71.7479, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 90.5166, 138.3286, 186.6220,
|
||||
)
|
||||
LINE_215_ANNUAL_KWH: float = 1083.4778
|
||||
LINE_221_COOLING_FUEL_KWH: float = 0.0000
|
||||
|
|
|
|||
|
|
@ -413,3 +413,25 @@ LINE_108_PER_M2_KWH: float = 0.0
|
|||
# solar space heating = 0 → Σ(98a) = Σ(98c) → (98a)/TFA = LINE_99_PER_M2_KWH;
|
||||
# (108) = LINE_108_PER_M2_KWH = 0 (no AC). So LINE_109 = LINE_99 exactly.
|
||||
LINE_109_FEE_KWH_PER_M2: float = 169.2897
|
||||
|
||||
# ============================================================================
|
||||
# §9a Energy requirements — Individual heating systems
|
||||
# ============================================================================
|
||||
LINE_201_SECONDARY_FRACTION: float = 0.1000
|
||||
LINE_202_MAIN_TOTAL_FRACTION: float = 0.9000
|
||||
LINE_206_MAIN_1_EFFICIENCY_PCT: float = 88.2000
|
||||
LINE_207_MAIN_2_EFFICIENCY_PCT: float = 0.0000
|
||||
LINE_208_SECONDARY_EFFICIENCY_PCT: float = 100.0000
|
||||
LINE_211_M_MAIN_1_FUEL_KWH: tuple[float, ...] = (
|
||||
1954.5034, 1649.5170, 1552.7436, 1119.1388, 736.2528, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 968.1611, 1466.0411, 1965.1473,
|
||||
)
|
||||
LINE_211_ANNUAL_KWH: float = 11411.5052
|
||||
LINE_213_M_MAIN_2_FUEL_KWH: tuple[float, ...] = (0.0,) * 12
|
||||
LINE_213_ANNUAL_KWH: float = 0.0000
|
||||
LINE_215_M_SECONDARY_FUEL_KWH: tuple[float, ...] = (
|
||||
191.5413, 161.6527, 152.1689, 109.6756, 72.1528, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 94.8798, 143.6720, 192.5844,
|
||||
)
|
||||
LINE_215_ANNUAL_KWH: float = 1118.3275
|
||||
LINE_221_COOLING_FUEL_KWH: float = 0.0000
|
||||
|
|
|
|||
|
|
@ -439,3 +439,25 @@ LINE_108_PER_M2_KWH: float = 0.0
|
|||
# solar space heating = 0 → Σ(98a) = Σ(98c) → (98a)/TFA = LINE_99_PER_M2_KWH;
|
||||
# (108) = LINE_108_PER_M2_KWH = 0 (no AC). So LINE_109 = LINE_99 exactly.
|
||||
LINE_109_FEE_KWH_PER_M2: float = 137.0700
|
||||
|
||||
# ============================================================================
|
||||
# §9a Energy requirements — Individual heating systems
|
||||
# ============================================================================
|
||||
LINE_201_SECONDARY_FRACTION: float = 0.1000
|
||||
LINE_202_MAIN_TOTAL_FRACTION: float = 0.9000
|
||||
LINE_206_MAIN_1_EFFICIENCY_PCT: float = 88.6000
|
||||
LINE_207_MAIN_2_EFFICIENCY_PCT: float = 0.0000
|
||||
LINE_208_SECONDARY_EFFICIENCY_PCT: float = 100.0000
|
||||
LINE_211_M_MAIN_1_FUEL_KWH: tuple[float, ...] = (
|
||||
2164.7554, 1825.9838, 1716.8828, 1226.9655, 802.7014, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 1078.7200, 1622.3099, 2168.0981,
|
||||
)
|
||||
LINE_211_ANNUAL_KWH: float = 12606.4169
|
||||
LINE_213_M_MAIN_2_FUEL_KWH: tuple[float, ...] = (0.0,) * 12
|
||||
LINE_213_ANNUAL_KWH: float = 0.0000
|
||||
LINE_215_M_SECONDARY_FUEL_KWH: tuple[float, ...] = (
|
||||
213.1081, 179.7580, 169.0176, 120.7879, 79.0215, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 106.1940, 159.7074, 213.4372,
|
||||
)
|
||||
LINE_215_ANNUAL_KWH: float = 1241.0317
|
||||
LINE_221_COOLING_FUEL_KWH: float = 0.0000
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import pytest
|
|||
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
cert_to_inputs,
|
||||
energy_requirements_section_from_cert,
|
||||
fabric_energy_efficiency_from_cert,
|
||||
heat_transmission_section_from_cert,
|
||||
internal_gains_section_from_cert,
|
||||
|
|
@ -699,3 +700,83 @@ def test_section_8f_line_109_fee_matches_pdf(fixture_name: str) -> None:
|
|||
# Assert
|
||||
assert actual is not None, f"{fixture_name}: fee_from_cert returned None"
|
||||
_pin(actual, expected, f"§8f LINE_109_FEE_KWH_PER_M2 {fixture_name}")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# §9a Energy requirements — LINE_201..LINE_221
|
||||
# ============================================================================
|
||||
|
||||
_SECTION_9A_MONTHLY_PINS: Final[tuple[tuple[str, str], ...]] = (
|
||||
("LINE_211_M_MAIN_1_FUEL_KWH", "main_1_fuel_monthly_kwh"),
|
||||
("LINE_213_M_MAIN_2_FUEL_KWH", "main_2_fuel_monthly_kwh"),
|
||||
("LINE_215_M_SECONDARY_FUEL_KWH", "secondary_fuel_monthly_kwh"),
|
||||
)
|
||||
|
||||
_SECTION_9A_SCALAR_PINS: Final[tuple[tuple[str, str], ...]] = (
|
||||
("LINE_201_SECONDARY_FRACTION", "secondary_heating_fraction"),
|
||||
("LINE_202_MAIN_TOTAL_FRACTION", "main_heating_total_fraction"),
|
||||
("LINE_206_MAIN_1_EFFICIENCY_PCT", "main_1_efficiency_pct"),
|
||||
("LINE_207_MAIN_2_EFFICIENCY_PCT", "main_2_efficiency_pct"),
|
||||
("LINE_208_SECONDARY_EFFICIENCY_PCT", "secondary_efficiency_pct"),
|
||||
("LINE_211_ANNUAL_KWH", "main_1_fuel_kwh_per_yr"),
|
||||
("LINE_213_ANNUAL_KWH", "main_2_fuel_kwh_per_yr"),
|
||||
("LINE_215_ANNUAL_KWH", "secondary_fuel_kwh_per_yr"),
|
||||
("LINE_221_COOLING_FUEL_KWH", "cooling_fuel_kwh_per_yr"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fixture_name,fixture_attr,result_attr",
|
||||
[
|
||||
(fix, line, attr)
|
||||
for fix in _FIXTURES
|
||||
for line, attr in _SECTION_9A_MONTHLY_PINS
|
||||
],
|
||||
ids=lambda x: x if isinstance(x, str) else None,
|
||||
)
|
||||
def test_section_9a_monthly_line_refs_match_pdf(
|
||||
fixture_name: str, fixture_attr: str, result_attr: str
|
||||
) -> None:
|
||||
"""§9a monthly pins — (211)m main 1 fuel, (213)m main 2 fuel, (215)m
|
||||
secondary fuel match the U985 PDF to abs=1e-4 for every Jan..Dec."""
|
||||
# Arrange
|
||||
mod = _FIXTURES[fixture_name]
|
||||
epc = mod.build_epc() # type: ignore[attr-defined]
|
||||
expected = getattr(mod, fixture_attr)
|
||||
|
||||
# Act
|
||||
er = energy_requirements_section_from_cert(epc)
|
||||
assert er is not None, f"{fixture_name}: energy_req_from_cert returned None"
|
||||
actual = getattr(er, result_attr)
|
||||
|
||||
# Assert
|
||||
for m in range(12):
|
||||
_pin(actual[m], expected[m], f"§9a {fixture_attr}[{m+1}] {fixture_name}")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fixture_name,fixture_attr,result_attr",
|
||||
[
|
||||
(fix, line, attr)
|
||||
for fix in _FIXTURES
|
||||
for line, attr in _SECTION_9A_SCALAR_PINS
|
||||
],
|
||||
ids=lambda x: x if isinstance(x, str) else None,
|
||||
)
|
||||
def test_section_9a_scalar_line_refs_match_pdf(
|
||||
fixture_name: str, fixture_attr: str, result_attr: str
|
||||
) -> None:
|
||||
"""§9a scalar pins — Table 11 fractions + per-system efficiencies +
|
||||
annual fuel-kWh totals + cooling fuel match the U985 PDF to abs=1e-4."""
|
||||
# Arrange
|
||||
mod = _FIXTURES[fixture_name]
|
||||
epc = mod.build_epc() # type: ignore[attr-defined]
|
||||
expected = getattr(mod, fixture_attr)
|
||||
|
||||
# Act
|
||||
er = energy_requirements_section_from_cert(epc)
|
||||
assert er is not None, f"{fixture_name}: energy_req_from_cert returned None"
|
||||
actual = getattr(er, result_attr)
|
||||
|
||||
# Assert
|
||||
_pin(actual, expected, f"§9a {fixture_attr} {fixture_name}")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue