mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
P5.10: SapResult.intermediate exposes rating-equation spec constants
Promotes _FLOOR_AREA_OFFSET_M2 → FLOOR_AREA_OFFSET_M2 (§13 ECF denominator, Table 12) and _ECF_LOG_THRESHOLD → ECF_LOG_THRESHOLD (SAP rating linear/log regime boundary at ECF = 3.5). Together with the deflator (P5.7) they fully document the §13 rating curve in trace mode.
This commit is contained in:
parent
3d56898944
commit
02f92e2b0c
3 changed files with 25 additions and 5 deletions
|
|
@ -44,7 +44,9 @@ from domain.sap.worksheet.heat_transmission import HeatTransmission
|
||||||
from domain.sap.worksheet.internal_gains import internal_gains_w
|
from domain.sap.worksheet.internal_gains import internal_gains_w
|
||||||
from domain.sap.worksheet.mean_internal_temperature import mean_internal_temperature_c
|
from domain.sap.worksheet.mean_internal_temperature import mean_internal_temperature_c
|
||||||
from domain.sap.worksheet.rating import (
|
from domain.sap.worksheet.rating import (
|
||||||
|
ECF_LOG_THRESHOLD,
|
||||||
ENERGY_COST_DEFLATOR,
|
ENERGY_COST_DEFLATOR,
|
||||||
|
FLOOR_AREA_OFFSET_M2,
|
||||||
energy_cost_factor,
|
energy_cost_factor,
|
||||||
sap_rating,
|
sap_rating,
|
||||||
sap_rating_integer,
|
sap_rating_integer,
|
||||||
|
|
@ -381,6 +383,8 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult:
|
||||||
"hot_water_primary_kwh_per_yr": hot_water_primary_kwh,
|
"hot_water_primary_kwh_per_yr": hot_water_primary_kwh,
|
||||||
"other_primary_kwh_per_yr": other_primary_kwh,
|
"other_primary_kwh_per_yr": other_primary_kwh,
|
||||||
"pv_primary_offset_kwh_per_yr": pv_primary_offset_kwh,
|
"pv_primary_offset_kwh_per_yr": pv_primary_offset_kwh,
|
||||||
|
"floor_area_offset_m2": FLOOR_AREA_OFFSET_M2,
|
||||||
|
"ecf_log_threshold": ECF_LOG_THRESHOLD,
|
||||||
}
|
}
|
||||||
|
|
||||||
return SapResult(
|
return SapResult(
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,22 @@ def test_calculate_exposes_primary_energy_breakdown() -> None:
|
||||||
assert result.primary_energy_kwh_per_yr == pytest.approx(expected_total, rel=1e-9)
|
assert result.primary_energy_kwh_per_yr == pytest.approx(expected_total, rel=1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
def test_calculate_exposes_rating_equation_spec_constants() -> None:
|
||||||
|
# Arrange — P5 trace mode: the §13 ECF denominator carries a 45 m²
|
||||||
|
# floor-area offset (Table 12) and the SAP rating splits between a
|
||||||
|
# linear and a log regime at ECF = 3.5. Surfacing both on
|
||||||
|
# `intermediate` documents the equation alongside the already-exposed
|
||||||
|
# ecf + deflator (P5.7), so the SAP rating curve is fully auditable.
|
||||||
|
inputs = _baseline_inputs()
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = calculate_sap_from_inputs(inputs)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert result.intermediate["floor_area_offset_m2"] == pytest.approx(45.0, rel=1e-12)
|
||||||
|
assert result.intermediate["ecf_log_threshold"] == pytest.approx(3.5, rel=1e-12)
|
||||||
|
|
||||||
|
|
||||||
def test_higher_main_heating_efficiency_reduces_fuel_use() -> None:
|
def test_higher_main_heating_efficiency_reduces_fuel_use() -> None:
|
||||||
# Arrange — Direction check: doubling the boiler efficiency must halve
|
# Arrange — Direction check: doubling the boiler efficiency must halve
|
||||||
# the main-heating fuel kWh, holding everything else constant.
|
# the main-heating fuel kWh, holding everything else constant.
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ from typing import Final
|
||||||
|
|
||||||
|
|
||||||
ENERGY_COST_DEFLATOR: Final[float] = 0.36
|
ENERGY_COST_DEFLATOR: Final[float] = 0.36
|
||||||
_FLOOR_AREA_OFFSET_M2: Final[float] = 45.0
|
FLOOR_AREA_OFFSET_M2: Final[float] = 45.0
|
||||||
_ECF_LOG_THRESHOLD: Final[float] = 3.5
|
ECF_LOG_THRESHOLD: Final[float] = 3.5
|
||||||
_SAP_LINEAR_INTERCEPT: Final[float] = 100.0
|
_SAP_LINEAR_INTERCEPT: Final[float] = 100.0
|
||||||
_SAP_LINEAR_SLOPE: Final[float] = 16.21
|
_SAP_LINEAR_SLOPE: Final[float] = 16.21
|
||||||
_SAP_LOG_INTERCEPT: Final[float] = 108.8
|
_SAP_LOG_INTERCEPT: Final[float] = 108.8
|
||||||
|
|
@ -38,13 +38,13 @@ def energy_cost_factor(
|
||||||
total_floor_area_m2: float,
|
total_floor_area_m2: float,
|
||||||
) -> float:
|
) -> float:
|
||||||
"""SAP 10.3 §13 equation (7): ECF = 0.36 × cost / (TFA + 45)."""
|
"""SAP 10.3 §13 equation (7): ECF = 0.36 × cost / (TFA + 45)."""
|
||||||
return ENERGY_COST_DEFLATOR * total_cost_gbp / (total_floor_area_m2 + _FLOOR_AREA_OFFSET_M2)
|
return ENERGY_COST_DEFLATOR * total_cost_gbp / (total_floor_area_m2 + FLOOR_AREA_OFFSET_M2)
|
||||||
|
|
||||||
|
|
||||||
def sap_rating(*, ecf: float) -> float:
|
def sap_rating(*, ecf: float) -> float:
|
||||||
"""SAP 10.3 §13 equations (8)/(9). Un-rounded result so callers can
|
"""SAP 10.3 §13 equations (8)/(9). Un-rounded result so callers can
|
||||||
inspect the continuous value; `sap_rating_integer` rounds and clamps."""
|
inspect the continuous value; `sap_rating_integer` rounds and clamps."""
|
||||||
if ecf >= _ECF_LOG_THRESHOLD:
|
if ecf >= ECF_LOG_THRESHOLD:
|
||||||
return _SAP_LOG_INTERCEPT - _SAP_LOG_SLOPE * log10(ecf)
|
return _SAP_LOG_INTERCEPT - _SAP_LOG_SLOPE * log10(ecf)
|
||||||
return _SAP_LINEAR_INTERCEPT - _SAP_LINEAR_SLOPE * ecf
|
return _SAP_LINEAR_INTERCEPT - _SAP_LINEAR_SLOPE * ecf
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ def environmental_impact_rating(
|
||||||
) -> float:
|
) -> float:
|
||||||
"""SAP 10.3 §14 equations (10)-(12). Un-rounded EI rating; mirrors the
|
"""SAP 10.3 §14 equations (10)-(12). Un-rounded EI rating; mirrors the
|
||||||
SAP rating curve but uses CO2 emissions per (TFA + 45) as the input."""
|
SAP rating curve but uses CO2 emissions per (TFA + 45) as the input."""
|
||||||
cf = co2_emissions_kg_per_yr / (total_floor_area_m2 + _FLOOR_AREA_OFFSET_M2)
|
cf = co2_emissions_kg_per_yr / (total_floor_area_m2 + FLOOR_AREA_OFFSET_M2)
|
||||||
if cf >= _CF_LOG_THRESHOLD:
|
if cf >= _CF_LOG_THRESHOLD:
|
||||||
return _EI_LOG_INTERCEPT - _EI_LOG_SLOPE * log10(cf)
|
return _EI_LOG_INTERCEPT - _EI_LOG_SLOPE * log10(cf)
|
||||||
return _EI_LINEAR_INTERCEPT - _EI_LINEAR_SLOPE * cf
|
return _EI_LINEAR_INTERCEPT - _EI_LINEAR_SLOPE * cf
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue