"""SAP 10.2 §2.6.4 — Mechanical Extract Ventilation (MEV) electricity. This module implements the §2.6.4 "Specific fan power" cascade for decentralised MEV systems plus the §5 Table 4f line (230a) annual electricity formula. Centralised MEV / MVHR / balanced systems share the same (230a) shape but pick their per-system SFP differently — deferred until a fixture exercises them. Spec references (SAP 10.2 14-03-2025): - §2.6.4 page 16 — equation (1) average SFP for decentralised MEV - Table 4f page 174 — line (230a) `IUF × SFP × 1.22 × V` annual kWh - Table 4g page 176 — default SFP (0.8 W/(l/s)) for unknown PCDB """ from __future__ import annotations from dataclasses import dataclass from typing import Final # SAP 10.2 §5 Table 4f line (230a): annual kWh = SFP × 1.22 × V. # The "× 1.22" coefficient absorbs continuous-run hours × 0.5 ach # (MEV throughput per §2.6.5) × air-density-and-unit conversion. _MEV_KWH_COEFF: Final[float] = 1.22 @dataclass(frozen=True) class MevFanEntry: """One fan in a decentralised MEV installation, with the IUF already resolved per the system's lodged ducting type. `sfp_w_per_l_per_s` is the PCDB-lodged Specific Fan Power for the fan's (location × wet-room-type) configuration; `flow_rate_l_per_s` is the SAP standard flow per wet-room-type (13 l/s kitchens, 8 l/s other wet rooms — see SAP 10.2 §2.6.4); `iuf` is the in-use factor from PCDB Table 329 for the fan's location (in-room with ducting → flexible/rigid IUF; through-wall → no-duct IUF). """ sfp_w_per_l_per_s: float flow_rate_l_per_s: float iuf: float def mev_sfp_av(fan_entries: tuple[MevFanEntry, ...]) -> float: """SAP 10.2 §2.6.4 equation (1): average SFP for a decentralised MEV system. SFPav = Σ(SFP_j × FR_j × IUF_j) / Σ(FR_j) The summation is over every fan in the installation (kitchen + each wet room) — each fan carries its own (SFP, FR, IUF) per the spec's `MevFanEntry` resolution. Returns 0.0 when no fans are lodged (empty installation — caller should not invoke `mev_decentralised_kwh_per_yr` in that case). """ if not fan_entries: return 0.0 weighted_sum = sum( entry.sfp_w_per_l_per_s * entry.flow_rate_l_per_s * entry.iuf for entry in fan_entries ) flow_sum = sum(entry.flow_rate_l_per_s for entry in fan_entries) if flow_sum == 0.0: return 0.0 return weighted_sum / flow_sum def mev_decentralised_kwh_per_yr( *, sfp_av_w_per_l_per_s: float, dwelling_volume_m3: float, ) -> float: """SAP 10.2 §5 Table 4f line (230a) annual electricity for the mechanical ventilation fans of a decentralised MEV system: E_fans_kwh = SFPav × 1.22 × V `sfp_av_w_per_l_per_s` is the IUF-adjusted average SFP from `mev_sfp_av` (i.e. the IUFs are already folded in via the equation (1) numerator). `dwelling_volume_m3` is worksheet line (5) — the sum of (3a)+(3b)+...+(3n) across every storey of every building part. """ return sfp_av_w_per_l_per_s * _MEV_KWH_COEFF * dwelling_volume_m3