"""SAP 10.2 Table 9a — heating utilisation factor η. η reduces the contribution of internal + solar gains when they outpace the dwelling's heat-loss rate. A well-insulated dwelling with large solar gains in October can't fully use those gains — it's already warm enough. Formula per Table 9a: a = 1 + τ / 15 where τ is the dwelling time constant (h) γ = G / L gain-to-loss ratio (W / W) if γ > 0 and γ ≠ 1: η = (1 − γ^a) / (1 − γ^(a+1)) if γ = 1: η = a / (a + 1) if γ ≤ 0: η = 1 The time constant τ = TMP / (3.6 × HLP) comes from the dwelling's thermal mass parameter and heat-loss parameter; computed by the orchestrator and passed in here. Reference: SAP 10.2 specification (14-03-2025) Table 9a (page 184). """ from __future__ import annotations def utilisation_factor( *, total_gains_w: float, heat_loss_rate_w: float, time_constant_h: float, ) -> float: """SAP 10.2 Table 9a heating utilisation factor η. γ = total_gains_w / heat_loss_rate_w; η ∈ (0, 1]. When the heat-loss rate is non-positive (dwelling already balanced or gaining), η = 1 so the gains are fully credited. """ if heat_loss_rate_w <= 0: return 1.0 gamma = total_gains_w / heat_loss_rate_w a = 1.0 + time_constant_h / 15.0 if gamma == 1.0: return a / (a + 1.0) return (1.0 - gamma**a) / (1.0 - gamma ** (a + 1.0))