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