mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
refactor: lift-and-shift packages/domain/src/domain/sap → domain/sap10_calculator
Migration of the SAP 10.2 calculator package from the uv-workspace
src-layout (`packages/domain/src/domain/sap`) to the root-level layout
(`domain/sap10_calculator`), matching the pattern already used by
`domain.addresses` / `domain.tasks` / `domain.postcode`.
Changes:
- `git mv packages/domain/src/domain/sap → domain/sap10_calculator`
(92 files; git auto-detected all as renames so blame/history is
preserved).
- Subpackage rename: `domain.sap` → `domain.sap10_calculator`. 48
Python files rewritten (`from domain.sap.X` → `from domain.sap10_
calculator.X`); zero remaining `domain.sap` refs after the sed pass.
- Path-string updates: 3 .py files (test fixtures + xlsx loader) +
6 markdown docs (CONTEXT.md, 2 ADRs, 3 sap-spec docs, sap10_
calculator/README.md) had hard-coded `packages/domain/src/domain/
sap/...` paths rewritten to `domain/sap10_calculator/...`.
- `Path(__file__).parents[N]` rebasing: the old tree was 3 levels
deeper than the new one (`packages/domain/src/`), so 4× `parents[7]`
became `parents[4]` and 1× `parents[6]` became `parents[3]` across
`tables/pcdb/{__init__.py, postcode_weather.py, etl.py}`,
`worksheet/tests/_xlsx_loader.py`, and `tests/test_pcdb_etl.py`.
- PEP 420 namespace package: deleted both `domain/__init__.py`
(root + workspace, both load-bearing only as empty/docstring) so
Python combines `domain.sap10_calculator` (root) and `domain.ml`
(workspace) into one namespace package. Confirmed via
`domain.__path__ == ['/workspaces/model/domain',
'/workspaces/model/packages/domain/src/domain']`. Without this,
the root `domain/__init__.py` shadowed the workspace one and
`domain.ml` was unreachable.
Verified:
- Full sweep (`backend/documents_parser/tests/test_summary_pdf_
mapper_chain.py + domain/sap10_calculator/worksheet/tests/test_
e2e_elmhurst_sap_score.py + domain/sap10_calculator/rdsap/tests/
test_golden_fixtures.py`): 99 passed / 19 failed — exact same
counts as pre-refactor. All 19 failures pre-existing (9 hand-built
001479 + 6 cohort diff + 4 cohort chain non-spec).
- Wider sweep (all sap10_calculator + domain.ml): 1654 passed /
20 failed (the +1 vs the focused sweep is the pre-existing
`test_roof_insulated_assumed_with_ni_thickness_uses_50mm_per_
section_5_11_4` which was already failing on the previous baseline).
- Pyright net-zero on the three load-bearing baselines:
`heat_transmission.py` 13, `cert_to_inputs.py` 35, `mapper.py` 33.
Lift-and-shift only — no semantic renames (`Sap10Calculator` stays
`Sap10Calculator`), no testpaths edits in pytest.ini (sap tests
continue to be invoked by explicit pytest paths).
Note: `domain.ml` still lives at `packages/domain/src/domain/ml/`.
Migrating it would close out the dual-`domain/` layout but is
out of scope for this commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
87b6045c97
commit
29ac35ccbe
98 changed files with 200 additions and 204 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"""End-to-end validation for the Elmhurst Summary→EpcPropertyData chain.
|
||||
|
||||
The 6 Elmhurst worksheet fixtures in `domain.sap.worksheet.tests`
|
||||
The 6 Elmhurst worksheet fixtures in `domain.sap10_calculator.worksheet.tests`
|
||||
build their `EpcPropertyData` synthetically — they validate the
|
||||
calculator + cascade in isolation from the mapper. This file pins
|
||||
the OTHER half of the chain: `from_elmhurst_site_notes` must produce
|
||||
|
|
@ -37,9 +37,9 @@ from typing import cast
|
|||
|
||||
from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor
|
||||
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
||||
from domain.sap.calculator import calculate_sap_from_inputs
|
||||
from domain.sap.rdsap.cert_to_inputs import SAP_10_2_SPEC_PRICES, cert_to_inputs
|
||||
from domain.sap.worksheet.tests import (
|
||||
from domain.sap10_calculator.calculator import calculate_sap_from_inputs
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import SAP_10_2_SPEC_PRICES, cert_to_inputs
|
||||
from domain.sap10_calculator.worksheet.tests import (
|
||||
_elmhurst_worksheet_000474 as _w000474,
|
||||
_elmhurst_worksheet_000477 as _w000477,
|
||||
_elmhurst_worksheet_000480 as _w000480,
|
||||
|
|
@ -63,7 +63,7 @@ _SUMMARY_001479_PDF = _FIXTURES / "Summary_001479.pdf"
|
|||
# matches worksheet continuous SAP at 1e-4".
|
||||
_API_001479_JSON = (
|
||||
Path(__file__).parents[3]
|
||||
/ "packages/domain/src/domain/sap/rdsap/tests/fixtures/golden"
|
||||
/ "domain/sap10_calculator/rdsap/tests/fixtures/golden"
|
||||
/ "0535-9020-6509-0821-6222.json"
|
||||
)
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ def _summary_pdf_to_textract_style_pages(pdf_path: Path) -> list[str]:
|
|||
def test_summary_000474_mapper_produces_three_building_parts() -> None:
|
||||
# Arrange — cert U985-0001-000474 is a mid-terrace with 3 building
|
||||
# parts (Main + 2 extensions) per the hand-built worksheet fixture
|
||||
# at packages/domain/src/domain/sap/worksheet/tests/
|
||||
# at domain/sap10_calculator/worksheet/tests/
|
||||
# _elmhurst_worksheet_000474.py. Routing the Summary PDF through
|
||||
# extractor + mapper must yield the same count.
|
||||
pages = _summary_pdf_to_textract_style_pages(_SUMMARY_000474_PDF)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Two boundary factories convert raw inputs to canonical members:
|
|||
- `BuildingPartIdentifier.extension(n)` (site-notes / construction id)
|
||||
|
||||
P6.1 starts P6 (strict-type EpcPropertyData) from the documented pain
|
||||
point in packages/domain/src/domain/sap/worksheet/dimensions.py:74-82.
|
||||
point in domain/sap10_calculator/worksheet/dimensions.py:74-82.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ The ML model is **not deprecated**. It is repurposed as a **residual learner** a
|
|||
A full SAP 10.3 worksheet plus the data-extraction rules from RdSAP 10 Appendix S. Module organisation:
|
||||
|
||||
```
|
||||
packages/domain/src/domain/sap/
|
||||
domain/sap10_calculator/
|
||||
__init__.py # Sap10Calculator entry point + SapResult dataclass
|
||||
worksheet/
|
||||
dimensions.py # §1
|
||||
|
|
@ -79,7 +79,7 @@ packages/domain/src/domain/sap/
|
|||
cascade_defaults.py # the RdSAP10 "assume-typical" rules (currently in rdsap_uvalues.py)
|
||||
```
|
||||
|
||||
The existing `domain.ml.*` modules stay where they are during Session A; they continue serving the live ML pipeline. Session B promotes them into `domain.sap.*` once parity is reached.
|
||||
The existing `domain.ml.*` modules stay where they are during Session A; they continue serving the live ML pipeline. Session B promotes them into `domain.sap10_calculator.*` once parity is reached.
|
||||
|
||||
## Sap10Calculator interface
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ We do **not** retire the existing ML pipeline until both validations pass.
|
|||
- **The six ML targets remain those from ADR-0007.** The residual head predicts deltas against the same six quantities.
|
||||
- **ADR-0008's physics-as-feature pattern stays valid for the ML residual head.** The residual head probably needs fewer features, but the cascade U-value defaults and SAP efficiency lookups remain useful as feature builders if the calculator subset alone underfits.
|
||||
- **`energy_rating_current` remains excluded from features.** Same leakage rule.
|
||||
- **RdSAP 10 cert-extraction rules are now first-class in the codebase.** Rules that were ad-hoc in `transform.py` move into `domain.sap.rdsap.appendix_s`.
|
||||
- **RdSAP 10 cert-extraction rules are now first-class in the codebase.** Rules that were ad-hoc in `transform.py` move into `domain.sap10_calculator.rdsap.appendix_s`.
|
||||
- **The training parquet schema continues at v2.x.** A new column `calculator_sap_score` lands as a non-breaking addition once Session A reaches parity. The schema version bumps to v3.0.0 only when the residual targets replace the raw targets — a coordinated AutoGluon-repo deploy, per ADR-0008's cutover discipline.
|
||||
|
||||
## SAP 10.2 → SAP 10.3 implications
|
||||
|
|
@ -147,7 +147,7 @@ Re-derivation work is bounded — a few hundred numbers across tables — and th
|
|||
|
||||
## Consequences
|
||||
|
||||
- A new top-level domain area `domain.sap.*` is introduced; over Sessions B/C it absorbs `domain.ml.{envelope,demand,ecf,rdsap_uvalues,sap_efficiencies,ventilation}.py`. The ML transform stops shipping those as standalone features once the residual head takes over.
|
||||
- A new top-level domain area `domain.sap10_calculator.*` is introduced; over Sessions B/C it absorbs `domain.ml.{envelope,demand,ecf,rdsap_uvalues,sap_efficiencies,ventilation}.py`. The ML transform stops shipping those as standalone features once the residual head takes over.
|
||||
- The codebase carries two SAP outputs: cert-reported `sap_score` (ground truth at training time) and calculator-emitted `sap_score` (ground truth at inference time for any RdSAP cert input). The product layer chooses; for "score this hypothetical post-retrofit state", calculator wins.
|
||||
- The deterministic calculator is **version-bound to SAP 10.3.** A future SAP 10.4 is a calculator MAJOR bump and an ADR. The ML residual head is SAP-version-agnostic only insofar as the residual distribution it learns stays stationary; in practice a spec bump retrains the residual head.
|
||||
- Spec PDFs live in `docs/sap-spec/` (this repo). The repo now carries the canonical reference for what the calculator computes. License: SAP 10.3 © Crown copyright 2026; RdSAP 10 © BRE — both are public-interest references for SAP-compliant software, included for traceability.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## Why this ADR exists
|
||||
|
||||
ADR-0009 was written before a second-order problem in the validation corpus was visible: the 250k-cert training parquet spans **multiple SAP spec versions** (SAP 10.1 from 2019, SAP 10.2 pre- and post-14-March-2025 amendment), each of which was the active table when its certs were lodged. The prior session's `domain.sap.tables.table_12_cert_calibration` layer was implicitly absorbing this version mixture into a single "best fit" price set ~10–25 % lower than the SAP 10.2 (14-03-2025) spec — closer to the SAP 10.1 era prices. Every spec-correctness slice that touched a downstream component (HW cylinder zero-loss, gas standing charges, Table 12a fractional blending) registered as a regression on the parity probe because the cert-cal layer had been numerically calibrated against the buggy state of every other component.
|
||||
ADR-0009 was written before a second-order problem in the validation corpus was visible: the 250k-cert training parquet spans **multiple SAP spec versions** (SAP 10.1 from 2019, SAP 10.2 pre- and post-14-March-2025 amendment), each of which was the active table when its certs were lodged. The prior session's `domain.sap10_calculator.tables.table_12_cert_calibration` layer was implicitly absorbing this version mixture into a single "best fit" price set ~10–25 % lower than the SAP 10.2 (14-03-2025) spec — closer to the SAP 10.1 era prices. Every spec-correctness slice that touched a downstream component (HW cylinder zero-loss, gas standing charges, Table 12a fractional blending) registered as a regression on the parity probe because the cert-cal layer had been numerically calibrated against the buggy state of every other component.
|
||||
|
||||
This ADR resolves four entangled decisions at once. They are coupled — none of them is the right call in isolation.
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ This ADR resolves four entangled decisions at once. They are coupled — none of
|
|||
|
||||
ADR-0009 named SAP 10.3 (13-01-2026) as the calculator's target. No SAP-10.3-lodged certs exist in the corpus; assessor software has not migrated. Targeting SAP 10.3 produces a calculator whose output is verifiable against no cert. The active target is SAP 10.2 (14-03-2025 amendment) — both the document RdSAP 10 (10-06-2025) cross-references for heating-system identification, and the amendment that current assessor software is on.
|
||||
|
||||
`packages/domain/src/domain/sap/tables/table_12.py` is re-labelled as SAP 10.2 (14-03-2025). Its CO2 factors are corrected to spec (0.210 kg/kWh mains gas, 0.136 kg/kWh standard electricity — the file currently has SAP 10.3 values 0.214 and 0.086). Prices already match SAP 10.2 (3.64 p mains gas, 16.49 p standard electricity, etc.) — the misleading "+25 % shift from SAP 10.2 to 10.3" comment is removed; the 13.19 p figure is from SAP 10.1, not SAP 10.2.
|
||||
`domain/sap10_calculator/tables/table_12.py` is re-labelled as SAP 10.2 (14-03-2025). Its CO2 factors are corrected to spec (0.210 kg/kWh mains gas, 0.136 kg/kWh standard electricity — the file currently has SAP 10.3 values 0.214 and 0.086). Prices already match SAP 10.2 (3.64 p mains gas, 16.49 p standard electricity, etc.) — the misleading "+25 % shift from SAP 10.2 to 10.3" comment is removed; the 13.19 p figure is from SAP 10.1, not SAP 10.2.
|
||||
|
||||
A future ADR retargets to SAP 10.3 once the cert corpus migrates (expected late 2026 or 2027 once BRE updates RdSAP to reference SAP 10.3).
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ The cert-calibration table is bug-masking. Its prices are pre-March-2025 SAP val
|
|||
|
||||
This includes the `cert_calibration_e7_codes` extension that routes codes 191–196 (direct-electric) and 691–696 (room heaters) to off-peak rates — Table 12a is explicit that "other direct-acting electric heating" bills 100 % at the high rate on a 7-hour tariff. The S-B14 finding that motivated this hack is in §8 of the handover as a documented dead-end.
|
||||
|
||||
`domain.sap.tables.table_12.unit_price_p_per_kwh` becomes the only price API. Parity probes are updated to use it.
|
||||
`domain.sap10_calculator.tables.table_12.unit_price_p_per_kwh` becomes the only price API. Parity probes are updated to use it.
|
||||
|
||||
### 3. Validation Cohort is filtered to a single spec-version window
|
||||
|
||||
|
|
@ -84,9 +84,9 @@ The 000490 Elmhurst fixture had a recorded -12.5% cost gap (£706 vs £807 PDF)
|
|||
|
||||
### Consequences
|
||||
|
||||
- **`packages/domain/src/domain/sap/tables/table_32.py`** ships the RdSAP10 unit prices + standing charges + Table 12 note (a) gating function. Table 12 keeps the CO2 + PEF columns.
|
||||
- **`packages/domain/src/domain/sap/tables/table_12a.py`** ships the high-rate-fraction lookups for off-peak split (Table 12a in SAP 10.2 PDF page 191 — RdSAP10 §19.1 cross-references this table directly). `Tariff.TEN_HOUR` carried for spec completeness even though RdSAP cert `meter_type` enum (1..5) has no 10-hour code.
|
||||
- **`packages/domain/src/domain/sap/worksheet/fuel_cost.py`** ships the §10a orchestrator producing `FuelCostResult` (32 fields, line refs (240)..(255)). `cert_to_inputs._fuel_cost` precompute wires it from cert state.
|
||||
- **`domain/sap10_calculator/tables/table_32.py`** ships the RdSAP10 unit prices + standing charges + Table 12 note (a) gating function. Table 12 keeps the CO2 + PEF columns.
|
||||
- **`domain/sap10_calculator/tables/table_12a.py`** ships the high-rate-fraction lookups for off-peak split (Table 12a in SAP 10.2 PDF page 191 — RdSAP10 §19.1 cross-references this table directly). `Tariff.TEN_HOUR` carried for spec completeness even though RdSAP cert `meter_type` enum (1..5) has no 10-hour code.
|
||||
- **`domain/sap10_calculator/worksheet/fuel_cost.py`** ships the §10a orchestrator producing `FuelCostResult` (32 fields, line refs (240)..(255)). `cert_to_inputs._fuel_cost` precompute wires it from cert state.
|
||||
- The 000474 Elmhurst fixture cost residual widened from -0.6% to +10.7% (SAP rating ceiling loosened 2 → 4) because the pre-amendment wrong-table-but-cancels-kWh accidentally compensated for upstream §4 HW kWh + Appendix L lighting overestimates. **§4 HW worksheet tightening is the next ticket** — see project memory `project_section_4_hw_next_ticket`. Ceiling drops back to 2 (or below) when that lands.
|
||||
- Golden corpus SAP tolerance widened ±7 → ±11 per the Validation Cohort discipline (oil unit price +55% from Table 12 → Table 32 moves oil-heated golden certs whose lodged SAP scores pre-date Table 32).
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,14 @@ plumbing fails loudly.
|
|||
### Public API (the only thing you need from the SAP module)
|
||||
|
||||
```python
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
cert_to_inputs, # Rating cascade
|
||||
cert_to_demand_inputs, # Demand cascade
|
||||
local_climate_for_cert,
|
||||
environmental_section_from_cert,
|
||||
primary_energy_section_from_cert,
|
||||
)
|
||||
from domain.sap.calculator import calculate_sap_from_inputs, SapResult
|
||||
from domain.sap10_calculator.calculator import calculate_sap_from_inputs, SapResult
|
||||
```
|
||||
|
||||
See `SAP_CALCULATOR.md` §2 for the recommended `dwelling_outputs(epc)`
|
||||
|
|
@ -101,8 +101,8 @@ side is what you need to map out:
|
|||
schema versions (SAP-Schema-18/19, RdSAP-Schema-18) to
|
||||
`EpcPropertyData` lives there.
|
||||
3. **Is SAP scoring already wired to the API?** Search the backend for
|
||||
imports of `domain.sap.rdsap.cert_to_inputs` or
|
||||
`domain.sap.calculator`. If it's not yet wired, the integration test
|
||||
imports of `domain.sap10_calculator.rdsap.cert_to_inputs` or
|
||||
`domain.sap10_calculator.calculator`. If it's not yet wired, the integration test
|
||||
is a forcing function for wiring it.
|
||||
4. **What's the response shape?** The 4 outputs above are what the EPC
|
||||
publishes; the API may already expose them, or may expose a wider
|
||||
|
|
@ -132,10 +132,10 @@ expanding.
|
|||
| File | Why |
|
||||
|---|---|
|
||||
| [`docs/sap-spec/SAP_CALCULATOR.md`](./SAP_CALCULATOR.md) | Module API + architecture (you're heading there) |
|
||||
| [`packages/domain/src/domain/sap/calculator.py`](../../packages/domain/src/domain/sap/calculator.py) | `SapResult` fields you'll assert against |
|
||||
| [`packages/domain/src/domain/sap/rdsap/cert_to_inputs.py`](../../packages/domain/src/domain/sap/rdsap/cert_to_inputs.py) | The 3 public entry points + the section helpers |
|
||||
| [`packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py`](../../packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py) | A reference fixture — `build_epc()` shows the EpcPropertyData shape |
|
||||
| [`packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py`](../../packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py) | The current e2e test pattern — model your integration test on this |
|
||||
| [`domain/sap10_calculator/calculator.py`](../../domain/sap10_calculator/calculator.py) | `SapResult` fields you'll assert against |
|
||||
| [`domain/sap10_calculator/rdsap/cert_to_inputs.py`](../../domain/sap10_calculator/rdsap/cert_to_inputs.py) | The 3 public entry points + the section helpers |
|
||||
| [`domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py`](../../domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py) | A reference fixture — `build_epc()` shows the EpcPropertyData shape |
|
||||
| [`domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py`](../../domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py) | The current e2e test pattern — model your integration test on this |
|
||||
| `backend/` (explore) | API entry points |
|
||||
| [`datatypes/epc/domain/mapper.py`](../../datatypes/epc/domain/mapper.py) | Schema → EpcPropertyData mappers |
|
||||
|
||||
|
|
@ -146,18 +146,18 @@ expanding.
|
|||
```bash
|
||||
# Confirm SAP calculator is still 930/930 green
|
||||
python -m pytest \
|
||||
packages/domain/src/domain/sap/worksheet/tests/test_section_cascade_pins.py \
|
||||
packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_section_cascade_pins.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
--no-cov --no-header --tb=no -q
|
||||
|
||||
# Show the 4 EPC outputs for fixture 000474
|
||||
cd packages/domain/src && python -c "
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
cert_to_inputs, local_climate_for_cert,
|
||||
environmental_section_from_cert, primary_energy_section_from_cert,
|
||||
)
|
||||
from domain.sap.calculator import calculate_sap_from_inputs
|
||||
from domain.sap.worksheet.tests import _elmhurst_worksheet_000474 as w
|
||||
from domain.sap10_calculator.calculator import calculate_sap_from_inputs
|
||||
from domain.sap10_calculator.worksheet.tests import _elmhurst_worksheet_000474 as w
|
||||
epc = w.build_epc()
|
||||
pc = local_climate_for_cert(epc)
|
||||
rating = calculate_sap_from_inputs(cert_to_inputs(epc))
|
||||
|
|
@ -183,7 +183,7 @@ postcode-conditions EI value, not what the EPC publishes.
|
|||
- **Extending the SAP calculator.** It's closed at the EPC-output layer.
|
||||
If you find an additional cert-shape variation that breaks the
|
||||
calculator, capture it as a new conformance fixture (see
|
||||
`packages/domain/src/domain/sap/README.md`) — don't paper over it in
|
||||
`domain/sap10_calculator/README.md`) — don't paper over it in
|
||||
the integration test.
|
||||
- **BEDF fuel pricing.** The Fuel Bill on the EPC uses postcode-specific
|
||||
BEDF prices (PCDB Table 200), which are deferred. The 4 outputs above
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
|||
import json, dataclasses
|
||||
from pathlib import Path
|
||||
|
||||
api = json.loads(Path('/workspaces/model/packages/domain/src/domain/sap/rdsap/tests/fixtures/golden/0535-9020-6509-0821-6222.json').read_text())
|
||||
api = json.loads(Path('/workspaces/model/domain/sap10_calculator/rdsap/tests/fixtures/golden/0535-9020-6509-0821-6222.json').read_text())
|
||||
api_mapped = EpcPropertyDataMapper.from_api_response(api)
|
||||
pages = _summary_pdf_to_textract_style_pages(Path('/workspaces/model/backend/documents_parser/tests/fixtures/Summary_001479.pdf'))
|
||||
sn = ElmhurstSiteNotesExtractor(pages).extract()
|
||||
|
|
@ -234,7 +234,7 @@ override field, (c) wait for more cert pairs to confirm pattern.
|
|||
|
||||
## Cached artefacts
|
||||
|
||||
- `packages/domain/src/domain/sap/rdsap/tests/fixtures/golden/0535-
|
||||
- `domain/sap10_calculator/rdsap/tests/fixtures/golden/0535-
|
||||
9020-6509-0821-6222.json` — API JSON for cert 001479 (RdSAP-Schema-
|
||||
21.0.1).
|
||||
- `backend/documents_parser/tests/fixtures/Summary_001479.pdf` —
|
||||
|
|
@ -273,8 +273,8 @@ before this rewrite).
|
|||
```bash
|
||||
PYTHONPATH=/workspaces/model:/workspaces/model/packages/domain/src \
|
||||
python -m pytest backend/documents_parser/tests/test_summary_pdf_mapper_chain.py \
|
||||
packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
packages/domain/src/domain/sap/rdsap/tests/test_golden_fixtures.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \
|
||||
--no-cov -q
|
||||
```
|
||||
Expect **99 passed / 19 failed**. All 19 failures pre-existing:
|
||||
|
|
|
|||
|
|
@ -10,21 +10,21 @@ Current Carbon, Current Primary Energy, and Fuel Bill).
|
|||
**Current state: 930/930 pins green** (768 rating + 90 demand + 72 e2e).
|
||||
|
||||
This document is the public API + architecture reference. For fixture
|
||||
authoring see [`packages/domain/src/domain/sap/README.md`](../../packages/domain/src/domain/sap/README.md).
|
||||
authoring see [`domain/sap10_calculator/README.md`](../../domain/sap10_calculator/README.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Public API
|
||||
|
||||
Three entry points, all in `domain.sap.rdsap.cert_to_inputs`:
|
||||
Three entry points, all in `domain.sap10_calculator.rdsap.cert_to_inputs`:
|
||||
|
||||
```python
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
cert_to_inputs, # SAP rating + EI rating (UK-avg climate)
|
||||
cert_to_demand_inputs, # Current Carbon + Current PE (postcode climate)
|
||||
local_climate_for_cert, # postcode → PostcodeClimate (None on miss)
|
||||
)
|
||||
from domain.sap.calculator import calculate_sap_from_inputs, SapResult
|
||||
from domain.sap10_calculator.calculator import calculate_sap_from_inputs, SapResult
|
||||
```
|
||||
|
||||
### 1.1 Rating cascade — `cert_to_inputs(epc)`
|
||||
|
|
@ -93,11 +93,11 @@ upgrade wall insulation), re-run, observe the delta. The shape:
|
|||
|
||||
```python
|
||||
import dataclasses
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
cert_to_inputs, local_climate_for_cert,
|
||||
environmental_section_from_cert, primary_energy_section_from_cert,
|
||||
)
|
||||
from domain.sap.calculator import calculate_sap_from_inputs
|
||||
from domain.sap10_calculator.calculator import calculate_sap_from_inputs
|
||||
|
||||
def dwelling_outputs(epc):
|
||||
"""The 4 EPC-facing outputs for any cert.
|
||||
|
|
@ -165,7 +165,7 @@ Two cascades stacked on a shared physics core:
|
|||
Climate is the only difference between the two cascades. Internally, the
|
||||
climate is plumbed through as either an `int` region index (0..21) or a
|
||||
`PostcodeClimate` instance (PCDB Table 172). Four functions in
|
||||
`domain.sap.climate.appendix_u` dispatch on `isinstance`:
|
||||
`domain.sap10_calculator.climate.appendix_u` dispatch on `isinstance`:
|
||||
`external_temperature_c`, `wind_speed_m_per_s`,
|
||||
`horizontal_solar_irradiance_w_per_m2`, plus `_latitude_deg` in
|
||||
`worksheet/solar_gains.py`.
|
||||
|
|
@ -192,7 +192,7 @@ on `CalculatorInputs`.
|
|||
## 4. File map
|
||||
|
||||
```
|
||||
packages/domain/src/domain/sap/
|
||||
domain/sap10_calculator/
|
||||
├── calculator.py # Top-level orchestrator (CalculatorInputs → SapResult)
|
||||
├── README.md # Fixture authoring cookbook
|
||||
├── rdsap/
|
||||
|
|
@ -288,12 +288,12 @@ monthly_infiltration_ach 6/6
|
|||
|
||||
```bash
|
||||
# Full SAP calculator suite (cascade pins + e2e + helpers)
|
||||
python -m pytest packages/domain/src/domain/sap/ --no-cov
|
||||
python -m pytest domain/sap10_calculator/ --no-cov
|
||||
|
||||
# Cascade pins only (the conformance suite)
|
||||
python -m pytest \
|
||||
packages/domain/src/domain/sap/worksheet/tests/test_section_cascade_pins.py \
|
||||
packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_section_cascade_pins.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
--no-cov --no-header --tb=no -q
|
||||
```
|
||||
|
||||
|
|
@ -316,7 +316,7 @@ These are non-negotiable per `[[feedback-zero-error-strict]]` /
|
|||
|
||||
## 6. Adding a new conformance fixture
|
||||
|
||||
See [`packages/domain/src/domain/sap/README.md#adding-a-new-elmhurst-conformance-fixture`](../../packages/domain/src/domain/sap/README.md#adding-a-new-elmhurst-conformance-fixture)
|
||||
See [`domain/sap10_calculator/README.md#adding-a-new-elmhurst-conformance-fixture`](../../domain/sap10_calculator/README.md#adding-a-new-elmhurst-conformance-fixture)
|
||||
for the step-by-step cookbook. Summary:
|
||||
|
||||
1. Drop a fixture module at `worksheet/tests/_elmhurst_worksheet_NNNNNN.py`
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ The assessor exports two PDFs from Elmhurst's RdSAP tool:
|
|||
|
||||
5. **Run the conformance tests**:
|
||||
```
|
||||
python -m pytest packages/domain/src/domain/sap/worksheet/tests/ \
|
||||
python -m pytest domain/sap10_calculator/worksheet/tests/ \
|
||||
-k elmhurst --no-cov -v
|
||||
```
|
||||
Each fixture appears 3× (one parametrize per section), pytest id = the cert ref number.
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
Drives the 12-month heat-balance loop from a typed `CalculatorInputs`
|
||||
aggregate and emits a typed `SapResult`. This module is the physics
|
||||
assembly only — the RdSAP cert→inputs mapping lives in
|
||||
`domain.sap.rdsap.cert_to_inputs`. Splitting the two keeps orchestration
|
||||
`domain.sap10_calculator.rdsap.cert_to_inputs`. Splitting the two keeps orchestration
|
||||
testable against synthetic inputs without dragging in cert-shape
|
||||
assumptions.
|
||||
|
||||
|
|
@ -44,15 +44,15 @@ from __future__ import annotations
|
|||
from dataclasses import dataclass, field
|
||||
from typing import Final, Optional, TYPE_CHECKING
|
||||
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
from domain.sap10_calculator.climate.appendix_u import external_temperature_c
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||
from domain.sap.worksheet.dimensions import Dimensions
|
||||
from domain.sap.worksheet.energy_requirements import EnergyRequirementsResult
|
||||
from domain.sap.worksheet.fuel_cost import FuelCostResult
|
||||
from domain.sap.worksheet.heat_transmission import HeatTransmission
|
||||
from domain.sap.worksheet.rating import (
|
||||
from domain.sap10_calculator.worksheet.dimensions import Dimensions
|
||||
from domain.sap10_calculator.worksheet.energy_requirements import EnergyRequirementsResult
|
||||
from domain.sap10_calculator.worksheet.fuel_cost import FuelCostResult
|
||||
from domain.sap10_calculator.worksheet.heat_transmission import HeatTransmission
|
||||
from domain.sap10_calculator.worksheet.rating import (
|
||||
ECF_LOG_THRESHOLD,
|
||||
ENERGY_COST_DEFLATOR,
|
||||
FLOOR_AREA_OFFSET_M2,
|
||||
|
|
@ -621,6 +621,6 @@ class Sap10Calculator:
|
|||
"""
|
||||
|
||||
def calculate(self, epc: "EpcPropertyData") -> SapResult:
|
||||
from domain.sap.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import cert_to_inputs
|
||||
|
||||
return calculate_sap_from_inputs(cert_to_inputs(epc))
|
||||
|
|
@ -20,7 +20,7 @@ from __future__ import annotations
|
|||
|
||||
from typing import Final
|
||||
|
||||
from domain.sap.tables.pcdb.postcode_weather import PostcodeClimate
|
||||
from domain.sap10_calculator.tables.pcdb.postcode_weather import PostcodeClimate
|
||||
|
||||
|
||||
# Table U1 — Mean external temperature (°C), 22 regions × 12 months.
|
||||
|
|
@ -8,7 +8,7 @@ global solar irradiance on a horizontal plane and monthly solar declination.
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.climate.appendix_u import (
|
||||
from domain.sap10_calculator.climate.appendix_u import (
|
||||
external_temperature_c,
|
||||
horizontal_solar_irradiance_w_per_m2,
|
||||
solar_declination_deg,
|
||||
|
|
@ -67,82 +67,82 @@ from domain.ml.sap_efficiencies import (
|
|||
seasonal_efficiency,
|
||||
water_heating_efficiency as _legacy_water_heating_efficiency,
|
||||
)
|
||||
from domain.sap.calculator import CalculatorInputs
|
||||
from domain.sap.tables.pcdb import gas_oil_boiler_record
|
||||
from domain.sap.tables.pcdb.parser import GasOilBoilerRecord
|
||||
from domain.sap.tables.pcdb.postcode_weather import (
|
||||
from domain.sap10_calculator.calculator import CalculatorInputs
|
||||
from domain.sap10_calculator.tables.pcdb import gas_oil_boiler_record
|
||||
from domain.sap10_calculator.tables.pcdb.parser import GasOilBoilerRecord
|
||||
from domain.sap10_calculator.tables.pcdb.postcode_weather import (
|
||||
PostcodeClimate,
|
||||
postcode_climate,
|
||||
)
|
||||
from domain.sap.tables.table_12 import (
|
||||
from domain.sap10_calculator.tables.table_12 import (
|
||||
co2_monthly_factors_kg_per_kwh,
|
||||
co2_factor_kg_per_kwh,
|
||||
pe_monthly_factors_kwh_per_kwh,
|
||||
primary_energy_factor,
|
||||
unit_price_p_per_kwh,
|
||||
)
|
||||
from domain.sap.tables.table_12a import (
|
||||
from domain.sap10_calculator.tables.table_12a import (
|
||||
Tariff,
|
||||
tariff_from_meter_type,
|
||||
)
|
||||
from domain.sap.tables.table_32 import (
|
||||
from domain.sap10_calculator.tables.table_32 import (
|
||||
additional_standing_charges_gbp,
|
||||
unit_price_p_per_kwh as table_32_unit_price_p_per_kwh,
|
||||
)
|
||||
from domain.sap.worksheet.fuel_cost import FuelCostResult, fuel_cost
|
||||
from domain.sap.worksheet.rating import (
|
||||
from domain.sap10_calculator.worksheet.fuel_cost import FuelCostResult, fuel_cost
|
||||
from domain.sap10_calculator.worksheet.rating import (
|
||||
ENERGY_COST_DEFLATOR,
|
||||
energy_cost_factor,
|
||||
environmental_impact_rating,
|
||||
sap_rating,
|
||||
sap_rating_integer,
|
||||
)
|
||||
from domain.sap.worksheet.dimensions import dimensions_from_cert
|
||||
from domain.sap.worksheet.internal_gains import (
|
||||
from domain.sap10_calculator.worksheet.dimensions import dimensions_from_cert
|
||||
from domain.sap10_calculator.worksheet.internal_gains import (
|
||||
InternalGainsResult,
|
||||
OvershadingCategory,
|
||||
internal_gains_from_cert,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import (
|
||||
from domain.sap10_calculator.worksheet.solar_gains import (
|
||||
ORIENTATION_BY_SAP10_CODE,
|
||||
RoofWindowInput,
|
||||
SolarGainsResult,
|
||||
solar_gains_from_cert,
|
||||
surface_solar_flux_w_per_m2,
|
||||
)
|
||||
from domain.sap.worksheet.heat_transmission import (
|
||||
from domain.sap10_calculator.worksheet.heat_transmission import (
|
||||
DwellingExposure,
|
||||
HeatTransmission,
|
||||
_AREA_ROUND_DP,
|
||||
_round_half_up,
|
||||
heat_transmission_from_cert,
|
||||
)
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
from domain.sap.worksheet.mean_internal_temperature import (
|
||||
from domain.sap10_calculator.climate.appendix_u import external_temperature_c
|
||||
from domain.sap10_calculator.worksheet.mean_internal_temperature import (
|
||||
MeanInternalTemperatureResult,
|
||||
mean_internal_temperature_monthly,
|
||||
)
|
||||
from domain.sap.worksheet.energy_requirements import (
|
||||
from domain.sap10_calculator.worksheet.energy_requirements import (
|
||||
EnergyRequirementsResult,
|
||||
space_heating_fuel_monthly_kwh,
|
||||
)
|
||||
from domain.sap.worksheet.fabric_energy_efficiency import (
|
||||
from domain.sap10_calculator.worksheet.fabric_energy_efficiency import (
|
||||
fabric_energy_efficiency_kwh_per_m2_yr,
|
||||
)
|
||||
from domain.sap.worksheet.space_cooling import (
|
||||
from domain.sap10_calculator.worksheet.space_cooling import (
|
||||
SpaceCoolingResult,
|
||||
space_cooling_monthly_kwh,
|
||||
)
|
||||
from domain.sap.worksheet.space_heating import (
|
||||
from domain.sap10_calculator.worksheet.space_heating import (
|
||||
SpaceHeatingResult,
|
||||
space_heating_monthly_kwh,
|
||||
)
|
||||
from domain.sap.worksheet.ventilation import (
|
||||
from domain.sap10_calculator.worksheet.ventilation import (
|
||||
MechanicalVentilationKind,
|
||||
VentilationResult,
|
||||
ventilation_from_inputs,
|
||||
)
|
||||
from domain.sap.worksheet.water_heating import (
|
||||
from domain.sap10_calculator.worksheet.water_heating import (
|
||||
TABLE_J1_TCOLD_FROM_MAINS_C,
|
||||
WaterHeatingResult,
|
||||
combi_loss_monthly_kwh_table_3b_row_1_instantaneous,
|
||||
|
|
@ -27,15 +27,15 @@ from domain.ml.tests._fixtures import (
|
|||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.calculator import Sap10Calculator, SapResult
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
from domain.sap10_calculator.calculator import Sap10Calculator, SapResult
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
cert_to_demand_inputs,
|
||||
cert_to_inputs,
|
||||
pcdb_combi_loss_override,
|
||||
)
|
||||
from domain.sap.tables.pcdb import GasOilBoilerRecord, gas_oil_boiler_record
|
||||
from domain.sap.worksheet.tests import _elmhurst_worksheet_000477 as _w000477
|
||||
from domain.sap.worksheet.water_heating import (
|
||||
from domain.sap10_calculator.tables.pcdb import GasOilBoilerRecord, gas_oil_boiler_record
|
||||
from domain.sap10_calculator.worksheet.tests import _elmhurst_worksheet_000477 as _w000477
|
||||
from domain.sap10_calculator.worksheet.water_heating import (
|
||||
combi_loss_monthly_kwh_table_3b_row_1_instantaneous,
|
||||
combi_loss_monthly_kwh_table_3c_two_profile_instantaneous,
|
||||
)
|
||||
|
|
@ -36,8 +36,8 @@ from typing import Any
|
|||
import pytest
|
||||
|
||||
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
||||
from domain.sap.calculator import calculate_sap_from_inputs
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
from domain.sap10_calculator.calculator import calculate_sap_from_inputs
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
SAP_10_2_SPEC_PRICES,
|
||||
cert_to_demand_inputs,
|
||||
cert_to_inputs,
|
||||
|
|
@ -27,13 +27,13 @@ import json
|
|||
from pathlib import Path
|
||||
from typing import Final, Optional
|
||||
|
||||
from domain.sap.tables.pcdb.parser import GasOilBoilerRecord
|
||||
from domain.sap10_calculator.tables.pcdb.parser import GasOilBoilerRecord
|
||||
|
||||
__all__ = ["GasOilBoilerRecord", "gas_oil_boiler_record"]
|
||||
|
||||
|
||||
_REPO_SAP_SPEC_DIR: Final[Path] = (
|
||||
Path(__file__).resolve().parents[7] / "docs" / "sap-spec"
|
||||
Path(__file__).resolve().parents[4] / "docs" / "sap-spec"
|
||||
)
|
||||
_TABLE_105_JSONL: Final[Path] = (
|
||||
_REPO_SAP_SPEC_DIR / "pcdb_table_105_gas_oil_boilers.jsonl"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Idempotent. Re-run when BRE publishes an updated pcdb10.dat. JSON files
|
||||
are committed in-repo alongside the source .dat so callers can load
|
||||
without a build step. Run via `python -m domain.sap.tables.pcdb.etl`.
|
||||
without a build step. Run via `python -m domain.sap10_calculator.tables.pcdb.etl`.
|
||||
|
||||
Reference: BRE PCDB pcdb10.dat (April 2026 revision).
|
||||
"""
|
||||
|
|
@ -13,7 +13,7 @@ import json
|
|||
from dataclasses import asdict
|
||||
from pathlib import Path
|
||||
|
||||
from domain.sap.tables.pcdb.parser import (
|
||||
from domain.sap10_calculator.tables.pcdb.parser import (
|
||||
GasOilBoilerRecord,
|
||||
RawPcdbRecord,
|
||||
parse_table_105,
|
||||
|
|
@ -75,7 +75,7 @@ def run_etl(*, source: Path, output_dir: Path) -> None:
|
|||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover — manual ETL invocation
|
||||
repo_root = Path(__file__).resolve().parents[7]
|
||||
repo_root = Path(__file__).resolve().parents[4]
|
||||
run_etl(
|
||||
source=repo_root / "docs" / "sap-spec" / "pcdb10.dat",
|
||||
output_dir=repo_root / "docs" / "sap-spec",
|
||||
|
|
@ -21,7 +21,7 @@ from typing import Final, Optional
|
|||
|
||||
|
||||
_PCDB_DAT_PATH: Final[Path] = (
|
||||
Path(__file__).resolve().parents[7] / "docs" / "sap-spec" / "pcdb10.dat"
|
||||
Path(__file__).resolve().parents[4] / "docs" / "sap-spec" / "pcdb10.dat"
|
||||
)
|
||||
_TABLE_172_TAG: Final[str] = "$172"
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ factors are largely unchanged. When the corpus migrates to SAP 10.3
|
|||
this module re-points to those values.
|
||||
|
||||
The Energy Cost Deflator stays at 0.36 (used in ECF — see
|
||||
`domain.sap.worksheet.rating`).
|
||||
`domain.sap10_calculator.worksheet.rating`).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -7,14 +7,14 @@ targets RdSAP10 cost per ADR-0010 amendment.
|
|||
|
||||
CO2 emission factors and primary energy factors are unchanged from
|
||||
SAP10.2 Table 12 (RdSAP10 §19.2), so they continue to live in
|
||||
`domain.sap.tables.table_12` rather than being duplicated here.
|
||||
`domain.sap10_calculator.tables.table_12` rather than being duplicated here.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final, Optional
|
||||
|
||||
from domain.sap.tables.table_12a import Tariff
|
||||
from domain.sap10_calculator.tables.table_12a import Tariff
|
||||
|
||||
|
||||
_DEFAULT_P_PER_KWH: Final[float] = 3.48 # fall back to mains gas
|
||||
|
|
@ -23,17 +23,17 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.calculator import (
|
||||
from domain.sap10_calculator.calculator import (
|
||||
CalculatorInputs,
|
||||
calculate_sap_from_inputs,
|
||||
)
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
from domain.sap.worksheet.dimensions import Dimensions
|
||||
from domain.sap.worksheet.heat_transmission import HeatTransmission
|
||||
from domain.sap.worksheet.mean_internal_temperature import (
|
||||
from domain.sap10_calculator.climate.appendix_u import external_temperature_c
|
||||
from domain.sap10_calculator.worksheet.dimensions import Dimensions
|
||||
from domain.sap10_calculator.worksheet.heat_transmission import HeatTransmission
|
||||
from domain.sap10_calculator.worksheet.mean_internal_temperature import (
|
||||
mean_internal_temperature_monthly,
|
||||
)
|
||||
from domain.sap.worksheet.space_heating import space_heating_monthly_kwh
|
||||
from domain.sap10_calculator.worksheet.space_heating import space_heating_monthly_kwh
|
||||
|
||||
|
||||
def _baseline_dwelling() -> CalculatorInputs:
|
||||
|
|
@ -20,18 +20,18 @@ from dataclasses import replace
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.calculator import (
|
||||
from domain.sap10_calculator.calculator import (
|
||||
CalculatorInputs,
|
||||
SapResult,
|
||||
calculate_sap_from_inputs,
|
||||
)
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
from domain.sap.worksheet.dimensions import Dimensions
|
||||
from domain.sap.worksheet.heat_transmission import HeatTransmission
|
||||
from domain.sap.worksheet.mean_internal_temperature import (
|
||||
from domain.sap10_calculator.climate.appendix_u import external_temperature_c
|
||||
from domain.sap10_calculator.worksheet.dimensions import Dimensions
|
||||
from domain.sap10_calculator.worksheet.heat_transmission import HeatTransmission
|
||||
from domain.sap10_calculator.worksheet.mean_internal_temperature import (
|
||||
mean_internal_temperature_monthly,
|
||||
)
|
||||
from domain.sap.worksheet.space_heating import space_heating_monthly_kwh
|
||||
from domain.sap10_calculator.worksheet.space_heating import space_heating_monthly_kwh
|
||||
|
||||
|
||||
def _baseline_inputs() -> CalculatorInputs:
|
||||
|
|
@ -15,15 +15,15 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.tables.pcdb.etl import run_etl
|
||||
from domain.sap.tables.pcdb.parser import (
|
||||
from domain.sap10_calculator.tables.pcdb.etl import run_etl
|
||||
from domain.sap10_calculator.tables.pcdb.parser import (
|
||||
parse_table_105,
|
||||
parse_table_105_row,
|
||||
parse_table_raw,
|
||||
)
|
||||
|
||||
|
||||
_REPO_ROOT: Path = Path(__file__).resolve().parents[6]
|
||||
_REPO_ROOT: Path = Path(__file__).resolve().parents[3]
|
||||
_PCDB_DAT_PATH: Path = _REPO_ROOT / "docs" / "sap-spec" / "pcdb10.dat"
|
||||
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ Reference: BRE PCDB pcdb10.dat (April 2026); user-verified records.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from domain.sap.tables.pcdb import gas_oil_boiler_record
|
||||
from domain.sap10_calculator.tables.pcdb import gas_oil_boiler_record
|
||||
|
||||
|
||||
def test_gas_oil_boiler_record_returns_verified_baxi_98() -> None:
|
||||
|
|
@ -10,7 +10,7 @@ Reference: BRE PCDB pcdb10.dat Table 172 (Postcodes).
|
|||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from domain.sap.tables.pcdb.postcode_weather import (
|
||||
from domain.sap10_calculator.tables.pcdb.postcode_weather import (
|
||||
PostcodeClimate,
|
||||
postcode_climate,
|
||||
)
|
||||
|
|
@ -15,7 +15,7 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.tables.table_12 import (
|
||||
from domain.sap10_calculator.tables.table_12 import (
|
||||
co2_factor_kg_per_kwh,
|
||||
primary_energy_factor,
|
||||
unit_price_p_per_kwh,
|
||||
|
|
@ -12,7 +12,7 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.tables.table_12a import (
|
||||
from domain.sap10_calculator.tables.table_12a import (
|
||||
OtherUse,
|
||||
Table12aSystem,
|
||||
Tariff,
|
||||
|
|
@ -13,8 +13,8 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.tables.table_12a import Tariff
|
||||
from domain.sap.tables.table_32 import (
|
||||
from domain.sap10_calculator.tables.table_12a import Tariff
|
||||
from domain.sap10_calculator.tables.table_32 import (
|
||||
additional_standing_charges_gbp,
|
||||
standing_charge_gbp,
|
||||
unit_price_p_per_kwh,
|
||||
|
|
@ -15,7 +15,7 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.validation.parity_report import (
|
||||
from domain.sap10_calculator.validation.parity_report import (
|
||||
ParityCase,
|
||||
ParityReport,
|
||||
build_parity_report,
|
||||
|
|
@ -30,7 +30,7 @@ will retire envelope.py in favour of this module (ADR-0009 §"Module
|
|||
layout").
|
||||
|
||||
U-value lookups cascade through `domain.ml.rdsap_uvalues` — migrating to
|
||||
`domain.sap.rdsap.cascade_defaults` in Session B.
|
||||
`domain.sap10_calculator.rdsap.cascade_defaults` in Session B.
|
||||
|
||||
Reference: SAP 10.2 specification §3 (pages 17-22); RdSAP 10 §5 (Tables
|
||||
6-24); xlsx worked example at `2026-05-19-17-18 RdSap10Worksheet.xlsx`,
|
||||
|
|
@ -23,7 +23,7 @@ from __future__ import annotations
|
|||
from dataclasses import dataclass
|
||||
from typing import Final
|
||||
|
||||
from domain.sap.worksheet.utilisation_factor import utilisation_factor
|
||||
from domain.sap10_calculator.worksheet.utilisation_factor import utilisation_factor
|
||||
|
||||
|
||||
_T_H1_C: Final[float] = 21.0
|
||||
|
|
@ -6,7 +6,7 @@ Two layers:
|
|||
implements the §U3.2 polynomial that converts the horizontal solar
|
||||
irradiance from Table U3 into a per-orientation per-pitch surface flux.
|
||||
Reads:
|
||||
- S_h,m from Appendix U Table U3 (already in `domain.sap.climate.appendix_u`)
|
||||
- S_h,m from Appendix U Table U3 (already in `domain.sap10_calculator.climate.appendix_u`)
|
||||
- δ from Appendix U Table U3 footer (already in `appendix_u.solar_declination_deg`)
|
||||
- φ from Appendix U Table U4 (this module)
|
||||
- k1..k9 from Appendix U Table U5 (this module)
|
||||
|
|
@ -35,12 +35,12 @@ from math import cos, floor, radians, sin
|
|||
from typing import Final
|
||||
|
||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData, SapWindow
|
||||
from domain.sap.tables.pcdb.postcode_weather import PostcodeClimate
|
||||
from domain.sap.climate.appendix_u import (
|
||||
from domain.sap10_calculator.tables.pcdb.postcode_weather import PostcodeClimate
|
||||
from domain.sap10_calculator.climate.appendix_u import (
|
||||
horizontal_solar_irradiance_w_per_m2,
|
||||
solar_declination_deg,
|
||||
)
|
||||
from domain.sap.worksheet.internal_gains import OvershadingCategory
|
||||
from domain.sap10_calculator.worksheet.internal_gains import OvershadingCategory
|
||||
|
||||
|
||||
def _round_area_2dp(value: float) -> float:
|
||||
|
|
@ -18,7 +18,7 @@ from __future__ import annotations
|
|||
from dataclasses import dataclass
|
||||
from typing import Final
|
||||
|
||||
from domain.sap.worksheet.heat_transmission import _round_half_up
|
||||
from domain.sap10_calculator.worksheet.heat_transmission import _round_half_up
|
||||
|
||||
|
||||
_MIN_KWH_PER_MONTH: Final[float] = 1.0
|
||||
|
|
@ -24,7 +24,7 @@ SECTION_8C_INTERMITTENCY_MONTHLY: tuple[float, ...] = (
|
|||
0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.25, 0.25, 0.0, 0.0, 0.0, 0.0,
|
||||
)
|
||||
|
||||
from domain.sap.worksheet.tests import (
|
||||
from domain.sap10_calculator.worksheet.tests import (
|
||||
_elmhurst_worksheet_000474 as w000474,
|
||||
_elmhurst_worksheet_000477 as w000477,
|
||||
_elmhurst_worksheet_000480 as w000480,
|
||||
|
|
@ -37,11 +37,11 @@ from domain.ml.tests._fixtures import (
|
|||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
from domain.sap10_calculator.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap10_calculator.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap10_calculator.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import (
|
||||
SECTION_8C_ALL_ZERO_MONTHLY,
|
||||
SECTION_8C_ETA_LOSS_ALL_ONE,
|
||||
SECTION_8C_INTERMITTENCY_MONTHLY,
|
||||
|
|
@ -35,11 +35,11 @@ from domain.ml.tests._fixtures import (
|
|||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
from domain.sap10_calculator.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap10_calculator.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap10_calculator.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import (
|
||||
SECTION_8C_ALL_ZERO_MONTHLY,
|
||||
SECTION_8C_ETA_LOSS_ALL_ONE,
|
||||
SECTION_8C_INTERMITTENCY_MONTHLY,
|
||||
|
|
@ -36,11 +36,11 @@ from domain.ml.tests._fixtures import (
|
|||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
from domain.sap10_calculator.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap10_calculator.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap10_calculator.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import (
|
||||
SECTION_8C_ALL_ZERO_MONTHLY,
|
||||
SECTION_8C_ETA_LOSS_ALL_ONE,
|
||||
SECTION_8C_INTERMITTENCY_MONTHLY,
|
||||
|
|
@ -34,8 +34,8 @@ from domain.ml.tests._fixtures import (
|
|||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
from domain.sap10_calculator.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap10_calculator.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
# RdSAP wall_construction code seen in the cert→worksheet mapping. The
|
||||
# Summary lists "CA Cavity" for both main and extension walls. The alt
|
||||
|
|
@ -258,9 +258,9 @@ def build_epc() -> EpcPropertyData:
|
|||
# on the EpcPropertyData domain object — we pass these into
|
||||
# `ventilation_from_inputs` alongside the cert-derived geometry).
|
||||
# ============================================================================
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap10_calculator.worksheet.ventilation import MechanicalVentilationKind
|
||||
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import (
|
||||
SECTION_8C_ALL_ZERO_MONTHLY,
|
||||
SECTION_8C_ETA_LOSS_ALL_ONE,
|
||||
SECTION_8C_INTERMITTENCY_MONTHLY,
|
||||
|
|
@ -39,11 +39,11 @@ from domain.ml.tests._fixtures import (
|
|||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
from domain.sap10_calculator.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap10_calculator.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap10_calculator.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import (
|
||||
SECTION_8C_ALL_ZERO_MONTHLY,
|
||||
SECTION_8C_ETA_LOSS_ALL_ONE,
|
||||
SECTION_8C_INTERMITTENCY_MONTHLY,
|
||||
|
|
@ -42,11 +42,11 @@ from domain.ml.tests._fixtures import (
|
|||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import Orientation, RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
from domain.sap10_calculator.worksheet.solar_gains import Orientation, RoofWindowInput, RooflightInput
|
||||
from domain.sap10_calculator.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap10_calculator.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import (
|
||||
SECTION_8C_ALL_ZERO_MONTHLY,
|
||||
SECTION_8C_ETA_LOSS_ALL_ONE,
|
||||
SECTION_8C_INTERMITTENCY_MONTHLY,
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"""Reader for the canonical SAP10.2 worksheet Excel — the source of
|
||||
truth used to build line-by-line conformance tests against
|
||||
`packages/domain/src/domain/sap/worksheet/`.
|
||||
`domain/sap10_calculator/worksheet/`.
|
||||
|
||||
The Excel file lives at the repo root and has two sheets — both contain
|
||||
the full worksheet, differing only on weather-data source:
|
||||
|
|
@ -20,7 +20,7 @@ from typing import Any, Iterable
|
|||
|
||||
import openpyxl
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[7]
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||
WORKSHEET_XLSX_PATH = _REPO_ROOT / "2026-05-19-17-18 RdSap10Worksheet.xlsx"
|
||||
|
||||
|
||||
|
|
@ -26,8 +26,8 @@ from domain.ml.tests._fixtures import (
|
|||
make_floor_dimension,
|
||||
make_minimal_sap10_epc,
|
||||
)
|
||||
from domain.sap.worksheet.dimensions import Dimensions, dimensions_from_cert
|
||||
from domain.sap.worksheet.tests._xlsx_loader import load_cells
|
||||
from domain.sap10_calculator.worksheet.dimensions import Dimensions, dimensions_from_cert
|
||||
from domain.sap10_calculator.worksheet.tests._xlsx_loader import load_cells
|
||||
|
||||
_RIR_FIXTURES_DIR = Path(__file__).parent / "fixtures" / "rir"
|
||||
|
||||
|
|
@ -507,7 +507,7 @@ def test_all_rir_shapes_apply_section_1_2_45m_convention_uniformly(
|
|||
|
||||
|
||||
from types import ModuleType # noqa: E402 (kept near the Elmhurst tests)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ( # noqa: E402
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ( # noqa: E402
|
||||
ALL_FIXTURES as _ELMHURST_FIXTURES,
|
||||
fixture_id as _elmhurst_fixture_id,
|
||||
)
|
||||
|
|
@ -24,9 +24,9 @@ from typing import Final
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.calculator import Sap10Calculator
|
||||
from domain.sap.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap.worksheet.tests import (
|
||||
from domain.sap10_calculator.calculator import Sap10Calculator
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap10_calculator.worksheet.tests import (
|
||||
_elmhurst_worksheet_000474 as _w000474,
|
||||
_elmhurst_worksheet_000477 as _w000477,
|
||||
_elmhurst_worksheet_000480 as _w000480,
|
||||
|
|
@ -35,7 +35,7 @@ from domain.sap.worksheet.tests import (
|
|||
_elmhurst_worksheet_000516 as _w000516,
|
||||
_elmhurst_worksheet_001479 as _w001479,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import (
|
||||
ALL_FIXTURES as _ELMHURST_FIXTURES,
|
||||
fixture_id as _elmhurst_fixture_id,
|
||||
)
|
||||
|
|
@ -6,7 +6,7 @@ Reference: SAP 10.2 specification (14-03-2025) worksheet block §9a
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from domain.sap.worksheet.energy_requirements import (
|
||||
from domain.sap10_calculator.worksheet.energy_requirements import (
|
||||
space_heating_fuel_monthly_kwh,
|
||||
)
|
||||
|
||||
|
|
@ -10,10 +10,10 @@ from types import ModuleType
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.worksheet.fabric_energy_efficiency import (
|
||||
from domain.sap10_calculator.worksheet.fabric_energy_efficiency import (
|
||||
fabric_energy_efficiency_kwh_per_m2_yr,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
|
||||
|
||||
def test_fabric_energy_efficiency_sums_heating_per_m2_and_cooling_per_m2() -> None:
|
||||
|
|
@ -8,8 +8,8 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap.worksheet.fuel_cost import FuelCostResult, fuel_cost
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap10_calculator.worksheet.fuel_cost import FuelCostResult, fuel_cost
|
||||
|
||||
|
||||
def test_single_rate_main_only_bills_kwh_at_high_rate_price() -> None:
|
||||
|
|
@ -7,7 +7,7 @@ a typed `HeatTransmission` breakdown so callers can audit each
|
|||
worksheet contribution.
|
||||
|
||||
U-values cascade through the RdSAP 10 §5 defaults (currently implemented
|
||||
in `domain.ml.rdsap_uvalues` — migrates to `domain.sap.rdsap.cascade_defaults`
|
||||
in `domain.ml.rdsap_uvalues` — migrates to `domain.sap10_calculator.rdsap.cascade_defaults`
|
||||
in Session B).
|
||||
|
||||
Reference: SAP 10.3 specification §3 (pages 17-22);
|
||||
|
|
@ -30,7 +30,7 @@ from domain.ml.tests._fixtures import (
|
|||
make_floor_dimension,
|
||||
make_minimal_sap10_epc,
|
||||
)
|
||||
from domain.sap.worksheet.heat_transmission import (
|
||||
from domain.sap10_calculator.worksheet.heat_transmission import (
|
||||
DwellingExposure,
|
||||
HeatTransmission,
|
||||
heat_transmission_from_cert,
|
||||
|
|
@ -1132,11 +1132,11 @@ def test_real_corpus_basement_cert_has_part_with_has_basement_true() -> None:
|
|||
|
||||
|
||||
from types import ModuleType # noqa: E402
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ( # noqa: E402
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ( # noqa: E402
|
||||
ALL_FIXTURES as _ELMHURST_FIXTURES,
|
||||
fixture_id as _elmhurst_fixture_id,
|
||||
)
|
||||
from domain.sap.worksheet.tests import ( # noqa: E402
|
||||
from domain.sap10_calculator.worksheet.tests import ( # noqa: E402
|
||||
_elmhurst_worksheet_000474 as _w000474,
|
||||
_elmhurst_worksheet_000490 as _w000490,
|
||||
)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"""Tests for SAP 10.2 §5 + Appendix L — internal gains.
|
||||
|
||||
Worksheet line refs (66)..(73) land in `domain.sap.worksheet.internal_gains`
|
||||
Worksheet line refs (66)..(73) land in `domain.sap10_calculator.worksheet.internal_gains`
|
||||
as monthly 12-tuple outputs. Each leaf function is unit-tested against the
|
||||
SAP 10.2 spec formula; the orchestrator is parametrized against every
|
||||
Elmhurst conformance fixture in `_elmhurst_fixtures.ALL_FIXTURES`.
|
||||
|
|
@ -12,7 +12,7 @@ Table 5a + Appendix L (lighting/appliances/cooking) + Appendix J Table 1b
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.worksheet.internal_gains import (
|
||||
from domain.sap10_calculator.worksheet.internal_gains import (
|
||||
InternalGainsResult,
|
||||
OvershadingCategory,
|
||||
PumpDateCategory,
|
||||
|
|
@ -44,7 +44,7 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
|
||||
|
||||
def test_metabolic_gains_are_60w_per_occupant_constant_across_months() -> None:
|
||||
|
|
@ -14,14 +14,14 @@ from types import ModuleType
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
from domain.sap.worksheet.mean_internal_temperature import (
|
||||
from domain.sap10_calculator.climate.appendix_u import external_temperature_c
|
||||
from domain.sap10_calculator.worksheet.mean_internal_temperature import (
|
||||
MeanInternalTemperatureResult,
|
||||
elsewhere_heating_temperature_c,
|
||||
mean_internal_temperature_monthly,
|
||||
off_period_temperature_reduction_c,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
|
||||
|
||||
# UK-average climate (region 0) external temperatures, Appendix U Table U1.
|
||||
|
|
@ -19,7 +19,7 @@ target is SAP 10.2.
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.worksheet.rating import (
|
||||
from domain.sap10_calculator.worksheet.rating import (
|
||||
energy_cost_factor,
|
||||
environmental_impact_rating,
|
||||
sap_rating,
|
||||
|
|
@ -16,7 +16,7 @@ from typing import Final
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
||||
cert_to_inputs,
|
||||
energy_requirements_section_from_cert,
|
||||
environmental_section_from_cert,
|
||||
|
|
@ -34,8 +34,8 @@ from domain.sap.rdsap.cert_to_inputs import (
|
|||
ventilation_from_cert,
|
||||
water_heating_section_from_cert,
|
||||
)
|
||||
from domain.sap.worksheet.dimensions import dimensions_from_cert
|
||||
from domain.sap.worksheet.tests import (
|
||||
from domain.sap10_calculator.worksheet.dimensions import dimensions_from_cert
|
||||
from domain.sap10_calculator.worksheet.tests import (
|
||||
_elmhurst_worksheet_000474 as _w000474,
|
||||
_elmhurst_worksheet_000477 as _w000477,
|
||||
_elmhurst_worksheet_000480 as _w000480,
|
||||
|
|
@ -19,8 +19,8 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
WindowTransmissionDetails,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.sap.worksheet.internal_gains import OvershadingCategory
|
||||
from domain.sap.worksheet.solar_gains import (
|
||||
from domain.sap10_calculator.worksheet.internal_gains import OvershadingCategory
|
||||
from domain.sap10_calculator.worksheet.solar_gains import (
|
||||
Orientation,
|
||||
RoofWindowInput,
|
||||
RooflightInput,
|
||||
|
|
@ -29,7 +29,7 @@ from domain.sap.worksheet.solar_gains import (
|
|||
window_solar_gain_w,
|
||||
z_solar_for_overshading,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
|
||||
|
||||
# Worksheet U985-0001-000490 reference (UK-avg weather, region 0):
|
||||
|
|
@ -9,12 +9,12 @@ from types import ModuleType
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
from domain.sap.worksheet.space_cooling import (
|
||||
from domain.sap10_calculator.climate.appendix_u import external_temperature_c
|
||||
from domain.sap10_calculator.worksheet.space_cooling import (
|
||||
space_cooling_monthly_kwh,
|
||||
utilisation_factor_loss,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
|
||||
|
||||
_FULLY_INACTIVE_GAINS_WINTER_TE_C: float = -10.0
|
||||
|
|
@ -11,13 +11,13 @@ from types import ModuleType
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
from domain.sap.worksheet.space_heating import (
|
||||
from domain.sap10_calculator.climate.appendix_u import external_temperature_c
|
||||
from domain.sap10_calculator.worksheet.space_heating import (
|
||||
SpaceHeatingResult,
|
||||
monthly_heat_requirement_kwh,
|
||||
space_heating_monthly_kwh,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
|
||||
|
||||
_UK_AVG_EXT_TEMP_C: tuple[float, ...] = tuple(
|
||||
|
|
@ -9,7 +9,7 @@ Reference: SAP 10.3 specification (13-01-2026) Table 9a (page 184).
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.worksheet.utilisation_factor import utilisation_factor
|
||||
from domain.sap10_calculator.worksheet.utilisation_factor import utilisation_factor
|
||||
|
||||
|
||||
def test_small_gain_loss_ratio_returns_eta_close_to_one() -> None:
|
||||
|
|
@ -12,8 +12,8 @@ Canonical worked example: `2026-05-19-17-18 RdSap10Worksheet.xlsx`,
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.worksheet.tests._xlsx_loader import load_cells
|
||||
from domain.sap.worksheet.ventilation import (
|
||||
from domain.sap10_calculator.worksheet.tests._xlsx_loader import load_cells
|
||||
from domain.sap10_calculator.worksheet.ventilation import (
|
||||
MechanicalVentilationKind,
|
||||
TABLE_U2_NON_REGIONAL_WIND_SPEED_M_S,
|
||||
VentilationResult,
|
||||
|
|
@ -461,7 +461,7 @@ def test_excel_worksheet_conformance_section_2_lines_6a_to_25m() -> None:
|
|||
|
||||
|
||||
from types import ModuleType # noqa: E402
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ( # noqa: E402
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ( # noqa: E402
|
||||
ALL_FIXTURES as _ELMHURST_FIXTURES,
|
||||
fixture_id as _elmhurst_fixture_id,
|
||||
)
|
||||
|
|
@ -479,7 +479,7 @@ def test_section_2_matches_elmhurst_worksheet(fixture: ModuleType) -> None:
|
|||
worksheet's (9) value.
|
||||
"""
|
||||
# Arrange
|
||||
from domain.sap.worksheet.dimensions import dimensions_from_cert
|
||||
from domain.sap10_calculator.worksheet.dimensions import dimensions_from_cert
|
||||
dims = dimensions_from_cert(fixture.build_epc())
|
||||
assert dims.storey_count == fixture.LINE_9_STOREYS, (
|
||||
f"dims.storey_count={dims.storey_count} should equal worksheet (9) "
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"""Tests for SAP 10.2 §4 — water heating energy requirements.
|
||||
|
||||
Worksheet line refs land in `domain.sap.worksheet.water_heating`. Each
|
||||
Worksheet line refs land in `domain.sap10_calculator.worksheet.water_heating`. Each
|
||||
test asserts a single line-ref output against the canonical xlsx worked
|
||||
example and/or Elmhurst conformance fixtures.
|
||||
|
||||
|
|
@ -12,14 +12,14 @@ from types import ModuleType
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.worksheet.tests import (
|
||||
from domain.sap10_calculator.worksheet.tests import (
|
||||
_elmhurst_worksheet_000474 as _w000474,
|
||||
_elmhurst_worksheet_000477 as _w000477,
|
||||
_elmhurst_worksheet_000490 as _w000490,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap.worksheet.tests._xlsx_loader import load_cells
|
||||
from domain.sap.worksheet.water_heating import (
|
||||
from domain.sap10_calculator.worksheet.tests._elmhurst_fixtures import ALL_FIXTURES, fixture_id
|
||||
from domain.sap10_calculator.worksheet.tests._xlsx_loader import load_cells
|
||||
from domain.sap10_calculator.worksheet.water_heating import (
|
||||
TABLE_J1_TCOLD_FROM_MAINS_C,
|
||||
annual_average_hot_water_l_per_day,
|
||||
annual_average_hot_water_other_uses_l_per_day,
|
||||
|
|
@ -654,7 +654,7 @@ def test_000474_cert_to_inputs_hot_water_kwh_closes_within_1pct_post_slice_2() -
|
|||
Equation D1 monthly cascade → effective annual η ~88% (vs the
|
||||
87.0% summer scalar) → HW kWh 2320 → ~2290 (+0% target)."""
|
||||
# Arrange
|
||||
from domain.sap.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import cert_to_inputs
|
||||
|
||||
epc = _w000474.build_epc()
|
||||
|
||||
|
|
@ -675,8 +675,8 @@ def test_000490_cert_to_inputs_hot_water_kwh_closes_via_equation_d1() -> None:
|
|||
(+6.2%). Post-slice-2 Equation D1 cascade → HW kWh closes toward
|
||||
2851 (target ±2%)."""
|
||||
# Arrange
|
||||
from domain.sap.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap.worksheet.tests import _elmhurst_worksheet_000490 as _w000490
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap10_calculator.worksheet.tests import _elmhurst_worksheet_000490 as _w000490
|
||||
|
||||
epc = _w000490.build_epc()
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
"""Shared domain types for the Ara modelling pipeline and sibling Domna services.
|
||||
|
||||
No persistence, no IO, no business logic. See README.md for layout.
|
||||
"""
|
||||
|
|
@ -232,7 +232,7 @@ def predicted_lighting_kwh(
|
|||
Missing counts treated as zero.
|
||||
|
||||
DEPRECATED for SAP rating use. The spec-faithful Appendix L L1-L11
|
||||
cascade is in `domain.sap.worksheet.internal_gains.annual_lighting_kwh`
|
||||
cascade is in `domain.sap10_calculator.worksheet.internal_gains.annual_lighting_kwh`
|
||||
and is what `cert_to_inputs` now plumbs into `inputs.lighting_kwh_per_yr`.
|
||||
This heuristic over-counts ~3× on the Elmhurst cohort (528 vs 140 kWh
|
||||
on 000474). Kept only for `domain.ml.ecf.energy_cost_factor` and
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue