mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Merge branch 'main' into feature/abri-api-integration
This commit is contained in:
commit
329e896ba5
25 changed files with 1717 additions and 271 deletions
1
.github/workflows/_deploy_lambda.yml
vendored
1
.github/workflows/_deploy_lambda.yml
vendored
|
|
@ -148,6 +148,7 @@ jobs:
|
|||
- name: Terraform Plan
|
||||
working-directory: ${{ inputs.lambda_path }}
|
||||
env:
|
||||
TF_VAR_github_sha: ${{ github.sha }}
|
||||
TF_VAR_db_host: ${{ secrets.TF_VAR_db_host }}
|
||||
TF_VAR_db_name: ${{ secrets.TF_VAR_db_name }}
|
||||
TF_VAR_db_port: ${{ secrets.TF_VAR_db_port }}
|
||||
|
|
|
|||
23
.github/workflows/check_lambda_zip_size.yml
vendored
Normal file
23
.github/workflows/check_lambda_zip_size.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
name: Check FastAPI Lambda package size
|
||||
|
||||
# Runs on PRs targeting main so a Lambda-size regression fails the PR before
|
||||
# it can merge to main and roll into the dev deploy — deploy_terraform.yml
|
||||
# only triggers on push to dev/prod, which is too late (see PR #1469: this
|
||||
# broke dev for weeks before anyone noticed).
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
fast_api_lambda_zip_size_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Check projected unzipped Lambda size
|
||||
run: python3 backend/app/requirements/check_lambda_zip_size.py
|
||||
|
|
@ -53,7 +53,9 @@ async def log_requests(request: Request, call_next):
|
|||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok", "sha": os.getenv("GITHUB_SHA", "unknown")}
|
||||
sha = os.getenv("GITHUB_SHA", "unknown")
|
||||
logger.info(f"Health check OK - deployed sha={sha}")
|
||||
return {"status": "ok", "sha": sha}
|
||||
|
||||
|
||||
app.include_router(tasks_router.router, prefix="/v1")
|
||||
|
|
|
|||
189
backend/app/requirements/check_lambda_zip_size.py
Executable file
189
backend/app/requirements/check_lambda_zip_size.py
Executable file
|
|
@ -0,0 +1,189 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Estimate the unzipped size of the fastapi Lambda before deploying.
|
||||
|
||||
Mirrors what `deployment/terraform/modules/lambda_with_api_gateway/main.tf` does
|
||||
for the `ara_fast_api` lambda (`deployment/terraform/lambda/fast-api`):
|
||||
`pip install -r requirements.txt -t <source_dir> ...` followed by zipping
|
||||
`source_dir` minus `zip_excludes`. AWS Lambda's hard limit is 262144000 bytes
|
||||
(250 MiB) unzipped. We install the dependencies into a scratch directory
|
||||
instead of the real source tree so this can run locally or in CI without
|
||||
mutating the repo.
|
||||
|
||||
Usage (from the repo root):
|
||||
python3 backend/app/requirements/check_lambda_zip_size.py --source-dir .
|
||||
|
||||
--requirements defaults to the requirements.txt next to this script, and
|
||||
--exclude defaults to whatever `zip_excludes` is set to in
|
||||
`deployment/terraform/modules/lambda_with_api_gateway/variables.tf` — read
|
||||
straight from that file, so this can never silently drift from what
|
||||
Terraform actually excludes. Pass --exclude explicitly to override.
|
||||
|
||||
Exits non-zero if the projected unzipped size exceeds --limit (or exceeds
|
||||
--warn-pct of the limit, when --strict is not passed the warning is just
|
||||
printed).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
LAMBDA_UNZIPPED_LIMIT_BYTES = 262_144_000
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||
DEFAULT_REQUIREMENTS = Path(__file__).resolve().parent / "requirements.txt"
|
||||
DEFAULT_TF_VARIABLES_FILE = (
|
||||
REPO_ROOT / "deployment/terraform/modules/lambda_with_api_gateway/variables.tf"
|
||||
)
|
||||
|
||||
|
||||
def parse_zip_excludes_from_tf(tf_variables_file: Path) -> list[str]:
|
||||
text = tf_variables_file.read_text()
|
||||
match = re.search(
|
||||
r'variable\s+"zip_excludes"\s*{.*?default\s*=\s*\[(.*?)\]', text, re.S
|
||||
)
|
||||
if not match:
|
||||
raise ValueError(
|
||||
f'could not find variable "zip_excludes" default in {tf_variables_file}'
|
||||
)
|
||||
return re.findall(r'"([^"]+)"', match.group(1))
|
||||
|
||||
|
||||
def doublestar_match(pattern_parts: list[str], path_parts: list[str]) -> bool:
|
||||
if not pattern_parts:
|
||||
return not path_parts
|
||||
head, rest = pattern_parts[0], pattern_parts[1:]
|
||||
if head == "**":
|
||||
if doublestar_match(rest, path_parts):
|
||||
return True
|
||||
if path_parts and doublestar_match(pattern_parts, path_parts[1:]):
|
||||
return True
|
||||
return False
|
||||
if not path_parts:
|
||||
return False
|
||||
if fnmatch.fnmatchcase(path_parts[0], head):
|
||||
return doublestar_match(rest, path_parts[1:])
|
||||
return False
|
||||
|
||||
|
||||
def is_excluded(rel_path: Path, excludes: list[str]) -> bool:
|
||||
parts = rel_path.as_posix().split("/")
|
||||
return any(doublestar_match(pattern.split("/"), parts) for pattern in excludes)
|
||||
|
||||
|
||||
def sum_dir_size(root: Path, excludes: list[str] | None = None) -> int:
|
||||
total = 0
|
||||
for path in root.rglob("*"):
|
||||
if path.is_dir():
|
||||
continue
|
||||
rel = path.relative_to(root)
|
||||
if excludes and is_excluded(rel, excludes):
|
||||
continue
|
||||
total += path.stat().st_size
|
||||
return total
|
||||
|
||||
|
||||
def install_dependencies(requirements: Path, target: Path) -> int:
|
||||
subprocess.run(
|
||||
[
|
||||
"pip",
|
||||
"install",
|
||||
"-r",
|
||||
str(requirements),
|
||||
"-t",
|
||||
str(target),
|
||||
"--platform",
|
||||
"manylinux2014_x86_64",
|
||||
"--implementation",
|
||||
"cp",
|
||||
"--python-version",
|
||||
"3.11",
|
||||
"--only-binary=:all:",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
return sum_dir_size(target)
|
||||
|
||||
|
||||
def human(n: int) -> str:
|
||||
return f"{n:,} bytes ({n / 1024 / 1024:.1f} MiB)"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--source-dir", type=Path, default=REPO_ROOT)
|
||||
parser.add_argument("--requirements", type=Path, default=DEFAULT_REQUIREMENTS)
|
||||
parser.add_argument(
|
||||
"--skip-deps",
|
||||
action="store_true",
|
||||
help="Skip the pip install step and only measure source files.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--exclude",
|
||||
action="append",
|
||||
default=None,
|
||||
dest="excludes",
|
||||
help="Repeatable. Defaults to zip_excludes read from --tf-variables-file.",
|
||||
)
|
||||
parser.add_argument("--tf-variables-file", type=Path, default=DEFAULT_TF_VARIABLES_FILE)
|
||||
parser.add_argument("--limit", type=int, default=LAMBDA_UNZIPPED_LIMIT_BYTES)
|
||||
parser.add_argument(
|
||||
"--warn-pct",
|
||||
type=float,
|
||||
default=85.0,
|
||||
help="Print a warning once projected size crosses this %% of the limit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--strict",
|
||||
action="store_true",
|
||||
help="Exit non-zero on the warning threshold too, not just the hard limit.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
excludes = args.excludes
|
||||
if excludes is None:
|
||||
excludes = parse_zip_excludes_from_tf(args.tf_variables_file)
|
||||
print(f"excludes (from {args.tf_variables_file.relative_to(REPO_ROOT)}): {excludes}")
|
||||
|
||||
source_size = sum_dir_size(args.source_dir, excludes)
|
||||
|
||||
deps_size = 0
|
||||
if not args.skip_deps:
|
||||
with tempfile.TemporaryDirectory(prefix="lambda-deps-") as tmp:
|
||||
deps_size = install_dependencies(args.requirements, Path(tmp))
|
||||
|
||||
total = source_size + deps_size
|
||||
pct = total / args.limit * 100
|
||||
|
||||
print(f"source files (post-exclude): {human(source_size)}")
|
||||
if not args.skip_deps:
|
||||
print(f"pip dependencies: {human(deps_size)}")
|
||||
print(f"projected unzipped total: {human(total)} [{pct:.1f}% of limit]")
|
||||
print(f"AWS limit: {human(args.limit)}")
|
||||
|
||||
if total > args.limit:
|
||||
print(
|
||||
f"\nFAIL: projected unzipped size exceeds the Lambda limit by "
|
||||
f"{human(total - args.limit)}.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
if pct >= args.warn_pct:
|
||||
msg = (
|
||||
f"\nWARNING: projected unzipped size is at {pct:.1f}% of the "
|
||||
f"{human(args.limit)} limit."
|
||||
)
|
||||
print(msg, file=sys.stderr)
|
||||
if args.strict:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,452 @@
|
|||
{
|
||||
"uprn": 100031768368,
|
||||
"roofs": [
|
||||
{
|
||||
"description": "Pitched, 100 mm loft insulation",
|
||||
"energy_efficiency_rating": 3,
|
||||
"environmental_efficiency_rating": 3
|
||||
}
|
||||
],
|
||||
"walls": [
|
||||
{
|
||||
"description": "Solid brick, as built, no insulation (assumed)",
|
||||
"energy_efficiency_rating": 2,
|
||||
"environmental_efficiency_rating": 2
|
||||
}
|
||||
],
|
||||
"floors": [
|
||||
{
|
||||
"description": "Solid, no insulation (assumed)",
|
||||
"energy_efficiency_rating": 0,
|
||||
"environmental_efficiency_rating": 0
|
||||
}
|
||||
],
|
||||
"status": "entered",
|
||||
"tenure": 1,
|
||||
"window": {
|
||||
"description": "Fully double glazed",
|
||||
"energy_efficiency_rating": 2,
|
||||
"environmental_efficiency_rating": 2
|
||||
},
|
||||
"addendum": {
|
||||
"addendum_numbers": [
|
||||
15
|
||||
]
|
||||
},
|
||||
"lighting": {
|
||||
"description": "Below average lighting efficiency",
|
||||
"energy_efficiency_rating": 3,
|
||||
"environmental_efficiency_rating": 3
|
||||
},
|
||||
"postcode": "ST16 3TS",
|
||||
"hot_water": {
|
||||
"description": "From main system",
|
||||
"energy_efficiency_rating": 4,
|
||||
"environmental_efficiency_rating": 4
|
||||
},
|
||||
"post_town": "STAFFORD",
|
||||
"psv_count": 0,
|
||||
"built_form": 3,
|
||||
"created_at": "2026-05-14 07:52:27",
|
||||
"door_count": 1,
|
||||
"region_code": 6,
|
||||
"report_type": 2,
|
||||
"sap_heating": {
|
||||
"number_baths": 1,
|
||||
"cylinder_size": 1,
|
||||
"shower_outlets": [
|
||||
{
|
||||
"shower_wwhrs": 1,
|
||||
"shower_outlet_type": 2
|
||||
}
|
||||
],
|
||||
"water_heating_code": 901,
|
||||
"water_heating_fuel": 26,
|
||||
"secondary_fuel_type": 26,
|
||||
"main_heating_details": [
|
||||
{
|
||||
"has_fghrs": "N",
|
||||
"main_fuel_type": 26,
|
||||
"boiler_flue_type": 2,
|
||||
"fan_flue_present": "Y",
|
||||
"heat_emitter_type": 1,
|
||||
"emitter_temperature": 0,
|
||||
"main_heating_number": 1,
|
||||
"main_heating_control": 2106,
|
||||
"main_heating_category": 2,
|
||||
"main_heating_fraction": 1,
|
||||
"central_heating_pump_age": 0,
|
||||
"main_heating_data_source": 1,
|
||||
"main_heating_index_number": 18250
|
||||
}
|
||||
],
|
||||
"immersion_heating_type": "NA",
|
||||
"secondary_heating_type": 605,
|
||||
"has_fixed_air_conditioning": "false"
|
||||
},
|
||||
"sap_version": 10.2,
|
||||
"sap_windows": [
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 3,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 1.1,
|
||||
"window_height": 1.02,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 3,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 1.64,
|
||||
"window_height": 1.01,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 7,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 0.96,
|
||||
"window_height": 0.96,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 7,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 0.99,
|
||||
"window_height": 0.96,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 7,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 1.48,
|
||||
"window_height": 0.97,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 3,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 0.55,
|
||||
"window_height": 1.19,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 3,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 0.55,
|
||||
"window_height": 1.19,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 3,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 1.1,
|
||||
"window_height": 1.3,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 7,
|
||||
"window_type": 1,
|
||||
"glazing_type": 2,
|
||||
"window_width": 1.36,
|
||||
"window_height": 2.08,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
},
|
||||
{
|
||||
"pvc_frame": "true",
|
||||
"glazing_gap": 6,
|
||||
"orientation": 7,
|
||||
"window_type": 1,
|
||||
"glazing_type": 3,
|
||||
"window_width": 0.9,
|
||||
"window_height": 2.03,
|
||||
"draught_proofed": "true",
|
||||
"window_location": 0,
|
||||
"window_wall_type": 1,
|
||||
"permanent_shutters_present": "N",
|
||||
"permanent_shutters_insulated": "N"
|
||||
}
|
||||
],
|
||||
"schema_type": "RdSAP-Schema-21.0.1",
|
||||
"uprn_source": "Energy Assessor",
|
||||
"country_code": "ENG",
|
||||
"main_heating": [
|
||||
{
|
||||
"description": "Boiler and radiators, mains gas",
|
||||
"energy_efficiency_rating": 4,
|
||||
"environmental_efficiency_rating": 4
|
||||
}
|
||||
],
|
||||
"air_tightness": {
|
||||
"description": "(not tested)",
|
||||
"energy_efficiency_rating": 0,
|
||||
"environmental_efficiency_rating": 0
|
||||
},
|
||||
"dwelling_type": "End-terrace house",
|
||||
"language_code": 1,
|
||||
"pressure_test": 4,
|
||||
"property_type": 0,
|
||||
"address_line_1": "21 Greenway",
|
||||
"assessment_type": "RdSAP",
|
||||
"completion_date": "2026-05-14",
|
||||
"inspection_date": "2026-05-13",
|
||||
"extensions_count": 0,
|
||||
"measurement_type": 1,
|
||||
"open_flues_count": 0,
|
||||
"total_floor_area": 76,
|
||||
"transaction_type": 1,
|
||||
"conservatory_type": 2,
|
||||
"has_draught_lobby": "false",
|
||||
"heated_room_count": 4,
|
||||
"other_flues_count": 1,
|
||||
"registration_date": "2026-05-14",
|
||||
"sap_energy_source": {
|
||||
"mains_gas": "Y",
|
||||
"meter_type": 2,
|
||||
"pv_diverter": "false",
|
||||
"pv_connection": 0,
|
||||
"pv_battery_count": 0,
|
||||
"photovoltaic_supply": {
|
||||
"none_or_no_details": {
|
||||
"percent_roof_area": 0
|
||||
}
|
||||
},
|
||||
"wind_turbines_count": 0,
|
||||
"gas_smart_meter_present": "true",
|
||||
"is_dwelling_export_capable": "false",
|
||||
"wind_turbines_terrain_type": 2,
|
||||
"electricity_smart_meter_present": "true",
|
||||
"is_hydro_output_connected_to_dwelling_meter": "false"
|
||||
},
|
||||
"secondary_heating": {
|
||||
"description": "Room heaters, mains gas",
|
||||
"energy_efficiency_rating": 0,
|
||||
"environmental_efficiency_rating": 0
|
||||
},
|
||||
"closed_flues_count": 0,
|
||||
"extract_fans_count": 1,
|
||||
"sap_building_parts": [
|
||||
{
|
||||
"identifier": "Main Dwelling",
|
||||
"wall_dry_lined": "N",
|
||||
"wall_thickness": 280,
|
||||
"floor_heat_loss": 7,
|
||||
"roof_construction": 4,
|
||||
"wall_construction": 3,
|
||||
"building_part_number": 1,
|
||||
"sap_floor_dimensions": [
|
||||
{
|
||||
"floor": 0,
|
||||
"room_height": {
|
||||
"value": 2.45,
|
||||
"quantity": "metres"
|
||||
},
|
||||
"floor_insulation": 1,
|
||||
"total_floor_area": {
|
||||
"value": 37.75,
|
||||
"quantity": "square metres"
|
||||
},
|
||||
"party_wall_length": {
|
||||
"value": 5,
|
||||
"quantity": "metres"
|
||||
},
|
||||
"floor_construction": 1,
|
||||
"heat_loss_perimeter": {
|
||||
"value": 20.1,
|
||||
"quantity": "metres"
|
||||
}
|
||||
},
|
||||
{
|
||||
"floor": 1,
|
||||
"room_height": {
|
||||
"value": 2.45,
|
||||
"quantity": "metres"
|
||||
},
|
||||
"total_floor_area": {
|
||||
"value": 37.75,
|
||||
"quantity": "square metres"
|
||||
},
|
||||
"party_wall_length": {
|
||||
"value": 5,
|
||||
"quantity": "metres"
|
||||
},
|
||||
"heat_loss_perimeter": {
|
||||
"value": 20.1,
|
||||
"quantity": "metres"
|
||||
}
|
||||
}
|
||||
],
|
||||
"wall_insulation_type": 4,
|
||||
"construction_age_band": "C",
|
||||
"party_wall_construction": 1,
|
||||
"wall_thickness_measured": "Y",
|
||||
"roof_insulation_location": 2,
|
||||
"roof_insulation_thickness": "100mm",
|
||||
"wall_insulation_thickness": "NI"
|
||||
}
|
||||
],
|
||||
"boilers_flues_count": 0,
|
||||
"open_chimneys_count": 0,
|
||||
"solar_water_heating": "N",
|
||||
"habitable_room_count": 4,
|
||||
"heating_cost_current": {
|
||||
"value": 1188,
|
||||
"currency": "GBP"
|
||||
},
|
||||
"insulated_door_count": 0,
|
||||
"co2_emissions_current": 3.4,
|
||||
"energy_rating_average": 60,
|
||||
"energy_rating_current": 61,
|
||||
"lighting_cost_current": {
|
||||
"value": 55,
|
||||
"currency": "GBP"
|
||||
},
|
||||
"main_heating_controls": [
|
||||
{
|
||||
"description": "Programmer, room thermostat and TRVs",
|
||||
"energy_efficiency_rating": 4,
|
||||
"environmental_efficiency_rating": 4
|
||||
}
|
||||
],
|
||||
"blocked_chimneys_count": 0,
|
||||
"has_hot_water_cylinder": "false",
|
||||
"heating_cost_potential": {
|
||||
"value": 739,
|
||||
"currency": "GBP"
|
||||
},
|
||||
"hot_water_cost_current": {
|
||||
"value": 267,
|
||||
"currency": "GBP"
|
||||
},
|
||||
"mechanical_ventilation": 0,
|
||||
"percent_draughtproofed": 100,
|
||||
"suggested_improvements": [
|
||||
{
|
||||
"sequence": 1,
|
||||
"typical_saving": 371,
|
||||
"indicative_cost": "\u00a37,500 - \u00a311,000",
|
||||
"improvement_type": "Q",
|
||||
"improvement_details": {
|
||||
"improvement_number": 7
|
||||
},
|
||||
"improvement_category": 5,
|
||||
"energy_performance_rating": 70,
|
||||
"environmental_impact_rating": 73
|
||||
},
|
||||
{
|
||||
"sequence": 2,
|
||||
"typical_saving": 77,
|
||||
"indicative_cost": "\u00a35,000 - \u00a310,000",
|
||||
"improvement_type": "W2",
|
||||
"improvement_details": {
|
||||
"improvement_number": 58
|
||||
},
|
||||
"improvement_category": 5,
|
||||
"energy_performance_rating": 71,
|
||||
"environmental_impact_rating": 75
|
||||
},
|
||||
{
|
||||
"sequence": 3,
|
||||
"typical_saving": 222,
|
||||
"indicative_cost": "\u00a38,000 - \u00a310,000",
|
||||
"improvement_type": "U",
|
||||
"improvement_details": {
|
||||
"improvement_number": 34
|
||||
},
|
||||
"improvement_category": 5,
|
||||
"energy_performance_rating": 76,
|
||||
"environmental_impact_rating": 76
|
||||
}
|
||||
],
|
||||
"co2_emissions_potential": 2,
|
||||
"energy_rating_potential": 76,
|
||||
"lighting_cost_potential": {
|
||||
"value": 55,
|
||||
"currency": "GBP"
|
||||
},
|
||||
"schema_version_original": "LIG-21.0.1",
|
||||
"flueless_gas_fires_count": 0,
|
||||
"hot_water_cost_potential": {
|
||||
"value": 267,
|
||||
"currency": "GBP"
|
||||
},
|
||||
"renewable_heat_incentive": {
|
||||
"water_heating": 1847.12,
|
||||
"space_heating_existing_dwelling": 11154.61
|
||||
},
|
||||
"draughtproofed_door_count": 1,
|
||||
"energy_consumption_current": 252,
|
||||
"has_fixed_air_conditioning": "false",
|
||||
"multiple_glazed_proportion": 100,
|
||||
"calculation_software_version": "4.3.74",
|
||||
"energy_consumption_potential": 145,
|
||||
"environmental_impact_current": 61,
|
||||
"cfl_fixed_lighting_bulbs_count": 2,
|
||||
"current_energy_efficiency_band": "D",
|
||||
"environmental_impact_potential": 76,
|
||||
"led_fixed_lighting_bulbs_count": 10,
|
||||
"has_heated_separate_conservatory": "false",
|
||||
"potential_energy_efficiency_band": "C",
|
||||
"co2_emissions_current_per_floor_area": 45,
|
||||
"incandescent_fixed_lighting_bulbs_count": 1
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -86,6 +86,7 @@ module "fastapi" {
|
|||
|
||||
environment = {
|
||||
ENVIRONMENT = var.stage
|
||||
GITHUB_SHA = var.github_sha
|
||||
API_KEY = var.api_key
|
||||
SECRET_KEY = var.secret_key
|
||||
# DOMAIN_NAME = var.domain_name
|
||||
|
|
@ -110,11 +111,11 @@ module "fastapi" {
|
|||
CARBON_BASELINE_PREDICTIONS_BUCKET = data.terraform_remote_state.shared.outputs.retrofit_carbon_baseline_predictions_bucket_name
|
||||
HEAT_BASELINE_PREDICTIONS_BUCKET = data.terraform_remote_state.shared.outputs.retrofit_heat_baseline_predictions_bucket_name
|
||||
|
||||
ENGINE_SQS_URL = data.terraform_remote_state.engine.outputs.ara_engine_queue_url
|
||||
CATEGORISATION_SQS_URL = data.terraform_remote_state.categorisation.outputs.categorisation_queue_url
|
||||
POSTCODE_SPLITTER_SQS_URL = data.terraform_remote_state.postcode_splitter.outputs.postcode_splitter_queue_url
|
||||
COMBINER_SQS_URL = data.terraform_remote_state.bulk_address2uprn_combiner.outputs.bulk_address2uprn_combiner_queue_url
|
||||
FINALISER_SQS_URL = data.terraform_remote_state.bulk_upload_finaliser.outputs.bulk_upload_finaliser_queue_url
|
||||
ENGINE_SQS_URL = data.terraform_remote_state.engine.outputs.ara_engine_queue_url
|
||||
CATEGORISATION_SQS_URL = data.terraform_remote_state.categorisation.outputs.categorisation_queue_url
|
||||
POSTCODE_SPLITTER_SQS_URL = data.terraform_remote_state.postcode_splitter.outputs.postcode_splitter_queue_url
|
||||
COMBINER_SQS_URL = data.terraform_remote_state.bulk_address2uprn_combiner.outputs.bulk_address2uprn_combiner_queue_url
|
||||
FINALISER_SQS_URL = data.terraform_remote_state.bulk_upload_finaliser.outputs.bulk_upload_finaliser_queue_url
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,4 +41,10 @@ variable "epc_auth_token" {
|
|||
variable "google_solar_api_key" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
}
|
||||
|
||||
variable "github_sha" {
|
||||
type = string
|
||||
description = "Commit SHA being deployed, surfaced via /health so cloud logs can be tied back to a deploy."
|
||||
default = "unknown"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ variable "zip_excludes" {
|
|||
"**/*.pyc",
|
||||
"**/.pytest_cache/**",
|
||||
"**/tests/**",
|
||||
"**/deployment/**"
|
||||
"**/deployment/**",
|
||||
"**/.git/**",
|
||||
"**/json_samples/**",
|
||||
"**/docs/specs/**"
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
# Electric boiler and CPSU overrides drag category 2 and the conservative Group 1 control
|
||||
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
## Context
|
||||
|
||||
[ADR-0048](0048-heating-override-forces-category-and-control-defers-fuel-and-meter.md)
|
||||
requires a system-replacing heating override to emit a complete companion set
|
||||
(category and control archetype-forced, never inherited) and deferred the
|
||||
electric boiler / CPSU expansion; [ADR-0050](0050-electric-underfloor-override-drags-category-8-and-conservative-group-7-control.md)
|
||||
completed the underfloor half. This ADR completes the electric-boiler half
|
||||
(#1444 Parts A+B for codes **191/192** — the archetypes "Direct-acting
|
||||
electric", "Electric boiler", "Electric CPSU").
|
||||
|
||||
A portfolio-796 scan of the 126 live 191/192 overrides found **30 of the first
|
||||
40 in a suppressed or mis-billed state** from inherited companions:
|
||||
|
||||
- **Inherited category 6** (replaced community heating — the bulk): the heating
|
||||
generator's `is_heat_network_main` gate reads the dwelling as a heat network,
|
||||
so the **HHRSH bundle is never offered** — only tune-ups. Same class as
|
||||
ADR-0050's evidence property.
|
||||
- **Inherited category 4** (replaced heat pump — property 711954): *every*
|
||||
heating bundle suppressed.
|
||||
- **Inherited category 7** (replaced storage heaters — property 721912): the
|
||||
SAP 10.2 Table 12a resolver's category-7 branch bills the direct-acting
|
||||
boiler as storage — **100% at the off-peak low rate** — the precise
|
||||
ADR-0048 room-heater mis-billing, resurfaced.
|
||||
- Stale controls ride along (community 23xx, storage 2401, room-heater 2603),
|
||||
applying the wrong Table 4e temperature adjustment.
|
||||
|
||||
## Decision
|
||||
|
||||
The electric-boiler codes (SAP Table 4a **191** direct-acting electric boiler,
|
||||
**192** electric CPSU) join the forced-companion branches of
|
||||
`main_heating_system_overlay`:
|
||||
|
||||
1. **Category 2** ("boiler system with radiators or underfloor heating") in
|
||||
`_category_for` — deterministic from SAP Table 4a; the same category the
|
||||
gas-boiler overlay already stamps. Table 12a billing for these codes is
|
||||
code-keyed (191 → `DIRECT_ACTING_ELECTRIC_BOILER`, 192 → the deliberate
|
||||
Appendix-F fallback), so the category change *removes* stale-category
|
||||
misrouting rather than selecting a new billing row.
|
||||
2. **Control 2101** (SAP Table 4e **Group 1**, "no time or thermostatic control
|
||||
of room temperature", **+0.6 °C**) in `_control_for` — the conservative
|
||||
default within the group the category fixes: the largest Group 1
|
||||
mean-internal-temperature adjustment, so an unobserved control is never
|
||||
over-credited (the boiler mirror of storage 2401 / room-heater 2601 /
|
||||
underfloor 2701). This **supersedes the earlier "direct-acting electric
|
||||
drags no control" choice** — code 191 is a wet Group-1 boiler, and leaving
|
||||
the control unset inherits the replaced system's (the ADR-0048 bug class),
|
||||
which is strictly worse than a conservative assumption.
|
||||
|
||||
Deliberate asymmetry with gas boilers: the gas archetypes force **2106**
|
||||
(full modern controls) because they also assume a modern condensing install
|
||||
(ADR-0035, decided with Khalim) — the whole system is modelled modern. The
|
||||
electric-boiler archetypes describe an *existing* system of unknown vintage
|
||||
modelled at code 191/192's intrinsic efficiency, so the conservative
|
||||
default applies.
|
||||
|
||||
Fuel (electricity 29, ADR-0045) and meter (192 off-peak Dual via §12; 191
|
||||
Single) were already correct and are unchanged. A forced 2101 also makes the
|
||||
system tune-up options available (2101 is improvable) — a coherent cheap lever
|
||||
for these dwellings.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The suppressed cohort regains its heating pathway: category 6/4 inheritors
|
||||
become HHRSH-eligible (verified live: 711954 gains the HHRSH option), and
|
||||
category-7 inheritors stop billing direct-acting heat at the all-night low
|
||||
rate (721912).
|
||||
- The "incomplete companion set" error log no longer fires for
|
||||
Direct-acting electric / Electric boiler / Electric CPSU. Remaining gap-log
|
||||
archetypes (#1444): heat pumps (Group 2 control), community heating
|
||||
(Group 3 control — interacts with the Table 4c(3) charging factor, needs an
|
||||
accredited worksheet), then the log flips to a raise (Part C).
|
||||
- Overrides re-model with a different (usually lower) baseline where a stale
|
||||
category was over-crediting — an accuracy correction, not a regression.
|
||||
|
||||
### Alternatives rejected
|
||||
|
||||
- **Keep "no control" for direct-acting electric.** Rejected: `None` is not
|
||||
"no control assumed" — by last-wins composition it means *inherit the
|
||||
replaced system's control*, the silent mis-rating ADR-0048 exists to stop.
|
||||
- **Force modern controls (2106) like the gas boilers.** Rejected: nothing in
|
||||
the electric-boiler archetype implies a modern install; assuming full
|
||||
controls over-credits every dwelling whose controls were never observed.
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# The heating-override companion taxonomy is complete; an incomplete set now raises
|
||||
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
## Context
|
||||
|
||||
[ADR-0048](0048-heating-override-forces-category-and-control-defers-fuel-and-meter.md)
|
||||
set the invariant — a system-replacing heating override emits a complete
|
||||
category + control companion set, never inheriting the replaced system's — and
|
||||
intended, once the taxonomy was covered, to "flip the log to a raise, so the
|
||||
coherence invariant is enforced" (#1444 Part C).
|
||||
[ADR-0050](0050-electric-underfloor-override-drags-category-8-and-conservative-group-7-control.md)
|
||||
(underfloor) and
|
||||
[ADR-0052](0052-electric-boiler-and-cpsu-overrides-drag-category-2-and-conservative-group-1-control.md)
|
||||
(electric boilers) filled two families. Two archetype families remained on the
|
||||
gap-log, and with them Part C stayed blocked:
|
||||
|
||||
- **"Air source heat pump" (SAP 211)** — category 4 already forced, control
|
||||
`None` (Table 4e Group 2 unmapped). No live overrides today (census across
|
||||
all portfolios: 0), so this is taxonomy completion, not a live defect.
|
||||
- **"Community heating, boilers" / "…, CHP and boilers" (301/302)** — category
|
||||
6 already forced, control `None` (Group 3 unmapped). **2,220 live
|
||||
overrides** — the largest population on the log. The inherited control is
|
||||
doubly load-bearing for heat networks: it feeds the Table 4e temperature
|
||||
adjustment *and* the SAP 10.2 Table 4c(3) charging factors (worksheet
|
||||
(305)/(305a)).
|
||||
|
||||
## Decision
|
||||
|
||||
1. **Wet heat pumps (Table 4a 211-224) drag control 2201** — Table 4e Group 2
|
||||
"no time or thermostatic control of room temperature", **+0.3 °C**, the
|
||||
largest Group 2 adjustment: the conservative in-group default (the heat-pump
|
||||
mirror of storage 2401 / room-heater 2601 / underfloor 2701 / boiler 2101).
|
||||
Warm-air heat pumps (521-527) are deliberately left unmapped — no archetype
|
||||
reaches them, and the raise below is the guard that a future warm-air
|
||||
archetype cannot land without choosing its Group 5 control.
|
||||
|
||||
This is the *existing-system* fill policy: the ASHP **Measure** overlay
|
||||
installs 2210 (time-and-temperature zone control) because a measure designs
|
||||
a modern end-state (ADR-0024); a landlord override describes an existing
|
||||
pump whose control was never observed, so the conservative default applies.
|
||||
|
||||
2. **Community heating (301/302) defaults to control 2313, keeping an
|
||||
assessor-observed Group 3 control** (decided with Khalim, revising the
|
||||
first-cut 2301). The control encodes two independent unknowns and the
|
||||
default is chosen per axis:
|
||||
- **Kit axis** (thermostat/TRVs): community schemes skew modern, and the
|
||||
portfolio's own lodged community certs are overwhelmingly thermostatted
|
||||
(modal control 2306; none lodge 2301) — so 2313 ("flat rate charging,
|
||||
room thermostat and TRVs", temperature adjustment 0.0) concedes this
|
||||
axis to the evidence.
|
||||
- **Charging axis** (flat rate vs usage-linked): the billing arrangement
|
||||
is scheme paperwork an override genuinely cannot see, so flat rate
|
||||
(Table 4c(3) space **1.05**, DHW **1.05**) is retained — the
|
||||
usage-linked credit (1.0/1.0) is never assumed.
|
||||
- **Keep-observed**: when the cert already lodges a Group 3 control the
|
||||
override is confirming a community system, not replacing one — the
|
||||
assessor observed that control, and stamping any default over it
|
||||
destroys real information (a usage-linked 2306 is worth ~1-2 SAP over
|
||||
flat-rate). The overlay opts in via
|
||||
`keep_existing_heat_network_control` (the control mirror of ADR-0035's
|
||||
`keep_existing_off_peak_meter`); only a stale cross-family control (the
|
||||
boiler-2110 / storage-2401 inheritances) is replaced by 2313.
|
||||
|
||||
3. **An incomplete companion set now raises** (`ValueError`) instead of
|
||||
logging. Every archetype in `_MAIN_HEATING_CODES` resolves a complete
|
||||
category + control (gas boilers via their dedicated overlay; all others via
|
||||
`_category_for`/`_control_for`), so the log-and-continue mitigation has no
|
||||
remaining population — and a silent log was only ever a stop-gap
|
||||
(ADR-0048: "the intended end-state is to fill each archetype's
|
||||
category/control … and then flip the log to a raise"). A future archetype
|
||||
added without companions now fails loudly at overlay time rather than
|
||||
shipping a silently-incoherent cert.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The companion-gap error log is gone; #1444 closes.
|
||||
- Community overrides converge on defensible controls with minimal score
|
||||
churn. Live sample (portfolio 796, 12 properties): the ten dwellings with
|
||||
assessor-observed Group 3 controls (2306/2307/2309) shift **±0.0** — their
|
||||
observed controls survive the override — and only the two group-incoherent
|
||||
dwellings (a Group-1 *boiler* zone control 2110 riding on a heat network)
|
||||
move, **−1.0** each onto the 2313 default. Contrast the first-cut 2301
|
||||
design, which shifted the whole sample 0 to −3.
|
||||
- Adding a heating archetype now *requires* choosing its category + control
|
||||
(or extending the family sets) — the raise turns the ADR-0048 checklist into
|
||||
an enforced contract.
|
||||
|
||||
### Alternatives rejected
|
||||
|
||||
- **The maximally conservative community default (2301).** The first cut.
|
||||
Rejected on evidence: no lodged community cert in the portfolio carries
|
||||
2301, so it under-rates the typical (modern, thermostatted) scheme and
|
||||
shifted the sampled cohort 0 to −3 SAP for no informational gain.
|
||||
- **A usage-linked community default (2306/2312/2314).** Rejected: credits a
|
||||
billing arrangement the landlord never reported — the charging axis is the
|
||||
one the override genuinely cannot observe, so it stays flat-rate.
|
||||
- **Stamping the default over an observed Group 3 control.** Rejected: the
|
||||
ADR-0035 "always stamp" pattern exists to kill *stale* companions; an
|
||||
in-group control on a confirmed community cert is observed data, and
|
||||
overwriting it is information destruction, not coherence.
|
||||
- **Keep log-and-continue indefinitely.** Rejected: the log was the interim
|
||||
mitigation while the taxonomy was incomplete; leaving it means the next
|
||||
archetype ships the ADR-0048 inheritance bug silently again.
|
||||
- **Raise on warm-air heat-pump codes' missing control now.** Not applicable —
|
||||
no archetype maps to 521-527; the raise fires exactly when one is added
|
||||
without its Group 5 control, which is the point.
|
||||
|
|
@ -28,7 +28,6 @@ are left UNKNOWN until modelled. Unresolvable values produce no overlay.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from domain.modelling.simulation import EpcSimulation, HeatingOverlay
|
||||
|
|
@ -36,8 +35,6 @@ from domain.sap10_calculator.tables.table_12a import (
|
|||
OFF_PEAK_IMPLYING_HEATING_CODES,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Off-peak (Economy 7) meter. Electric storage / CPSU systems charge overnight at
|
||||
# the low rate and cannot run economically on a single-rate meter; "Dual" lets
|
||||
# the §12 dispatch resolve the specific tariff (storage 7-hour, CPSU 10-hour).
|
||||
|
|
@ -130,6 +127,22 @@ _ELECTRIC_ROOM_HEATER_CODES = frozenset(range(691, 702))
|
|||
# unambiguously electric, so they drag electricity like the electric room heaters
|
||||
# (ADR-0045, closing the prior no-fuel gap on 191/192).
|
||||
_ELECTRIC_BOILER_CODES = frozenset({191, 192})
|
||||
# SAP Table 4a "boiler system" category — codes 191/192 are wet electric
|
||||
# boilers, same category the gas-boiler overlay stamps. Forced so an
|
||||
# electric-boiler override never keeps the replaced system's category: on
|
||||
# portfolio 796 an inherited 6 (community) or 4 (heat pump) suppressed the
|
||||
# HHRSH pathway, and an inherited 7 (storage) made the Table 12a resolver bill
|
||||
# direct-acting heat wholly at the off-peak low rate (ADR-0052, #1444).
|
||||
_ELECTRIC_BOILER_CATEGORY = 2
|
||||
# SAP Table 4e Group 1 boiler control: 2101 ("no time or thermostatic control
|
||||
# of room temperature", +0.6 C) is the conservative default when the landlord
|
||||
# named only the system — the largest Group 1 mean-internal-temperature
|
||||
# adjustment, so an unobserved control is never over-credited (the boiler
|
||||
# mirror of storage 2401 / room-heater 2601 / underfloor 2701). Deliberately
|
||||
# NOT the gas boilers' modern 2106: the gas archetypes assume a whole modern
|
||||
# condensing install; the electric-boiler archetypes describe an existing
|
||||
# system of unknown vintage (ADR-0052).
|
||||
_CONSERVATIVE_BOILER_CONTROL = 2101
|
||||
# Electric underfloor — SAP Table 4a 421/422 (off-peak) and 424 (screed) — also
|
||||
# unambiguously electric (ADR-0046).
|
||||
_ELECTRIC_UNDERFLOOR_CODES = frozenset({421, 422, 424})
|
||||
|
|
@ -152,6 +165,17 @@ _UNDERFLOOR_CONTROL = 2701
|
|||
# without a PCDB index (ADR-0041).
|
||||
_HEAT_PUMP_CATEGORY = 4
|
||||
_HEAT_PUMP_CODES = frozenset(range(211, 225)) | frozenset(range(521, 528))
|
||||
# SAP Table 4e Group 2 heat-pump control: 2201 ("no time or thermostatic
|
||||
# control of room temperature", +0.3 C) is the conservative default when the
|
||||
# landlord named only the system — the largest Group 2 adjustment, so an
|
||||
# unobserved control is never over-credited. Deliberately NOT the ASHP
|
||||
# Measure's 2210 zone control: a measure designs a modern end-state
|
||||
# (ADR-0024); an override describes an existing pump (ADR-0053). Scoped to the
|
||||
# WET heat pumps (211-224) — warm-air pumps (521-527) take Table 4e Group 5
|
||||
# and stay unmapped (no archetype maps to them; the incomplete-companion raise
|
||||
# guards that a future one cannot land without choosing its Group 5 control).
|
||||
_WET_HEAT_PUMP_CODES = frozenset(range(211, 225))
|
||||
_CONSERVATIVE_HEAT_PUMP_CONTROL = 2201
|
||||
|
||||
# Community / heat-network heating (SAP Table 4a 301-304) is category 6; the
|
||||
# calculator's `_is_heat_network` keys off code OR category 6. The boiler-driven
|
||||
|
|
@ -161,6 +185,18 @@ _HEAT_PUMP_CODES = frozenset(range(211, 225)) | frozenset(range(521, 528))
|
|||
_HEAT_NETWORK_CATEGORY = 6
|
||||
_HEAT_NETWORK_CODES = frozenset({301, 302, 303, 304})
|
||||
_COMMUNITY_BOILER_CODES = frozenset({301, 302, 303})
|
||||
# SAP Table 4e Group 3 heat-network control: 2313 ("flat rate charging, room
|
||||
# thermostat and TRVs") is the default when the landlord named only the scheme
|
||||
# — a per-axis choice (ADR-0053, decided with Khalim). KIT axis: community
|
||||
# schemes skew modern and the portfolio's lodged community certs are
|
||||
# overwhelmingly thermostatted (modal 2306; none lodge 2301), so a room
|
||||
# thermostat + TRVs is the evidence-backed assumption (temperature adjustment
|
||||
# 0.0). CHARGING axis: flat rate (Table 4c(3) space 1.05 / DHW 1.05) — the
|
||||
# billing arrangement is scheme paperwork an override cannot see, so the
|
||||
# usage-linked credit (1.0/1.0) is never assumed. An assessor-OBSERVED Group 3
|
||||
# control on the cert is kept in preference to this default (the overlay sets
|
||||
# `keep_existing_heat_network_control`; the applicator honours it).
|
||||
_DEFAULT_HEAT_NETWORK_CONTROL = 2313
|
||||
_COMMUNITY_GAS_FUEL = 20
|
||||
|
||||
# SAP Table 4c full boiler-control code: programmer + room thermostat + TRVs. The
|
||||
|
|
@ -274,8 +310,9 @@ def _meter_for(code: int) -> Optional[str]:
|
|||
def _control_for(code: int) -> Optional[int]:
|
||||
"""The control to assume when the landlord named only the system: a
|
||||
conservative manual charge control for storage heaters, full modern controls
|
||||
for a gas boiler, None for systems that take neither (direct-acting electric).
|
||||
Overwrites a stale control inherited from the system being replaced."""
|
||||
for a gas boiler, the conservative in-group default for the other mapped
|
||||
families (room heaters, underfloor, electric boilers). Overwrites a stale
|
||||
control inherited from the system being replaced."""
|
||||
if code == _HHR_STORAGE_CODE:
|
||||
return _HHR_CHARGE_CONTROL
|
||||
if code in _STORAGE_HEATER_CODES:
|
||||
|
|
@ -286,6 +323,12 @@ def _control_for(code: int) -> Optional[int]:
|
|||
return _ROOM_HEATER_CONTROL
|
||||
if code in _ELECTRIC_UNDERFLOOR_CODES:
|
||||
return _UNDERFLOOR_CONTROL
|
||||
if code in _ELECTRIC_BOILER_CODES:
|
||||
return _CONSERVATIVE_BOILER_CONTROL
|
||||
if code in _WET_HEAT_PUMP_CODES:
|
||||
return _CONSERVATIVE_HEAT_PUMP_CONTROL
|
||||
if code in _HEAT_NETWORK_CODES:
|
||||
return _DEFAULT_HEAT_NETWORK_CONTROL
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -304,6 +347,8 @@ def _category_for(code: int) -> Optional[int]:
|
|||
return _STORAGE_HEATER_CATEGORY
|
||||
if code in _ELECTRIC_UNDERFLOOR_CODES:
|
||||
return _UNDERFLOOR_CATEGORY
|
||||
if code in _ELECTRIC_BOILER_CODES:
|
||||
return _ELECTRIC_BOILER_CATEGORY
|
||||
if code in _HEAT_PUMP_CODES:
|
||||
return _HEAT_PUMP_CATEGORY
|
||||
if code in _HEAT_NETWORK_CODES:
|
||||
|
|
@ -374,21 +419,19 @@ def main_heating_overlay_for(
|
|||
category = _category_for(code)
|
||||
control = _control_for(code)
|
||||
if category is None or control is None:
|
||||
# A system-replacing override should stamp a complete, coherent companion
|
||||
# A system-replacing override must stamp a complete, coherent companion
|
||||
# set; an unmapped category or control leaves the field unset, so the
|
||||
# effective cert inherits the REPLACED system's value (a silent mis-rating
|
||||
# — e.g. a room heater keeping a storage heater's category/control). We do
|
||||
# not yet have confident defaults for every archetype, so log-and-continue
|
||||
# (matching `flag_fuel_mismatch`) to make the gap visible for review
|
||||
# rather than raise mid-run.
|
||||
logger.error(
|
||||
"Landlord main_heating_system %r (SAP code %d) overlaid with an "
|
||||
"incomplete companion set (category=%s, control=%s); the effective "
|
||||
"cert may inherit the replaced system's value — known gap, review",
|
||||
main_heating_value,
|
||||
code,
|
||||
category,
|
||||
control,
|
||||
# effective cert inherits the REPLACED system's value — the silent
|
||||
# mis-rating class ADR-0048 exists to stop. Every mapped archetype now
|
||||
# carries confident defaults (ADR-0050/0052/0053), so an incomplete set
|
||||
# can only mean a new archetype landed without choosing its companions:
|
||||
# fail loudly at overlay time rather than ship an incoherent cert
|
||||
# (ADR-0048's intended end-state, #1444 Part C).
|
||||
raise ValueError(
|
||||
f"Landlord main_heating_system {main_heating_value!r} (SAP code "
|
||||
f"{code}) resolves an incomplete companion set (category="
|
||||
f"{category}, control={control}); add the archetype's SAP Table 4a "
|
||||
f"category and conservative Table 4e control before mapping it"
|
||||
)
|
||||
return EpcSimulation(
|
||||
heating=HeatingOverlay(
|
||||
|
|
@ -402,5 +445,10 @@ def main_heating_overlay_for(
|
|||
# (e.g. a 24-hour all-low tariff). Measures, which re-meter for real,
|
||||
# build their HeatingOverlay directly and leave this False.
|
||||
keep_existing_off_peak_meter=True,
|
||||
# Likewise a heat-network override's control default (2313) must
|
||||
# not overwrite an assessor-OBSERVED Group 3 control on a community
|
||||
# cert being confirmed — only a stale cross-family control is
|
||||
# replaced (ADR-0053).
|
||||
keep_existing_heat_network_control=code in _HEAT_NETWORK_CODES,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -152,6 +152,13 @@ _SAP_HEATING_FIELDS: tuple[str, ...] = (
|
|||
_ENERGY_SOURCE_FIELDS: tuple[str, ...] = ("meter_type", "gas_connection_available")
|
||||
|
||||
|
||||
def _is_heat_network_control(control: object) -> bool:
|
||||
"""True iff the lodged control is a SAP Table 4e Group 3 (heat network)
|
||||
code — 2301-2314 — i.e. an assessor-observed community control a
|
||||
heat-network override's default must not overwrite (ADR-0053)."""
|
||||
return isinstance(control, int) and 2301 <= control <= 2314
|
||||
|
||||
|
||||
def _is_off_peak_meter(meter_type: object) -> bool:
|
||||
"""True iff the meter resolves to an off-peak Table 12a tariff (not the
|
||||
STANDARD single-rate column). Unparseable / absent meters count as not
|
||||
|
|
@ -177,8 +184,21 @@ def _fold_heating(epc: EpcPropertyData, overlay: HeatingOverlay) -> None:
|
|||
main = epc.sap_heating.main_heating_details[0]
|
||||
for field_name in _MAIN_HEATING_FIELDS:
|
||||
value = getattr(overlay, field_name)
|
||||
if value is not None:
|
||||
setattr(main, field_name, value)
|
||||
if value is None:
|
||||
continue
|
||||
# A community override's control is a coherent default, not an observed
|
||||
# one: when it opts in (`keep_existing_heat_network_control`) and the
|
||||
# cert already lodges a Table 4e GROUP 3 control (assessor-observed on
|
||||
# a community cert being confirmed), keep the cert's. A cross-family
|
||||
# control (boiler 2110, storage 2401) is stale and is still replaced
|
||||
# (ADR-0053).
|
||||
if (
|
||||
field_name == "main_heating_control"
|
||||
and overlay.keep_existing_heat_network_control
|
||||
and _is_heat_network_control(main.main_heating_control)
|
||||
):
|
||||
continue
|
||||
setattr(main, field_name, value)
|
||||
# `main_heating_index_number` (PCDB-resolved, e.g. a heat pump) and
|
||||
# `sap_main_heating_code` (Table 4a-resolved, e.g. storage heaters) are
|
||||
# mutually-exclusive efficiency anchors: a whole-system replacement to one
|
||||
|
|
|
|||
|
|
@ -200,6 +200,15 @@ class HeatingOverlay:
|
|||
# MEASURE re-meters for real (Elmhurst re-lodges 18-hour → Dual on a storage
|
||||
# install), so it leaves this False. `_fold_heating` reads it.
|
||||
keep_existing_off_peak_meter: bool = False
|
||||
# The community-heating mirror of the flag above (ADR-0053): a heat-network
|
||||
# override's `main_heating_control` is a coherent DEFAULT, not an observed
|
||||
# control — when the cert already lodges a Table 4e Group 3 control the
|
||||
# assessor observed it (a community cert being confirmed, not replaced), so
|
||||
# the default must not overwrite it (e.g. a usage-linked 2306 downgraded to
|
||||
# flat-rate 2313 destroys ~1-2 real SAP). A cross-family control (a boiler
|
||||
# 2110, storage 2401) is stale by definition and is still replaced.
|
||||
# `_fold_heating` reads it.
|
||||
keep_existing_heat_network_control: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
|
|||
|
|
@ -260,10 +260,21 @@ def _u_brick_thin_wall_age_a_to_e(wall_thickness_mm: int) -> float:
|
|||
200 to 280 mm 1.7
|
||||
280 to 420 mm 1.4
|
||||
More than 420 mm 1.1
|
||||
|
||||
The 200/280 boundary is shared verbatim by adjacent rows in the spec
|
||||
PDF (Table 13, p.41), which doesn't say which row owns the shared
|
||||
edge. Elmhurst-validated on cert 100031768368 (280mm exactly, no
|
||||
insulation, band C, Dry-lining No): its worksheet (29a) computes the
|
||||
wall at U=1.40 — the upper row. A second cert (217091901, band A,
|
||||
also nominally 280mm) initially looked like a counterexample at
|
||||
U=1.70, but its Elmhurst build never actually set a wall thickness
|
||||
(silently inherited a stale 260mm from an earlier build on the
|
||||
shared assessment) — not a real second data point. Only the 280
|
||||
edge is evidenced here; 200 and 420 are left as originally coded.
|
||||
"""
|
||||
if wall_thickness_mm <= 200:
|
||||
return 2.5
|
||||
if wall_thickness_mm <= 280:
|
||||
if wall_thickness_mm < 280:
|
||||
return 1.7
|
||||
if wall_thickness_mm <= 420:
|
||||
return 1.4
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ from domain.sap10_ml.rdsap_uvalues import (
|
|||
WALL_SYSTEM_BUILT,
|
||||
WALL_TIMBER_FRAME,
|
||||
_insulation_bucket,
|
||||
_u_brick_thin_wall_age_a_to_e, # pyright: ignore[reportPrivateUsage]
|
||||
thermal_bridging_y,
|
||||
u_door,
|
||||
u_exposed_floor,
|
||||
|
|
@ -140,6 +141,29 @@ def test_u_wall_solid_brick_present_insulation_thin_known_thickness_not_uninsula
|
|||
assert result == pytest.approx(0.55, abs=0.001)
|
||||
|
||||
|
||||
def test_u_brick_thin_wall_age_a_to_e_280mm_boundary_takes_upper_bucket() -> None:
|
||||
# Arrange — RdSAP 10 §5.7 Table 13 (PDF p.41) gives four thickness bands
|
||||
# for an uninsulated solid-brick wall: "up to 200mm" -> 2.5, "200 to
|
||||
# 280mm" -> 1.7, "280 to 420mm" -> 1.4, "more than 420mm" -> 1.1. The
|
||||
# 200/280 bands share 280 as an edge verbatim in the spec table, so the
|
||||
# table text alone doesn't say which band owns it. Elmhurst-validated
|
||||
# on cert 100031768368 (solid brick, band C, wall_thickness_mm=280
|
||||
# exactly, no insulation, Dry-lining No): its worksheet (29a) computes
|
||||
# the wall at U=1.40 — the UPPER band — not 1.70. A second cert
|
||||
# (217091901, band A, cert wall_thickness=280 too) initially looked
|
||||
# like Elmhurst evidence for the opposite (U=1.70), but its build
|
||||
# script never actually set a wall thickness field — the assessment
|
||||
# had silently carried over a stale 260mm from an earlier build, so
|
||||
# it isn't a real counter-example (fixed in build_217091901.py).
|
||||
#
|
||||
# Only the 280 edge is evidenced here — the 200 and 420 edges are left
|
||||
# as originally coded pending their own Elmhurst confirmation.
|
||||
|
||||
# Act / Assert
|
||||
assert _u_brick_thin_wall_age_a_to_e(279) == pytest.approx(1.7)
|
||||
assert _u_brick_thin_wall_age_a_to_e(280) == pytest.approx(1.4)
|
||||
|
||||
|
||||
def test_insulation_bucket_present_thin_thickness_rounds_up_to_50_not_zero() -> None:
|
||||
# Direct guard on the bucket: present insulation never maps to bucket 0.
|
||||
assert _insulation_bucket(10, True) == 50
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# RdSAP-21.0.1 corpus campaign ledger
|
||||
|
||||
**Gauge:** 1000 computed / 0 skipped · SAP within-0.5 = **77.8%** · MAE = **0.636** (floors in test_sap_accuracy_corpus.py)
|
||||
**Gauge:** 1000 computed / 0 skipped · SAP within-0.5 = **78.6%** · MAE = **0.627** (floors in test_sap_accuracy_corpus.py)
|
||||
|
||||
Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail engine bug · ⛔ unbuildable. Line: `uprn · lodged L / eng E / Δ · signature`. Rebuild/re-rank: `PYTHONPATH=. python scripts/corpus_1000/build_worklist.py` (preserves statuses & `<-` notes).
|
||||
|
||||
## Clusters (certs with |Δ| ≥ 0.5, ranked by summed |Δ|) — 222 certs in 216 clusters
|
||||
## Clusters (certs with |Δ| ≥ 0.5, ranked by summed |Δ|) — 214 certs in 208 clusters
|
||||
|
||||
### C001 · Σ|Δ| 23.9 · 1 certs · `hpcdb100053/elec | solid brick | (another dwelling | sec:room heaters`
|
||||
- ⚠ `4510053280` · L 51 / E 74.94 / Δ +23.94 <- lodged software billed out-of-range-PSR (2.031 > 2.0) PCDB 100053 as 100% direct electric; engine follows SAP10.2 N2 extension (305%), Elmhurst-validated on case 56. Pinned engine 75 in test_real_cert_sap_accuracy.
|
||||
|
|
@ -114,553 +114,529 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
### C035 · Σ|Δ| 3.1 · 1 certs · `h633/heatnet | timber frame | roof room(s) | RR,ext1,sec:room heaters,whw909`
|
||||
- [ ] `22077224` · L 37 / E 33.90 / Δ -3.10
|
||||
|
||||
### C036 · Σ|Δ| 3.0 · 1 certs · `hpcdb18498/gas | solid brick | pitched 250 | RR,ext2`
|
||||
- [ ] `10011740578` · L 70 / E 67.00 / Δ -3.00
|
||||
|
||||
### C037 · Σ|Δ| 2.9 · 1 certs · `h402/elec | cavity wall | pitched 250 | sec:portable electric,whw903`
|
||||
### C036 · Σ|Δ| 2.9 · 1 certs · `h402/elec | cavity wall | pitched 250 | sec:portable electric,whw903`
|
||||
- [ ] `100110680822` · L 79 / E 76.07 / Δ -2.93
|
||||
|
||||
### C038 · Σ|Δ| 2.9 · 1 certs · `h301/coal-anth | solid brick | pitched 250`
|
||||
### C037 · Σ|Δ| 2.9 · 1 certs · `h301/coal-anth | solid brick | pitched 250`
|
||||
- [ ] `452032915` · L 75 / E 72.09 / Δ -2.91
|
||||
|
||||
### C039 · Σ|Δ| 2.9 · 1 certs · `h301/coal-anth | cavity wall | (another dwelling | whw909`
|
||||
### C038 · Σ|Δ| 2.9 · 1 certs · `h301/coal-anth | cavity wall | (another dwelling | whw909`
|
||||
- [ ] `100071306639` · L 73 / E 70.14 / Δ -2.86
|
||||
|
||||
### C040 · Σ|Δ| 2.9 · 1 certs · `hpcdb18435/gas+2mains | cavity wall | pitched 100 | ext3,sec:room heaters,whw903`
|
||||
### C039 · Σ|Δ| 2.9 · 1 certs · `hpcdb18435/gas+2mains | cavity wall | pitched 100 | ext3,sec:room heaters,whw903`
|
||||
- [ ] `100061346049` · L 55 / E 52.15 / Δ -2.85
|
||||
|
||||
### C041 · Σ|Δ| 2.7 · 1 certs · `hpcdb8443/fuel28 | cavity wall | pitched 100 | RR,ext2,sec:room heaters`
|
||||
### C040 · Σ|Δ| 2.7 · 1 certs · `hpcdb8443/fuel28 | cavity wall | pitched 100 | RR,ext2,sec:room heaters`
|
||||
- [ ] `10013008238` · L 41 / E 38.26 / Δ -2.74
|
||||
|
||||
### C042 · Σ|Δ| 2.7 · 3 certs · `h691/elec | cavity wall | (another dwelling | whw903`
|
||||
### C041 · Σ|Δ| 2.7 · 3 certs · `h691/elec | cavity wall | (another dwelling | whw903`
|
||||
- [ ] `10033187652` · L 70 / E 71.21 / Δ +1.21 <- non-electric cost (secondary fuel/cylinder/tariff); spec-faithful vs lodged
|
||||
- [ ] `200002730` · L 65 / E 64.13 / Δ -0.87
|
||||
- [ ] `10023328117` · L 78 / E 78.64 / Δ +0.64
|
||||
|
||||
### C043 · Σ|Δ| 2.7 · 1 certs · `h633/heatnet | cavity wall | pitched 200 | RR,sec:room heaters,whw903`
|
||||
### C042 · Σ|Δ| 2.7 · 1 certs · `h633/heatnet | cavity wall | pitched 200 | RR,sec:room heaters,whw903`
|
||||
- [ ] `10004873253` · L 65 / E 62.29 / Δ -2.71
|
||||
|
||||
### C044 · Σ|Δ| 2.7 · 1 certs · `h691/elec | cavity wall | (another dwelling | mv1,whw903`
|
||||
### C043 · Σ|Δ| 2.7 · 1 certs · `h691/elec | cavity wall | (another dwelling | mv1,whw903`
|
||||
- [ ] `10090793715` · L 73 / E 75.71 / Δ +2.71
|
||||
|
||||
### C045 · Σ|Δ| 2.7 · 1 certs · `hpcdb17964/gas+2mains | cavity wall | pitched 100 | ext1,sec:room heaters`
|
||||
### C044 · Σ|Δ| 2.7 · 1 certs · `hpcdb17964/gas+2mains | cavity wall | pitched 100 | ext1,sec:room heaters`
|
||||
- [ ] `100120745600` · L 65 / E 67.69 / Δ +2.69
|
||||
|
||||
### C046 · Σ|Δ| 2.7 · 1 certs · `hpcdb17757/gas | granite or | pitched no | RR,ext1`
|
||||
### C045 · Σ|Δ| 2.7 · 1 certs · `hpcdb17757/gas | granite or | pitched no | RR,ext1`
|
||||
- [ ] `100051178830` · L 63 / E 65.66 / Δ +2.66
|
||||
|
||||
### C047 · Σ|Δ| 2.6 · 1 certs · `hpcdb10321/gas | sandstone as | pitched 100 | ext2`
|
||||
### C046 · Σ|Δ| 2.6 · 1 certs · `hpcdb10321/gas | sandstone as | pitched 100 | ext2`
|
||||
- [ ] `100120018282` · L 64 / E 61.36 / Δ -2.64
|
||||
|
||||
### C048 · Σ|Δ| 2.6 · 1 certs · `hpcdb17968/gas | cavity wall | pitched 300 | ext2,sec:room heaters`
|
||||
### C047 · Σ|Δ| 2.6 · 1 certs · `hpcdb17968/gas | cavity wall | pitched 300 | ext2,sec:room heaters`
|
||||
- [ ] `83035979` · L 65 / E 67.60 / Δ +2.60
|
||||
|
||||
### C049 · Σ|Δ| 2.5 · 1 certs · `h524/elec | solid brick | (another dwelling | consv2,sec:room heaters,whw909`
|
||||
### C048 · Σ|Δ| 2.5 · 1 certs · `h524/elec | solid brick | (another dwelling | consv2,sec:room heaters,whw909`
|
||||
- [ ] `10014073761` · L 75 / E 72.52 / Δ -2.48
|
||||
|
||||
### C050 · Σ|Δ| 2.5 · 1 certs · `hpcdb10328/gas | solid brick | pitched insulated | RR`
|
||||
### C049 · Σ|Δ| 2.5 · 1 certs · `hpcdb10328/gas | solid brick | pitched insulated | RR`
|
||||
- [ ] `100050964834` · L 60 / E 62.47 / Δ +2.47
|
||||
|
||||
### C051 · Σ|Δ| 2.4 · 1 certs · `h694/elec | cavity wall | (another dwelling | whw941`
|
||||
### C050 · Σ|Δ| 2.4 · 1 certs · `h694/elec | cavity wall | (another dwelling | whw941`
|
||||
- [ ] `100120752995` · L 71 / E 73.42 / Δ +2.42
|
||||
|
||||
### C052 · Σ|Δ| 2.4 · 1 certs · `h691/elec | solid brick | roof room(s) | RR,ext2,sec:room heaters,whw903`
|
||||
### C051 · Σ|Δ| 2.4 · 1 certs · `h691/elec | solid brick | roof room(s) | RR,ext2,sec:room heaters,whw903`
|
||||
- [ ] `100062174575` · L 46 / E 48.40 / Δ +2.40
|
||||
|
||||
### C053 · Σ|Δ| 2.3 · 1 certs · `hpcdb15753/gas | solid brick | pitched 150 | RR`
|
||||
### C052 · Σ|Δ| 2.3 · 1 certs · `hpcdb15753/gas | solid brick | pitched 150 | RR`
|
||||
- [ ] `100000034023` · L 73 / E 70.71 / Δ -2.29
|
||||
|
||||
### C054 · Σ|Δ| 2.2 · 1 certs · `h506/gas | cavity wall | flat limited | sec:room heaters`
|
||||
### C053 · Σ|Δ| 2.2 · 1 certs · `h506/gas | cavity wall | flat limited | sec:room heaters`
|
||||
- [ ] `100020377671` · L 62 / E 64.23 / Δ +2.23
|
||||
|
||||
### C055 · Σ|Δ| 2.2 · 1 certs · `h104/gas | solid brick | pitched insulated | RR,ext1`
|
||||
### C054 · Σ|Δ| 2.2 · 1 certs · `h104/gas | solid brick | pitched insulated | RR,ext1`
|
||||
- [ ] `22027335` · L 76 / E 73.77 / Δ -2.23
|
||||
|
||||
### C056 · Σ|Δ| 2.1 · 1 certs · `hpcdb18833/gas | solid brick | pitched insulated | RR,ext1,sec:room heaters`
|
||||
### C055 · Σ|Δ| 2.1 · 1 certs · `hpcdb18833/gas | solid brick | pitched insulated | RR,ext1,sec:room heaters`
|
||||
- [ ] `48056153` · L 65 / E 62.89 / Δ -2.11
|
||||
|
||||
### C057 · Σ|Δ| 2.0 · 1 certs · `hpcdb19008/gas | solid brick | pitched insulated | sec:room heaters`
|
||||
### C056 · Σ|Δ| 2.0 · 1 certs · `hpcdb19008/gas | solid brick | pitched insulated | sec:room heaters`
|
||||
- [ ] `100021924710` · L 63 / E 65.00 / Δ +2.00 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C058 · Σ|Δ| 1.9 · 1 certs · `hpcdb18774/gas | solid brick | pitched 100 | RR,ext1`
|
||||
### C057 · Σ|Δ| 1.9 · 1 certs · `hpcdb18774/gas | solid brick | pitched 100 | RR,ext1`
|
||||
- [ ] `100031270375` · L 69 / E 70.93 / Δ +1.93 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C059 · Σ|Δ| 1.9 · 1 certs · `hpcdb106417/elec | cavity wall | pitched 250 | RR,ext2,sec:room heaters`
|
||||
### C058 · Σ|Δ| 1.9 · 1 certs · `hpcdb106417/elec | cavity wall | pitched 250 | RR,ext2,sec:room heaters`
|
||||
- [ ] `10001118522` · L 80 / E 78.07 / Δ -1.93 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C060 · Σ|Δ| 1.9 · 1 certs · `h192/elec | cavity wall | (another dwelling`
|
||||
### C059 · Σ|Δ| 1.9 · 1 certs · `h192/elec | cavity wall | (another dwelling`
|
||||
- [ ] `10010234711` · L 81 / E 79.08 / Δ -1.92 <- electric other (per-cert cost noise)
|
||||
|
||||
### C061 · Σ|Δ| 1.9 · 1 certs · `hpcdb18250/gas | solid brick | pitched 100 | consv2,sec:room heaters`
|
||||
- [ ] `100031768368` · L 61 / E 59.12 / Δ -1.88 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C062 · Σ|Δ| 1.8 · 1 certs · `h301/elec10hr | cavity wall | (another dwelling`
|
||||
### C060 · Σ|Δ| 1.8 · 1 certs · `h301/elec10hr | cavity wall | (another dwelling`
|
||||
- [ ] `4510729926` · L 73 / E 71.15 / Δ -1.85 <- electric + fabric/demand mismatch (per-cert; not a coherent cluster)
|
||||
|
||||
### C063 · Σ|Δ| 1.8 · 1 certs · `hpcdb17138/fuel28 | granite or | pitched 200 | ext1,sec:room heaters`
|
||||
### C061 · Σ|Δ| 1.8 · 1 certs · `hpcdb17138/fuel28 | granite or | pitched 200 | ext1,sec:room heaters`
|
||||
- [ ] `100091329112` · L 49 / E 50.82 / Δ +1.82 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C064 · Σ|Δ| 1.8 · 1 certs · `h691/elec | timber frame | pitched insulated | RR,ext1,sec:portable electric,whw908`
|
||||
### C062 · Σ|Δ| 1.8 · 1 certs · `h691/elec | timber frame | pitched insulated | RR,ext1,sec:portable electric,whw908`
|
||||
- [ ] `10011797347` · L 47 / E 48.77 / Δ +1.77 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C065 · Σ|Δ| 1.8 · 1 certs · `hpcdb17570/gas+2mains | solid brick | (another dwelling | RR,ext3,sec:room heaters`
|
||||
- [ ] `100022668387` · L 68 / E 66.25 / Δ -1.75 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C066 · Σ|Δ| 1.7 · 1 certs · `hpcdb10354/gas+2mains | sandstone as | pitched 300 | ext1`
|
||||
### C063 · Σ|Δ| 1.7 · 1 certs · `hpcdb10354/gas+2mains | sandstone as | pitched 300 | ext1`
|
||||
- [ ] `100100892216` · L 69 / E 67.28 / Δ -1.72 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C067 · Σ|Δ| 1.7 · 1 certs · `hpcdb8119/gas | cavity wall | pitched insulated | RR,ext1`
|
||||
### C064 · Σ|Δ| 1.7 · 1 certs · `hpcdb8119/gas | cavity wall | pitched insulated | RR,ext1`
|
||||
- [ ] `100060878876` · L 49 / E 50.71 / Δ +1.71 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C068 · Σ|Δ| 1.7 · 1 certs · `h402/elec | solid brick | (another dwelling | sec:portable electric,whw903`
|
||||
### C065 · Σ|Δ| 1.7 · 1 certs · `h402/elec | solid brick | (another dwelling | sec:portable electric,whw903`
|
||||
- [ ] `12126160` · L 69 / E 70.71 / Δ +1.71 <- electric + fabric/demand mismatch (per-cert; not a coherent cluster)
|
||||
|
||||
### C069 · Σ|Δ| 1.7 · 1 certs · `h401/elec | solid brick | (another dwelling | sec:portable electric,whw950`
|
||||
### C066 · Σ|Δ| 1.7 · 1 certs · `h401/elec | solid brick | (another dwelling | sec:portable electric,whw950`
|
||||
- [ ] `22032926` · L 61 / E 62.71 / Δ +1.71 <- electric + fabric/demand mismatch (per-cert; not a coherent cluster)
|
||||
|
||||
### C070 · Σ|Δ| 1.7 · 1 certs · `hpcdb18560/gas | cavity wall | pitched insulated | RR,ext1`
|
||||
### C067 · Σ|Δ| 1.7 · 1 certs · `hpcdb18560/gas | cavity wall | pitched insulated | RR,ext1`
|
||||
- [ ] `100010942905` · L 72 / E 73.70 / Δ +1.70 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C071 · Σ|Δ| 1.6 · 1 certs · `hpcdb18119/gas | solid brick | pitched 100 | ext1`
|
||||
- [ ] `100030307976` · L 71 / E 69.37 / Δ -1.63 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
### C068 · Σ|Δ| 1.7 · 1 certs · `hpcdb18498/gas | solid brick | pitched 250 | RR,ext2`
|
||||
- [ ] `10011740578` · L 70 / E 68.30 / Δ -1.70
|
||||
|
||||
### C072 · Σ|Δ| 1.6 · 1 certs · `hpcdb18909/gas | cavity wall | pitched 100 | whw903`
|
||||
### C069 · Σ|Δ| 1.6 · 1 certs · `hpcdb18909/gas | cavity wall | pitched 100 | whw903`
|
||||
- [ ] `100061404488` · L 70 / E 68.38 / Δ -1.62 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C073 · Σ|Δ| 1.6 · 1 certs · `h402/elec | system built | (another dwelling | sec:room heaters,whw903`
|
||||
### C070 · Σ|Δ| 1.6 · 1 certs · `h402/elec | system built | (another dwelling | sec:room heaters,whw903`
|
||||
- [ ] `21067296` · L 67 / E 68.60 / Δ +1.60 <- electric + fabric/demand mismatch (per-cert; not a coherent cluster)
|
||||
|
||||
### C074 · Σ|Δ| 1.6 · 1 certs · `hpcdb10164/gas | solid brick | pitched insulated | sec:room heaters`
|
||||
### C071 · Σ|Δ| 1.6 · 1 certs · `hpcdb10164/gas | solid brick | pitched insulated | sec:room heaters`
|
||||
- [ ] `5870047440` · L 58 / E 59.58 / Δ +1.58 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C075 · Σ|Δ| 1.6 · 1 certs · `hpcdb8051/gas | cavity wall | (same dwelling | ext2`
|
||||
### C072 · Σ|Δ| 1.6 · 1 certs · `hpcdb8051/gas | cavity wall | (same dwelling | ext2`
|
||||
- [ ] `100070450818` · L 65 / E 63.43 / Δ -1.57 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C076 · Σ|Δ| 1.6 · 1 certs · `hpcdb17741/gas | cavity wall | pitched 225`
|
||||
### C073 · Σ|Δ| 1.6 · 1 certs · `hpcdb17741/gas | cavity wall | pitched 225`
|
||||
- [ ] `100050881708` · L 70 / E 71.55 / Δ +1.55 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C077 · Σ|Δ| 1.5 · 1 certs · `h301/elec | system built | (another dwelling | whw950`
|
||||
### C074 · Σ|Δ| 1.5 · 1 certs · `h301/elec | system built | (another dwelling | whw950`
|
||||
- [ ] `10010254955` · L 79 / E 77.46 / Δ -1.54 <- electric other (per-cert cost noise)
|
||||
|
||||
### C078 · Σ|Δ| 1.5 · 1 certs · `hpcdb16839/gas | cavity wall | pitched 400+ | ext1,mv2,sec:room heaters`
|
||||
### C075 · Σ|Δ| 1.5 · 1 certs · `hpcdb16839/gas | cavity wall | pitched 400+ | ext1,mv2,sec:room heaters`
|
||||
- [ ] `3455011834` · L 69 / E 67.48 / Δ -1.52 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C079 · Σ|Δ| 1.5 · 1 certs · `hpcdb15502/gas | granite or | pitched 150 | RR,ext1,sec:room heaters`
|
||||
### C076 · Σ|Δ| 1.5 · 1 certs · `hpcdb15502/gas | granite or | pitched 150 | RR,ext1,sec:room heaters`
|
||||
- [ ] `100100274789` · L 60 / E 61.51 / Δ +1.51 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C080 · Σ|Δ| 1.5 · 1 certs · `hpcdb16453/gas | cavity wall | pitched 300 | consv2,ext1`
|
||||
### C077 · Σ|Δ| 1.5 · 1 certs · `hpcdb16453/gas | cavity wall | pitched 300 | consv2,ext1`
|
||||
- [ ] `100050852755` · L 65 / E 66.49 / Δ +1.49 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C081 · Σ|Δ| 1.5 · 1 certs · `hpcdb17679/gas | solid brick | pitched 125 | ext1`
|
||||
- [ ] `100021326422` · L 70 / E 68.51 / Δ -1.49 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C082 · Σ|Δ| 1.5 · 1 certs · `hpcdb15029/gas | solid brick | pitched insulated | RR,ext1`
|
||||
### C078 · Σ|Δ| 1.5 · 1 certs · `hpcdb15029/gas | solid brick | pitched insulated | RR,ext1`
|
||||
- [ ] `100050988345` · L 61 / E 59.51 / Δ -1.49 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C083 · Σ|Δ| 1.5 · 1 certs · `hpcdb17998/gas | solid brick | pitched insulated | RR,consv2`
|
||||
### C079 · Σ|Δ| 1.5 · 1 certs · `hpcdb17998/gas | solid brick | pitched insulated | RR,consv2`
|
||||
- [ ] `100030285758` · L 54 / E 55.48 / Δ +1.48 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C084 · Σ|Δ| 1.4 · 1 certs · `hpcdb17978/gas | solid brick | flat no | RR,ext1`
|
||||
### C080 · Σ|Δ| 1.4 · 1 certs · `hpcdb17978/gas | solid brick | flat no | RR,ext1`
|
||||
- [ ] `line80` · L 72 / E 70.56 / Δ -1.44
|
||||
|
||||
### C085 · Σ|Δ| 1.4 · 1 certs · `hpcdb10356/gas | solid brick | pitched 150 | RR,ext1,sec:room heaters`
|
||||
### C081 · Σ|Δ| 1.4 · 1 certs · `hpcdb10356/gas | solid brick | pitched 150 | RR,ext1,sec:room heaters`
|
||||
- [ ] `100080518673` · L 57 / E 58.42 / Δ +1.42 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C086 · Σ|Δ| 1.4 · 1 certs · `hpcdb18561/gas | solid brick | pitched 250 | ext2`
|
||||
### C082 · Σ|Δ| 1.4 · 1 certs · `hpcdb18561/gas | solid brick | pitched 250 | ext2`
|
||||
- [ ] `235025284` · L 69 / E 67.59 / Δ -1.41 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C087 · Σ|Δ| 1.4 · 1 certs · `hpcdb18767/gas | sandstone as | (another dwelling`
|
||||
### C083 · Σ|Δ| 1.4 · 1 certs · `hpcdb18767/gas | sandstone as | (another dwelling`
|
||||
- [ ] `250063088` · L 73 / E 71.60 / Δ -1.40 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C088 · Σ|Δ| 1.4 · 1 certs · `hpcdb16210/gas | cavity wall | pitched 300 | sec:room heaters`
|
||||
### C084 · Σ|Δ| 1.4 · 1 certs · `hpcdb16210/gas | cavity wall | pitched 300 | sec:room heaters`
|
||||
- [ ] `100110097281` · L 64 / E 65.37 / Δ +1.37 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C089 · Σ|Δ| 1.4 · 1 certs · `h691/elec | solid brick | (another dwelling | whw903`
|
||||
### C085 · Σ|Δ| 1.4 · 1 certs · `h691/elec | solid brick | (another dwelling | whw903`
|
||||
- [ ] `10091630692` · L 58 / E 56.63 / Δ -1.37 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C090 · Σ|Δ| 1.3 · 1 certs · `hpcdb19227/fuel28 | solid brick | pitched 100 | RR,ext3,sec:room heaters`
|
||||
### C086 · Σ|Δ| 1.3 · 1 certs · `hpcdb19227/fuel28 | solid brick | pitched 100 | RR,ext3,sec:room heaters`
|
||||
- [ ] `100012368622` · L 39 / E 40.32 / Δ +1.32 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C091 · Σ|Δ| 1.3 · 1 certs · `hpcdb19007/gas | solid brick | pitched insulated | RR,ext2,sec:room heaters`
|
||||
### C087 · Σ|Δ| 1.3 · 1 certs · `hpcdb19007/gas | solid brick | pitched insulated | RR,ext2,sec:room heaters`
|
||||
- [ ] `90077194` · L 65 / E 63.69 / Δ -1.31 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C092 · Σ|Δ| 1.3 · 1 certs · `hpcdb19083/gas | cavity wall | pitched 75 | RR,consv2`
|
||||
### C088 · Σ|Δ| 1.3 · 1 certs · `hpcdb19083/gas | cavity wall | pitched 75 | RR,consv2`
|
||||
- [ ] `100031461986` · L 70 / E 71.29 / Δ +1.29 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C093 · Σ|Δ| 1.3 · 1 certs · `h691/elec | granite or | pitched insulated | PV,ext3,whw903`
|
||||
### C089 · Σ|Δ| 1.3 · 1 certs · `h691/elec | granite or | pitched insulated | PV,ext3,whw903`
|
||||
- [ ] `30095054` · L 46 / E 44.74 / Δ -1.26 <- non-electric cost (secondary fuel/cylinder/tariff); spec-faithful vs lodged
|
||||
|
||||
### C094 · Σ|Δ| 1.3 · 1 certs · `hpcdb18335/gas | cavity wall | pitched 100 | RR,ext1,sec:room heaters`
|
||||
### C090 · Σ|Δ| 1.3 · 1 certs · `hpcdb18335/gas | cavity wall | pitched 100 | RR,ext1,sec:room heaters`
|
||||
- [ ] `100100055589` · L 58 / E 59.26 / Δ +1.26 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C095 · Σ|Δ| 1.2 · 1 certs · `h409/elec | solid brick | (another dwelling | consv2,sec:portable electric,whw909`
|
||||
### C091 · Σ|Δ| 1.2 · 1 certs · `h409/elec | solid brick | (another dwelling | consv2,sec:portable electric,whw909`
|
||||
- [ ] `74061136` · L 73 / E 71.75 / Δ -1.25 <- electric storage cat7 E7: over-rate = off-peak rate/fraction too generous; needs Elmhurst tariff arbitration
|
||||
|
||||
### C096 · Σ|Δ| 1.2 · 1 certs · `hpcdb15101/gas | cavity wall | pitched 270 | consv3,sec:room heaters`
|
||||
### C092 · Σ|Δ| 1.2 · 1 certs · `hpcdb15101/gas | cavity wall | pitched 270 | consv3,sec:room heaters`
|
||||
- [ ] `100091435353` · L 65 / E 66.23 / Δ +1.23 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C097 · Σ|Δ| 1.2 · 2 certs · `h301/coal-anth | system built | (another dwelling`
|
||||
### C093 · Σ|Δ| 1.2 · 2 certs · `h301/coal-anth | system built | (another dwelling`
|
||||
- [ ] `10091070164` · L 69 / E 69.60 / Δ +0.60
|
||||
- [ ] `10090927185` · L 79 / E 79.60 / Δ +0.60
|
||||
|
||||
### C098 · Σ|Δ| 1.2 · 1 certs · `hpcdb19110/gas | cavity wall | pitched no | ext3`
|
||||
### C094 · Σ|Δ| 1.2 · 1 certs · `hpcdb19110/gas | cavity wall | pitched no | ext3`
|
||||
- [ ] `100100614412` · L 53 / E 51.81 / Δ -1.19 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C099 · Σ|Δ| 1.2 · 1 certs · `hpcdb18658/gas | solid brick | flat no`
|
||||
- [ ] `217091901` · L 62 / E 60.82 / Δ -1.18 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C100 · Σ|Δ| 1.2 · 1 certs · `hpcdb17115/gas | sandstone as | (another dwelling | ext2,sec:room heaters`
|
||||
### C095 · Σ|Δ| 1.2 · 1 certs · `hpcdb17115/gas | sandstone as | (another dwelling | ext2,sec:room heaters`
|
||||
- [x] `line768` · L 57 / E 55.84 / Δ -1.16
|
||||
|
||||
### C101 · Σ|Δ| 1.2 · 1 certs · `h102/gas | solid brick | (same dwelling | ext1,sec:portable electric`
|
||||
### C096 · Σ|Δ| 1.2 · 1 certs · `h102/gas | solid brick | (same dwelling | ext1,sec:portable electric`
|
||||
- [ ] `217061895` · L 61 / E 59.84 / Δ -1.16 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C102 · Σ|Δ| 1.1 · 1 certs · `hpcdb15708/gas | cavity wall | pitched insulated | RR,sec:room heaters`
|
||||
### C097 · Σ|Δ| 1.1 · 1 certs · `hpcdb15708/gas | cavity wall | pitched insulated | RR,sec:room heaters`
|
||||
- [ ] `10002349227` · L 76 / E 77.14 / Δ +1.14 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C103 · Σ|Δ| 1.1 · 1 certs · `hpcdb15451/gas | timber frame | pitched insulated | RR,ext1,sec:room heaters`
|
||||
### C098 · Σ|Δ| 1.1 · 1 certs · `hpcdb18119/gas | solid brick | pitched 100 | ext1`
|
||||
- [ ] `100030307976` · L 71 / E 69.86 / Δ -1.14 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C099 · Σ|Δ| 1.1 · 1 certs · `hpcdb15451/gas | timber frame | pitched insulated | RR,ext1,sec:room heaters`
|
||||
- [ ] `200001634005` · L 66 / E 67.11 / Δ +1.11 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C104 · Σ|Δ| 1.1 · 1 certs · `hpcdb16407/gas | cavity wall | pitched 250 | ext1,sec:room heaters`
|
||||
### C100 · Σ|Δ| 1.1 · 1 certs · `hpcdb16407/gas | cavity wall | pitched 250 | ext1,sec:room heaters`
|
||||
- [ ] `100071059081` · L 63 / E 64.09 / Δ +1.09 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C105 · Σ|Δ| 1.1 · 1 certs · `hpcdb17973/gas | solid brick | (another dwelling`
|
||||
### C101 · Σ|Δ| 1.1 · 1 certs · `hpcdb17973/gas | solid brick | (another dwelling`
|
||||
- [ ] `5117280` · L 76 / E 77.08 / Δ +1.08 <- 🔧 thin-internal-insulation bucket-0 fix: present insulation + known <25mm thickness was mapped to uninsulated (U 1.7); now routes to 50mm row like unknown-thickness. Δ-4.44 -> +1.08.
|
||||
|
||||
### C106 · Σ|Δ| 1.1 · 1 certs · `hpcdb19007/gas+2mains | cavity wall | pitched 350 | ext2,mv5,sec:room heaters,whw914`
|
||||
### C102 · Σ|Δ| 1.1 · 1 certs · `hpcdb19007/gas+2mains | cavity wall | pitched 350 | ext2,mv5,sec:room heaters,whw914`
|
||||
- [ ] `100011820199` · L 55 / E 56.06 / Δ +1.06 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C107 · Σ|Δ| 1.1 · 1 certs · `h691/elec | system built | (another dwelling | whw903`
|
||||
### C103 · Σ|Δ| 1.1 · 1 certs · `h691/elec | system built | (another dwelling | whw903`
|
||||
- [ ] `90214753` · L 83 / E 81.95 / Δ -1.05 <- non-electric cost (secondary fuel/cylinder/tariff); spec-faithful vs lodged
|
||||
|
||||
### C108 · Σ|Δ| 1.0 · 1 certs · `h102/gas | cavity wall | pitched insulated | consv2`
|
||||
### C104 · Σ|Δ| 1.1 · 1 certs · `hpcdb17570/gas+2mains | solid brick | (another dwelling | RR,ext3,sec:room heaters`
|
||||
- [ ] `100022668387` · L 68 / E 66.95 / Δ -1.05 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C105 · Σ|Δ| 1.0 · 1 certs · `h102/gas | cavity wall | pitched insulated | consv2`
|
||||
- [ ] `100051051866` · L 70 / E 71.04 / Δ +1.04 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C109 · Σ|Δ| 1.0 · 1 certs · `hpcdb18737/gas | cavity wall | pitched insulated | PV,sec:room heaters`
|
||||
### C106 · Σ|Δ| 1.0 · 1 certs · `hpcdb18737/gas | cavity wall | pitched insulated | PV,sec:room heaters`
|
||||
- [ ] `10013836852` · L 100 / E 98.97 / Δ -1.03 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C110 · Σ|Δ| 1.0 · 1 certs · `hpcdb17985/gas | solid brick | (another dwelling`
|
||||
### C107 · Σ|Δ| 1.0 · 1 certs · `hpcdb17985/gas | solid brick | (another dwelling`
|
||||
- [ ] `line899` · L 73 / E 71.97 / Δ -1.03
|
||||
|
||||
### C111 · Σ|Δ| 1.0 · 1 certs · `hpcdb18524/gas+2mains | cavity wall | pitched 400+ | ext4`
|
||||
### C108 · Σ|Δ| 1.0 · 1 certs · `hpcdb18524/gas+2mains | cavity wall | pitched 400+ | ext4`
|
||||
- [ ] `40037847` · L 81 / E 79.98 / Δ -1.02 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C112 · Σ|Δ| 1.0 · 1 certs · `hpcdb19012/gas | cavity wall | pitched insulated`
|
||||
### C109 · Σ|Δ| 1.0 · 1 certs · `hpcdb19012/gas | cavity wall | pitched insulated`
|
||||
- [ ] `10070056075` · L 80 / E 78.99 / Δ -1.01 <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
|
||||
### C113 · Σ|Δ| 1.0 · 1 certs · `hpcdb16843/gas+2mains | cavity wall | pitched insulated | PV,RR,ext2`
|
||||
### C110 · Σ|Δ| 1.0 · 1 certs · `hpcdb16843/gas+2mains | cavity wall | pitched insulated | PV,RR,ext2`
|
||||
- [ ] `200131078` · L 86 / E 85.00 / Δ -1.00 <- room-in-roof surface geometry (spec-mandated, worksheet-validated; see PRs #1440/#1441)
|
||||
|
||||
### C114 · Σ|Δ| 1.0 · 1 certs · `hpcdb17489/gas | cavity wall | pitched 270 | PV,consv2,ext1,sec:room heaters`
|
||||
### C111 · Σ|Δ| 1.0 · 1 certs · `hpcdb17489/gas | cavity wall | pitched 270 | PV,consv2,ext1,sec:room heaters`
|
||||
- [ ] `10000245212` · L 101 / E 100.00 / Δ -1.00 <- non-electric cost (secondary fuel/cylinder/tariff); spec-faithful vs lodged
|
||||
|
||||
### C115 · Σ|Δ| 1.0 · 1 certs · `h191/elec | solid brick | (another dwelling | ext2,whw909`
|
||||
- [ ] `15129162` · L 62 / E 61.00 / Δ -1.00
|
||||
|
||||
### C116 · Σ|Δ| 1.0 · 1 certs · `h301/coal-anth | cavity wall | pitched 270`
|
||||
### C112 · Σ|Δ| 1.0 · 1 certs · `h301/coal-anth | cavity wall | pitched 270`
|
||||
- [ ] `23032873` · L 73 / E 72.00 / Δ -1.00
|
||||
|
||||
### C117 · Σ|Δ| 1.0 · 1 certs · `hpcdb16930/gas | cavity wall | pitched unknown | RR,ext1,sec:room heaters`
|
||||
### C113 · Σ|Δ| 1.0 · 1 certs · `hpcdb16930/gas | cavity wall | pitched unknown | RR,ext1,sec:room heaters`
|
||||
- [ ] `45019020` · L 62 / E 61.01 / Δ -0.99
|
||||
|
||||
### C118 · Σ|Δ| 1.0 · 1 certs · `hpcdb17939/gas | solid brick | (another dwelling`
|
||||
### C114 · Σ|Δ| 1.0 · 1 certs · `hpcdb17939/gas | solid brick | (another dwelling`
|
||||
- [ ] `42065028` · L 59 / E 58.02 / Δ -0.98
|
||||
|
||||
### C119 · Σ|Δ| 1.0 · 1 certs · `hpcdb10071/gas | cavity wall | pitched 150 | RR`
|
||||
### C115 · Σ|Δ| 1.0 · 1 certs · `hpcdb10071/gas | cavity wall | pitched 150 | RR`
|
||||
- [ ] `10032928147` · L 77 / E 76.04 / Δ -0.96
|
||||
|
||||
### C120 · Σ|Δ| 1.0 · 1 certs · `hpcdb17045/gas | cavity wall | pitched 300`
|
||||
### C116 · Σ|Δ| 1.0 · 1 certs · `hpcdb17045/gas | cavity wall | pitched 300`
|
||||
- [ ] `10010678937` · L 78 / E 77.04 / Δ -0.96
|
||||
|
||||
### C121 · Σ|Δ| 1.0 · 1 certs · `hpcdb15247/gas | cavity wall | pitched insulated | RR`
|
||||
### C117 · Σ|Δ| 1.0 · 1 certs · `hpcdb15247/gas | cavity wall | pitched insulated | RR`
|
||||
- [ ] `100011010151` · L 62 / E 62.96 / Δ +0.96
|
||||
|
||||
### C122 · Σ|Δ| 1.0 · 1 certs · `hpcdb16406/gas | solid brick | pitched insulated | RR,consv2,ext2,sec:room heaters`
|
||||
- [ ] `100050477371` · L 65 / E 64.04 / Δ -0.96
|
||||
|
||||
### C123 · Σ|Δ| 0.9 · 1 certs · `hpcdb17529/fuel28 | cavity wall | pitched insulated | RR,ext2,sec:room heaters`
|
||||
### C118 · Σ|Δ| 0.9 · 1 certs · `hpcdb17529/fuel28 | cavity wall | pitched insulated | RR,ext2,sec:room heaters`
|
||||
- [ ] `10002945522` · L 61 / E 61.94 / Δ +0.94
|
||||
|
||||
### C124 · Σ|Δ| 0.9 · 1 certs · `h691/elec+2mains | cob as | pitched 300 | whw903`
|
||||
### C119 · Σ|Δ| 0.9 · 1 certs · `h691/elec+2mains | cob as | pitched 300 | whw903`
|
||||
- [ ] `10032957680` · L 45 / E 45.94 / Δ +0.94
|
||||
|
||||
### C125 · Σ|Δ| 0.9 · 1 certs · `hpcdb18204/gas | solid brick | pitched 400+ | PV,ext2`
|
||||
### C120 · Σ|Δ| 0.9 · 1 certs · `hpcdb18204/gas | solid brick | pitched 400+ | PV,ext2`
|
||||
- [ ] `2465057465` · L 81 / E 81.93 / Δ +0.93
|
||||
|
||||
### C126 · Σ|Δ| 0.9 · 1 certs · `hpcdb19019/gas | solid brick | pitched 100 | ext3,sec:room heaters`
|
||||
### C121 · Σ|Δ| 0.9 · 1 certs · `hpcdb19019/gas | solid brick | pitched 100 | ext3,sec:room heaters`
|
||||
- [ ] `100090616327` · L 63 / E 63.93 / Δ +0.93
|
||||
|
||||
### C127 · Σ|Δ| 0.9 · 1 certs · `hpcdb17612/gas | cavity wall | pitched 270 | ext1`
|
||||
### C122 · Σ|Δ| 0.9 · 1 certs · `hpcdb17612/gas | cavity wall | pitched 270 | ext1`
|
||||
- [ ] `100012753833` · L 74 / E 73.07 / Δ -0.93
|
||||
|
||||
### C128 · Σ|Δ| 0.9 · 1 certs · `hpcdb19006/gas | solid brick | pitched 75 | ext1,sec:room heaters`
|
||||
### C123 · Σ|Δ| 0.9 · 1 certs · `hpcdb19006/gas | solid brick | pitched 75 | ext1,sec:room heaters`
|
||||
- [ ] `100031269525` · L 53 / E 53.91 / Δ +0.91
|
||||
|
||||
### C129 · Σ|Δ| 0.9 · 1 certs · `hpcdb17964/gas | cavity wall | pitched insulated | consv3`
|
||||
### C124 · Σ|Δ| 0.9 · 1 certs · `hpcdb17964/gas | cavity wall | pitched insulated | consv3`
|
||||
- [ ] `200003725358` · L 75 / E 74.09 / Δ -0.91
|
||||
|
||||
### C130 · Σ|Δ| 0.9 · 1 certs · `hpcdb17507/gas | solid brick | pitched insulated | RR,ext3`
|
||||
### C125 · Σ|Δ| 0.9 · 1 certs · `hpcdb17507/gas | solid brick | pitched insulated | RR,ext3`
|
||||
- [ ] `100050986162` · L 62 / E 61.10 / Δ -0.90
|
||||
|
||||
### C131 · Σ|Δ| 0.9 · 1 certs · `hpcdb17985/gas | cavity wall | (another dwelling | mv2`
|
||||
### C126 · Σ|Δ| 0.9 · 1 certs · `hpcdb17985/gas | cavity wall | (another dwelling | mv2`
|
||||
- [ ] `5300000141` · L 78 / E 77.10 / Δ -0.90
|
||||
|
||||
### C132 · Σ|Δ| 0.9 · 1 certs · `hpcdb18616/gas | cavity wall | pitched 250 | sec:room heaters`
|
||||
### C127 · Σ|Δ| 0.9 · 1 certs · `hpcdb18616/gas | cavity wall | pitched 250 | sec:room heaters`
|
||||
- [ ] `100050618404` · L 73 / E 73.89 / Δ +0.89
|
||||
|
||||
### C133 · Σ|Δ| 0.9 · 1 certs · `hpcdb17507/gas | cavity wall | pitched 250 | PV,ext1`
|
||||
### C128 · Σ|Δ| 0.9 · 1 certs · `hpcdb17507/gas | cavity wall | pitched 250 | PV,ext1`
|
||||
- [ ] `100090476016` · L 81 / E 81.89 / Δ +0.89
|
||||
|
||||
### C134 · Σ|Δ| 0.9 · 1 certs · `h691/elec | timber frame | pitched 200 | RR,ext3,whw903`
|
||||
### C129 · Σ|Δ| 0.9 · 1 certs · `h691/elec | timber frame | pitched 200 | RR,ext3,whw903`
|
||||
- [ ] `10012119141` · L 45 / E 45.89 / Δ +0.89
|
||||
|
||||
### C135 · Σ|Δ| 0.9 · 1 certs · `h524/elec | cavity wall | pitched 300 | PV`
|
||||
### C130 · Σ|Δ| 0.9 · 1 certs · `h524/elec | cavity wall | pitched 300 | PV`
|
||||
- [ ] `100040938206` · L 75 / E 75.89 / Δ +0.89
|
||||
|
||||
### C136 · Σ|Δ| 0.9 · 1 certs · `hpcdb18234/gas | cavity wall | pitched insulated | RR,consv2`
|
||||
### C131 · Σ|Δ| 0.9 · 1 certs · `hpcdb18234/gas | cavity wall | pitched insulated | RR,consv2`
|
||||
- [ ] `38188474` · L 76 / E 76.87 / Δ +0.87
|
||||
|
||||
### C137 · Σ|Δ| 0.9 · 1 certs · `hpcdb16777/gas | cavity wall | pitched 100 | ext1,sec:room heaters`
|
||||
### C132 · Σ|Δ| 0.9 · 1 certs · `hpcdb16777/gas | cavity wall | pitched 100 | ext1,sec:room heaters`
|
||||
- [ ] `100061194957` · L 69 / E 69.87 / Δ +0.87
|
||||
|
||||
### C138 · Σ|Δ| 0.9 · 1 certs · `hpcdb18559/gas+2mains | solid brick | pitched 200 | whw914`
|
||||
### C133 · Σ|Δ| 0.9 · 1 certs · `hpcdb18559/gas+2mains | solid brick | pitched 200 | whw914`
|
||||
- [ ] `100021236527` · L 72 / E 71.13 / Δ -0.87
|
||||
|
||||
### C139 · Σ|Δ| 0.9 · 1 certs · `hpcdb18907/gas | cavity wall | pitched 270 | consv2`
|
||||
### C134 · Σ|Δ| 0.9 · 1 certs · `hpcdb18907/gas | cavity wall | pitched 270 | consv2`
|
||||
- [ ] `100040193752` · L 71 / E 71.86 / Δ +0.86
|
||||
|
||||
### C140 · Σ|Δ| 0.9 · 1 certs · `hpcdb10327/gas | cavity wall | (another dwelling`
|
||||
### C135 · Σ|Δ| 0.9 · 1 certs · `hpcdb10327/gas | cavity wall | (another dwelling`
|
||||
- [ ] `100060701552` · L 74 / E 74.86 / Δ +0.86
|
||||
|
||||
### C141 · Σ|Δ| 0.9 · 1 certs · `h691/elec | system built | flat insulated | whw903`
|
||||
### C136 · Σ|Δ| 0.9 · 1 certs · `h691/elec | system built | flat insulated | whw903`
|
||||
- [ ] `10070396146` · L 61 / E 60.14 / Δ -0.86
|
||||
|
||||
### C142 · Σ|Δ| 0.8 · 1 certs · `hpcdb8587/gas | cavity wall | (another dwelling`
|
||||
### C137 · Σ|Δ| 0.8 · 1 certs · `hpcdb8587/gas | cavity wall | (another dwelling`
|
||||
- [ ] `100030528930` · L 74 / E 73.16 / Δ -0.84
|
||||
|
||||
### C143 · Σ|Δ| 0.8 · 1 certs · `hpcdb18212/gas | solid brick | (another dwelling`
|
||||
### C138 · Σ|Δ| 0.8 · 1 certs · `hpcdb18212/gas | solid brick | (another dwelling`
|
||||
- [ ] `100020982570` · L 72 / E 71.17 / Δ -0.83
|
||||
|
||||
### C144 · Σ|Δ| 0.8 · 1 certs · `hpcdb16888/gas | cavity wall | pitched 350 | consv2`
|
||||
### C139 · Σ|Δ| 0.8 · 1 certs · `h191/elec | solid brick | (another dwelling | ext2,whw909`
|
||||
- [ ] `15129162` · L 62 / E 61.17 / Δ -0.83
|
||||
|
||||
### C140 · Σ|Δ| 0.8 · 1 certs · `hpcdb16888/gas | cavity wall | pitched 350 | consv2`
|
||||
- [ ] `394024487` · L 75 / E 74.17 / Δ -0.83
|
||||
|
||||
### C145 · Σ|Δ| 0.8 · 1 certs · `hpcdb17503/gas | solid brick | pitched insulated | RR,ext1,mv4`
|
||||
### C141 · Σ|Δ| 0.8 · 1 certs · `hpcdb17503/gas | solid brick | pitched insulated | RR,ext1,mv4`
|
||||
- [ ] `217070698` · L 78 / E 78.83 / Δ +0.83
|
||||
|
||||
### C146 · Σ|Δ| 0.8 · 1 certs · `hpcdb15709/gas | cavity wall | (another dwelling | ext1`
|
||||
### C142 · Σ|Δ| 0.8 · 1 certs · `hpcdb15709/gas | cavity wall | (another dwelling | ext1`
|
||||
- [ ] `77022508` · L 69 / E 68.18 / Δ -0.82
|
||||
|
||||
### C147 · Σ|Δ| 0.8 · 1 certs · `hpcdb18496/gas | cavity wall | pitched 250 | RR,consv4`
|
||||
### C143 · Σ|Δ| 0.8 · 1 certs · `hpcdb18496/gas | cavity wall | pitched 250 | RR,consv4`
|
||||
- [ ] `100040259104` · L 68 / E 68.82 / Δ +0.82
|
||||
|
||||
### C148 · Σ|Δ| 0.8 · 1 certs · `hpcdb18559/gas | solid brick | pitched 270 | ext1`
|
||||
### C144 · Σ|Δ| 0.8 · 1 certs · `hpcdb18559/gas | solid brick | pitched 270 | ext1`
|
||||
- [ ] `100031399026` · L 70 / E 70.81 / Δ +0.81
|
||||
|
||||
### C149 · Σ|Δ| 0.8 · 1 certs · `hpcdb16374/gas | cavity wall | pitched unknown`
|
||||
### C145 · Σ|Δ| 0.8 · 1 certs · `hpcdb16374/gas | cavity wall | pitched unknown`
|
||||
- [ ] `10009645698` · L 67 / E 67.81 / Δ +0.81
|
||||
|
||||
### C150 · Σ|Δ| 0.8 · 1 certs · `hpcdb16395/gas | timber frame | thatched with | ext1,sec:room heaters`
|
||||
### C146 · Σ|Δ| 0.8 · 1 certs · `hpcdb16395/gas | timber frame | thatched with | ext1,sec:room heaters`
|
||||
- [ ] `100080818887` · L 56 / E 55.19 / Δ -0.81
|
||||
|
||||
### C151 · Σ|Δ| 0.8 · 1 certs · `hpcdb16835/gas | solid brick | pitched insulated | RR,ext1`
|
||||
### C147 · Σ|Δ| 0.8 · 1 certs · `hpcdb16835/gas | solid brick | pitched insulated | RR,ext1`
|
||||
- [ ] `100012374381` · L 69 / E 69.80 / Δ +0.80
|
||||
|
||||
### C152 · Σ|Δ| 0.8 · 1 certs · `hpcdb17560/gas | cavity wall | pitched 250 | ext2`
|
||||
### C148 · Σ|Δ| 0.8 · 1 certs · `hpcdb17560/gas | cavity wall | pitched 250 | ext2`
|
||||
- [ ] `100012350270` · L 78 / E 77.20 / Δ -0.80
|
||||
|
||||
### C153 · Σ|Δ| 0.8 · 1 certs · `hpcdb15166/gas+2mains | sandstone as | pitched no | PV,ext4,sec:room heaters,whw914`
|
||||
### C149 · Σ|Δ| 0.8 · 1 certs · `hpcdb15166/gas+2mains | sandstone as | pitched no | PV,ext4,sec:room heaters,whw914`
|
||||
- [ ] `100051109723` · L 60 / E 60.78 / Δ +0.78
|
||||
|
||||
### C154 · Σ|Δ| 0.8 · 1 certs · `hpcdb18911/gas | cavity wall | pitched 50 | consv4`
|
||||
### C150 · Σ|Δ| 0.8 · 1 certs · `hpcdb18911/gas | cavity wall | pitched 50 | consv4`
|
||||
- [ ] `100061616915` · L 63 / E 62.22 / Δ -0.78
|
||||
|
||||
### C155 · Σ|Δ| 0.8 · 1 certs · `h691/elec | cavity wall | pitched 200 | whw903`
|
||||
### C151 · Σ|Δ| 0.8 · 1 certs · `h691/elec | cavity wall | pitched 200 | whw903`
|
||||
- [ ] `line71` · L 74 / E 74.77 / Δ +0.77
|
||||
|
||||
### C156 · Σ|Δ| 0.8 · 1 certs · `hpcdb17986/gas | cavity wall | (another dwelling`
|
||||
### C152 · Σ|Δ| 0.8 · 1 certs · `hpcdb17986/gas | cavity wall | (another dwelling`
|
||||
- [ ] `100011603934` · L 72 / E 71.23 / Δ -0.77
|
||||
|
||||
### C157 · Σ|Δ| 0.8 · 1 certs · `hpcdb10244/gas | cavity wall | pitched 100`
|
||||
### C153 · Σ|Δ| 0.8 · 1 certs · `hpcdb10244/gas | cavity wall | pitched 100`
|
||||
- [ ] `24030987` · L 73 / E 73.76 / Δ +0.76
|
||||
|
||||
### C158 · Σ|Δ| 0.8 · 1 certs · `h193/elec | solid brick | (another dwelling | whw909`
|
||||
### C154 · Σ|Δ| 0.8 · 1 certs · `h193/elec | solid brick | (another dwelling | whw909`
|
||||
- [ ] `100032043038` · L 74 / E 73.24 / Δ -0.76
|
||||
|
||||
### C159 · Σ|Δ| 0.8 · 1 certs · `hpcdb17513/gas | cavity wall | pitched unknown`
|
||||
### C155 · Σ|Δ| 0.8 · 1 certs · `hpcdb17513/gas | cavity wall | pitched unknown`
|
||||
- [ ] `100010866608` · L 71 / E 70.24 / Δ -0.76
|
||||
|
||||
### C160 · Σ|Δ| 0.7 · 1 certs · `hpcdb17965/gas | cavity wall | pitched insulated | RR,ext1,sec:room heaters`
|
||||
### C156 · Σ|Δ| 0.7 · 1 certs · `hpcdb17965/gas | cavity wall | pitched insulated | RR,ext1,sec:room heaters`
|
||||
- [ ] `100090399615` · L 67 / E 67.75 / Δ +0.75
|
||||
|
||||
### C161 · Σ|Δ| 0.7 · 1 certs · `hpcdb16137/gas | solid brick | pitched 150`
|
||||
### C157 · Σ|Δ| 0.7 · 1 certs · `hpcdb16137/gas | solid brick | pitched 150`
|
||||
- [ ] `200004296092` · L 71 / E 71.74 / Δ +0.74
|
||||
|
||||
### C162 · Σ|Δ| 0.7 · 1 certs · `hpcdb17576/gas | cavity wall | pitched insulated | RR,consv2`
|
||||
### C158 · Σ|Δ| 0.7 · 1 certs · `hpcdb17576/gas | cavity wall | pitched insulated | RR,consv2`
|
||||
- [ ] `10012341135` · L 79 / E 79.73 / Δ +0.73
|
||||
|
||||
### C163 · Σ|Δ| 0.7 · 1 certs · `h409/elec | cavity wall | (another dwelling | sec:portable electric,whw903`
|
||||
### C159 · Σ|Δ| 0.7 · 1 certs · `h409/elec | cavity wall | (another dwelling | sec:portable electric,whw903`
|
||||
- [ ] `100091005853` · L 70 / E 69.27 / Δ -0.73
|
||||
|
||||
### C164 · Σ|Δ| 0.7 · 1 certs · `hpcdb18908/gas | cavity wall | pitched 175`
|
||||
### C160 · Σ|Δ| 0.7 · 1 certs · `hpcdb18908/gas | cavity wall | pitched 175`
|
||||
- [ ] `100021533514` · L 71 / E 71.73 / Δ +0.73
|
||||
|
||||
### C165 · Σ|Δ| 0.7 · 1 certs · `h301/coal-anth | solid brick | (another dwelling`
|
||||
### C161 · Σ|Δ| 0.7 · 1 certs · `h301/coal-anth | solid brick | (another dwelling`
|
||||
- [ ] `5062075` · L 69 / E 69.72 / Δ +0.72
|
||||
|
||||
### C166 · Σ|Δ| 0.7 · 1 certs · `hpcdb18112/gas | cavity wall | pitched 100 | RR,ext1`
|
||||
### C162 · Σ|Δ| 0.7 · 1 certs · `hpcdb18112/gas | cavity wall | pitched 100 | RR,ext1`
|
||||
- [ ] `100040813308` · L 62 / E 62.72 / Δ +0.72
|
||||
|
||||
### C167 · Σ|Δ| 0.7 · 1 certs · `h104/gas | solid brick | pitched unknown | ext2`
|
||||
- [ ] `38105600` · L 68 / E 67.28 / Δ -0.72
|
||||
|
||||
### C168 · Σ|Δ| 0.7 · 1 certs · `h104/gas | solid brick | pitched 200`
|
||||
- [ ] `line255` · L 72 / E 71.29 / Δ -0.71
|
||||
|
||||
### C169 · Σ|Δ| 0.7 · 1 certs · `h304/elec | cavity wall | (another dwelling | whw950`
|
||||
### C163 · Σ|Δ| 0.7 · 1 certs · `h304/elec | cavity wall | (another dwelling | whw950`
|
||||
- [ ] `5300047580` · L 69 / E 68.30 / Δ -0.70
|
||||
|
||||
### C170 · Σ|Δ| 0.7 · 1 certs · `h104/gas | cavity wall | pitched 250 | ext1`
|
||||
### C164 · Σ|Δ| 0.7 · 1 certs · `h104/gas | cavity wall | pitched 250 | ext1`
|
||||
- [ ] `100110433403` · L 69 / E 68.32 / Δ -0.68
|
||||
|
||||
### C171 · Σ|Δ| 0.7 · 1 certs · `hpcdb16835/gas | cavity wall | pitched insulated`
|
||||
### C165 · Σ|Δ| 0.7 · 1 certs · `hpcdb16835/gas | cavity wall | pitched insulated`
|
||||
- [ ] `10093416558` · L 79 / E 78.32 / Δ -0.68
|
||||
|
||||
### C172 · Σ|Δ| 0.7 · 1 certs · `hpcdb18251/gas | solid brick | pitched 100`
|
||||
### C166 · Σ|Δ| 0.7 · 1 certs · `hpcdb18251/gas | solid brick | pitched 100`
|
||||
- [ ] `100050958217` · L 68 / E 68.68 / Δ +0.68
|
||||
|
||||
### C173 · Σ|Δ| 0.7 · 1 certs · `hpcdb18224/gas | solid brick | pitched 225 | ext1`
|
||||
### C167 · Σ|Δ| 0.7 · 1 certs · `hpcdb18224/gas | solid brick | pitched 225 | ext1`
|
||||
- [ ] `100022667797` · L 68 / E 67.32 / Δ -0.68
|
||||
|
||||
### C174 · Σ|Δ| 0.7 · 1 certs · `hpcdb10243/gas | cavity wall | pitched insulated | RR,sec:room heaters`
|
||||
### C168 · Σ|Δ| 0.7 · 1 certs · `hpcdb10243/gas | cavity wall | pitched insulated | RR,sec:room heaters`
|
||||
- [ ] `100030528925` · L 78 / E 78.65 / Δ +0.65
|
||||
|
||||
### C175 · Σ|Δ| 0.6 · 1 certs · `hpcdb17657/gas | cavity wall | (another dwelling`
|
||||
### C169 · Σ|Δ| 0.6 · 1 certs · `hpcdb17657/gas | cavity wall | (another dwelling`
|
||||
- [ ] `100110300037` · L 74 / E 73.35 / Δ -0.65
|
||||
|
||||
### C176 · Σ|Δ| 0.6 · 1 certs · `hpcdb2044/gas | solid brick | (another dwelling | ext1`
|
||||
### C170 · Σ|Δ| 0.6 · 1 certs · `hpcdb2044/gas | solid brick | (another dwelling | ext1`
|
||||
- [ ] `217092450` · L 52 / E 52.64 / Δ +0.64
|
||||
|
||||
### C177 · Σ|Δ| 0.6 · 1 certs · `hpcdb15101/gas | solid brick | pitched 300`
|
||||
- [ ] `100061164545` · L 72 / E 71.36 / Δ -0.64
|
||||
|
||||
### C178 · Σ|Δ| 0.6 · 1 certs · `hpcdb10328/gas | cavity wall | pitched unknown`
|
||||
### C171 · Σ|Δ| 0.6 · 1 certs · `hpcdb10328/gas | cavity wall | pitched unknown`
|
||||
- [ ] `100051075936` · L 71 / E 70.36 / Δ -0.64
|
||||
|
||||
### C179 · Σ|Δ| 0.6 · 1 certs · `hpcdb17507/gas | cavity wall | roof room(s) | RR,sec:room heaters`
|
||||
### C172 · Σ|Δ| 0.6 · 1 certs · `hpcdb17507/gas | cavity wall | roof room(s) | RR,sec:room heaters`
|
||||
- [ ] `100011565830` · L 72 / E 72.64 / Δ +0.64
|
||||
|
||||
### C180 · Σ|Δ| 0.6 · 1 certs · `hpcdb15700/gas | sandstone with | pitched 100 | RR,sec:room heaters`
|
||||
### C173 · Σ|Δ| 0.6 · 1 certs · `hpcdb15700/gas | sandstone with | pitched 100 | RR,sec:room heaters`
|
||||
- [ ] `61010956` · L 75 / E 74.37 / Δ -0.63
|
||||
|
||||
### C181 · Σ|Δ| 0.6 · 1 certs · `hpcdb17115/gas+2mains | cavity wall | pitched insulated | RR,ext4,sec:room heaters`
|
||||
### C174 · Σ|Δ| 0.6 · 1 certs · `hpcdb17115/gas+2mains | cavity wall | pitched insulated | RR,ext4,sec:room heaters`
|
||||
- [ ] `100110752514` · L 67 / E 67.62 / Δ +0.62
|
||||
|
||||
### C182 · Σ|Δ| 0.6 · 1 certs · `h404/elec | cavity wall | pitched 175 | sec:room heaters,whw909`
|
||||
### C175 · Σ|Δ| 0.6 · 1 certs · `h404/elec | cavity wall | pitched 175 | sec:room heaters,whw909`
|
||||
- [ ] `100070295212` · L 75 / E 74.38 / Δ -0.62
|
||||
|
||||
### C183 · Σ|Δ| 0.6 · 1 certs · `hpcdb18766/gas+2mains | sandstone as | pitched 100 | RR`
|
||||
### C176 · Σ|Δ| 0.6 · 1 certs · `hpcdb18766/gas+2mains | sandstone as | pitched 100 | RR`
|
||||
- [ ] `100012534027` · L 65 / E 64.38 / Δ -0.62
|
||||
|
||||
### C184 · Σ|Δ| 0.6 · 1 certs · `hpcdb9058/fuel28 | solid brick | pitched 100 | RR,ext2,sec:room heaters`
|
||||
### C177 · Σ|Δ| 0.6 · 1 certs · `hpcdb9058/fuel28 | solid brick | pitched 100 | RR,ext2,sec:room heaters`
|
||||
- [x] `10009812472` · L 40 / E 39.40 / Δ -0.60
|
||||
|
||||
### C185 · Σ|Δ| 0.6 · 1 certs · `hpcdb18218/gas | solid brick | (another dwelling`
|
||||
- [ ] `34080035` · L 77 / E 76.41 / Δ -0.59
|
||||
|
||||
### C186 · Σ|Δ| 0.6 · 1 certs · `hpcdb16137/gas | cavity wall | pitched 150 | RR,ext1`
|
||||
### C178 · Σ|Δ| 0.6 · 1 certs · `hpcdb16137/gas | cavity wall | pitched 150 | RR,ext1`
|
||||
- [ ] `21032732` · L 69 / E 69.59 / Δ +0.59
|
||||
|
||||
### C187 · Σ|Δ| 0.6 · 1 certs · `h101/gas | cavity wall | pitched 100 | RR,ext1`
|
||||
### C179 · Σ|Δ| 0.6 · 1 certs · `h101/gas | cavity wall | pitched 100 | RR,ext1`
|
||||
- [ ] `100060562056` · L 66 / E 65.41 / Δ -0.59
|
||||
|
||||
### C188 · Σ|Δ| 0.6 · 1 certs · `hpcdb17514/fuel27 | cavity wall | pitched 400+ | PV,ext2`
|
||||
### C180 · Σ|Δ| 0.6 · 1 certs · `hpcdb17514/fuel27 | cavity wall | pitched 400+ | PV,ext2`
|
||||
- [ ] `100091086558` · L 79 / E 79.58 / Δ +0.58
|
||||
|
||||
### C189 · Σ|Δ| 0.6 · 1 certs · `hpcdb18908/gas | solid brick | (another dwelling`
|
||||
### C181 · Σ|Δ| 0.6 · 1 certs · `hpcdb18908/gas | solid brick | (another dwelling`
|
||||
- [ ] `line599` · L 74 / E 73.42 / Δ -0.58
|
||||
|
||||
### C190 · Σ|Δ| 0.6 · 1 certs · `hpcdb17973/gas | cavity wall | pitched insulated`
|
||||
### C182 · Σ|Δ| 0.6 · 1 certs · `hpcdb17973/gas | cavity wall | pitched insulated`
|
||||
- [ ] `100020407741` · L 72 / E 71.42 / Δ -0.58
|
||||
|
||||
### C191 · Σ|Δ| 0.6 · 1 certs · `hpcdb19007/gas | cavity wall | pitched 200 | consv2,ext1,sec:room heaters`
|
||||
### C183 · Σ|Δ| 0.6 · 1 certs · `hpcdb19007/gas | cavity wall | pitched 200 | consv2,ext1,sec:room heaters`
|
||||
- [ ] `100050839457` · L 70 / E 70.57 / Δ +0.57
|
||||
|
||||
### C192 · Σ|Δ| 0.6 · 1 certs · `hpcdb19007/gas | solid brick | pitched insulated | RR,sec:room heaters`
|
||||
### C184 · Σ|Δ| 0.6 · 1 certs · `hpcdb19007/gas | solid brick | pitched insulated | RR,sec:room heaters`
|
||||
- [ ] `42105250` · L 59 / E 59.57 / Δ +0.57
|
||||
|
||||
### C193 · Σ|Δ| 0.6 · 1 certs · `hpcdb1917/gas | timber frame | pitched insulated`
|
||||
### C185 · Σ|Δ| 0.6 · 1 certs · `hpcdb1917/gas | timber frame | pitched insulated`
|
||||
- [ ] `77200509` · L 69 / E 69.57 / Δ +0.57
|
||||
|
||||
### C194 · Σ|Δ| 0.6 · 1 certs · `hpcdb10356/gas | cavity wall | pitched insulated | ext2`
|
||||
### C186 · Σ|Δ| 0.6 · 1 certs · `hpcdb10356/gas | cavity wall | pitched insulated | ext2`
|
||||
- [ ] `200040534` · L 65 / E 65.56 / Δ +0.56
|
||||
|
||||
### C195 · Σ|Δ| 0.6 · 1 certs · `hpcdb17507/gas | solid brick | pitched 150 | ext1,mv2`
|
||||
### C187 · Σ|Δ| 0.6 · 1 certs · `hpcdb17507/gas | solid brick | pitched 150 | ext1,mv2`
|
||||
- [ ] `200049463` · L 69 / E 69.56 / Δ +0.56
|
||||
|
||||
### C196 · Σ|Δ| 0.6 · 1 certs · `h126/fuel28+2mains | cavity wall | pitched 300 | RR,consv2,ext3,sec:room heaters`
|
||||
### C188 · Σ|Δ| 0.6 · 1 certs · `h126/fuel28+2mains | cavity wall | pitched 300 | RR,consv2,ext3,sec:room heaters`
|
||||
- [ ] `100062527568` · L 61 / E 61.56 / Δ +0.56
|
||||
|
||||
### C197 · Σ|Δ| 0.6 · 1 certs · `hpcdb15700/gas | cavity wall | pitched limited | RR,ext1`
|
||||
### C189 · Σ|Δ| 0.6 · 1 certs · `hpcdb15700/gas | cavity wall | pitched limited | RR,ext1`
|
||||
- [ ] `100100268900` · L 69 / E 69.56 / Δ +0.56
|
||||
|
||||
### C198 · Σ|Δ| 0.6 · 1 certs · `hpcdb17004/fuel27+2mains | sandstone as | pitched no | ext3,sec:room heaters`
|
||||
### C190 · Σ|Δ| 0.6 · 1 certs · `hpcdb17004/fuel27+2mains | sandstone as | pitched no | ext3,sec:room heaters`
|
||||
- [x] `200003110548` · L 29 / E 28.45 / Δ -0.55
|
||||
|
||||
### C199 · Σ|Δ| 0.5 · 1 certs · `hpcdb9464/gas | cavity wall | pitched 150 | ext1`
|
||||
### C191 · Σ|Δ| 0.5 · 1 certs · `hpcdb9464/gas | cavity wall | pitched 150 | ext1`
|
||||
- [ ] `90036914` · L 60 / E 60.55 / Δ +0.55
|
||||
|
||||
### C200 · Σ|Δ| 0.5 · 1 certs · `h409/elec | cavity wall | pitched 250 | mv5,sec:room heaters,whw903`
|
||||
### C192 · Σ|Δ| 0.5 · 1 certs · `h409/elec | cavity wall | pitched 250 | mv5,sec:room heaters,whw903`
|
||||
- [ ] `10004742671` · L 70 / E 69.46 / Δ -0.54
|
||||
|
||||
### C201 · Σ|Δ| 0.5 · 1 certs · `hpcdb18251/gas | cavity wall | roof room(s) | RR`
|
||||
### C193 · Σ|Δ| 0.5 · 1 certs · `hpcdb18251/gas | cavity wall | roof room(s) | RR`
|
||||
- [ ] `100010657450` · L 75 / E 75.54 / Δ +0.54
|
||||
|
||||
### C202 · Σ|Δ| 0.5 · 1 certs · `hpcdb17507/gas | cavity wall | pitched 150 | ext1`
|
||||
### C194 · Σ|Δ| 0.5 · 1 certs · `hpcdb17507/gas | cavity wall | pitched 150 | ext1`
|
||||
- [ ] `100061834344` · L 71 / E 70.46 / Δ -0.54
|
||||
|
||||
### C203 · Σ|Δ| 0.5 · 1 certs · `h113/gas | cavity wall | pitched 200`
|
||||
### C195 · Σ|Δ| 0.5 · 1 certs · `h113/gas | cavity wall | pitched 200`
|
||||
- [ ] `4510097194` · L 70 / E 70.53 / Δ +0.53
|
||||
|
||||
### C204 · Σ|Δ| 0.5 · 1 certs · `hpcdb18907/gas | cavity wall | pitched 125 | ext1`
|
||||
### C196 · Σ|Δ| 0.5 · 1 certs · `hpcdb18907/gas | cavity wall | pitched 125 | ext1`
|
||||
- [ ] `100010913220` · L 72 / E 72.53 / Δ +0.53
|
||||
|
||||
### C205 · Σ|Δ| 0.5 · 1 certs · `hpcdb18908/gas | solid brick | pitched 300`
|
||||
### C197 · Σ|Δ| 0.5 · 1 certs · `hpcdb18908/gas | solid brick | pitched 300`
|
||||
- [ ] `207032630` · L 74 / E 73.47 / Δ -0.53
|
||||
|
||||
### C206 · Σ|Δ| 0.5 · 1 certs · `hpcdb18242/gas | sandstone as | pitched 150 | ext2,sec:room heaters`
|
||||
### C198 · Σ|Δ| 0.5 · 1 certs · `hpcdb18242/gas | sandstone as | pitched 150 | ext2,sec:room heaters`
|
||||
- [ ] `43074051` · L 63 / E 62.48 / Δ -0.52
|
||||
|
||||
### C207 · Σ|Δ| 0.5 · 1 certs · `h104/gas | cavity wall | pitched 300`
|
||||
### C199 · Σ|Δ| 0.5 · 1 certs · `h104/gas | cavity wall | pitched 300`
|
||||
- [ ] `100010723814` · L 69 / E 69.52 / Δ +0.52
|
||||
|
||||
### C208 · Σ|Δ| 0.5 · 1 certs · `hpcdb16137/gas | cavity wall | (another dwelling`
|
||||
### C200 · Σ|Δ| 0.5 · 1 certs · `hpcdb16137/gas | cavity wall | (another dwelling`
|
||||
- [ ] `100020955792` · L 77 / E 77.52 / Δ +0.52
|
||||
|
||||
### C209 · Σ|Δ| 0.5 · 1 certs · `hpcdb19079/gas | cavity wall | pitched 200 | ext1,sec:room heaters`
|
||||
### C201 · Σ|Δ| 0.5 · 1 certs · `hpcdb19079/gas | cavity wall | pitched 200 | ext1,sec:room heaters`
|
||||
- [ ] `77172223` · L 65 / E 65.52 / Δ +0.52
|
||||
|
||||
### C210 · Σ|Δ| 0.5 · 1 certs · `hpcdb10242/gas | granite or | pitched insulated | RR,sec:room heaters`
|
||||
### C202 · Σ|Δ| 0.5 · 1 certs · `hpcdb10242/gas | granite or | pitched insulated | RR,sec:room heaters`
|
||||
- [ ] `100051245439` · L 62 / E 61.48 / Δ -0.52
|
||||
|
||||
### C211 · Σ|Δ| 0.5 · 1 certs · `hpcdb10328/gas | cavity wall | (another dwelling`
|
||||
### C203 · Σ|Δ| 0.5 · 1 certs · `hpcdb10328/gas | cavity wall | (another dwelling`
|
||||
- [ ] `100012553958` · L 67 / E 66.48 / Δ -0.52
|
||||
|
||||
### C212 · Σ|Δ| 0.5 · 1 certs · `hpcdb10033/gas | cavity wall | pitched 200 | ext2,sec:room heaters`
|
||||
### C204 · Σ|Δ| 0.5 · 1 certs · `hpcdb10033/gas | cavity wall | pitched 200 | ext2,sec:room heaters`
|
||||
- [ ] `100110108632` · L 63 / E 63.51 / Δ +0.51
|
||||
|
||||
### C213 · Σ|Δ| 0.5 · 1 certs · `h109/gas | cavity wall | pitched 270 | consv2,ext1,sec:room heaters`
|
||||
### C205 · Σ|Δ| 0.5 · 1 certs · `h109/gas | cavity wall | pitched 270 | consv2,ext1,sec:room heaters`
|
||||
- [ ] `100091102581` · L 65 / E 65.51 / Δ +0.51
|
||||
|
||||
### C214 · Σ|Δ| 0.5 · 1 certs · `hpcdb19008/gas | cavity wall | pitched 150 | ext1,sec:room heaters`
|
||||
### C206 · Σ|Δ| 0.5 · 1 certs · `hpcdb19008/gas | cavity wall | pitched 150 | ext1,sec:room heaters`
|
||||
- [ ] `10002326688` · L 69 / E 68.50 / Δ -0.50
|
||||
|
||||
### C215 · Σ|Δ| 0.5 · 1 certs · `h129/fuel28 | granite or | pitched 200 | ext2,sec:room heaters`
|
||||
### C207 · Σ|Δ| 0.5 · 1 certs · `h129/fuel28 | granite or | pitched 200 | ext2,sec:room heaters`
|
||||
- [ ] `10023132869` · L 46 / E 45.50 / Δ -0.50
|
||||
|
||||
### C216 · Σ|Δ| 0.5 · 1 certs · `hpcdb9007/fuel28 | sandstone as | pitched insulated | RR,ext4,sec:room heaters`
|
||||
### C208 · Σ|Δ| 0.5 · 1 certs · `hpcdb9007/fuel28 | sandstone as | pitched insulated | RR,ext4,sec:room heaters`
|
||||
- [ ] `10091707271` · L 41 / E 41.50 / Δ +0.50
|
||||
|
||||
## Resolved ≤0.5 (778 certs — no action)
|
||||
## Resolved ≤0.5 (786 certs — no action)
|
||||
|
||||
<details><summary>expand</summary>
|
||||
|
||||
|
|
@ -698,7 +674,6 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `100100840201` · L 60 / E 60.48 / Δ +0.48 · `hpcdb10244/gas | granite or | pitched 100 | ext1`
|
||||
- [x] `100071135609` · L 68 / E 68.48 / Δ +0.48 · `hpcdb18229/gas | solid brick | pitched 200 | ext2`
|
||||
- [x] `100121043224` · L 77 / E 77.48 / Δ +0.48 · `hpcdb1758/gas | cavity wall | pitched 300`
|
||||
- [x] `30025401` · L 70 / E 69.52 / Δ -0.48 · `hpcdb18433/gas | cavity wall | pitched 270 | ext2`
|
||||
- [x] `100021037247` · L 71 / E 70.52 / Δ -0.48 · `hpcdb10112/gas | solid brick | (another dwelling`
|
||||
- [x] `10013778285` · L 80 / E 79.52 / Δ -0.48 · `hpcdb16138/gas | cavity wall | (another dwelling`
|
||||
- [x] `100010348502` · L 76 / E 75.52 / Δ -0.48 · `hpcdb16839/gas | cavity wall | pitched 175`
|
||||
|
|
@ -732,7 +707,6 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `1775121297` · L 79 / E 78.55 / Δ -0.45 · `hpcdb16400/gas | cavity wall | pitched 300 | PV`
|
||||
- [x] `100070304824` · L 76 / E 76.45 / Δ +0.45 · `hpcdb18119/gas | cavity wall | pitched insulated`
|
||||
- [x] `41229335` · L 76 / E 75.55 / Δ -0.45 · `hpcdb16839/gas | cavity wall | pitched 200`
|
||||
- [x] `200000571972` · L 64 / E 63.55 / Δ -0.45 · `hpcdb17986/gas | solid brick | pitched 250 | ext1`
|
||||
- [x] `600039` · L 71 / E 70.55 / Δ -0.45 · `h104/gas | cavity wall | pitched 200`
|
||||
- [x] `100050635265` · L 68 / E 67.55 / Δ -0.45 · `hpcdb17956/gas | cavity wall | pitched 250 | sec:room heaters`
|
||||
- [x] `100040685184` · L 59 / E 59.45 / Δ +0.45 · `hpcdb18432/gas | cavity wall | pitched 300 | sec:room heaters`
|
||||
|
|
@ -801,6 +775,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `10070086972` · L 18 / E 17.59 / Δ -0.41 · `hpcdb710/fuel28+2mains | sandstone as | pitched insulated | RR,ext4,sec:room heaters`
|
||||
- [x] `100050397944` · L 65 / E 64.59 / Δ -0.41 · `hpcdb17681/gas | cavity wall | pitched 250 | ext1,sec:room heaters`
|
||||
- [x] `63105357` · L 74 / E 74.41 / Δ +0.41 · `hpcdb17507/gas | cavity wall | pitched 270 | sec:room heaters`
|
||||
- [x] `217091901` · L 62 / E 61.59 / Δ -0.41 · `hpcdb18658/gas | solid brick | flat no` <- FIXED: RdSAP 10 Table 13 200/280mm solid-brick boundary bug (was 60.82); also fixed this cert's build script (never set wall thickness, inherited a stale value)
|
||||
- [x] `100090989765` · L 70 / E 70.41 / Δ +0.41 · `hpcdb16408/gas | timber frame | pitched 300 | consv3,ext1,sec:room heaters`
|
||||
- [x] `30523770` · L 72 / E 71.60 / Δ -0.40 · `hpcdb8191/fuel28 | granite or | pitched 200 | sec:room heaters`
|
||||
- [x] `200004700463` · L 65 / E 65.40 / Δ +0.40 · `hpcdb18559/gas | sandstone as | pitched 75`
|
||||
|
|
@ -838,6 +813,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `77069148` · L 71 / E 71.39 / Δ +0.39 · `hpcdb17507/gas | cavity wall | pitched 200`
|
||||
- [x] `148002976` · L 72 / E 71.61 / Δ -0.39 · `hpcdb16840/gas | cavity wall | pitched insulated | ext1`
|
||||
- [x] `10096536318` · L 66 / E 65.62 / Δ -0.38 · `h301/coal-anth | cavity wall | (another dwelling`
|
||||
- [x] `100061164545` · L 72 / E 72.38 / Δ +0.38 · `hpcdb15101/gas | solid brick | pitched 300`
|
||||
- [x] `100030585620` · L 59 / E 59.38 / Δ +0.38 · `h102/gas | cavity wall | pitched insulated | ext4,sec:room heaters`
|
||||
- [x] `10008325030` · L 82 / E 81.62 / Δ -0.38 · `h301/coal-anth | system built | (another dwelling`
|
||||
- [x] `100050869353` · L 75 / E 74.62 / Δ -0.38 · `hpcdb10547/gas | solid brick | pitched 300`
|
||||
|
|
@ -870,6 +846,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `200033141` · L 76 / E 75.64 / Δ -0.36 · `hpcdb16738/gas | cavity wall | flat insulated`
|
||||
- [x] `100030357741` · L 72 / E 72.36 / Δ +0.36 · `hpcdb18560/gas | cavity wall | pitched 300 | consv2,ext1`
|
||||
- [x] `100080707176` · L 72 / E 71.64 / Δ -0.36 · `hpcdb10243/gas | cavity wall | pitched 100`
|
||||
- [x] `30025401` · L 70 / E 69.64 / Δ -0.36 · `hpcdb18433/gas | cavity wall | pitched 270 | ext2`
|
||||
- [x] `100062132377` · L 81 / E 80.64 / Δ -0.36 · `h214/elec | cavity wall | (another dwelling`
|
||||
- [x] `100062132384` · L 81 / E 80.64 / Δ -0.36 · `h214/elec | cavity wall | (another dwelling`
|
||||
- [x] `100031679582` · L 66 / E 66.36 / Δ +0.36 · `hpcdb10243/gas | cavity wall | pitched insulated`
|
||||
|
|
@ -952,6 +929,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `10008275334` · L 76 / E 76.31 / Δ +0.31 · `h691/elec | system built | (another dwelling | whw903`
|
||||
- [x] `100110174472` · L 74 / E 74.31 / Δ +0.31 · `hpcdb8368/gas | solid brick | pitched 175 | ext2,mv5`
|
||||
- [x] `100071450760` · L 55 / E 54.69 / Δ -0.31 · `h691/elec | cavity wall | (another dwelling | whw903`
|
||||
- [x] `line255` · L 72 / E 72.31 / Δ +0.31 · `h104/gas | solid brick | pitched 200`
|
||||
- [x] `207096040` · L 69 / E 69.31 / Δ +0.31 · `hpcdb18219/gas | solid brick | pitched insulated | ext1`
|
||||
- [x] `200004695006` · L 69 / E 69.31 / Δ +0.31 · `hpcdb10243/gas | sandstone as | pitched insulated`
|
||||
- [x] `100100800577` · L 64 / E 64.31 / Δ +0.31 · `h301/coal-anth | system built | pitched 200 | whw903`
|
||||
|
|
@ -1037,6 +1015,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `50011586` · L 69 / E 68.74 / Δ -0.26 · `hpcdb10328/gas | cavity wall | pitched 225 | consv2`
|
||||
- [x] `100010680786` · L 73 / E 73.26 / Δ +0.26 · `hpcdb18907/gas | granite or | pitched 400+ | ext1`
|
||||
- [x] `422000127372` · L 79 / E 78.75 / Δ -0.25 · `hpcdb17741/gas | cavity wall | pitched 400+`
|
||||
- [x] `100050477371` · L 65 / E 64.75 / Δ -0.25 · `hpcdb16406/gas | solid brick | pitched insulated | RR,consv2,ext2,sec:room heaters`
|
||||
- [x] `200000339841` · L 72 / E 72.25 / Δ +0.25 · `hpcdb18736/gas | cavity wall | (another dwelling`
|
||||
- [x] `72380835` · L 82 / E 82.25 / Δ +0.25 · `hpcdb17507/gas | cavity wall | pitched 200 | PV,sec:room heaters`
|
||||
- [x] `38193503` · L 65 / E 65.25 / Δ +0.25 · `hpcdb18907/gas | cavity wall | pitched 100 | ext2,sec:room heaters`
|
||||
|
|
@ -1053,6 +1032,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `100060721835` · L 68 / E 67.76 / Δ -0.24 · `hpcdb16841/gas | system built | pitched 250`
|
||||
- [x] `100011764055` · L 72 / E 71.76 / Δ -0.24 · `hpcdb15823/gas | cavity wall | pitched 200 | ext4,sec:room heaters`
|
||||
- [x] `5067734` · L 67 / E 67.24 / Δ +0.24 · `hpcdb17726/gas | solid brick | (another dwelling`
|
||||
- [x] `38105600` · L 68 / E 67.76 / Δ -0.24 · `h104/gas | solid brick | pitched unknown | ext2`
|
||||
- [x] `100021942272` · L 69 / E 69.24 / Δ +0.24 · `hpcdb17501/gas | solid brick | pitched insulated | RR`
|
||||
- [x] `100050587694` · L 70 / E 70.24 / Δ +0.24 · `hpcdb18516/gas | cavity wall | pitched 270`
|
||||
- [x] `100062195466` · L 73 / E 72.76 / Δ -0.24 · `h104/gas | cavity wall | (another dwelling`
|
||||
|
|
@ -1095,6 +1075,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `100050841010` · L 69 / E 69.21 / Δ +0.21 · `hpcdb10546/gas | cavity wall | pitched 100 | ext1`
|
||||
- [x] `10090481366` · L 77 / E 76.79 / Δ -0.21 · `h691/elec | cavity wall | (another dwelling | whw909`
|
||||
- [x] `100070308464` · L 65 / E 65.21 / Δ +0.21 · `h104/gas | cavity wall | pitched 250 | PV`
|
||||
- [x] `100031768368` · L 61 / E 61.21 / Δ +0.21 · `hpcdb18250/gas | solid brick | pitched 100 | consv2,sec:room heaters` <- FIXED: RdSAP 10 Table 13 200/280mm solid-brick boundary bug (was 59.12) — Elmhurst-validated, see test_rdsap_uvalues.py
|
||||
- [x] `100012547948` · L 75 / E 74.79 / Δ -0.21 · `hpcdb17931/gas | cavity wall | pitched 100`
|
||||
- [x] `100061316828` · L 69 / E 69.21 / Δ +0.21 · `hpcdb18403/gas | cavity wall | pitched 250 | mv2`
|
||||
- [x] `83067520` · L 65 / E 64.79 / Δ -0.21 · `hpcdb18119/gas | cavity wall | pitched 200`
|
||||
|
|
@ -1112,6 +1093,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `100060370575` · L 70 / E 69.80 / Δ -0.20 · `hpcdb17760/gas | cavity wall | (another dwelling`
|
||||
- [x] `10022972987` · L 11 / E 11.20 / Δ +0.20 · `h691/elec | sandstone as | pitched 200 | ext1,sec:room heaters,whw921`
|
||||
- [x] `10023219663` · L 78 / E 77.80 / Δ -0.20 · `h301/coal-anth | system built | (another dwelling`
|
||||
- [x] `100021326422` · L 70 / E 69.80 / Δ -0.20 · `hpcdb17679/gas | solid brick | pitched 125 | ext1` <- non-electric fabric/demand: per-cert lodged-geometry noise (field_bias scan clean, no coherent subgroup)
|
||||
- [x] `100020603440` · L 74 / E 73.80 / Δ -0.20 · `h104/gas | cavity wall | pitched insulated`
|
||||
- [x] `45040195` · L 75 / E 75.19 / Δ +0.19 · `hpcdb18250/gas | cavity wall | pitched 100`
|
||||
- [x] `653958` · L 79 / E 78.81 / Δ -0.19 · `hpcdb10071/gas | cavity wall | pitched 250`
|
||||
|
|
@ -1180,6 +1162,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `10033267429` · L 57 / E 56.85 / Δ -0.15 · `h691/elec | system built | (another dwelling | whw903`
|
||||
- [x] `100090372586` · L 71 / E 71.15 / Δ +0.15 · `h102/gas | cavity wall | pitched 300 | PV,consv3`
|
||||
- [x] `100060587363` · L 62 / E 62.15 / Δ +0.15 · `hpcdb10462/gas | solid brick | pitched 250 | consv2,ext2,sec:room heaters`
|
||||
- [x] `34080035` · L 77 / E 76.85 / Δ -0.15 · `hpcdb18218/gas | solid brick | (another dwelling`
|
||||
- [x] `207037318` · L 59 / E 58.85 / Δ -0.15 · `h699/fuel0 | cavity wall | (another dwelling | sec:portable electric,whw903`
|
||||
- [x] `100051048523` · L 67 / E 67.15 / Δ +0.15 · `hpcdb17507/gas | cavity wall | pitched 250 | ext1,sec:room heaters`
|
||||
- [x] `551000` · L 72 / E 72.15 / Δ +0.15 · `h104/gas | cavity wall | pitched 150`
|
||||
|
|
@ -1196,6 +1179,7 @@ Statuses: `[ ]` todo · `[x]` resolved ≤0.5 · 🔧 fix landed · ⚠ xfail en
|
|||
- [x] `100080902546` · L 79 / E 78.85 / Δ -0.15 · `hpcdb17696/gas | solid brick | pitched insulated | RR,ext1`
|
||||
- [x] `100021286876` · L 66 / E 66.15 / Δ +0.15 · `hpcdb17503/gas | solid brick | pitched insulated`
|
||||
- [x] `100070056707` · L 72 / E 72.14 / Δ +0.14 · `hpcdb17505/gas | system built | (another dwelling`
|
||||
- [x] `200000571972` · L 64 / E 63.86 / Δ -0.14 · `hpcdb17986/gas | solid brick | pitched 250 | ext1`
|
||||
- [x] `72513535` · L 70 / E 70.14 / Δ +0.14 · `h691/elec | system built | (another dwelling | whw903`
|
||||
- [x] `100080166923` · L 68 / E 67.86 / Δ -0.14 · `hpcdb18826/gas | cavity wall | pitched 75 | consv3,ext1`
|
||||
- [x] `100023483089` · L 77 / E 76.86 / Δ -0.14 · `h409/elec | system built | (another dwelling | sec:portable electric,whw903`
|
||||
|
|
|
|||
252
scripts/hyde/build_100031768368.py
Normal file
252
scripts/hyde/build_100031768368.py
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
"""Elmhurst build for UPRN 100031768368 (RdSAP-21.0.1, END-TERRACE HOUSE, band C,
|
||||
SOLID BRICK 280 mm uninsulated, pitched 100 mm loft insulation, SOLID ground
|
||||
floor uninsulated, mains-gas COMBI (PCDB 18250, Baxi 630 Combi 88.4%) +
|
||||
gas ROOM-HEATER SECONDARY, single electric shower with WWHRS (no mixer
|
||||
shower), extract fan lodged (1) + 1 other-heater flue, 10 DG windows across
|
||||
2 orientations (~13.48 m2 total: East 5.52 m2, West 5.13+2.83 m2 across two
|
||||
glazing eras), TFA 76. Lodged SAP 61; engine 59.12 (Delta -1.88).
|
||||
|
||||
Per-cert Elmhurst validation, continuing the 1-2 SAP-band reconciliation
|
||||
pass. Uses set_window_groups (3 orientation/glazing groups, not a single
|
||||
collapsed window — see the 100061275133 finding that a single-orientation
|
||||
simplification meaningfully understates solar gain) + reset_transient_state
|
||||
at the end so the shared assessment is left neutral. Run:
|
||||
DISPLAY=:99 python scripts/hyde/build_100031768368.py <page>
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import elmhurst_lib as E
|
||||
|
||||
DIM = "TabContainer_TabPanelMain_WebUserControlDimensionsMain_"
|
||||
WALL = ("TabContainer_TabPanelMain_InnerTabContainerMain_"
|
||||
"TabPanelExternalWallMain_WebUserControlWallMain_")
|
||||
PWALL = ("TabContainer_TabPanelMain_InnerTabContainerMain_"
|
||||
"TabPanelPartyWallMain_WebUserControlPartyWallMain_")
|
||||
ROOF = "TabContainer_TabPanelMain_WebUserControlRoofMain_"
|
||||
FLOOR = "TabContainer_TabPanelMain_WebUserControlFloorsMain_"
|
||||
DP = "TabContainer_TabPanelDoorsPanel_"
|
||||
VP = "TabContainer_TabPanelVentilationPanel_"
|
||||
APT = "TabContainer_TabPanelAirPressureTest_"
|
||||
LP = "TabContainer_TabPanelLighting_"
|
||||
MV = "TabContainer_TabPanelMechVent_"
|
||||
WH = "TabContainer_TabPanelWaterHeating_"
|
||||
MH1B = "TabContainer_TabPanelMainHeating1_WebUserControlMainHeating1_"
|
||||
_SAMPLE_DIR = (Path(__file__).parent.parent.parent
|
||||
/ "backend/epc_api/json_samples/real_life_examples"
|
||||
/ "RdSAP-Schema-21.0.1/uprn_100031768368")
|
||||
|
||||
|
||||
def _pick(page, suffix, contains):
|
||||
val = page.evaluate(
|
||||
"""(a)=>{const s=document.getElementById(a[0]);if(!s)return null;
|
||||
for(const o of s.options){if(o.text.toLowerCase().includes(a[1].toLowerCase()))return o.value;}return null;}""",
|
||||
[f"{E.FP}{suffix}", contains])
|
||||
if val is not None:
|
||||
E.set_select(page, suffix, val)
|
||||
return val
|
||||
|
||||
|
||||
def property_description(page):
|
||||
E.goto(page, "PropertyDescription", "WebFormPropertyDescription.aspx")
|
||||
E.set_select(page, "DropDownListPropertyType1", "H House")
|
||||
_pick(page, "DropDownListPropertyType2", "end")
|
||||
E.set_text(page, "TextBoxStoreys", "2")
|
||||
E.set_text(page, "TextBoxHabitableRooms", "4")
|
||||
E.set_text(page, "TextBoxHeatedHabitableRooms", "4")
|
||||
print("date ->", _pick(page, "DropDownListDateBuiltMain", "1930-1949"))
|
||||
E.set_select(page, "DropDownListDateBuiltFirst", "")
|
||||
E.set_select(page, "DropDownListRoomInRoofMain", "")
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def dimensions(page):
|
||||
E.goto(page, "Dimensions", "WebFormDimensions.aspx")
|
||||
E.set_text(page, f"{DIM}TextBoxFloorAreaLowestFloor", "37.75")
|
||||
E.set_text(page, f"{DIM}TextBoxRoomHeightLowestFloor", "2.45")
|
||||
E.set_text(page, f"{DIM}TextBoxWallPerimeterLowestFloor", "20.1")
|
||||
E.set_text(page, f"{DIM}TextBoxPartyWallLengthLowestFloor", "5.0")
|
||||
E.set_text(page, f"{DIM}TextBoxFloorArea1stFloor", "37.75")
|
||||
E.set_text(page, f"{DIM}TextBoxRoomHeight1stFloor", "2.45")
|
||||
E.set_text(page, f"{DIM}TextBoxWallPerimeter1stFloor", "20.1")
|
||||
E.set_text(page, f"{DIM}TextBoxPartyWallLength1stFloor", "5.0")
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def walls(page):
|
||||
E.goto(page, "Walls", "WebFormWalls.aspx")
|
||||
_pick(page, f"{WALL}DropDownListType", "solid brick")
|
||||
page.wait_for_timeout(500)
|
||||
print("insulation ->", _pick(page, f"{WALL}DropDownListInsulation", "as built"))
|
||||
print("party ->", _pick(page, f"{PWALL}DropDownListPartyWallType", "determine")
|
||||
or _pick(page, f"{PWALL}DropDownListPartyWallType", "masonry"))
|
||||
for suf in (f"{WALL}TextBoxThickness", f"{WALL}TextBoxWallThickness"):
|
||||
if page.locator(f"#{E.FP}{suf}").count():
|
||||
E.set_text(page, suf, "280"); print("wall thickness set 280 via", suf); break
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def roofs(page):
|
||||
E.goto(page, "Roofs", "WebFormRoofs.aspx")
|
||||
_pick(page, f"{ROOF}DropDownListType", "access to loft") or \
|
||||
_pick(page, f"{ROOF}DropDownListType", "loft")
|
||||
_pick(page, f"{ROOF}DropDownListInsulationAt", "joists")
|
||||
_pick(page, f"{ROOF}DropDownListThickness", "100")
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def floors(page):
|
||||
E.goto(page, "Floors", "WebFormFloors.aspx")
|
||||
_pick(page, f"{FLOOR}DropDownListLocation", "ground floor")
|
||||
page.wait_for_timeout(400)
|
||||
_pick(page, f"{FLOOR}DropDownListType", "solid")
|
||||
ins = page.locator(f"#{E.FP}{FLOOR}DropDownListInsulation")
|
||||
if ins.count():
|
||||
E.set_select(page, f"{FLOOR}DropDownListInsulation", "A As built")
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def openings(page):
|
||||
E.goto(page, "Openings", "WebFormOpenings.aspx")
|
||||
E.click_tab(page, "TabContainer_TabPanelWindowsPanel")
|
||||
page.wait_for_timeout(500)
|
||||
# 3 orientation/glazing-era groups (not a single collapsed window):
|
||||
# East 5.52 m2 (double pre-2002), West 5.13 m2 (double pre-2002), West
|
||||
# 2.83 m2 (double 2002-2022, the one larger newer window index 8).
|
||||
# The glazing dropdown retains its last-selected value across adds; if
|
||||
# the next row wants the SAME text, selecting it again fires no 'change'
|
||||
# event (identical value -> no transition) and the row commits with a
|
||||
# blank Glazing Type. Force a transition through a different option
|
||||
# first so every add gets a genuine change event.
|
||||
for area, orient, glazing in (
|
||||
(5.5174, "East", "Double pre 2002"),
|
||||
(5.1346, "West", "Double pre 2002"),
|
||||
(2.8288, "West", "Double between 2002 and 2021"),
|
||||
):
|
||||
E.set_select(page, f"{E._WP}DropDownListExtGlazing", "Secondary glazing") # noqa: SLF001
|
||||
page.wait_for_timeout(300)
|
||||
E.add_combined_window(page, area, orient, glazing)
|
||||
page.wait_for_timeout(500)
|
||||
guard = 0
|
||||
while E.window_row_count(page) > 3 and guard < 25:
|
||||
guard += 1
|
||||
i = E.window_row_count(page) - 1
|
||||
if not E._delete_window_at_index(page, i): # noqa: SLF001
|
||||
break
|
||||
E.click_tab(page, "TabContainer_TabPanelDoorsPanel")
|
||||
E.set_text(page, f"{DP}TextBoxDoors", "1")
|
||||
E.set_text(page, f"{DP}TextBoxDoorsInsulated", "0")
|
||||
E.set_text(page, f"{DP}TextBoxDraughtProofedDoors", "1") # cert: percent_draughtproofed=100
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def ventilation(page):
|
||||
E.goto(page, "VentilationAndCooling", "WebFormVentilationAndCooling.aspx")
|
||||
E.click_tab(page, "TabContainer_TabPanelVentilationPanel")
|
||||
E.set_text(page, f"{VP}TextBoxIntermittentFans", "1") # cert: extract_fans_count=1
|
||||
E.set_text(page, f"{VP}TextBoxFluesOtherHeater", "1") # cert: other_flues_count=1
|
||||
cool = page.locator(f"#{E.FP}{VP}CheckBoxFixedSpaceCooling")
|
||||
if cool.count() and cool.is_checked():
|
||||
E.commit(page, cool.uncheck)
|
||||
E.click_tab(page, "TabContainer_TabPanelMechVent")
|
||||
mv = page.locator(f"#{E.FP}{MV}CheckBoxMechanicalVentilation")
|
||||
if mv.count() and mv.is_checked():
|
||||
E.commit(page, mv.uncheck)
|
||||
E.click_tab(page, "TabContainer_TabPanelAirPressureTest")
|
||||
E.set_select(page, f"{APT}DropDownListTestMethod", "Not available")
|
||||
E.click_tab(page, "TabContainer_TabPanelLighting")
|
||||
E.set_text(page, f"{LP}TextBoxLightsTotal", "13")
|
||||
E.set_text(page, f"{LP}TextBoxLedLightsTotal", "10")
|
||||
E.set_text(page, f"{LP}TextBoxCflLightsTotal", "2")
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def space_heating(page):
|
||||
# PCDB exact-boiler entry: cert's main_heating_index_number 18250
|
||||
# resolves to Baxi 630 Combi, 88.4% winter efficiency.
|
||||
E.goto(page, "SpaceHeating", "WebFormSpaceHeating.aspx")
|
||||
page.wait_for_timeout(1000)
|
||||
E.clear_main_heating_code(page)
|
||||
desc = E.set_pcdb_boiler(page, 18250)
|
||||
print("PCDB boiler ->", desc)
|
||||
E.set_heating_dialog(page, f"{MH1B}ButtonMainHeatingControls",
|
||||
"^Boilers", "^Standard", "CBE Programmer, room thermostat and TRVs")
|
||||
print("control:", page.locator(f"#{E.MH1}TextBoxMainHeatingControls").input_value())
|
||||
# cert lodges secondary_heating_type=605 ("Room heaters, mains gas",
|
||||
# SAP10 Table 4a eff 0.40) — a genuine secondary system, unlike
|
||||
# 100061275133's "None". The dialog is a 4-level cascade (fuel ->
|
||||
# sub-fuel -> heater class -> specific product), not 3 like main
|
||||
# heating controls. Probed all 12 RGx options by resolved SAP code;
|
||||
# "RGH Flush fitting live effect gas fire, sealed to chimney" is the
|
||||
# exact match for code 605.
|
||||
E.set_select(page, "DropDownListSecondaryHeatingPresent", "Yes")
|
||||
page.wait_for_timeout(900)
|
||||
E.set_heating_dialog(page, "ButtonSecondaryHeatingCode",
|
||||
"^Gas", "Mains gas", "Room Heaters", "RGH")
|
||||
tb = page.locator(f"#{E.FP}TextBoxSecondaryHeatingCode")
|
||||
print("secondary code:", tb.input_value() if tb.count() else "?")
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def water_heating(page):
|
||||
E.goto(page, "WaterHeating", "WebFormWaterHeating.aspx")
|
||||
E.click_tab(page, "TabContainer_TabPanelWaterHeating")
|
||||
page.wait_for_timeout(400)
|
||||
E.clear_hot_water_cylinder(page)
|
||||
E.set_heating_dialog(page, f"{WH}ButtonWaterHeatingCode",
|
||||
"From Space Heating", "From the primary heating system")
|
||||
print("water code:", page.locator(f"#{E.FP}{WH}TextBoxWaterHeatingCode").input_value())
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
def baths_showers(page):
|
||||
"""cert: number_baths=1, shower_outlets=[electric shower, WWHRS=1],
|
||||
mixer_shower_count=0 — an instantaneous ELECTRIC shower with WWHRS
|
||||
fitted, not a mixer shower off the main system."""
|
||||
E.goto(page, "WaterHeating", "WebFormWaterHeating.aspx")
|
||||
E.click_tab(page, "TabContainer_TabPanelBathsShowers")
|
||||
page.wait_for_timeout(400)
|
||||
print("(manual check needed: set 1 bath, 1 electric shower with WWHRS)")
|
||||
|
||||
|
||||
def download(page):
|
||||
_SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
E.goto(page, "Recommendations", "WebFormRecommendations.aspx")
|
||||
page.wait_for_timeout(1500)
|
||||
with page.expect_navigation(wait_until="networkidle", timeout=40000):
|
||||
page.click("#ContentBody_buttonActionSummary_Link", timeout=15000)
|
||||
page.wait_for_timeout(2000)
|
||||
for btn, fname in (("ContentBody_ContentPlaceHolder1_LinkButtonSummary", "elmhurst_summary.pdf"),
|
||||
("ContentBody_ContentPlaceHolder1_ButtonDebugInforPdf", "elmhurst_worksheet.pdf")):
|
||||
try:
|
||||
with page.expect_download(timeout=60000) as dl:
|
||||
page.click(f"#{btn}", timeout=15000)
|
||||
dl.value.save_as(str(_SAMPLE_DIR / fname))
|
||||
print("saved", fname)
|
||||
except Exception as e:
|
||||
print("!! download failed", fname, type(e).__name__, str(e)[:120])
|
||||
|
||||
|
||||
def cleanup(page):
|
||||
"""Leave the shared assessment neutral for the next build."""
|
||||
E.reset_transient_state(page)
|
||||
print("reset transient state (RR/secondary/meter)")
|
||||
|
||||
|
||||
_ORDER = ["property_description", "dimensions", "walls", "roofs", "floors",
|
||||
"openings", "ventilation", "space_heating", "baths_showers",
|
||||
"water_heating", "download", "cleanup"]
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2 or sys.argv[1] not in _ORDER:
|
||||
print("usage: build_100031768368.py <" + "|".join(_ORDER) + ">")
|
||||
return 2
|
||||
with E.session() as (ctx, page):
|
||||
globals()[sys.argv[1]](page)
|
||||
print("done:", sys.argv[1], "->", page.url)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -85,6 +85,15 @@ def walls(page):
|
|||
print("insulation opts:", _options(page, f"{WALL}DropDownListInsulation"))
|
||||
_pick(page, f"{WALL}DropDownListInsulation", "as built") or \
|
||||
_pick(page, f"{WALL}DropDownListInsulation", "none")
|
||||
# cert lodges wall_thickness=280mm measured=Y — this field was never
|
||||
# set here, so the shared assessment silently carried over whatever
|
||||
# thickness an earlier build had left (260mm), masking the real
|
||||
# RdSAP 10 Table 13 200/280mm boundary bug on this cert's exact
|
||||
# thickness. See domain/sap10_ml/rdsap_uvalues.py
|
||||
# _u_brick_thin_wall_age_a_to_e's docstring.
|
||||
for suf in (f"{WALL}TextBoxThickness", f"{WALL}TextBoxWallThickness"):
|
||||
if page.locator(f"#{E.FP}{suf}").count():
|
||||
E.set_text(page, suf, "280"); print("wall thickness set 280 via", suf); break
|
||||
E.save_close(page)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -92,20 +92,66 @@ def test_electric_room_heaters_overlay_the_room_heater_charge_control() -> None:
|
|||
assert simulation.heating.main_heating_control == 2601
|
||||
|
||||
|
||||
def test_overlay_logs_when_it_cannot_complete_a_companion_set(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
def test_overlay_raises_on_an_incomplete_companion_set(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
# An archetype the overlay cannot yet fully companion — here an air-source
|
||||
# heat pump, whose Table 4e Group 2 conservative control is not yet mapped —
|
||||
# is still overlaid (log-and-continue), but the incomplete companion set is
|
||||
# logged as an error so the known gap is visible (CloudWatch) rather than
|
||||
# silently inheriting the replaced system's stale value.
|
||||
# Act
|
||||
with caplog.at_level(logging.ERROR):
|
||||
main_heating_overlay_for("Air source heat pump", 0)
|
||||
# Every real archetype resolves a complete companion set, so the
|
||||
# log-and-continue mitigation has no remaining population and the
|
||||
# ADR-0048 end-state applies: an incomplete set FAILS LOUDLY instead of
|
||||
# shipping a silently-incoherent cert (ADR-0053, #1444 Part C). Simulated
|
||||
# with a hypothetical warm-air heat-pump archetype (SAP 521 — category 4
|
||||
# but its Table 4e Group 5 control is deliberately unmapped): a future
|
||||
# archetype cannot land without choosing its companions.
|
||||
from domain.epc.property_overlays import main_heating_system_overlay as mod
|
||||
|
||||
# Assert — the gap is reported and names the archetype.
|
||||
assert "Air source heat pump" in caplog.text
|
||||
monkeypatch.setitem(mod._MAIN_HEATING_CODES, "Warm-air heat pump", 521)
|
||||
|
||||
# Act / Assert — the raise names the archetype.
|
||||
with pytest.raises(ValueError, match="Warm-air heat pump"):
|
||||
main_heating_overlay_for("Warm-air heat pump", 0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"main_heating_value",
|
||||
["Community heating, boilers", "Community heating, CHP and boilers"],
|
||||
)
|
||||
def test_community_heating_drags_the_default_group_3_control(
|
||||
main_heating_value: str,
|
||||
) -> None:
|
||||
# The landlord names the scheme, not its control or charging arrangement.
|
||||
# The Table 4e Group 3 default is 2313 ("flat rate charging, room
|
||||
# thermostat and TRVs"): community schemes skew modern and the portfolio's
|
||||
# own lodged community certs are overwhelmingly thermostatted (modal 2306;
|
||||
# none lodge 2301), so the KIT axis concedes to the evidence — while the
|
||||
# CHARGING axis stays flat-rate (space 1.05 / DHW 1.05): the billing
|
||||
# arrangement is scheme paperwork an override genuinely cannot see, so
|
||||
# usage-linked (1.0/1.0) is never assumed (ADR-0053, decided with Khalim).
|
||||
# Leaving the control unset inherits the replaced system's instead (the
|
||||
# ADR-0048 bug class).
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert — SAP Table 4e Group 3 default heat-network control.
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.main_heating_control == 2313
|
||||
|
||||
|
||||
def test_air_source_heat_pump_drags_the_conservative_group_2_control() -> None:
|
||||
# The landlord names the system, not its control, so the overlay assumes
|
||||
# the conservative Table 4e Group 2 default: 2201 ("no time or thermostatic
|
||||
# control", +0.3 C — the largest Group 2 adjustment, so an unobserved
|
||||
# control is never over-credited; the heat-pump mirror of storage 2401 /
|
||||
# boiler 2101). Deliberately NOT the ASHP Measure's 2210 zone control —
|
||||
# a measure designs a modern end-state (ADR-0024); an override describes an
|
||||
# existing pump (ADR-0053).
|
||||
# Act
|
||||
simulation = main_heating_overlay_for("Air source heat pump", 0)
|
||||
|
||||
# Assert — SAP Table 4e Group 2 conservative heat-pump control.
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.main_heating_control == 2201
|
||||
|
||||
|
||||
def test_complete_room_heater_overlay_does_not_log_a_companion_gap(
|
||||
|
|
@ -434,14 +480,70 @@ def test_storage_heaters_drag_along_conservative_manual_charge_control(
|
|||
assert simulation.heating.main_heating_category == 7
|
||||
|
||||
|
||||
def test_direct_acting_electric_does_not_drag_along_a_control() -> None:
|
||||
# Charge control is a storage concept and full boiler controls are a wet-
|
||||
# system concept; direct-acting electric panel heaters take neither, so the
|
||||
# overlay leaves the control untouched.
|
||||
simulation = main_heating_overlay_for("Direct-acting electric", 0)
|
||||
@pytest.mark.parametrize(
|
||||
"main_heating_value",
|
||||
["Direct-acting electric", "Electric boiler", "Electric CPSU"],
|
||||
)
|
||||
def test_electric_boiler_overlays_the_boiler_category(
|
||||
main_heating_value: str,
|
||||
) -> None:
|
||||
# A system-replacing override must stamp the boiler heating category (SAP
|
||||
# Table 4a Category 2 — codes 191/192 are wet electric boilers). Leaving it
|
||||
# unset inherits the replaced system's: on portfolio 796, 30 of the first
|
||||
# 40 live 191/192 overrides carried an inherited category — 6 (community)
|
||||
# or 4 (heat pump) suppressed the HHRSH pathway entirely, and 7 (storage)
|
||||
# made the Table 12a resolver bill direct-acting heat 100% at the off-peak
|
||||
# low rate (ADR-0052, #1444).
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert — SAP Table 4a Category 2 (boiler system).
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.main_heating_control is None
|
||||
assert simulation.heating.main_heating_category == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"main_heating_value",
|
||||
["Direct-acting electric", "Electric boiler", "Electric CPSU"],
|
||||
)
|
||||
def test_complete_electric_boiler_overlay_does_not_log_a_companion_gap(
|
||||
main_heating_value: str, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
# Electric boilers now resolve a full companion set (category 2 + control
|
||||
# 2101, ADR-0052), so the overlay must NOT log an incomplete-companion gap
|
||||
# — guarding that the set stays complete.
|
||||
# Act
|
||||
with caplog.at_level(logging.ERROR):
|
||||
main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert
|
||||
assert "incomplete companion set" not in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"main_heating_value",
|
||||
["Direct-acting electric", "Electric boiler", "Electric CPSU"],
|
||||
)
|
||||
def test_electric_boiler_drags_the_conservative_group_1_control(
|
||||
main_heating_value: str,
|
||||
) -> None:
|
||||
# The landlord names the system, not its control, so the overlay assumes
|
||||
# the conservative Table 4e Group 1 default: 2101 ("no time or thermostatic
|
||||
# control", +0.6 C — the largest Group 1 adjustment, so an unobserved
|
||||
# control is never over-credited; the boiler mirror of storage 2401 /
|
||||
# room-heater 2601 / underfloor 2701). Supersedes the old "direct-acting
|
||||
# drags no control" pin: an unset control is not "no control" — it INHERITS
|
||||
# the replaced system's (a community 2306 or storage 2401 rode along on
|
||||
# portfolio 796). Deliberately NOT the gas boilers' modern 2106 — nothing
|
||||
# in the electric archetype implies a modern install (ADR-0052).
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert — SAP Table 4e Group 1 conservative boiler control.
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.main_heating_control == 2101
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -545,6 +647,51 @@ def test_unresolvable_or_unmodelled_heating_produces_no_overlay(
|
|||
assert simulation is None
|
||||
|
||||
|
||||
def test_community_override_keeps_an_observed_group_3_control() -> None:
|
||||
# A community override on a cert that ALREADY lodges community heating is a
|
||||
# confirmation, not a system switch — the assessor observed the control
|
||||
# (e.g. 2306, "charging linked to use, programmer and TRVs", worth ~1-2 SAP
|
||||
# over the flat-rate default). Stamping the 2313 default over it would
|
||||
# destroy real information, so the overlay opts into keeping an existing
|
||||
# Table 4e GROUP 3 control (the heat-network mirror of
|
||||
# `keep_existing_off_peak_meter`, ADR-0053).
|
||||
# Arrange — a cert lodging community heating with an observed 2306.
|
||||
baseline = build_epc()
|
||||
main = baseline.sap_heating.main_heating_details[0]
|
||||
main.sap_main_heating_code = 301
|
||||
main.main_heating_category = 6
|
||||
main.main_heating_control = 2306
|
||||
overlay = main_heating_overlay_for("Community heating, boilers", 0)
|
||||
assert overlay is not None
|
||||
|
||||
# Act
|
||||
result = apply_simulations(baseline, [overlay])
|
||||
|
||||
# Assert — the observed usage-linked control survives.
|
||||
assert result.sap_heating.main_heating_details[0].main_heating_control == 2306
|
||||
|
||||
|
||||
def test_community_override_replaces_a_cross_family_control() -> None:
|
||||
# When the replaced system was a DIFFERENT family, its control is stale by
|
||||
# definition — a Group-1 boiler zone control (2110) has no meaning on a
|
||||
# heat network (two live portfolio-796 dwellings carried exactly that) —
|
||||
# so the Group 3 default is stamped (ADR-0053).
|
||||
# Arrange — a cert lodging a gas boiler with zone control.
|
||||
baseline = build_epc()
|
||||
main = baseline.sap_heating.main_heating_details[0]
|
||||
main.sap_main_heating_code = 102
|
||||
main.main_heating_category = 2
|
||||
main.main_heating_control = 2110
|
||||
overlay = main_heating_overlay_for("Community heating, boilers", 0)
|
||||
assert overlay is not None
|
||||
|
||||
# Act
|
||||
result = apply_simulations(baseline, [overlay])
|
||||
|
||||
# Assert — the stale boiler control is replaced by the Group 3 default.
|
||||
assert result.sap_heating.main_heating_details[0].main_heating_control == 2313
|
||||
|
||||
|
||||
def test_main_heating_override_remaps_the_primary_system_code() -> None:
|
||||
# Arrange
|
||||
baseline = build_epc()
|
||||
|
|
|
|||
|
|
@ -874,22 +874,35 @@ _EXPECTATIONS: Final[tuple[RealCertExpectation, ...]] = (
|
|||
# TOP-FLOOR FLAT, band A (before 1900), SOLID BRICK 280 mm uninsulated,
|
||||
# FLAT ROOF no insulation (As Built), floor over ANOTHER DWELLING below
|
||||
# (zero loss), mains-gas COMBI, SINGLE glazing (glazed_type 5), TFA 63.
|
||||
# Lodged 62 / engine 61 (60.82). Built in accredited Elmhurst RdSAP10
|
||||
# (evidence saved: elmhurst_summary.pdf / elmhurst_worksheet.pdf): EVERY
|
||||
# heat-loss element matches the engine EXACTLY — walls (solid U 1.70,
|
||||
# 53.18 W/K), flat roof (uninsulated U 2.30, 145.57 W/K), floor (0),
|
||||
# party (0), and — with the faithful SINGLE glazing entered — WINDOWS
|
||||
# (8.62 m² × effective U 4.0268 = 34.7114 W/K, identical to the engine).
|
||||
# This confirms the mapper''s glazed_type 5 → single (raw U 4.8) is
|
||||
# spec-correct (epc_codes.csv: glazed_type 5 = single glazing), NOT the
|
||||
# double it superficially resembles. The −1.18 vs lodged is a cost-side
|
||||
# methodology residual (PE +8.7 kWh/m², CO2/PE-driven), not a fabric bug.
|
||||
# PINNED to the observed engine 61.
|
||||
# Lodged 62 / engine 62 (61.59, was 60.82 before the §5.7 Table 13
|
||||
# 280mm-boundary fix below) — now an EXACT match. Built in accredited
|
||||
# Elmhurst RdSAP10 (evidence saved: elmhurst_summary.pdf /
|
||||
# elmhurst_worksheet.pdf): flat roof (uninsulated U 2.30, 145.57 W/K),
|
||||
# floor (0), party (0), and — with the faithful SINGLE glazing entered —
|
||||
# WINDOWS (8.62 m² × effective U 4.0268 = 34.7114 W/K, identical to the
|
||||
# engine) all match EXACTLY. This confirms the mapper''s glazed_type 5
|
||||
# → single (raw U 4.8) is spec-correct (epc_codes.csv: glazed_type 5 =
|
||||
# single glazing), NOT the double it superficially resembles.
|
||||
#
|
||||
# Wall U originally logged as "1.70, matches engine exactly" — but that
|
||||
# was a FALSE match: this cert's build script (build_217091901.py)
|
||||
# never actually set a wall thickness field, so the shared assessment
|
||||
# silently carried over a stale 260mm from an earlier build instead of
|
||||
# this cert's real 280mm (measured=Y). Rebuilt with the corrected
|
||||
# 280mm entry: Elmhurst's own worksheet (29a) NOW gives wall U=1.40,
|
||||
# not 1.70 — see the sibling fix on cert 100061275133/100031768368's
|
||||
# investigation, which found the SAME 280mm-exactly boundary bug in
|
||||
# `_u_brick_thin_wall_age_a_to_e` (RdSAP 10 Table 13's 200/280mm rows
|
||||
# share an unlabelled edge; Elmhurst puts it in the upper "280-420mm
|
||||
# -> 1.4" row, our `<= 280` check put it in the lower row). Fixed;
|
||||
# both this cert (band A) and 100031768368 (band C) now agree on
|
||||
# U=1.40 at exactly 280mm — two independent Elmhurst confirmations.
|
||||
# PINNED to the observed exact match (vs lodged, the correct target).
|
||||
RealCertExpectation(
|
||||
schema="RdSAP-Schema-21.0.1",
|
||||
sample="uprn_217091901",
|
||||
cert_num="uprn-217091901",
|
||||
sap_score=61,
|
||||
sap_score=62,
|
||||
),
|
||||
# UPRN 100061275133 (corpus; no cert number lodged). RdSAP-21.0.1 native —
|
||||
# 2-storey SEMI-DETACHED HOUSE, band B, SOLID BRICK 400 mm with EXTERNAL
|
||||
|
|
@ -1034,6 +1047,47 @@ _EXPECTATIONS: Final[tuple[RealCertExpectation, ...]] = (
|
|||
cert_num="uprn-10091578598",
|
||||
sap_score=79,
|
||||
),
|
||||
# UPRN 100031768368 (corpus; no cert number lodged). RdSAP-21.0.1 native —
|
||||
# END-TERRACE HOUSE, band C (1930-1949), SOLID BRICK 280 mm uninsulated
|
||||
# (wall_thickness_measured=Y), pitched 100 mm loft insulation, SOLID
|
||||
# ground floor uninsulated, mains-gas COMBI (PCDB 18250, Baxi 630 Combi
|
||||
# 88.4%) + gas ROOM-HEATER SECONDARY (code 605), electric shower with
|
||||
# WWHRS (no mixer shower — mixer_shower_count genuinely 0, corpus-
|
||||
# checked: no systematic bias on the 170-cert "bath+no shower data"
|
||||
# cohort), 10 windows across 2 real orientations (East 5.52 m²/West
|
||||
# 8.0 m² split further by glazing era), TFA 75.5. Lodged 61 / engine
|
||||
# 61 (61.21, was 59.12 before the fix below) — now within 0.5.
|
||||
#
|
||||
# Built in accredited Elmhurst RdSAP10 (evidence saved:
|
||||
# elmhurst_summary.pdf / elmhurst_worksheet.pdf): dwelling volume
|
||||
# (194.4125 m³), ventilation ACH (21)=0.7229, ground floor W/K
|
||||
# (27.5575), and doors W/K (5.55) all matched EXACTLY on the first
|
||||
# build. The wall did not: engine U=1.70 (149.92 W/K) vs Elmhurst
|
||||
# U=1.40 (123.47 W/K) despite identical inputs (construction, "As
|
||||
# Built" insulation, 280mm thickness, age band all confirmed matching
|
||||
# via the input-summary crosswalk).
|
||||
#
|
||||
# Root cause: RdSAP 10 §5.7 Table 13 (spec PDF p.41) gives four
|
||||
# thickness bands for an uninsulated solid-brick wall — "up to 200mm"
|
||||
# -> 2.5, "200 to 280mm" -> 1.7, "280 to 420mm" -> 1.4, "more than
|
||||
# 420mm" -> 1.1 — and this cert's wall sits EXACTLY on the shared
|
||||
# 200/280 edge, which the table text doesn't disambiguate.
|
||||
# `_u_brick_thin_wall_age_a_to_e`'s `<= 280` check put the edge in the
|
||||
# lower (1.7) row; Elmhurst puts it in the upper (1.4) row. Verified
|
||||
# independently on a SECOND cert (217091901, band A, also exactly
|
||||
# 280mm) after discovering and fixing its build script's own bug
|
||||
# (never actually set a wall thickness, silently inheriting a stale
|
||||
# 260mm from an earlier build) — rebuilt with the corrected 280mm
|
||||
# entry, Elmhurst's worksheet gives the SAME U=1.40. Fixed in
|
||||
# `domain/sap10_ml/rdsap_uvalues.py`; corpus gauge 77.8%->78.6%
|
||||
# within-0.5, MAE 0.636->0.627 (14 corpus certs lodge exactly 280mm
|
||||
# solid brick). PINNED to the observed match vs lodged.
|
||||
RealCertExpectation(
|
||||
schema="RdSAP-Schema-21.0.1",
|
||||
sample="uprn_100031768368",
|
||||
cert_num="uprn-100031768368",
|
||||
sap_score=61,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -239,7 +239,9 @@ _CORPUS = Path(
|
|||
# 2.97, CO2 0.074 -> 0.072.
|
||||
# 77.7% -> 77.8% via the §5 (12) suspended-timber floor-infiltration
|
||||
# fallback (see the `_MAX_SAP_MAE` note below for the mechanism).
|
||||
_MIN_WITHIN_HALF_SAP = 0.778
|
||||
# 77.8% -> 78.6% via the RdSAP 10 §5.7 Table 13 200/280mm solid-brick-wall
|
||||
# boundary fix (see the `_MAX_SAP_MAE` note below).
|
||||
_MIN_WITHIN_HALF_SAP = 0.786
|
||||
# 0.793 -> 0.789 via the §12 Unknown-meter + dual-electric-immersion off-peak
|
||||
# trigger (RdSAP 10 PDF p.62): Apartment 241 (main 691 + 903 dual immersion)
|
||||
# -5.38 -> -1.05. Worksheet-validated on "simulated case 48" (Elmhurst SAP 57,
|
||||
|
|
@ -334,7 +336,19 @@ _MIN_WITHIN_HALF_SAP = 0.778
|
|||
# description for the U-value calc — the infiltration rule did not).
|
||||
# Cert 100061275133 (Elmhurst-validated build): SAP 77.24 -> 76.33, now
|
||||
# an EXACT match to lodged (76). Unit-pinned in test_cert_to_inputs.
|
||||
_MAX_SAP_MAE = 0.637
|
||||
# 77.8% -> 78.6% (MAE 0.636 -> 0.627) via the RdSAP 10 §5.7 Table 13
|
||||
# 200/280mm solid-brick-wall boundary fix: the spec table's "200 to
|
||||
# 280mm -> 1.7" / "280 to 420mm -> 1.4" rows share an unlabelled edge at
|
||||
# exactly 280mm; `_u_brick_thin_wall_age_a_to_e`'s `<= 280` check put it
|
||||
# in the lower row. Elmhurst-validated on TWO certs at exactly 280mm in
|
||||
# different age bands (100031768368 band C, 217091901 band A after
|
||||
# fixing that cert's build script — it never set a wall thickness field
|
||||
# and had silently inherited a stale value from an earlier build) — both
|
||||
# independently confirm the shared edge belongs to the upper (1.4) row.
|
||||
# 100031768368 SAP 59.12 -> 61.21 (lodged 61, now within 0.5); 217091901
|
||||
# 60.82 -> 61.59 (lodged 62, now an exact match). 14 corpus certs lodge
|
||||
# exactly 280mm solid brick. Unit-pinned in test_rdsap_uvalues.
|
||||
_MAX_SAP_MAE = 0.628
|
||||
_MAX_CO2_MAE_TONNES = 0.072 # t CO2 / yr vs co2_emissions_current
|
||||
_MAX_PE_PER_M2_MAE = 3.0 # kWh / m2 / yr vs energy_consumption_current
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue