mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
docs(profile-case34): mark the space-demand residual closed (450e33e1)
The §2 (13) draught-lobby fix landed the +46.3 kWh space-heating over-count on the worksheet; the tracked diagnostic's header and run-banner now reflect the closed state (Δ +0.0036 SAP, sub-2dp-rounding) instead of the open gap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
450e33e15d
commit
b7d283cd3a
1 changed files with 95 additions and 0 deletions
95
scripts/profile_case34.py
Normal file
95
scripts/profile_case34.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
"""Decompose simulated case 34 (electric-storage corridor flat) vs its
|
||||
dr87/P960 worksheet, channel by channel.
|
||||
|
||||
Routes the tracked fixture
|
||||
`backend/documents_parser/tests/fixtures/Summary_case34_storage_flat.pdf`
|
||||
through extractor -> mapper -> calculator (Summary path) and prints the §3
|
||||
heat-transmission breakdown + the space-heating demand / ventilation /
|
||||
gains / MIT intermediates against the worksheet line refs.
|
||||
|
||||
The fabric (33), bridging (36), (31) and the door channel are EXACT after
|
||||
HEAD c10881ae. The +46.3 kWh/yr (+0.41%) space-heating over-count was the
|
||||
§2 (13) draught lobby: a corridor flat assumes a lobby (0.0), not the 0.05
|
||||
no-lobby penalty (RdSAP 10 spec p.30) — closed at HEAD 450e33e1, which lands
|
||||
effective air change (25)m on the worksheet (avg 0.6024) and SAP at 35.3130
|
||||
vs ws 35.3094 (Δ +0.0036, sub-2dp-rounding). The residual now sits at the
|
||||
worksheet's own 2dp display floor (walls -0.017 W/K). Use this to audit.
|
||||
|
||||
PYTHONPATH=/workspaces/model python scripts/profile_case34.py
|
||||
"""
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor
|
||||
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
||||
from domain.sap10_calculator.calculator import calculate_sap_from_inputs
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
SAP_10_2_SPEC_PRICES,
|
||||
cert_to_inputs,
|
||||
heat_transmission_section_from_cert,
|
||||
)
|
||||
|
||||
_FIXTURE = (
|
||||
Path(__file__).parent.parent
|
||||
/ "backend/documents_parser/tests/fixtures/Summary_case34_storage_flat.pdf"
|
||||
)
|
||||
|
||||
|
||||
def _pages(pdf: Path) -> list[str]:
|
||||
info = subprocess.run(
|
||||
["pdfinfo", str(pdf)], capture_output=True, text=True, check=True
|
||||
).stdout
|
||||
pc = int(re.search(r"Pages:\s+(\d+)", info).group(1)) # type: ignore[union-attr]
|
||||
pages: list[str] = []
|
||||
for i in range(1, pc + 1):
|
||||
layout = subprocess.run(
|
||||
["pdftotext", "-layout", "-f", str(i), "-l", str(i), str(pdf), "-"],
|
||||
capture_output=True, text=True, check=True,
|
||||
).stdout
|
||||
toks: list[str] = []
|
||||
for line in layout.splitlines():
|
||||
if not line.strip():
|
||||
toks.append("")
|
||||
continue
|
||||
toks.extend([p for p in re.split(r"\s{2,}", line.strip()) if p])
|
||||
pages.append("\n".join(toks))
|
||||
return pages
|
||||
|
||||
|
||||
def main() -> None:
|
||||
sn = ElmhurstSiteNotesExtractor(_pages(_FIXTURE)).extract()
|
||||
epc = EpcPropertyDataMapper.from_elmhurst_site_notes(sn)
|
||||
ht = heat_transmission_section_from_cert(epc)
|
||||
print("== §3 HEAT TRANSMISSION (all EXACT at c10881ae) ==")
|
||||
print(f"(31) ext elem area = {ht.total_external_element_area_m2:.4f} | ws 120.369")
|
||||
print(f"(29a) walls = {ht.walls_w_per_k:.4f} | ws 30.902")
|
||||
print(f"(30) roof = {ht.roof_w_per_k:.4f} | ws 91.954")
|
||||
print(f"(28a) floor = {ht.floor_w_per_k:.4f} | ws 27.986")
|
||||
print(f"(26) doors = {ht.doors_w_per_k:.4f} | ws 8.14 (corridor 1.4 + ext 3.0)")
|
||||
print(f"(33) fabric = {ht.fabric_heat_loss_w_per_k:.4f} | ws 207.484")
|
||||
print(f"(36) bridging = {ht.thermal_bridging_w_per_k:.4f} | ws 18.054")
|
||||
print(f"(37) total fabric = {ht.total_w_per_k:.4f} | ws 225.538")
|
||||
|
||||
res = calculate_sap_from_inputs(cert_to_inputs(epc, prices=SAP_10_2_SPEC_PRICES))
|
||||
im = res.intermediate
|
||||
print("\n== RESIDUAL CLOSED at 450e33e1: was +46.3 kWh, now -0.96 kWh ==")
|
||||
keys = [
|
||||
"useful_space_heating_kwh_per_yr", # ws (98) = 11357.24
|
||||
"infiltration_ach", "infiltration_w_per_k",
|
||||
"internal_gains_annual_avg_w", # ws (73)/(84)
|
||||
"mean_internal_temp_annual_avg_c", # ws (85)-(93)
|
||||
]
|
||||
for k in keys:
|
||||
if k in im:
|
||||
print(f" {k} = {round(im[k], 4)}")
|
||||
print(
|
||||
"\nworksheet targets: (98) space heat=11357.24 | (35) TMP=250 |"
|
||||
" (25)m eff ach 0.46-0.64 | (20) shelter=0.85 | (19) sheltered sides=2"
|
||||
)
|
||||
print(f"\nSAP cont = {res.sap_score_continuous:.4f} | ws 35.3094 | "
|
||||
f"Δ = {res.sap_score_continuous - 35.3094:+.4f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Reference in a new issue