mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Map surveyed PasHub PV arrays into the Appendix M photovoltaic_arrays input 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bc58cf4930
commit
9a1422f6d7
2 changed files with 63 additions and 0 deletions
|
|
@ -74,6 +74,54 @@ class TestRoofConstructionTypeCoding:
|
|||
assert extension_part.roof_construction_type == "Flat"
|
||||
|
||||
|
||||
class TestPhotovoltaicArrayMapping:
|
||||
"""A surveyed per-system PV block ("PV 1: kWp value: 2.43 kWp", pitch 30°,
|
||||
orientation "South East", overshading "None or very little") must reach
|
||||
`sap_energy_source.photovoltaic_arrays` — the ONLY field the calculator's
|
||||
Appendix M generation path reads. Dropping it silently zeroes the PV credit
|
||||
(issue #1590 bug 1; fixture 507639151843 under-rates by 13.6 SAP).
|
||||
"""
|
||||
|
||||
def test_surveyed_pv_array_reaches_photovoltaic_arrays(self) -> None:
|
||||
# Arrange — the survey's PV block: 2.43 kWp, 30°, SE, no overshading.
|
||||
data = load("pashub_rdsap_site_notes_example1.json")
|
||||
data["renewables"]["photovoltaic_array"] = True
|
||||
data["renewables"]["pv_arrays"] = [
|
||||
{
|
||||
"kwp": 2.43,
|
||||
"pitch_degrees": 30,
|
||||
"orientation": "South East",
|
||||
"overshading": "None or very little",
|
||||
}
|
||||
]
|
||||
survey = from_dict(PasHubRdSapSiteNotes, data)
|
||||
|
||||
# Act
|
||||
result = EpcPropertyDataMapper.from_site_notes(survey)
|
||||
|
||||
# Assert — SAP codes: pitch 30° → 2, "South East" → octant 4,
|
||||
# "None or very little" → 1 (ZPV=1.0).
|
||||
arrays = result.sap_energy_source.photovoltaic_arrays
|
||||
assert arrays is not None
|
||||
assert len(arrays) == 1
|
||||
assert arrays[0].peak_power == 2.43
|
||||
assert arrays[0].pitch == 2
|
||||
assert arrays[0].orientation == 4
|
||||
assert arrays[0].overshading == 1
|
||||
|
||||
def test_no_pv_block_keeps_arrays_absent(self) -> None:
|
||||
# Arrange — the fixture as lodged (no PV).
|
||||
survey = from_dict(
|
||||
PasHubRdSapSiteNotes, load("pashub_rdsap_site_notes_example1.json")
|
||||
)
|
||||
|
||||
# Act
|
||||
result = EpcPropertyDataMapper.from_site_notes(survey)
|
||||
|
||||
# Assert
|
||||
assert result.sap_energy_source.photovoltaic_arrays is None
|
||||
|
||||
|
||||
class TestRoofInsulationNoneCoding:
|
||||
"""A surveyed roof-space "Insulation At: None" is the assessor explicitly
|
||||
recording ZERO loft insulation — `from_site_notes` must lodge an explicit
|
||||
|
|
|
|||
|
|
@ -222,6 +222,19 @@ class Conservatories:
|
|||
has_conservatory: bool
|
||||
|
||||
|
||||
@dataclass
|
||||
class PvArrayDetail:
|
||||
"""One surveyed PV array from the Renewables section's per-system block
|
||||
("PV {n}: kWp value / Photovoltaic Pitch / Orientation / Overshading").
|
||||
Raw survey labels for orientation/overshading — coding to SAP ints happens
|
||||
at the mapper boundary (ADR-0015)."""
|
||||
|
||||
kwp: Optional[float] = None
|
||||
pitch_degrees: Optional[int] = None
|
||||
orientation: Optional[str] = None
|
||||
overshading: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Renewables:
|
||||
wind_turbines: bool
|
||||
|
|
@ -231,6 +244,8 @@ class Renewables:
|
|||
hydro: bool
|
||||
pv_connection: Optional[str] = None
|
||||
percent_roof_covered_pv: Optional[int] = None
|
||||
pv_diverter_present: Optional[bool] = None
|
||||
pv_arrays: Optional[List[PvArrayDetail]] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue