Commit graph

5835 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
4291193ba8 Slice S0380.158: SAP 10.2 Table 4f warm-air heating system fans
SAP 10.2 Table 4f (PDF p.174) row "Warm air heating system fans"
+ footnote e) — verbatim:

  Warm air heating system fans e)        SFP × 0.4 × V

  e) SFP is the specific fan power from the database record for the
     warm air unit if applicable; otherwise 1.5 W/(l/s). These values
     of SFP include the in-use factor.
     If the heating system is a warm air unit and there is balanced
     whole house mechanical ventilation, the electricity for warm
     air circulation should not be included in addition to the
     electricity for mechanical ventilation. However it is included
     for a warm air system and MEV or PIV from outside.
     V is the volume of the dwelling in m³.

Per Table 4a (PDF p.165-166), warm-air systems are:
  - Category 5: heat pumps with warm-air distribution (codes 521,
    523, 524 electric; 525, 526, 527 gas-fired)
  - Category 9: warm-air systems NOT heat pump (501-511, 520 gas-
    fired; 512-514 liquid-fired; 515 Electricaire electric)

Pre-slice the cascade's `_table_4f_additive_components` docstring
explicitly listed "(230b) Warm-air heating fans + (230c) for warm-
air pump" as "Not yet wired" — every Cat 5 / Cat 9 warm-air corpus
variant resolved `pumps_fans_kwh_per_yr` to 0. For electric 2 (code
524 Cat 5 air-source warm-air HP, no MV, V = 227.25 m³), the P960
worksheet block 11a (249) lodges 136.35 kWh × 13.67 p/kWh = £18.64
where the cascade computed 0.

New `_TABLE_4A_WARM_AIR_SAP_CODES` frozenset (22 codes) + leaf helper
`_table_4f_warm_air_heating_fans_kwh(main, dwelling_volume_m3,
has_balanced_mv)` wired at the orchestrator pumps_fans summation
alongside the existing circulation-pump and gas-flue-fan helpers.
Footnote-e balanced-MV omission reads `epc.sap_ventilation.
mechanical_ventilation_kind` via the new
`_has_balanced_mechanical_ventilation` predicate (returns True for
MVHR / MV; False for MEV / PIV / NATURAL).

Per-line walk evidence: cascade `pumps_fans_kwh_per_yr` = 0.0000 vs
worksheet (249) = 136.3500 = 1.5 × 0.4 × 227.25 exactly. Default SFP
from footnote e matches; PCDB warm-air-unit SFP lookup deferred
until a fixture exercises it.

Closures electric 2:
  pumps_fans_kwh_per_yr: 0 → 136.35 (EXACT match to worksheet)
  ΔSAP +0.7002 → −0.1087 (residual swung past worksheet — the +0.70
    pre-slice was an under-counted-fan offset; spec-correct fix lands
    just past zero, exposing a small upstream SH cascade gap likely
    in the Cat 5 warm-air HP Table 4a SH efficiency or Table 9c MIT
    cascade for warm-air mains — follow-up slice)
  Δcost −£16.14 → +£2.50
  ΔCO2 −2.37 → +16.54 kg
  ΔPE −108.58 → +97.69 kWh

No regressions on the other 24 cohort variants — the warm-air-code
gate fires only when `sap_main_heating_code` is in the new frozenset
and only electric 2 has a warm-air SAP code in the corpus. Extended
handover suite: 902 pass / 0 fail (was 901 — +1 from the new AAA
test). Pyright net-zero (43 → 43).

Σ |ΔSAP_c| across the 25-variant cohort: 2.87 → 2.30 (~20%
reduction from this slice).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 15:40:25 +00:00
Khalim Conn-Kowlessar
8507728237 Slice S0380.157: SAP 10.2 Table 2b note b) WHC=903 electric-immersion guard
SAP 10.2 Table 2b note b) (PDF p.159) — verbatim:

  Multiply Temperature Factor by 0.9 if there is separate time
  control of domestic hot water (boiler systems, warm air systems
  and heat pump systems).

