S0380.226: map Elmhurst "Jacket" cylinder insulation → loose-jacket (code 2)

The Summary-path mapper raised UnmappedElmhurstLabel for a §15.1
"Cylinder Insulation Type: Jacket" lodging — only "Foam" (→1, factory)
was mapped. SAP10 cylinder_insulation_type uses 2 for loose jacket
(matching the GOV.UK API codes), and SAP 10.2 Table 2 Note 1 gives it a
separate ~2× storage-loss factor that the cascade now handles
(S0380.224). Add "Jacket" → 2 for cross-mapper parity with the API path
and so the loose-jacket storage-loss branch fires on the Summary path.

Surfaced by simulated case 19 (a 210 L jacket cylinder + electric storage
heaters), which previously couldn't extract at all. §4 suite 2397 passed;
mapper.py pyright unchanged at 32.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-04 17:07:37 +00:00
parent 3b442f9606
commit c236aa5836
2 changed files with 35 additions and 0 deletions

View file

@ -4330,3 +4330,32 @@ def test_from_elmhurst_site_notes_matches_hand_built_000516() -> None:
f"hand-built EpcPropertyData for cohort cert 000516:\n " +
"\n ".join(diffs)
)
def test_elmhurst_jacket_cylinder_insulation_maps_to_loose_jacket_code_2() -> None:
# Arrange — an Elmhurst §15.1 "Cylinder Insulation Type: Jacket"
# lodging is a loose jacket, which SAP 10.2 Table 2 Note 1 gives a
# separate (higher) storage-loss factor than factory foam. The SAP10
# `cylinder_insulation_type` enum uses 2 for loose jacket (1 = factory
# foam), matching the GOV.UK API path — so the Summary "Jacket" label
# must resolve to 2 for cross-mapper parity, and so the
# loose-jacket storage-loss branch (S0380.224) fires. Observed on the
# simulated-case-19 worksheet (210 L jacket cylinder + storage heaters).
from datatypes.epc.domain.mapper import _elmhurst_cylinder_insulation_code # pyright: ignore[reportPrivateUsage]
# Act
code = _elmhurst_cylinder_insulation_code("Jacket", cylinder_present=True)
# Assert
assert code == 2
def test_elmhurst_foam_cylinder_insulation_still_maps_to_factory_code_1() -> None:
# Arrange — regression guard: the factory-foam label is unchanged.
from datatypes.epc.domain.mapper import _elmhurst_cylinder_insulation_code # pyright: ignore[reportPrivateUsage]
# Act
code = _elmhurst_cylinder_insulation_code("Foam", cylinder_present=True)
# Assert
assert code == 1

View file

@ -4710,8 +4710,14 @@ _ELMHURST_CYLINDER_SIZE_LABEL_TO_SAP10: Dict[str, int] = {
# which SAP 10.2 Table 2 Note 2 treats as factory-applied PU foam).
# Other labels (Loose Jacket, None) raise `UnmappedElmhurstLabel`
# until a fixture exercises them.
# SAP10 cylinder_insulation_type enum: 1 = factory-applied foam,
# 2 = loose jacket (matching the GOV.UK API codes). SAP 10.2 Table 2
# Note 1 gives loose jacket a separate, ~2× higher storage-loss factor;
# the cascade's loose-jacket branch is wired (S0380.224), so "Jacket"
# resolves to 2 for cross-mapper parity with the API path.
_ELMHURST_CYLINDER_INSULATION_LABEL_TO_SAP10: Dict[str, int] = {
"Foam": 1,
"Jacket": 2,
}