mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Sibling migration to the sap10_calculator move — `domain.ml` now lives
at the root-level layout (`domain/sap10_ml/`) matching the pattern
already used by `domain.addresses`, `domain.tasks`, `domain.postcode`,
and `domain.sap10_calculator`.
Changes:
- `git mv packages/domain/src/domain/ml → domain/sap10_ml` (19 files;
history preserved).
- Subpackage rename: `domain.ml` → `domain.sap10_ml`. 32 references
rewritten across .py and .md files: 11 internal + 21 external
(datatypes/epc/domain/mapper.py, 14 files in domain/sap10_calculator,
2 backend tests, 2 ADRs, 1 README, 1 design doc).
- Path-string updates: `pytest.ini` testpath
`packages/domain/src/domain/ml/tests` → `domain/sap10_ml/tests` so
ML tests stay in the default auto-discovered sweep. `CONTEXT.md`
also updated.
`packages/domain/src/domain/` is now empty — the workspace `domain/`
tree has been fully migrated. Together with the `domain/__init__.py`
deletions from the sap10_calculator commit (29ac35cc), `domain` is
now a single root-level namespace package with subpackages
{addresses, sap10_calculator, sap10_ml, tasks} + the standalone
`postcode.py` module.
Verified:
- Focused sweep (backend mapper-chain + sap10_calculator worksheet
e2e + golden fixtures): 99 passed / 19 failed — identical baseline.
- Wider sweep (all sap10_calculator + sap10_ml): 1654 passed / 20
failed (same pre-existing failures).
- domain/sap10_ml/tests: 210/210 PASSED at new path.
- Pyright net-zero: heat_transmission.py 13, cert_to_inputs.py 35,
mapper.py 33, rdsap_uvalues.py 1 (all unchanged from baseline).
Note: `packages/domain/pyproject.toml` still declares
`packages = ["src/domain"]` for the hatchling wheel — that target
directory is now empty and the wheel build is effectively a no-op.
Retiring the workspace package or repointing the wheel is a follow-up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
56 lines
2.2 KiB
Python
56 lines
2.2 KiB
Python
"""Ventilation heat-loss W/K from SAP10.2 §C + RdSAP10 §5 / Table 5.
|
||
|
||
The ventilation feature complements `envelope_heat_loss_w_per_k` (conduction +
|
||
thermal bridging only). Catastrophic-low-SAP homes are dominated by
|
||
infiltration that the conduction model can't see: open chimneys, no
|
||
draught-proofing, leaky old windows. Tracer-bullet scope:
|
||
|
||
ACH_total = ACH_struct + (chimney_m3_h / volume_m3) − DP_reduction
|
||
W/K = ACH_total × volume_m3 × 0.33
|
||
|
||
Where 0.33 = ρ_air × c_p_air in kWh/(m³·K) at typical UK indoor conditions.
|
||
|
||
Scope explicitly deferred:
|
||
- Mechanical ventilation (MVHR / MEV) — `mechanical_ventilation` is 100% null
|
||
in the 250k corpus, so no signal to act on yet.
|
||
- Pressure-test override — `pressure_test` is also 100% null (see slice 18e
|
||
candidate in HANDOFF §7-D).
|
||
- Open flues / passive vents / extract fans / flueless gas fires — read off
|
||
`sap_ventilation` which itself is sparsely populated.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Final, Optional
|
||
|
||
|
||
_STRUCTURAL_ACH_MASONRY: Final[float] = 0.35
|
||
_STRUCTURAL_ACH_TIMBER: Final[float] = 0.25
|
||
_DRAUGHT_PROOFING_MAX_REDUCTION: Final[float] = 0.05
|
||
_OPEN_CHIMNEY_M3_PER_H: Final[float] = 40.0
|
||
_AIR_HEAT_CAPACITY_KWH_PER_M3_K: Final[float] = 0.33
|
||
|
||
|
||
def ventilation_heat_loss_w_per_k(
|
||
total_floor_area_m2: float,
|
||
avg_room_height_m: float,
|
||
*,
|
||
is_timber_frame: bool,
|
||
open_chimneys_count: Optional[int],
|
||
window_pct_draught_proofed: Optional[float],
|
||
) -> float:
|
||
"""SAP10.2 §C ventilation heat-loss in W/K, never null.
|
||
|
||
Linear sum: structural infiltration ACH + chimneys ACH − draught-proofing
|
||
reduction, multiplied by dwelling volume × 0.33.
|
||
"""
|
||
if total_floor_area_m2 <= 0 or avg_room_height_m <= 0:
|
||
return 0.0
|
||
volume_m3 = total_floor_area_m2 * avg_room_height_m
|
||
struct_ach = _STRUCTURAL_ACH_TIMBER if is_timber_frame else _STRUCTURAL_ACH_MASONRY
|
||
chimney_m3_h = (open_chimneys_count or 0) * _OPEN_CHIMNEY_M3_PER_H
|
||
chimney_ach = chimney_m3_h / volume_m3
|
||
dp_share = (window_pct_draught_proofed or 0.0) / 100.0
|
||
dp_reduction = _DRAUGHT_PROOFING_MAX_REDUCTION * dp_share
|
||
total_ach = max(0.0, struct_ach - dp_reduction) + chimney_ach
|
||
return total_ach * volume_m3 * _AIR_HEAT_CAPACITY_KWH_PER_M3_K
|