The parenthetical list restricts the rule to systems where the heat
generator (boiler / warm-air / HP) is the device heating the
cylinder. Electric immersion is NOT in that list because the
immersion isn't a heat-generator system feeding DHW — it sits inside
the cylinder. The ×0.9 multiplier reflects shorter cylinder-heating
periods when a boiler / HP / warm-air operates on a separate timer
for DHW vs SH; if the heat generator doesn't feed the cylinder at all
(because the immersion does), there's no such timing effect.

Pre-slice `_separately_timed_dhw` returned True for any Cat 4 HP
main BEFORE consulting WHC (line 3872 `if main.main_heating_category
== 4: return True`). For electric 2 (sap_main_heating_code=524 Cat 5
warm-air ASHP, main_heating_category=4 per Elmhurst mapper, WHC=903
electric immersion + cylinder + cylinder thermostat lodged), the
cat-4 branch fired before the existing `_is_electric_water` check
could route the cert to False. The cascade applied ×0.9 to the
Temperature Factor (53), pulling (55) from 1.2294 → 1.1064 → cascade
annual (56) = 403.87 vs worksheet (56) annual = 448.73.

Same WHC=903 principle as the prior slice S0380.156 (Table 3 zero-
loss list for electric immersion): when HW is independent of the
main heating, main-heating-specific DHW rules don't apply — even
when the main happens to be a HP / boiler / warm-air system.

Fix: new top-of-function `if epc.sap_heating.water_heating_code ==
_WHC_ELECTRIC_IMMERSION: return False` guard in
`_separately_timed_dhw`. Reuses the constant introduced in S0380.156.

Closures electric 2:
  Cylinder (56) storage loss annual 403.87 → 448.73 (matches
  worksheet 1.2294 × 365 = 448.73 EXACT within rounding)
  HW kWh demand 2339.24 → 2384.12 (matches worksheet (62)/(64) =
  2384.116 EXACT)
  ΔSAP +0.8118 → +0.7002
  Δcost −£18.71 → −£16.14
  ΔCO2 −7.21 → −2.37 kg
  ΔPE −161.68 → −108.58 kWh

The remaining +0.70 SAP residual is a separate upstream gap (likely
warm-air-HP SH cascade or Table 4a SH efficiency for code 524) —
follow-up slice.

No regressions on the other 24 cohort variants. Cohort-1 ASHP certs
(Cat 4 HP + WHC=901 = HW from HP + cylinder) keep ×0.9 as before
because their WHC=901 doesn't trigger the new guard. Extended
handover suite: 901 pass / 0 fail (was 900 — +1 from the new AAA
test). Pyright net-zero (43 → 43).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 15:40:25 +00:00
Khalim Conn-Kowlessar
c727b3cb5a Slice S0380.156: SAP 10.2 Table 3 WHC=903 electric-immersion zero-loss guard
SAP 10.2 Table 3 (PDF p.160) verbatim:

  Primary loss is set to zero for the following:
      Electric immersion heater
      Combi boiler ...
      CPSU ...
      Boiler and thermal store within a single casing
      Separate boiler and thermal store connected by no more than 1.5
      m of insulated pipework
      Direct-acting electric boiler
      Heat pump (...) with hot water vessel integral to package

The Elmhurst WHC=903 lodging signals exactly the first row: "HW from
a separate electric immersion heater" — the cylinder is heated by an
immersion element inside the tank, no primary pipework between any
heat generator and the cylinder. The rule is universal: regardless
of what main heating exists for space heating, electric immersion
means no primary circuit means no primary loss.

Pre-slice `_primary_loss_applies` only consulted `water_heating_code`
in the Table 4a wet-boiler branch (codes 151-161 / 191-196). The Cat
4 HP branch returned True unconditionally when no PCDB record was
lodged; the Cat 1/2 boiler branch returned True unconditionally; the
PCDB Table 322 + Table 4b non-PCDB branches likewise. For the
electric 2 corpus variant (sap_main_heating_code=524 Cat 5 warm-air
ASHP, main_heating_category=4 per Elmhurst mapper, no PCDB record,
WHC=903 + cylinder), the Cat-4 branch falsely returned True and the
cascade added ~510 kWh/yr primary loss to a system with no primary
circuit at all.

Per-line walk discipline applied: cascade `water_heating_from_cert`
output dump showed `primary_loss_monthly_kwh_annual = 509.98` while
worksheet (59)m = 0 every month → spec lookup found Table 3 verbatim
"Electric immersion heater" zero-loss line.

Adds `_WHC_ELECTRIC_IMMERSION: Final[int] = 903` constant + a
top-of-function `if water_heating_code == _WHC_ELECTRIC_IMMERSION:
return False` guard that fires before any of the system-type-keyed
branches.

Closures electric 2:
  HW kWh 2849.22 → 2339.24 (matches worksheet (62)/(64) = 2384.12
  within the residual ~45 kWh storage-loss gap)
  ΔSAP −0.4584 → +0.8118 (cascade swung past the worksheet by +1.27
  — the pre-slice 'near-correct' value was offsetting cascade bugs
  per [[feedback-software-no-special-handling]]; the +0.81 residual
  exposes a separate upstream gap to chase in a follow-up slice)
  Δcost +£10.56 → −£18.71
  ΔCO2 +47.89 → −7.21 kg
  ΔPE +443.13 → −161.68 kWh

No regressions on the other 24 cohort variants — only electric 2 has
the (Cat 4 HP, no PCDB, WHC=903) combination in the corpus.
Extended handover suite: 900 pass / 0 fail (was 899 — +1 from the
new AAA test). Pyright net-zero (43 → 43).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 15:40:25 +00:00
Khalim Conn-Kowlessar
f4a759ade3 docs: handover post S0380.153..155
Session landed three spec-clean slices closing four major residuals:

- S0380.153 (Table 3 middle row for solid-fuel boilers): SF3 EXACT all
  4 metrics (+0.30 → -0.0000). Found the rule that solid-fuel boilers
  don't ship with dual programmers per §9.2.4.

- S0380.154 (§12.4.4 back-boiler summer-immersion): SF2 SAP+cost
  EXACT (+1.86 → -0.0000 SAP; -£42.84 → -£0.00 cost). Implemented HW
  fuel kWh split + monthly blend across cost / CO2 / PE / standing.

- S0380.155 (Table 4a HP water-column dispatch): gshp closed ±0.02
  SAP (+0.94 → -0.0178). HW kWh 841 → 1138 matches worksheet exactly.

Σ |ΔSAP_c| 14.5 (session start of S0380.150) → 2.7 = 81% reduction
across 6 slices, two sessions.

Handover doc captures:
- Per-line discipline (walk worksheet before forming hypothesis)
- Elmhurst-vs-spec divergences to defer (lighting-PE +48.66 cluster
  uses Table 12 annual factor; spec Table 12d mandates monthly)
- Ranked open fronts (electric 5 R=0.20 storage MIT, electric 2
  warm-air HP HW, deferred lighting-PE cluster)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 15:40:25 +00:00
Daniel Roth
a68ab01d70 All downloaded PasHub files uploaded to SharePoint property folder 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
3d2a21d9d9 All downloaded PasHub files uploaded to SharePoint property folder 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
815173bc79 tweak local trigger 2026-06-04 15:40:25 +00:00
Daniel Roth
35b65d0d8d adjust comment about evidence type 2026-06-04 15:40:25 +00:00
Daniel Roth
5fa6f32644 Service persists mcs_certificate file_type when evidence_category is MCS cert 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
367f6f6cf9 Downloaded files carry evidence_category as DownloadedFile 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
1e2b997f2f Downloaded files carry evidence_category as DownloadedFile 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
e18c300deb MCS cert identified by evidence_category in get_core_file_type 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
1af1d4a82c MCS cert identified by evidence_category in get_core_file_type 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
5ed3bf73e8 evidence categories plus typehinting 2026-06-04 15:40:25 +00:00
Daniel Roth
020a24d345 run() returns core and other file paths 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
d8ec12065f run() returns core and other file paths 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
0aa6a4fc30 Other files persisted to DB with file_type OTHER 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
1650762ae2 Other files persisted to DB with file_type OTHER 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
c86dbeb4a1 Upload other files to S3 when get_other_files is True 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
098f60ecfd Upload other files to S3 when get_other_files is True 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
9c38f45c98 tidying for readability 2026-06-04 15:40:25 +00:00
Daniel Roth
c9a2ce4921 Service deletes other-file temp paths after run 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
f8d2bb8049 Service deletes other-file temp paths after run 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
49e7b7fea6 Wire service to get_evidence_files_by_job_id; retire get_core_evidence_files_by_job_id 🟪 2026-06-04 15:40:25 +00:00
Daniel Roth
662f6de0ab get_evidence_files_by_job_id downloads other files when include_other=True 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
c4ffaaa069 get_evidence_files_by_job_id downloads other files when include_other=True 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
f95b6bdd7d get_evidence_files_by_job_id returns DownloadedFiles with empty other when include_other=False 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
665dc69ad5 get_evidence_files_by_job_id returns DownloadedFiles with empty other when include_other=False 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
e7c679e0db Group evidence into core and other via _group_into_core_and_other_files 🟪 2026-06-04 15:40:25 +00:00
Daniel Roth
99229844b5 _select_other_files returns non-core evidence files 🟩 2026-06-04 15:40:25 +00:00
Daniel Roth
db796747d9 _select_other_files returns non-core evidence files 🟥 2026-06-04 15:40:25 +00:00
Daniel Roth
6cb6c8c756 allow for missing deal stage column when triggering sqs from file 2026-06-04 15:40:25 +00:00
Daniel Roth
790e430aff rename local handler trigger script 2026-06-04 15:40:25 +00:00
Jun-te Kim
a4670f8bc0 deploy lambda 2026-06-04 15:32:51 +00:00
Jun-te Kim
261fae2e79 reformatted to be DDD structure 2026-06-04 14:50:04 +00:00
Jun-te Kim
dfd05ba28b tests files 2026-06-04 11:47:42 +00:00
Jun-te Kim
c614ff6388 save local changes 2026-06-03 12:41:56 +00:00
Jun-te Kim
bf166e7f46 test suite to pick it up 2026-06-02 15:33:46 +00:00
Jun-te Kim
6c8fe86cf9 ddd tests 2026-06-02 15:31:42 +00:00
Jun-te Kim
25ba1427b1 seperate ddd tests 2026-06-02 15:30:58 +00:00
Jun-te Kim
4e02eb7c77 more tests to ensure we don't deploy something that is brokern 2026-06-02 15:03:20 +00:00
Jun-te Kim
a3c80b6691
Merge pull request #1147 from Hestia-Homes/feature/landlord_data
address2uprn was missing a dependency
2026-06-02 11:50:42 +01:00
Jun-te Kim
144233a5f3 backend was missing a dependency 2026-06-02 10:46:29 +00:00
Jun-te Kim
feb3bc08f0
Merge pull request #1144 from Hestia-Homes/feature/landlord_data
if you change the descript it destories and make a new one instead of…
2026-06-02 10:39:17 +01:00
Jun-te Kim
f3ad339cf5 if you change the descript it destories and make a new one instead of edit 2026-06-02 09:36:31 +00:00
Jun-te Kim
8accb51383
Merge pull request #1142 from Hestia-Homes/feature/landlord_data
iam permissions for my lambda to import location
2026-06-02 09:43:32 +01:00
Jun-te Kim
04dc1b20fe iam permissions 2026-06-01 21:08:19 +00:00
Jun-te Kim
0a123c6723
Merge pull request #1122 from Hestia-Homes/feature/landlord_data
Feature/landlord data
2026-06-01 20:25:20 +01:00
Jun-te Kim
616744a606 Merge remote-tracking branch 'origin/main' into feature/landlord_data
# Conflicts:
#	datatypes/epc/schema/rdsap_schema_21_0_0.py
#	datatypes/epc/schema/rdsap_schema_21_0_1.py
2026-06-01 17:02:20 +00:00
Jun-te Kim
bf3b689f15 Remove EPC and asset_list changes unrelated to SAL handler
This branch's objective is the SAL ingestion handler
(applications/SAL/handler.py) and its dependency tree. Drop work
that crept in but is unreferenced by it:

- EPC feature: domain/epc, infrastructure/epc (gov_uk + historical
  clients), tests/infrastructure/epc
- datatypes/epc edits (instantaneous_wwhrs Optional) reverted to main
- asset_list/app.py local data-file/column tweak reverted to main

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 16:39:09 +00:00