From 702150002f9c88ca16e74e70f8fb2f59d57b2825 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 08:31:04 +0000 Subject: [PATCH 01/19] =?UTF-8?q?fix(fuel):=20cost=20main=20heating=20syst?= =?UTF-8?q?em=202=20at=20its=20own=20fuel=20price,=20not=20main=201's=20(S?= =?UTF-8?q?AP=2010.2=20=C2=A710a=20worksheet=20line=20213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Main heating system 2's space-heating fuel cost (worksheet (213)) was billed at main system 1's Table 32 unit price (`main_2_high_rate_gbp_per_kwh` reused `main_1_high_rate_gbp_per_kwh`). For a dual-FUEL pair this grossly mis-costs the second main: cert 10032957680 "Copse Cottage" (main 1 electric room heaters fuel 30, main 2 wood logs fuel 6) charged its 9481 kWh of wood at 13.19 p/kWh instead of 4.23 p/kWh — +£850/yr → SAP 21.75 vs lodged 45. Route main 2 through its own fuel code (`_main_fuel_code(details[1])`), mirroring the existing secondary-fuel handling. Copse Cottage 21.75 -> 45.94. Corpus within-0.5 holds 72.5%, SAP MAE 0.815 -> 0.793 (ratcheted ceiling 0.82 -> 0.80); CO2/PE unchanged. Same-fuel dual mains (gas+gas) unaffected. Off-peak-tariff dual-fuel mains still defer to the legacy scalar path (separate slice). Spec-cited unit pin added (AAA). pyright not installed locally — strict type gate not run. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/rdsap/cert_to_inputs.py | 22 +++++++- .../rdsap/test_cert_to_inputs.py | 54 +++++++++++++++++++ .../epc_client/test_sap_accuracy_corpus.py | 2 +- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 8227764b..1614ef05 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -6927,6 +6927,26 @@ def _fuel_cost( main_1_high_rate_gbp_per_kwh = ( table_32_unit_price_p_per_kwh(main_fuel_code) * _PENCE_TO_GBP ) + # Main heating system 2 is costed at ITS OWN fuel price (SAP 10.2 §10a + # worksheet line (213) bills main system 2's fuel separately from main 1's + # (211)) — Table 32 unit price keyed on main 2's fuel code. Pre-fix this + # column reused `main_1_high_rate_gbp_per_kwh`, charging a dual-fuel second + # main (e.g. wood logs SAP code 633, fuel 6 @ 4.23 p/kWh) at the main-1 + # electric rate (13.19 p/kWh), grossly over-costing electric+wood + # room-heater dwellings (cert 10032957680 "Copse Cottage" +£850/yr -> + # SAP -23). Falls back to main 1's price only when no second main is lodged. + main_2_detail = ( + epc.sap_heating.main_heating_details[1] + if epc.sap_heating + and len(epc.sap_heating.main_heating_details or []) >= 2 + else None + ) + main_2_fuel_code = _main_fuel_code(main_2_detail) + main_2_high_rate_gbp_per_kwh = ( + table_32_unit_price_p_per_kwh(main_2_fuel_code) * _PENCE_TO_GBP + if main_2_fuel_code is not None + else main_1_high_rate_gbp_per_kwh + ) water_high_rate_gbp_per_kwh = ( table_32_unit_price_p_per_kwh(water_heating_fuel_code) * _PENCE_TO_GBP @@ -6976,7 +6996,7 @@ def _fuel_cost( main_1_low_rate_gbp_per_kwh=0.0, main_1_high_rate_fraction=1.0, main_2_kwh_per_yr=main_2_kwh, - main_2_high_rate_gbp_per_kwh=main_1_high_rate_gbp_per_kwh, + main_2_high_rate_gbp_per_kwh=main_2_high_rate_gbp_per_kwh, main_2_low_rate_gbp_per_kwh=0.0, main_2_high_rate_fraction=1.0 if main_2_kwh > 0.0 else 0.0, secondary_kwh_per_yr=secondary_kwh, diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index e1ca3f7e..4f04720a 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -473,6 +473,60 @@ def test_heat_network_primary_loss_uses_p1_h3_all_months_per_table_3() -> None: assert abs(sum(wh_result.primary_loss_monthly_kwh) - expected_primary_kwh) <= 1e-6 +def test_dual_main_system_2_costed_at_its_own_fuel_price() -> None: + # Arrange — SAP 10.2 §10a: main heating system 2's fuel cost (worksheet + # line (213)) is billed at ITS OWN Table 32 fuel price, separately from + # main system 1's (211). A dwelling with main-1 = electric room heaters + # (SAP 691, fuel 30 standard electricity @ 13.19 p/kWh) and main-2 = + # wood-log room heaters (SAP 633, fuel 6 @ 4.23 p/kWh), 50/50 split, on a + # single-rate meter (so the §10a standard path runs). Pre-fix the main-2 + # column reused main-1's electric price, charging the wood at 13.19 p/kWh + # and grossly over-costing electric+wood dwellings (cert 10032957680 + # "Copse Cottage" SAP 21.75 -> 45.94 vs lodged 45). + main_1_electric = MainHeatingDetail( + has_fghrs=False, + main_fuel_type=30, # standard electricity + heat_emitter_type=0, + emitter_temperature="NA", + main_heating_control=2106, + main_heating_category=10, + sap_main_heating_code=691, + main_heating_fraction=50, + ) + main_2_wood = MainHeatingDetail( + has_fghrs=False, + main_fuel_type=6, # wood logs — Table 32 = 4.23 p/kWh + heat_emitter_type=0, + emitter_temperature="NA", + main_heating_control=2106, + main_heating_category=10, + sap_main_heating_code=633, + main_heating_fraction=50, + ) + epc = make_minimal_sap10_epc( + total_floor_area_m2=_TYPICAL_TFA_M2, + habitable_rooms_count=4, + country_code="ENG", + sap_building_parts=[make_building_part(construction_age_band="D")], + sap_heating=make_sap_heating( + main_heating_details=[main_1_electric, main_2_wood], + ), + ) + + # Act + inputs = cert_to_inputs(epc) + result = Sap10Calculator().calculate(epc) + fc = inputs.fuel_cost + main_2_kwh = result.main_2_heating_fuel_kwh_per_yr + + # Assert — main-2 wood billed at 4.23 p/kWh (0.0423 £/kWh), NOT main-1's + # electric 13.19 p/kWh. (213) main_2_total_cost = kWh × wood price. + assert main_2_kwh > 0.0 + assert abs(fc.main_2_total_cost_gbp - main_2_kwh * 0.0423) <= 1e-6 + # And explicitly NOT the electric rate (the pre-fix value). + assert abs(fc.main_2_total_cost_gbp - main_2_kwh * 0.1319) > 1.0 + + def test_cylinder_thermostat_assumed_present_per_sap_9_4_9() -> None: # Arrange — SAP 10.2 §9.4.9 (PDF p.32): "A cylinder thermostat should be # assumed to be present when the domestic hot water is obtained from a diff --git a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py index df64ed59..e3447b47 100644 --- a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py +++ b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py @@ -194,7 +194,7 @@ _CORPUS = Path( # stress worksheet (simulated case 46): closed its last ventilation residual # (our Jan ACH 9.14 -> 9.0748 exact; SAP 29 -> 30 = accredited Elmhurst). _MIN_WITHIN_HALF_SAP = 0.72 -_MAX_SAP_MAE = 0.82 +_MAX_SAP_MAE = 0.80 _MAX_CO2_MAE_TONNES = 0.09 # t CO2 / yr vs co2_emissions_current _MAX_PE_PER_M2_MAE = 3.7 # kWh / m2 / yr vs energy_consumption_current From 44fff767224508ce7c473b4c794707cba8ddeeeb Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 08:31:04 +0000 Subject: [PATCH 02/19] fix(fuel): raise UnpricedFuelCode for unrecognised fuels instead of silently defaulting to mains gas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `table_32.unit_price_p_per_kwh` silently returned the mains-gas default (3.48 p/kWh) for any fuel code it could not resolve to a Table 32 price or a translatable gov-API enum. An unhandled fuel billed at the gas rate mis-costs the dwelling (same failure mode as the dual-main wood-vs-electric over-cost). Raise `UnpricedFuelCode` (new, mirrors MissingMainFuelType / UnmappedSapCode) so the gap surfaces at the price boundary. `None` (no fuel lodged) still defaults — callers resolve "no system" upstream. 0 corpus impact: all 1000 certs compute (every lodged fuel resolves), so this is a forward guard against future/unmapped fuels. Unit pin added; existing None-default test docstring tightened. pyright not installed locally — strict type gate not run. Co-Authored-By: Claude Opus 4.8 (1M context) --- domain/sap10_calculator/exceptions.py | 23 +++++++++++++++++++ domain/sap10_calculator/tables/table_32.py | 15 +++++++++--- .../domain/sap10_calculator/test_table_32.py | 21 ++++++++++++++--- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/domain/sap10_calculator/exceptions.py b/domain/sap10_calculator/exceptions.py index a91bd9cd..374f6545 100644 --- a/domain/sap10_calculator/exceptions.py +++ b/domain/sap10_calculator/exceptions.py @@ -61,3 +61,26 @@ class MissingMainFuelType(ValueError): ) self.value = value self.sap_main_heating_code = sap_main_heating_code + + +class UnpricedFuelCode(ValueError): + """A concrete (non-None) fuel code reached the Table 32 unit-price + lookup but resolves to neither a Table 32 price code nor a gov-API + enum that translates to one. + + Raised instead of silently falling back to the mains-gas price + (3.48 p/kWh): an unrecognised fuel billed at the gas rate produces a + misleading cost (e.g. the dual-main wood-vs-electric over-cost on + cert 10032957680). Surface the gap at the price boundary so the fuel + is either added to `UNIT_PRICE_P_PER_KWH` / `API_FUEL_TO_TABLE_32` + or canonicalised upstream. A `None` code (no fuel lodged) is NOT an + error — callers resolve "no system" before pricing. + """ + + def __init__(self, fuel_code: object) -> None: + super().__init__( + f"no Table 32 unit price for fuel code {fuel_code!r}; add it to " + f"UNIT_PRICE_P_PER_KWH or API_FUEL_TO_TABLE_32, or canonicalise " + f"the code upstream before pricing" + ) + self.fuel_code = fuel_code diff --git a/domain/sap10_calculator/tables/table_32.py b/domain/sap10_calculator/tables/table_32.py index 14544aea..da22b5ce 100644 --- a/domain/sap10_calculator/tables/table_32.py +++ b/domain/sap10_calculator/tables/table_32.py @@ -17,6 +17,7 @@ from __future__ import annotations from typing import Final, Optional +from domain.sap10_calculator.exceptions import UnpricedFuelCode from domain.sap10_calculator.tables.table_12a import Tariff @@ -184,8 +185,16 @@ STANDING_CHARGE_GBP_PER_YR: Final[dict[int, float]] = { def unit_price_p_per_kwh(fuel_code: Optional[int]) -> float: """Unit price (p/kWh) for the given fuel code. Accepts either a Table 32 code or a gov API `main_fuel_type` / `water_heating_fuel` - enum; translates the latter via `API_FUEL_TO_TABLE_32`. Unknown → - mains gas (3.48 p/kWh).""" + enum; translates the latter via `API_FUEL_TO_TABLE_32`. + + `None` (no fuel lodged) → mains-gas default; callers resolve a + "no system" before pricing. A concrete but UNRECOGNISED code raises + `UnpricedFuelCode` rather than silently defaulting to the gas price — + an unhandled fuel billed at 3.48 p/kWh mis-costs the dwelling (the + same failure mode as the dual-main wood-vs-electric over-cost). The + strict-raise surfaces the gap at the price boundary; 0 corpus certs + hit it today (every lodged fuel resolves), so the raise is a guard + against future / unmapped fuels, mirroring `MissingMainFuelType`.""" if fuel_code is None: return _DEFAULT_P_PER_KWH if fuel_code in UNIT_PRICE_P_PER_KWH: @@ -193,7 +202,7 @@ def unit_price_p_per_kwh(fuel_code: Optional[int]) -> float: translated = API_FUEL_TO_TABLE_32.get(fuel_code) if translated is not None and translated in UNIT_PRICE_P_PER_KWH: return UNIT_PRICE_P_PER_KWH[translated] - return _DEFAULT_P_PER_KWH + raise UnpricedFuelCode(fuel_code) def standing_charge_gbp(fuel_code: Optional[int]) -> float: diff --git a/tests/domain/sap10_calculator/test_table_32.py b/tests/domain/sap10_calculator/test_table_32.py index b297a78d..59e2c93c 100644 --- a/tests/domain/sap10_calculator/test_table_32.py +++ b/tests/domain/sap10_calculator/test_table_32.py @@ -13,6 +13,7 @@ from __future__ import annotations import pytest +from domain.sap10_calculator.exceptions import UnpricedFuelCode from domain.sap10_calculator.tables.table_12a import Tariff from domain.sap10_calculator.tables.table_32 import ( additional_standing_charges_gbp, @@ -143,9 +144,9 @@ def test_unit_price_translates_api_fuel_enum_via_api_fuel_to_table_32() -> None: def test_unit_price_defaults_to_mains_gas_when_code_is_none() -> None: - """Mirrors `table_12.unit_price_p_per_kwh` behaviour: unknown / missing - fuel codes fall back to mains gas. cert_to_inputs occasionally has to - resolve a price for a cert with a missing main_fuel_type.""" + """A `None` fuel code (no fuel lodged) falls back to mains gas — callers + resolve a "no system" before pricing, so None is not an error. A concrete + UNRECOGNISED code raises instead (see the next test).""" # Arrange fuel_code = None @@ -156,6 +157,20 @@ def test_unit_price_defaults_to_mains_gas_when_code_is_none() -> None: assert price == 3.48 +def test_unit_price_raises_on_unrecognised_fuel_code() -> None: + """A concrete (non-None) fuel code that resolves to neither a Table 32 + price nor a translatable gov-API enum raises `UnpricedFuelCode` rather + than silently defaulting to the mains-gas price — an unhandled fuel + billed at 3.48 p/kWh mis-costs the dwelling (the dual-main wood-vs- + electric failure mode). Guards against future/unmapped fuels.""" + # Arrange — 999 is not a Table 32 code nor a gov-API fuel enum. + fuel_code = 999 + + # Act / Assert + with pytest.raises(UnpricedFuelCode): + unit_price_p_per_kwh(fuel_code) + + @pytest.mark.parametrize( "fuel_code, expected_standing_gbp, fuel_name", [ From 6a4539f26b8bf3f9807760e51f782ef76a8f217d Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 08:46:00 +0000 Subject: [PATCH 03/19] =?UTF-8?q?fix(fuel):=20close=20case=2047=20?= =?UTF-8?q?=E2=80=94=20correct=20the=20second=20main=20heating=20system's?= =?UTF-8?q?=20fuel=20(off-peak=20pricing=20+=20Elmhurst=20Summary=20solid-?= =?UTF-8?q?fuel)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two second-main fuel errors mis-cost a dual-main dwelling whose two main systems burn different fuels (SAP 10.2 §10a worksheet (213) bills main 2 at its own fuel): 1. Off-peak/legacy scalar cost path (calculator.py + cert_to_inputs.py): main 2's kWh was priced at main 1's `space_heating_fuel_cost_gbp_per_kwh` scalar. Split main 1 vs main 2 and price main 2 at its OWN rate via the new `_main_2_space_heating_fuel_cost_gbp_per_kwh` (+ CalculatorInputs field). Scoped to a NON-electric second main (wood/oil/coal) — an electric second main keeps main 1's scalar (its off-peak Table 12a split is the deferred §10a slice; per-system splitting it regresses the off-peak electric cohort, certs 13 Parkers Hill / 34 Dunley Road). 0 corpus impact (no corpus cert has a non-electric second main on an off-peak meter). 2. Elmhurst Summary mapper (mapper.py): when §14.1 omits the Fuel Type cell, a fuel-fired second main (room-heater SAP code) inherited main 1's fuel. Derive it from the SAP code's Table 4a category (solid 631-636 -> house coal, gas -> mains gas, liquid -> oil) before the main-1 inherit, mirroring `_elmhurst_secondary_fuel_from_sap_code` (same modal sub-fuel caveat). Boiler codes (<601) still inherit main 1 (case 6 oil rads+UFH). simulated case 47 (electric room heaters + solid room heaters 633): our SAP 37.81 -> 55.09 vs Elmhurst current 57 (residual is the wood-vs-coal sub-fuel the Summary export does not carry). Corpus unchanged 72.5% / MAE 0.793; batch 0 raised / 0 diverge; 000565 e2e green. (mapper.py also carries an earlier, behaviour-free roof-window doc comment.) Spec-cited unit pins added (AAA). pyright not installed locally — strict type gate not run. Co-Authored-By: Claude Opus 4.8 (1M context) --- datatypes/epc/domain/mapper.py | 31 ++++++ domain/sap10_calculator/calculator.py | 20 +++- .../sap10_calculator/rdsap/cert_to_inputs.py | 32 ++++++ .../rdsap/test_cert_to_inputs.py | 99 +++++++++++++++++++ 4 files changed, 181 insertions(+), 1 deletion(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 380192d3..40c32b0c 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -5857,6 +5857,21 @@ def _is_elmhurst_roof_window( # room-in-roof. The §11 row alone can't separate them; the room-in-roof # context is the discriminator (the location string is a §11 lodging # artifact, so it is not a reliable vertical signal either). + # KNOWN LIMIT (falls through to vertical below) — a roof window on a PLAIN + # PITCHED roof (not a room-in-roof, BP roof type not A/NR) with a normal + # U <= 3.0 is UNDETECTABLE from the Elmhurst Summary §11. The Summary has + # no roof/window "Type" column (that exists only in the U985/P960 + # *worksheet* input echo, which this pipeline does not read), and such + # rooflights lodge with Location "External wall" and the vertical U — + # byte-identical to ordinary wall windows. Khalim's "simulated case 46" + # exercises this: 2 of its 6 openings are roof windows (worksheet (27a), + # combined area 1.70, U_eff 2.1062) yet opening 1 (wall) and opening 2 + # (roof) read character-for-character the same in §11, so the extractor + # cannot separate them. The residual is 29.58 vs Elmhurst 29.68 (both round + # to 30) — accepted as a lossy-Summary limit, not a bug. The gov-API path + # is unaffected: roof windows carry a dedicated signal there + # (`window_wall_type == 4`, see `_api_is_roof_window`; 55 corpus certs / + # 124 roof windows routed correctly, worksheet-validated U/solar treatment). if not _elmhurst_bp_has_room_in_roof(w, survey): return False return w.u_value > _ELMHURST_ROOF_WINDOW_U_THRESHOLD @@ -7163,6 +7178,22 @@ def _map_elmhurst_main_heating_2( and mh2.main_heating_sap_code in _ELECTRIC_SAP_MAIN_HEATING_CODES ): main_fuel_int = _STANDARD_ELECTRICITY_FUEL_CODE + # A fuel-fired second main (room-heater SAP code) whose §14.1 omits the + # Fuel Type cell must derive its fuel from the SAP code's Table 4a CATEGORY + # — NOT inherit Main 1's fuel below. Mirrors `_elmhurst_secondary_fuel_ + # from_sap_code` (same modal-fuel + sub-fuel caveat): a wood/coal solid + # second main (633) billed at Main 1's electric rate is the dual-main + # over-cost (simulated case 47: electric room heaters + solid room heaters + # 633). Boiler codes (<601) fall through to the Main-1 inherit (case 6: + # one oil boiler feeding rads + underfloor). + if main_fuel_int is None: + _sc = mh2.main_heating_sap_code + if _sc in _ELMHURST_SECONDARY_SOLID_CODES: + main_fuel_int = _SECONDARY_FUEL_HOUSE_COAL + elif _sc in _ELMHURST_SECONDARY_GAS_CODES: + main_fuel_int = _SECONDARY_FUEL_MAINS_GAS + elif _sc in _ELMHURST_SECONDARY_LIQUID_CODES: + main_fuel_int = _SECONDARY_FUEL_HEATING_OIL # §14.1 Main Heating2 often omits the "Fuel Type" cell when the # second main system shares Main 1's fuel (simulated case 6: one oil # boiler feeding radiators + underfloor, so the Summary lodges the diff --git a/domain/sap10_calculator/calculator.py b/domain/sap10_calculator/calculator.py index fb859bf6..74b038e0 100644 --- a/domain/sap10_calculator/calculator.py +++ b/domain/sap10_calculator/calculator.py @@ -301,6 +301,13 @@ class CalculatorInputs: secondary_heating_fraction: float = 0.0 secondary_heating_efficiency: float = 1.0 secondary_heating_fuel_cost_gbp_per_kwh: float = 0.0 + # Main heating system 2's fuel cost per kWh (legacy/off-peak scalar path). + # main system 2 may burn a DIFFERENT fuel from main 1 (e.g. wood logs vs + # electric room heaters) — pricing its kWh at main 1's rate grossly mis- + # costs it. Defaults to 0.0; cert_to_inputs sets it to main 2's own rate + # when a second main is lodged (the term is multiplied by main 2's kWh, + # which is 0 when no second main exists, so the default is inert). + main_2_heating_fuel_cost_gbp_per_kwh: float = 0.0 # SAP10.2 (107)m — space cooling requirement kWh per month from §8c # orchestrator `space_cooling_monthly_kwh`. Includes spec Jun-Aug # inclusion mask + 1-kWh clamp. Default (0,)*12 for backwards @@ -579,7 +586,18 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult: inputs.pv_generation_kwh_per_yr * inputs.pv_export_credit_gbp_per_kwh ) - main_heating_cost = main_fuel_kwh * inputs.space_heating_fuel_cost_gbp_per_kwh + # `main_fuel_kwh` aggregates main systems 1 and 2 (see (211)+(213) + # above). Bill main 2 at ITS OWN fuel rate, not main 1's — a dual-fuel + # pair (e.g. electric room heaters + wood logs) is mis-costed otherwise + # (the §10a standard path already splits these; this is the legacy/ + # off-peak scalar path). main 2's kWh is 0 when no second main is + # lodged, so the default 0.0 rate is inert. + main_2_fuel_kwh = inputs.energy_requirements.main_2_fuel_kwh_per_yr + main_1_fuel_kwh = main_fuel_kwh - main_2_fuel_kwh + main_heating_cost = ( + main_1_fuel_kwh * inputs.space_heating_fuel_cost_gbp_per_kwh + + main_2_fuel_kwh * inputs.main_2_heating_fuel_cost_gbp_per_kwh + ) secondary_heating_cost = ( secondary_fuel_kwh * inputs.secondary_heating_fuel_cost_gbp_per_kwh ) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 1614ef05..281d87b5 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -2511,6 +2511,33 @@ def _space_heating_fuel_cost_gbp_per_kwh( return blended * _PENCE_TO_GBP +def _main_2_space_heating_fuel_cost_gbp_per_kwh( + epc: EpcPropertyData, + tariff: Tariff, + prices: PriceTable, +) -> float: + """Main heating system 2's space-heating fuel rate (£/kWh) for the + legacy/off-peak scalar cost path. Keyed on main 2's OWN fuel via the same + `_space_heating_fuel_cost_gbp_per_kwh` logic (off-peak Table 12a split when + main 2 is electric, flat Table 32 rate otherwise) — so a wood-log or other + non-electric second main is not billed at main 1's electric rate. Returns + 0.0 when no second main is lodged (multiplied by main 2's 0 kWh). + + Scoped to a NON-electric second main (the unambiguous correction): a wood/ + oil/coal second main billed at main 1's rate is plainly wrong. An ELECTRIC + second main keeps main 1's space-heating scalar — its off-peak Table 12a + high/low split is the deferred §10a off-peak slice, and applying the split + per-system here regresses the off-peak electric cohort (certs 13 Parkers + Hill / 34 Dunley Road).""" + details = epc.sap_heating.main_heating_details if epc.sap_heating else None + if not details or len(details) < 2: + return 0.0 + main_2 = details[1] + if _is_electric_main(main_2): + return _space_heating_fuel_cost_gbp_per_kwh(details[0], tariff, prices) + return _space_heating_fuel_cost_gbp_per_kwh(main_2, tariff, prices) + + def _main_space_heating_high_rate_fraction( main: Optional[MainHeatingDetail], tariff: Tariff, @@ -7899,6 +7926,11 @@ def cert_to_inputs( space_heating_fuel_cost_gbp_per_kwh=_space_heating_fuel_cost_gbp_per_kwh( main, _rdsap_tariff(epc), prices ), + main_2_heating_fuel_cost_gbp_per_kwh=( + _main_2_space_heating_fuel_cost_gbp_per_kwh( + epc, _rdsap_tariff(epc), prices + ) + ), hot_water_fuel_cost_gbp_per_kwh=hw_cost_rate, other_fuel_cost_gbp_per_kwh=_other_fuel_cost_gbp_per_kwh( _rdsap_tariff(epc), prices diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index 4f04720a..add25b03 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -85,6 +85,7 @@ from domain.sap10_calculator.rdsap.cert_to_inputs import ( _secondary_heating_fraction_for_category, # pyright: ignore[reportPrivateUsage] _section_12_4_4_summer_immersion_applies, # pyright: ignore[reportPrivateUsage] _separately_timed_dhw, # pyright: ignore[reportPrivateUsage] + _main_2_space_heating_fuel_cost_gbp_per_kwh, # pyright: ignore[reportPrivateUsage] _space_heating_fuel_cost_gbp_per_kwh, # pyright: ignore[reportPrivateUsage] _tariff_high_low_rates_p_per_kwh, # pyright: ignore[reportPrivateUsage] _thermal_mass_parameter_kj_per_m2_k, # pyright: ignore[reportPrivateUsage] @@ -527,6 +528,104 @@ def test_dual_main_system_2_costed_at_its_own_fuel_price() -> None: assert abs(fc.main_2_total_cost_gbp - main_2_kwh * 0.1319) > 1.0 +def test_off_peak_main_2_non_electric_priced_at_own_fuel() -> None: + # Arrange — legacy/off-peak scalar path. Main 1 = electric room heaters, + # Main 2 = WOOD logs (fuel 6, Table 32 4.23 p/kWh). On an off-peak tariff + # the second main's space-heating rate must be its OWN fuel (wood flat + # rate), not main 1's electric rate — SAP 10.2 §10a worksheet (213). + from domain.sap10_calculator.tables.table_12a import Tariff + + main_1_electric = MainHeatingDetail( + has_fghrs=False, main_fuel_type=30, heat_emitter_type=0, + emitter_temperature="NA", main_heating_control=2106, + main_heating_category=10, sap_main_heating_code=691, + main_heating_fraction=50, + ) + main_2_wood = MainHeatingDetail( + has_fghrs=False, main_fuel_type=6, heat_emitter_type=0, + emitter_temperature="NA", main_heating_control=2106, + main_heating_category=10, sap_main_heating_code=633, + main_heating_fraction=50, + ) + epc = make_minimal_sap10_epc( + sap_building_parts=[make_building_part(construction_age_band="D")], + sap_heating=make_sap_heating( + main_heating_details=[main_1_electric, main_2_wood], + ), + ) + + # Act + rate = _main_2_space_heating_fuel_cost_gbp_per_kwh( + epc, Tariff.SEVEN_HOUR, SAP_10_2_SPEC_PRICES + ) + + # Assert — wood flat rate 4.23 p/kWh, not the off-peak electric rate. + assert abs(rate - 0.0423) <= 1e-9 + + +def test_off_peak_main_2_electric_keeps_main_1_scalar() -> None: + # Arrange — both mains electric on an off-peak tariff. The electric + # second-main off-peak Table 12a split is the deferred §10a slice, so the + # helper keeps main 1's space-heating scalar (no per-system split here) to + # avoid regressing the off-peak electric cohort. + from domain.sap10_calculator.tables.table_12a import Tariff + + main_1 = MainHeatingDetail( + has_fghrs=False, main_fuel_type=29, heat_emitter_type=0, + emitter_temperature="NA", main_heating_control=2106, + main_heating_category=10, sap_main_heating_code=691, + main_heating_fraction=50, + ) + main_2 = MainHeatingDetail( + has_fghrs=False, main_fuel_type=29, heat_emitter_type=0, + emitter_temperature="NA", main_heating_control=2106, + main_heating_category=10, sap_main_heating_code=691, + main_heating_fraction=50, + ) + epc = make_minimal_sap10_epc( + sap_building_parts=[make_building_part(construction_age_band="D")], + sap_heating=make_sap_heating(main_heating_details=[main_1, main_2]), + ) + + # Act — main 2's rate equals main 1's space-heating scalar. + main_2_rate = _main_2_space_heating_fuel_cost_gbp_per_kwh( + epc, Tariff.SEVEN_HOUR, SAP_10_2_SPEC_PRICES + ) + main_1_rate = _space_heating_fuel_cost_gbp_per_kwh( + main_1, Tariff.SEVEN_HOUR, SAP_10_2_SPEC_PRICES + ) + + # Assert + assert abs(main_2_rate - main_1_rate) <= 1e-9 + + +def test_elmhurst_main_2_solid_sap_code_derives_house_coal_not_main_1_fuel() -> None: + # Arrange — Elmhurst §14.1 Main Heating2 lodges SAP code 633 (solid-fuel + # room heater) with NO "Fuel Type" cell, on a dwelling whose Main 1 is + # electric. The second main must derive its fuel from the SAP code's + # Table 4a category (solid → house coal 11, per the documented modal sub- + # fuel convention), NOT inherit Main 1's electric fuel — otherwise the + # solid second main is billed at the electric rate (simulated case 47 + # dual-main over-cost: SAP 37.81 -> 55.09). + from datatypes.epc.surveys.elmhurst_site_notes import MainHeating2 + from datatypes.epc.domain.mapper import ( # pyright: ignore[reportPrivateUsage] + _map_elmhurst_main_heating_2, + ) + + mh2 = MainHeating2( + fuel_type="", # §14.1 omits the Fuel Type cell + percentage_of_heat=50, + main_heating_sap_code=633, # solid-fuel room heater + ) + + # Act — Main 1 resolved to electricity (fallback_fuel_type=30). + detail = _map_elmhurst_main_heating_2(mh2, fallback_fuel_type=30) + + # Assert — house coal (11), not the inherited electric 30. + assert detail is not None + assert detail.main_fuel_type == 11 + + def test_cylinder_thermostat_assumed_present_per_sap_9_4_9() -> None: # Arrange — SAP 10.2 §9.4.9 (PDF p.32): "A cylinder thermostat should be # assumed to be present when the domestic hot water is obtained from a From 2ac5ec6eb57b6d779d9cba667fa8bba941aa484b Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 08:46:00 +0000 Subject: [PATCH 04/19] chore(tooling): add median column to corpus profiler; document shutter non-implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit profile_corpus_error.py: print median signed error alongside the mean in each feature bucket. The mean is dragged by fat-tail register anomalies (e.g. electric room heaters mean -1.09 but median -0.01) — median is the outlier-resistant lens for finding TRUE systematic slices, so hunt by |median|, not |signed|. heat_transmission.py: document why permanent-shutter R is deliberately NOT applied (Elmhurst uses R=0.04 curtains on every window incl. insulated shutters, proven on case 46; API-path trial worsens MAE). Comment-only. pyright not installed locally — strict type gate not run. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../worksheet/heat_transmission.py | 19 +++++++++++++++++++ scripts/profile_corpus_error.py | 10 ++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/domain/sap10_calculator/worksheet/heat_transmission.py b/domain/sap10_calculator/worksheet/heat_transmission.py index a0d773d9..32ae5bf7 100644 --- a/domain/sap10_calculator/worksheet/heat_transmission.py +++ b/domain/sap10_calculator/worksheet/heat_transmission.py @@ -129,6 +129,25 @@ _DEFAULT_STOREY_HEIGHT_M: Final[float] = 2.5 _CONSERVATORY_WALL_THICKNESS_MM: Final[int] = 300 # SAP10.2 §3.2 curtain/blind thermal resistance applied to windows (and # roof windows) — turns raw window U into the worksheet's (27) effective U. +# +# PERMANENT SHUTTERS ARE DELIBERATELY NOT APPLIED. RdSAP 10 Table 24 note +# (PDF p.51) genuinely specifies a larger R in formula (2) for permanent +# shutters (0.13 uninsulated / 0.16 insulated) instead of this 0.04 curtain +# allowance — but the accredited Elmhurst engine ("SAP 10 WORKSHEET ... +# Version 10.2, February 2022") does NOT implement it, and the lodged +# register we target was produced by that engine. Proven on Khalim's +# "simulated case 46" worksheet: its INSULATED-shutter window (Metal, +# U_raw 1.74) bills on (27) at U_eff 1.6268 = 1/(1/1.74 + 0.04), NOT the +# R=0.16 value 1.3611; its UNINSULATED-shutter window (Wood, U_raw 1.69) +# bills at 1.5830 = 1/(1/1.69 + 0.04), not the R=0.13 value 1.3856 — i.e. +# R=0.04 on every window regardless of the lodged shutter type. A reverted +# trial (`_window_added_resistance_m2k_per_w`, in git history) re-broke the +# Elmhurst-pinned 000565 e2e fixture. The gov-API path was separately tested +# (2026-06-21): 13 corpus certs lodge shuttered windows; applying the spec R +# WORSENS cohort MAE 1.067 -> 1.245 (8 of 13 certs move away from lodged, +# cert 41 +2.19 -> +3.94) while only band-gaming +3 borderline under-raters +# into within-0.5 — net negative, and it would diverge from the very engine +# that produced the lodged SAPs. Re-enable only if Elmhurst ships the clause. _WINDOW_CURTAIN_RESISTANCE_M2K_PER_W: Final[float] = 0.04 # SAP10 glazing-type code (the cascade enum used on `SapWindow.glazing_type`, diff --git a/scripts/profile_corpus_error.py b/scripts/profile_corpus_error.py index 6ef2c4e6..92f5b78e 100644 --- a/scripts/profile_corpus_error.py +++ b/scripts/profile_corpus_error.py @@ -182,11 +182,17 @@ def main() -> None: w05 = sum(1 for e in es if abs(e) < 0.5) mabs = stats.mean(abs(e) for e in es) waste = (cnt - w05) * mabs + # MEDIAN signed error is the outlier-RESISTANT bias lens. The + # `signed` mean is dragged by the fat-tail register anomalies, so a + # cohort can show a large mean bias while being symmetric noise + # (e.g. electric room heaters: mean -1.09 but median -0.01). Hunt + # slices by |median|, not |signed| — only |median| flags a TRUE + # one-directional systematic shift worth fixing. bucket_lines.append((waste, ( f" {fn:22s}={val:<20.20s} n={cnt:4d} " f"within0.5={w05 / cnt * 100:4.0f}% " - f"signed={stats.mean(es):+6.2f} mean|err|={mabs:5.2f} " - f"[waste={waste:6.0f}]" + f"signed={stats.mean(es):+6.2f} median={stats.median(es):+6.2f} " + f"mean|err|={mabs:5.2f} [waste={waste:6.0f}]" ))) print(f"TOP ERROR-CARRYING BUCKETS (n_out x mean|err|; min-n={min_n}):") for _, line in sorted(bucket_lines, key=lambda x: -x[0])[:40]: From 3548f1f31ac6ab98c551275c260373eb11f374c8 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 09:48:21 +0000 Subject: [PATCH 05/19] =?UTF-8?q?docs(tariff):=20record=20Unknown-meter=20?= =?UTF-8?q?off-peak=20as=20a=20verified=20non-fix=20(RdSAP=2010=20=C2=A712?= =?UTF-8?q?=20Rules,=20table=5F12a)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Khalim's hand-built "simulated case 48" Elmhurst worksheet (main 691 room heaters + Unknown meter + 903 dual electric immersion + cylinder) proves Elmhurst resolves an Unknown meter + Rule-3 room-heater main to 10-Hour Off Peak — Elmhurst SAP 57; ours (STANDARD) 45; routing Unknown+Rule3 to off-peak (10h) gives 55, 7h gives 45 (so Elmhurst uses 10-hour, RdSAP 10 §12 Rule 3, PDF p.62). So the prior "Rule 3 is not off-peak evidence" comment was wrong about Elmhurst's behaviour. But adopting it REGRESSES the lodged-register corpus 72.5%->71.8% / MAE 0.793->0.827: of 11 Unknown+Rule-3 corpus certs only Apartment 241 improves (-5.38->-1.05); the other 10 overshoot +2.7..+9.1 (Flat 2 +9.11). The register's meter_type=3 certs were lodged with STANDARD-tariff costing — the gov-API "Unknown" is lossy and does not mean off-peak. North star is reproducing the lodged register, not Elmhurst's deliberate-Unknown worksheet, so KEEP STANDARD (same "Elmhurst != noisy register" family as roof-windows/shutters). Comment-only; no behaviour change (corpus gauge unchanged 72.5% / 0.793). pyright not installed in this container — strict type gate not run locally. Co-Authored-By: Claude Opus 4.8 (1M context) --- domain/sap10_calculator/tables/table_12a.py | 25 ++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/domain/sap10_calculator/tables/table_12a.py b/domain/sap10_calculator/tables/table_12a.py index ab5394f1..b41819b0 100644 --- a/domain/sap10_calculator/tables/table_12a.py +++ b/domain/sap10_calculator/tables/table_12a.py @@ -342,11 +342,26 @@ def rdsap_tariff_for_cert( # evidence the dwelling is on an off-peak tariff — these charge # overnight at the low rate and cannot run economically on a single # rate, so the tariff is implied. Direct-acting electric / room heaters - # / heat pumps (Rule 3) are NOT off-peak evidence (they run on demand - # and exist on single-rate meters too), so they keep STANDARD here - # rather than being mis-billed 100% at the off-peak low rate. A - # non-electric main also keeps STANDARD (no Rule 4 default — Unknown - # must not force off-peak on a gas dwelling). + # / heat pumps (Rule 3) keep STANDARD here. A non-electric main also + # keeps STANDARD (no Rule 4 default — Unknown must not force off-peak + # on a gas dwelling). + # + # VERIFIED NON-FIX (2026-06-23, "simulated case 48" Elmhurst worksheet): + # Elmhurst DOES resolve an Unknown meter + room-heater main (SAP 691) to + # 10-Hour Off Peak (its Rule 3) — a hand-built case-48 Summary (main 691, + # Unknown meter, 903 dual electric immersion) scores Elmhurst SAP 57; our + # STANDARD gives 45, routing Unknown+Rule3 to off-peak gives 55 (7-hour + # gives 45 — confirms Elmhurst uses 10-hour). So the "Rule 3 is not off- + # peak evidence" intuition is WRONG about Elmhurst's behaviour. BUT + # adopting it REGRESSES the lodged-register corpus (72.5%->71.8%, MAE + # 0.793->0.827): of 11 Unknown+Rule3 corpus certs only ONE improves + # (Apartment 241 -5.38->-1.05); the other 10 overshoot +2.7..+9.1 (Flat 2 + # +9.11). The register's meter_type=3 certs were lodged with STANDARD- + # tariff costing (their low ratings only reconcile at 13.19p flat) — the + # gov-API "Unknown" is lossy and does NOT mean off-peak was used. Since + # our north star is reproducing the lodged register (not Elmhurst's + # deliberate-Unknown worksheet path), KEEP STANDARD. Same "Elmhurst != the + # noisy register" family as roof-windows/shutters. Do NOT re-litigate. if _meter_is_unknown(meter_type): if main_codes & _RULE_1_CPSU_CODES: return Tariff.TEN_HOUR From 22fe4f41b8c752c96cf692a4d5ce1472520307fc Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 09:58:02 +0000 Subject: [PATCH 06/19] =?UTF-8?q?fix(tariff):=20Unknown=20meter=20+=20dual?= =?UTF-8?q?=20electric=20immersion=20=E2=86=92=20off-peak=20per=20=C2=A712?= =?UTF-8?q?=20(RdSAP=2010=20PDF=20p.62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supersedes the previous "verified non-fix" doc (3548f1f3): the spec DOES make this a fix — Khalim was right that the Unknown-meter branch is driven by the heating/water system, not a blanket STANDARD. RdSAP 10 §12 (PDF p.62): "If the electricity meter is unknown, treat as single meter EXCEPT where main heating OR WATER HEATING are intended to run off an off-peak tariff (per systems listed in the text box above) ... If that results in a dual meter, assign tariff per rules 1 to 4." The text-box off-peak systems include DUAL ELECTRIC IMMERSION. Our `rdsap_tariff_for_cert` only triggered the Unknown→off-peak exception on a storage/CPSU MAIN — it ignored the dual-electric-immersion WATER-heating trigger, so an Unknown-meter dwelling with a non-storage main (e.g. room heaters) + dual immersion was billed STANDARD (13.19p flat) when it should be dual → Rules 1-4 on the main. Fix: thread `water_is_off_peak_dual_immersion` (whc 903 + immersion lodged dual via `_immersion_is_single is False`) into the Unknown-meter branch; when any text-box trigger is present, resolve via the same Rules 1-4 dispatch (room heaters → Rule 3 → 10-hour). Single-immersion / instantaneous (whc 909) certs correctly stay STANDARD (no text-box system). Worksheet-validated on "simulated case 48" (main 691 + Unknown meter + 903 dual immersion): Elmhurst 10-Hour Off Peak, SAP 57; ours 45 → 55 (7-hour gives 45, confirming 10-hour). Flips exactly ONE corpus cert — Apartment 241 (the genuine -5.38 under-rater, main 691 + dual immersion) -5.38 → -1.05; every other Unknown+dual-immersion cert already has a storage main (Rule 2). Corpus within-0.5 holds 72.5%, MAE 0.793 → 0.789 (improved). CO2/PE unchanged. GSHP/WSHP-main trigger (the other §12 Unknown exception bullet) is a separate follow-up. Gates green: corpus 72.5%/0.789, batch worksheet 0 raised/0 diverge, 000565 e2e 11/11, suite 2987 passed (2 known pre-existing fails). pyright not installed in this container — strict type gate not run locally. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/rdsap/cert_to_inputs.py | 12 ++++ domain/sap10_calculator/tables/table_12a.py | 65 +++++++++++-------- .../domain/sap10_calculator/test_table_12a.py | 45 +++++++++++-- 3 files changed, 88 insertions(+), 34 deletions(-) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 281d87b5..3f1294e5 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -1741,12 +1741,24 @@ def _rdsap_tariff(epc: EpcPropertyData) -> Tariff: and heat_pump_record(detail.main_heating_index_number) is not None ) + # §12 Unknown-meter exception: "water heating ... intended to run off an + # off-peak tariff" via the text-box "dual electric immersion" system — + # whc 903 (HW from a separate electric immersion) lodged as dual + # (`_immersion_is_single` is False). This makes an Unknown-meter dwelling + # "dual" even when the main is a single-rate-capable system (e.g. room + # heaters), per RdSAP 10 §12 (PDF p.62). + water_dual_immersion = ( + _int_or_none(epc.sap_heating.water_heating_code) == _WHC_ELECTRIC_IMMERSION + and _immersion_is_single(epc) is False + ) + return rdsap_tariff_for_cert( epc.sap_energy_source.meter_type, main_1_sap_code=main_1.sap_main_heating_code if main_1 else None, main_2_sap_code=main_2.sap_main_heating_code if main_2 else None, main_1_is_heat_pump_database=_hp_db(main_1), main_2_is_heat_pump_database=_hp_db(main_2), + water_is_off_peak_dual_immersion=water_dual_immersion, ) diff --git a/domain/sap10_calculator/tables/table_12a.py b/domain/sap10_calculator/tables/table_12a.py index b41819b0..d79d9fbe 100644 --- a/domain/sap10_calculator/tables/table_12a.py +++ b/domain/sap10_calculator/tables/table_12a.py @@ -285,6 +285,7 @@ def rdsap_tariff_for_cert( main_2_sap_code: Optional[int] = None, main_1_is_heat_pump_database: bool = False, main_2_is_heat_pump_database: bool = False, + water_is_off_peak_dual_immersion: bool = False, ) -> Tariff: """RdSAP 10 §12 page 62 — full meter+heating tariff dispatch. @@ -308,6 +309,12 @@ def rdsap_tariff_for_cert( Table 362 heat-pump record. Callers compute this via `heat_pump_record(main_heating_index_number) is not None`. + `water_is_off_peak_dual_immersion` signals the §12 Unknown-meter + exception "water heating ... intended to run off an off-peak tariff" + via the text-box "dual electric immersion" system (whc 903 + dual + immersion). On an Unknown meter this is enough to make the dwelling + "dual"; the 7-/10-hour choice then follows Rules 1-4 on the main. + Cert 000565 (Main 1 SAP code 224 ASHP + Dual meter) → Rule 3 → TEN_HOUR, matching the worksheet's "10 Hour Off Peak" lodging. """ @@ -337,36 +344,38 @@ def rdsap_tariff_for_cert( # Dual meter — §12 Rules 1-4, where Rule 4 is the 7-hour default. if base is Tariff.SEVEN_HOUR: return _rules_1_to_3() or Tariff.SEVEN_HOUR - # "Unknown" meter (code 3): the assessor didn't record the tariff, but - # an electric CPSU (Rule 1) or STORAGE (Rule 2) main is physical - # evidence the dwelling is on an off-peak tariff — these charge - # overnight at the low rate and cannot run economically on a single - # rate, so the tariff is implied. Direct-acting electric / room heaters - # / heat pumps (Rule 3) keep STANDARD here. A non-electric main also - # keeps STANDARD (no Rule 4 default — Unknown must not force off-peak - # on a gas dwelling). + # "Unknown" meter (code 3, inaccessible): §12 (PDF p.62) — "treat as + # single meter EXCEPT where: main heating OR WATER HEATING are intended to + # run off an off-peak tariff (per systems listed in the text box above) or + # main heating is ground source or water source heat pump. If that results + # in a dual meter, assign tariff per rules 1 to 4." The text-box off-peak + # systems are electric storage heaters (401-409), underfloor (421/422), + # dry-core/water-storage boiler (193/195), CPSU (192), and DUAL ELECTRIC + # IMMERSION. So the off-peak trigger is NOT "any electric main" — a + # direct-acting / room-heater main on its own keeps the dwelling on a + # single meter (STANDARD); it only goes off-peak when one of the text-box + # systems is present. Once triggered, the meter becomes "dual" and the + # 7-hour/10-hour choice is made by the SAME Rules 1-4 on the main heating + # (so e.g. room heaters + dual immersion → Rule 3 → 10-hour). # - # VERIFIED NON-FIX (2026-06-23, "simulated case 48" Elmhurst worksheet): - # Elmhurst DOES resolve an Unknown meter + room-heater main (SAP 691) to - # 10-Hour Off Peak (its Rule 3) — a hand-built case-48 Summary (main 691, - # Unknown meter, 903 dual electric immersion) scores Elmhurst SAP 57; our - # STANDARD gives 45, routing Unknown+Rule3 to off-peak gives 55 (7-hour - # gives 45 — confirms Elmhurst uses 10-hour). So the "Rule 3 is not off- - # peak evidence" intuition is WRONG about Elmhurst's behaviour. BUT - # adopting it REGRESSES the lodged-register corpus (72.5%->71.8%, MAE - # 0.793->0.827): of 11 Unknown+Rule3 corpus certs only ONE improves - # (Apartment 241 -5.38->-1.05); the other 10 overshoot +2.7..+9.1 (Flat 2 - # +9.11). The register's meter_type=3 certs were lodged with STANDARD- - # tariff costing (their low ratings only reconcile at 13.19p flat) — the - # gov-API "Unknown" is lossy and does NOT mean off-peak was used. Since - # our north star is reproducing the lodged register (not Elmhurst's - # deliberate-Unknown worksheet path), KEEP STANDARD. Same "Elmhurst != the - # noisy register" family as roof-windows/shutters. Do NOT re-litigate. + # Worksheet-validated (2026-06-23, Khalim's "simulated case 48": main 691 + # room heaters + Unknown meter + 903 DUAL electric immersion): Elmhurst + # SAP 57; ours 45 when this stayed STANDARD, 55 once the dual-immersion + # trigger routes it through Rule 3 → 10-hour (7-hour gives 45 — confirms + # 10-hour). The dual-immersion trigger flips exactly ONE corpus cert + # (Apartment 241, the genuine -5.38 under-rater, main 691 + 903 dual + # immersion); every other Unknown+dual-immersion cert already has a + # storage main (Rule 2). Single-immersion 691 certs (Flat 7, Flat 2) and + # whc-909 instantaneous certs correctly STAY standard — they carry no + # text-box off-peak system. (GSHP/WSHP-main trigger: separate follow-up.) if _meter_is_unknown(meter_type): - if main_codes & _RULE_1_CPSU_CODES: - return Tariff.TEN_HOUR - if main_codes & _RULE_2_STORAGE_CODES: - return Tariff.SEVEN_HOUR + off_peak_evidence = ( + bool(main_codes & _RULE_1_CPSU_CODES) + or bool(main_codes & _RULE_2_STORAGE_CODES) + or water_is_off_peak_dual_immersion + ) + if off_peak_evidence: + return _rules_1_to_3() or Tariff.SEVEN_HOUR return Tariff.STANDARD # Single (code 2) or any other explicit non-off-peak meter. return base diff --git a/tests/domain/sap10_calculator/test_table_12a.py b/tests/domain/sap10_calculator/test_table_12a.py index a43ef5e2..8ce42b09 100644 --- a/tests/domain/sap10_calculator/test_table_12a.py +++ b/tests/domain/sap10_calculator/test_table_12a.py @@ -62,17 +62,50 @@ def test_unknown_meter_infers_off_peak_from_electric_storage_main() -> None: def test_unknown_meter_does_not_infer_off_peak_for_room_heater_or_heat_pump() -> None: - # Arrange — direct-acting electric room heaters (Rule 3, SAP 691) and - # heat pumps run ON DEMAND and exist on single-rate meters too, so they - # are NOT evidence of an off-peak tariff. On an Unknown meter they keep - # STANDARD — billing them 100% at the off-peak low rate would - # over-credit (room heaters draw mostly at the high rate). + # Arrange — RdSAP 10 §12 (PDF p.62): on an Unknown meter the off-peak + # exception fires only when a text-box off-peak SYSTEM is present (storage + # / underfloor / dry-core / CPSU main, OR dual electric immersion water + # heating, OR GSHP/WSHP main). A direct-acting room-heater (Rule 3, SAP + # 691) or heat-pump main is NOT by itself such a system — absent a + # dual-immersion (or GSHP/WSHP) trigger the dwelling stays on a single + # meter (STANDARD). The 7-/10-hour choice in Rules 1-4 only applies ONCE + # the meter is already established as dual. - # Act / Assert + # Act / Assert — room heater / heat pump with no dual-immersion trigger. assert rdsap_tariff_for_cert(3, main_1_sap_code=691) is Tariff.STANDARD assert rdsap_tariff_for_cert(3, main_1_is_heat_pump_database=True) is Tariff.STANDARD +def test_unknown_meter_dual_electric_immersion_triggers_off_peak_via_rules() -> None: + # Arrange — RdSAP 10 §12 (PDF p.62): "If the electricity meter is unknown, + # treat as single meter EXCEPT where ... water heating [is] intended to + # run off an off-peak tariff (per systems listed in the text box above)" + # — the text box lists DUAL ELECTRIC IMMERSION. "If that results in a dual + # meter, assign tariff per rules 1 to 4." So an Unknown meter + dual + # electric immersion makes the dwelling dual; the 7-/10-hour choice then + # follows Rules 1-4 on the MAIN heating. Worksheet-validated on Khalim's + # "simulated case 48" (main 691 room heaters + Unknown meter + 903 dual + # immersion → Elmhurst 10-Hour Off Peak, SAP 57; ours 45→55). Corpus cert + # "Apartment 241" (main 691 + dual immersion) moved -5.38 → -1.05. + + # Act / Assert — dual immersion + room-heater main → Rule 3 → 10-hour; + # + storage main → Rule 2 → 7-hour (already off-peak); + gas main → Rule 4 + # default → 7-hour. WITHOUT the dual-immersion flag, the room-heater main + # stays STANDARD (single electric immersion is not a trigger). + assert rdsap_tariff_for_cert( + 3, main_1_sap_code=691, water_is_off_peak_dual_immersion=True + ) is Tariff.TEN_HOUR + assert rdsap_tariff_for_cert( + 3, main_1_sap_code=402, water_is_off_peak_dual_immersion=True + ) is Tariff.SEVEN_HOUR + assert rdsap_tariff_for_cert( + 3, main_1_sap_code=102, water_is_off_peak_dual_immersion=True + ) is Tariff.SEVEN_HOUR + assert rdsap_tariff_for_cert( + 3, main_1_sap_code=691, water_is_off_peak_dual_immersion=False + ) is Tariff.STANDARD + + def test_unknown_meter_with_non_electric_main_stays_standard() -> None: # Arrange — an "Unknown" meter on a GAS-heated dwelling (SAP 102) has # no off-peak evidence, so it must NOT pick up the Rule-4 Dual default From 4f208f472c8749bd147036054dae0fbb9408bdfb Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 09:59:15 +0000 Subject: [PATCH 07/19] =?UTF-8?q?test(corpus):=20ratchet=20SAP=20MAE=20flo?= =?UTF-8?q?or=200.80=20->=200.79=20after=20the=20=C2=A712=20dual-immersion?= =?UTF-8?q?=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MAE improved 0.793 -> 0.789 via the Unknown-meter + dual-electric-immersion off-peak trigger (commit 22fe4f41). Ratchet the ceiling so the gain can't silently regress. within-0.5 unchanged (72.5%). pyright not installed in this container — strict type gate not run locally. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/infrastructure/epc_client/test_sap_accuracy_corpus.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py index e3447b47..16d7300f 100644 --- a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py +++ b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py @@ -194,7 +194,11 @@ _CORPUS = Path( # stress worksheet (simulated case 46): closed its last ventilation residual # (our Jan ACH 9.14 -> 9.0748 exact; SAP 29 -> 30 = accredited Elmhurst). _MIN_WITHIN_HALF_SAP = 0.72 -_MAX_SAP_MAE = 0.80 +# 0.793 -> 0.789 via the §12 Unknown-meter + dual-electric-immersion off-peak +# trigger (RdSAP 10 PDF p.62): Apartment 241 (main 691 + 903 dual immersion) +# -5.38 -> -1.05. Worksheet-validated on "simulated case 48" (Elmhurst SAP 57, +# 10-Hour Off Peak). within-0.5 holds 72.5%. +_MAX_SAP_MAE = 0.79 _MAX_CO2_MAE_TONNES = 0.09 # t CO2 / yr vs co2_emissions_current _MAX_PE_PER_M2_MAE = 3.7 # kWh / m2 / yr vs energy_consumption_current From fd0530159facd9526cc334ddc231913facba1999 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 10:09:59 +0000 Subject: [PATCH 08/19] =?UTF-8?q?feat(tariff):=20complete=20=C2=A712=20Unk?= =?UTF-8?q?nown-meter=20clause=20=E2=80=94=20GSHP/WSHP=20main=20triggers?= =?UTF-8?q?=20off-peak=20(RdSAP=2010=20PDF=20p.62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the follow-up from 22fe4f41. RdSAP 10 §12's third Unknown-meter exception bullet — "main heating is ground source or water source heat pump" — was unimplemented. Add `_GROUND_OR_WATER_SOURCE_HEAT_PUMP_CODES` (Table 4a, SAP 10.2 PDF p.176-177: ground 211/215/221/225 + warm-air 521/525; water 213/216/223/226 + warm-air 523/526) to the Unknown-meter off-peak triggers; once dual, Rules 1-4 (Rule 3) resolve it to 10-hour. AIR-source heat pumps (214/217/224/227, 524/527) are deliberately EXCLUDED — the spec names only ground/water source. Verified the only Unknown-meter heat pumps in the corpus are "3/10 Bedford House" (main 214 = AIR source), which correctly KEEP STANDARD. 0 corpus certs carry a GSHP/WSHP on an Unknown meter, so this is a spec-completeness forward guard (gauge unchanged 72.5% / 0.789), same family as the Scotland-J wall / rafters-M roof 0-impact spec fixes. Coverage gap noted in-code: a database-index heat pump without a 211/213-style SAP code can't have its source type read from the code alone (rare). Spec-pinned (test_unknown_meter_ground_or_water_source_heat_pump_triggers_off_ peak). Gates green: corpus 72.5%/0.789, batch worksheet 0 raised/0 diverge, suite 2989 passed (2 known pre-existing fails). pyright not installed locally. Co-Authored-By: Claude Opus 4.8 (1M context) --- domain/sap10_calculator/tables/table_12a.py | 22 ++++++++++++++++++- .../domain/sap10_calculator/test_table_12a.py | 19 ++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/domain/sap10_calculator/tables/table_12a.py b/domain/sap10_calculator/tables/table_12a.py index d79d9fbe..72ea8f7b 100644 --- a/domain/sap10_calculator/tables/table_12a.py +++ b/domain/sap10_calculator/tables/table_12a.py @@ -262,6 +262,19 @@ _RULE_3_TEN_HOUR_CODES: Final[frozenset[int]] = frozenset( + [691, 692, 693, 694, 699] # electric room heaters (Table 4a) ) +# §12 Unknown-meter exception: "main heating is ground source or water source +# heat pump" makes the dwelling dual even though the same heat pump on a Single +# meter stays standard. This is the GROUND/WATER-source subset of Table 4a +# (SAP 10.2 PDF p.176-177): ground 211/215/221/225 + warm-air 521/525, water +# 213/216/223/226 + warm-air 523/526. AIR-source (214/217/224/227, 524/527) is +# DELIBERATELY EXCLUDED — the spec names only ground/water source. A heat pump +# lodged via a PCDB database index without one of these SAP codes can't have +# its source type read from the code alone (coverage gap: rare, 0 corpus certs). +_GROUND_OR_WATER_SOURCE_HEAT_PUMP_CODES: Final[frozenset[int]] = frozenset( + {211, 215, 221, 225, 521, 525} # ground source (radiator + warm-air) + | {213, 216, 223, 226, 523, 526} # water source (radiator + warm-air) +) + def _meter_is_unknown(meter_type: object) -> bool: """True when the meter is the RdSAP "Unknown" sentinel (code 3 / the @@ -367,11 +380,18 @@ def rdsap_tariff_for_cert( # immersion); every other Unknown+dual-immersion cert already has a # storage main (Rule 2). Single-immersion 691 certs (Flat 7, Flat 2) and # whc-909 instantaneous certs correctly STAY standard — they carry no - # text-box off-peak system. (GSHP/WSHP-main trigger: separate follow-up.) + # text-box off-peak system. The §12 third exception bullet ("main heating + # is ground source or water source heat pump") is the + # `_GROUND_OR_WATER_SOURCE_HEAT_PUMP_CODES` check — AIR-source heat pumps + # (214/224 etc.) are NOT a trigger and stay standard (e.g. corpus certs + # "3/10 Bedford House", main 214 ASHP on Unknown meters — verified 2026-06- + # 23 they keep STANDARD). No corpus cert carries a GSHP/WSHP on an Unknown + # meter, so this trigger is a spec-completeness forward guard (0 impact). if _meter_is_unknown(meter_type): off_peak_evidence = ( bool(main_codes & _RULE_1_CPSU_CODES) or bool(main_codes & _RULE_2_STORAGE_CODES) + or bool(main_codes & _GROUND_OR_WATER_SOURCE_HEAT_PUMP_CODES) or water_is_off_peak_dual_immersion ) if off_peak_evidence: diff --git a/tests/domain/sap10_calculator/test_table_12a.py b/tests/domain/sap10_calculator/test_table_12a.py index 8ce42b09..9a70dbfa 100644 --- a/tests/domain/sap10_calculator/test_table_12a.py +++ b/tests/domain/sap10_calculator/test_table_12a.py @@ -106,6 +106,25 @@ def test_unknown_meter_dual_electric_immersion_triggers_off_peak_via_rules() -> ) is Tariff.STANDARD +def test_unknown_meter_ground_or_water_source_heat_pump_triggers_off_peak() -> None: + # Arrange — RdSAP 10 §12 (PDF p.62) third Unknown-meter exception bullet: + # "main heating is ground source or water source heat pump." Per SAP 10.2 + # Table 4a (PDF p.176-177) ground source = 211/215/221/225 (+ warm-air + # 521/525), water source = 213/216/223/226 (+ warm-air 523/526); AIR + # source (214/224 etc.) is NOT named, so it stays standard. Once the GSHP/ + # WSHP makes the meter dual, Rules 1-4 (Rule 3, heat pump 211-224) → 10h. + # 0 corpus certs carry a GSHP/WSHP on an Unknown meter (the only Unknown- + # meter HPs are 3/10 Bedford House, main 214 = AIR source) — this is a + # spec-completeness forward guard. + + # Act / Assert — ground/water source → 10-hour; air source → STANDARD. + assert rdsap_tariff_for_cert(3, main_1_sap_code=211) is Tariff.TEN_HOUR + assert rdsap_tariff_for_cert(3, main_1_sap_code=213) is Tariff.TEN_HOUR + assert rdsap_tariff_for_cert(3, main_1_sap_code=521) is Tariff.TEN_HOUR + assert rdsap_tariff_for_cert(3, main_1_sap_code=214) is Tariff.STANDARD + assert rdsap_tariff_for_cert(3, main_1_sap_code=224) is Tariff.STANDARD + + def test_unknown_meter_with_non_electric_main_stays_standard() -> None: # Arrange — an "Unknown" meter on a GAS-heated dwelling (SAP 102) has # no off-peak evidence, so it must NOT pick up the Rule-4 Dual default From 4db05e843cf1df3d68017af59152f9cb0a46d1f3 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 11:33:07 +0000 Subject: [PATCH 09/19] =?UTF-8?q?fix(ventilation):=20dMEV=20takes=20lodged?= =?UTF-8?q?=200=20intermittent=20fans,=20not=20the=20Table=205=20default?= =?UTF-8?q?=20(SAP=2010.2=20=C2=A72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chasing the space-heating demand gap on "simulated case 48" (main 691 + Unknown meter + 903 dual immersion): our SAP 55 vs Elmhurst 57. Every §10a cost line already matched to the penny; the residual was demand — our space-heating energy 3849.8 kWh vs Elmhurst 3513.8 (+9.6%). Traced through the worksheet: our ventilation heat loss (38) ran ~35.5 W/K vs Elmhurst 27.76 — we were adding 20 m3/h of intermittent extract fans (the Table 5 age-band default) on a dwelling with a decentralised mechanical extract (dMEV) system that lodges 0 fans. SAP 10.2 §2 (PDF p.13): a whole-house mechanical EXTRACT system provides extraction via the (23a) 0.5 system air-change rate; the lodged intermittent extract-fan count (7a) is then explicit — a lodged 0 means 0 (the dMEV is the ventilation), NOT "unknown". The Table 5 default is an unknown-fallback for NATURALLY ventilated dwellings only, so it must not be substituted here. Fix: for EXTRACT_OR_PIV_OUTSIDE, take vc.intermittent_fans as-is (no age-band default). Worksheet-proven on two dMEV builds of cert 000565: "case 48" lodges (7a)=0 -> our SAP 55 -> 57 EXACT; the original 000565 fixture lodges (7a)=2 and keeps 2 (its e2e pins are unchanged). An earlier draft that forced fans=0 broke 000565 (which legitimately has 2) — corrected to "lodged as-is". within-0.5 72.5% -> 72.6%, MAE 0.789 -> 0.788; CO2/PE unchanged. The fix also reduces a systematic under-rating bias in the 21-cert dMEV cohort (median dSAP -0.22 -> -0.08). Scoped to EXTRACT_OR_PIV_OUTSIDE; balanced MVHR/MV kinds left untouched pending their own worksheet. SAP-schema regression test_18_0_0 pin 80 -> 81 (closer to its lodged 84, same cause). Spec-pinned in test_cert_to_inputs (dMEV-lodged-0 vs natural-default). pyright not installed in this container -- strict type gate not run locally. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../epc/domain/tests/test_from_sap_schema.py | 5 +- .../sap10_calculator/rdsap/cert_to_inputs.py | 11 ++++ .../rdsap/test_cert_to_inputs.py | 50 +++++++++++++++++++ .../epc_client/test_sap_accuracy_corpus.py | 5 +- 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/datatypes/epc/domain/tests/test_from_sap_schema.py b/datatypes/epc/domain/tests/test_from_sap_schema.py index 64b6ee5c..6991b28d 100644 --- a/datatypes/epc/domain/tests/test_from_sap_schema.py +++ b/datatypes/epc/domain/tests/test_from_sap_schema.py @@ -506,7 +506,10 @@ class TestFromSapSchema16_2: epc = EpcPropertyDataMapper.from_api_response(load("sap_18_0_0.json")) assert isinstance(epc, EpcPropertyData) assert epc.uprn == 10094601287 - assert Sap10Calculator().calculate(epc).sap_score == 80 # lodged 84 + # 80 -> 81 (closer to lodged 84) after the dMEV intermittent-fan fix: + # this cert is EXTRACT_OR_PIV_OUTSIDE with a lodged 0 fans, so the + # Table 5 age-band default is no longer substituted (SAP 10.2 §2 (7a)). + assert Sap10Calculator().calculate(epc).sap_score == 81 # lodged 84 def test_16_0_dispatches_via_16_x_path_with_tenure_default(self) -> None: # SAP-Schema-16.0 is the same reduced-field 16.x shape; it omits the diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 3f1294e5..72c9d4d8 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -5090,6 +5090,17 @@ def ventilation_from_cert( # can override via a future plumbing slice; the spec default # is what every MEV / MV / MVHR cohort cert lodges today. mv_system_ach = 0.5 + # For a whole-house mechanical EXTRACT system (MEV / dMEV) the + # lodged intermittent extract-fan count (7a) is taken AS-IS — the + # Table 5 age-band default must NOT be substituted for a lodged 0. + # On a mechanically-ventilated dwelling the fan count is explicit + # (the dMEV is the ventilation), so 0 means 0, not "unknown". + # Worksheet-proven on two dMEV builds of 000565: "case 48" lodges + # (7a)=0 → SAP 57 exact, while the original 000565 fixture lodges + # (7a)=2 → unchanged. Scoped to EXTRACT_OR_PIV_OUTSIDE; balanced + # MVHR/MV kinds are left untouched pending their own worksheet. + if mv_kind is MechanicalVentilationKind.EXTRACT_OR_PIV_OUTSIDE: + intermittent_fans = vc.intermittent_fans return ventilation_from_inputs( volume_m3=vol, storey_count=storeys, diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index add25b03..175ec7c0 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -1762,6 +1762,56 @@ def test_ventilation_from_cert_applies_table_5_default_when_lodged_zero() -> Non ) +def test_ventilation_from_cert_dmev_takes_lodged_zero_fans_not_table_5_default() -> None: + # Arrange — on a dwelling with a whole-house mechanical EXTRACT system + # (dMEV → EXTRACT_OR_PIV_OUTSIDE) the lodged intermittent extract-fan + # count (7a) is explicit: a lodged 0 means 0 (the dMEV is the + # ventilation), NOT "unknown". The SAP 10.2 §2 Table 5 age-band default + # must NOT be substituted as it would on a NATURALLY ventilated dwelling. + # Worksheet-proven: two dMEV builds of cert 000565 — "simulated case 48" + # lodges (7a)=0 and Elmhurst's worksheet uses 0 (→ SAP 57 exact), while + # the original 000565 fixture lodges (7a)=2 and Elmhurst uses 2. The same + # Table 5 default that this age-G NATURAL case applies (1 fan above) must + # be suppressed here. + from domain.sap10_calculator.worksheet.ventilation import ( + MechanicalVentilationKind, + ) + age_g_part = make_building_part( + floor_dimensions=[ + make_floor_dimension(total_floor_area_m2=45.0, floor=0), + make_floor_dimension(total_floor_area_m2=45.0, floor=1), + ], + construction_age_band='G', + ) + base = _typical_semi_detached_epc() + epc_dmev = make_minimal_sap10_epc( + total_floor_area_m2=_TYPICAL_TFA_M2, + habitable_rooms_count=4, + region_code="1", + sap_building_parts=[age_g_part], + sap_windows=base.sap_windows, + sap_heating=base.sap_heating, + sap_ventilation=SapVentilation( + extract_fans_count=0, + mechanical_ventilation_kind=( + MechanicalVentilationKind.EXTRACT_OR_PIV_OUTSIDE.name + ), + ), + ) + + # Act + v = ventilation_from_cert(epc_dmev) + + # Assert — 0 intermittent fans contribute 0 to (8), so openings ACH = 0 + # (no Table 5 age-G default fan); the mechanical system is applied via + # the separate (23a) 0.5 system air-change rate instead. + assert abs(v.openings_ach) <= 1e-9, ( + f"openings ACH {v.openings_ach:.6f} should be 0 for a dMEV cert " + f"lodging 0 fans (no Table 5 default substituted)" + ) + assert v.mv_system_ach == 0.5 + + def test_ventilation_from_cert_uses_lodged_fans_below_age_default_not_floored() -> None: # Arrange — RdSAP 10 §4.1 Table 5 (PDF p.28): "Number of extract fans if # known; if unknown: [age-band default]." The default is an UNKNOWN- diff --git a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py index 16d7300f..e8b47d6a 100644 --- a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py +++ b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py @@ -197,7 +197,10 @@ _MIN_WITHIN_HALF_SAP = 0.72 # 0.793 -> 0.789 via the §12 Unknown-meter + dual-electric-immersion off-peak # trigger (RdSAP 10 PDF p.62): Apartment 241 (main 691 + 903 dual immersion) # -5.38 -> -1.05. Worksheet-validated on "simulated case 48" (Elmhurst SAP 57, -# 10-Hour Off Peak). within-0.5 holds 72.5%. +# 10-Hour Off Peak). Then 0.789 -> 0.788 (within-0.5 72.5% -> 72.6%) via the +# dMEV intermittent-fan fix: an EXTRACT_OR_PIV_OUTSIDE cert lodging 0 fans now +# takes 0 (not the Table 5 age-band default) — same "case 48" worksheet closes +# its space-heating demand to land SAP 57 exact. _MAX_SAP_MAE = 0.79 _MAX_CO2_MAE_TONNES = 0.09 # t CO2 / yr vs co2_emissions_current _MAX_PE_PER_M2_MAE = 3.7 # kWh / m2 / yr vs energy_consumption_current From 34cbd7d66c9b3c3c1035638dbe80d7796370f2db Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 19:29:17 +0000 Subject: [PATCH 10/19] feat(pcdb): parse Table 323 (Centralised MEV / MVHR) + Table 329 efficiency IUF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MVHR (24a) heat-recovery support, part 1: the PCDB data layer. PCDB Table 323 (PCDF Spec Rev 6b §A.18, Format 426; pcdb10.dat carries Format 431, header `$323,431,...`) holds the per-wet-room SFP + heat- exchanger efficiency for centralised MEV / MVHR units. Added `MvhrRecord` / `MvhrDataPoint`, `parse_centralised_mv_row` / `parse_table_323`, the ETL step, the committed jsonl, and the `mvhr_record(pcdb_id)` runtime lookup (mirrors Table 322). SAP 10.2 §2.6.4/§2.6.6: "MVHR ... SFP is a single value depending on the number of wet rooms" — each test group's leading field is the wet-room count; callers select the group matching the dwelling lodgement. Worksheet-proven on simulated case 49 (000565, 2 wet rooms, Vent Axia Sentinel Kinetic B 500140 → flow 21.0, SFP 0.88, efficiency 91%). Also decoded the MVHR heat-recovery efficiency in-use factor from Table 329 (Format 432): system_type 3 ducts-inside-envelope = 0.90 (case-49 (23c) = 91 × 0.90 = 81.9%), cross-checked against system_type 10 = 0.70 (= SAP 10.2 Table 4g default heat-recovery in-use factor). "Table 4h is no longer used – data now stored in the PCDB" (SAP 10.2 p.176). The outside-envelope efficiency columns + with-scheme SFP blocks are preserved verbatim in `raw` (no fixture exercises them yet). Note: pyright strict type gate not run locally (pyright not installed). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/tables/pcdb/__init__.py | 40 + .../pcdb_table_323_centralised_mev_mvhr.jsonl | 822 ++++++++++++++++++ domain/sap10_calculator/tables/pcdb/etl.py | 18 + domain/sap10_calculator/tables/pcdb/parser.py | 144 ++- .../test_pcdb_table_323_lookup.py | 105 +++ 5 files changed, 1125 insertions(+), 4 deletions(-) create mode 100644 domain/sap10_calculator/tables/pcdb/data/pcdb_table_323_centralised_mev_mvhr.jsonl create mode 100644 tests/domain/sap10_calculator/test_pcdb_table_323_lookup.py diff --git a/domain/sap10_calculator/tables/pcdb/__init__.py b/domain/sap10_calculator/tables/pcdb/__init__.py index 017d98e1..6fd2991e 100644 --- a/domain/sap10_calculator/tables/pcdb/__init__.py +++ b/domain/sap10_calculator/tables/pcdb/__init__.py @@ -32,7 +32,10 @@ from domain.sap10_calculator.tables.pcdb.parser import ( GasOilBoilerRecord, HeatPumpRecord, MevFanConfig, + MvhrDataPoint, + MvhrRecord, MvInUseFactorsRecord, + parse_centralised_mv_row, parse_decentralised_mev_row, parse_heat_pump_row_raw, parse_mv_in_use_factors_row, @@ -43,11 +46,14 @@ __all__ = [ "GasOilBoilerRecord", "HeatPumpRecord", "MevFanConfig", + "MvhrDataPoint", + "MvhrRecord", "MvInUseFactorsRecord", "decentralised_mev_record", "gas_oil_boiler_record", "heat_pump_record", "mv_in_use_factors_record", + "mvhr_record", ] @@ -58,6 +64,9 @@ _TABLE_105_JSONL: Final[Path] = ( _TABLE_322_JSONL: Final[Path] = ( _PCDB_DATA_DIR / "pcdb_table_322_decentralised_mev.jsonl" ) +_TABLE_323_JSONL: Final[Path] = ( + _PCDB_DATA_DIR / "pcdb_table_323_centralised_mev_mvhr.jsonl" +) _TABLE_329_JSONL: Final[Path] = ( _PCDB_DATA_DIR / "pcdb_table_329_mv_in_use_factors.jsonl" ) @@ -181,6 +190,37 @@ def decentralised_mev_record(pcdb_id: int) -> Optional[DecentralisedMevRecord]: return _TABLE_322_BY_ID.get(pcdb_id) +def _load_table_323() -> dict[int, MvhrRecord]: + """Read the Table 323 NDJSON at import time and build a by-pcdb-id + dict of typed `MvhrRecord`s (centralised MEV + MVHR). Returns an + empty dict when the jsonl is missing (ETL bootstrap concession; + production callers always observe the committed file).""" + records_by_id: dict[int, MvhrRecord] = {} + if not _TABLE_323_JSONL.exists(): + return records_by_id + with _TABLE_323_JSONL.open(encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line: + continue + data = json.loads(line) + raw_fields = tuple(data["raw"]) + record = parse_centralised_mv_row(",".join(raw_fields)) + records_by_id[record.pcdb_id] = record + return records_by_id + + +_TABLE_323_BY_ID: Final[dict[int, MvhrRecord]] = _load_table_323() + + +def mvhr_record(pcdb_id: int) -> Optional[MvhrRecord]: + """Table 323 lookup by `MV PCDF Reference Number` (cert lodgement + field) for centralised MEV / MVHR systems. Returns None when the + index is not in Table 323 — caller falls back to the SAP 10.2 Table + 4g default data (MVHR raw SFP 2.0, efficiency 66%).""" + return _TABLE_323_BY_ID.get(pcdb_id) + + def _load_table_329() -> dict[int, MvInUseFactorsRecord]: """Read the Table 329 NDJSON at import time and build a by-system- type dict of typed `MvInUseFactorsRecord`s. Returns empty when the diff --git a/domain/sap10_calculator/tables/pcdb/data/pcdb_table_323_centralised_mev_mvhr.jsonl b/domain/sap10_calculator/tables/pcdb/data/pcdb_table_323_centralised_mev_mvhr.jsonl new file mode 100644 index 00000000..2b796447 --- /dev/null +++ b/domain/sap10_calculator/tables/pcdb/data/pcdb_table_323_centralised_mev_mvhr.jsonl @@ -0,0 +1,822 @@ +{"pcdb_id": 500002, "raw": ["500002", "020002", "0", "2013/Oct/02 17:18", "Vent Axia Ltd", "Vent Axia", "MVDC - MS", "", "", "2012", "", "1", "0", "2", "1", "", "3", "1", "21.0", "0.24", "", "2", "29.0", "0.18", "", "3", "37.0", "0.21"]} +{"pcdb_id": 500003, "raw": ["500003", "020003", "0", "2007/Oct/18 08:54", "The Nuaire Group", "Nuaire", "ES MEV", "", "2006", "2007", "", "1", "0", "2", "2", "", "3", "1", "21.0", "0.34", "", "2", "29.0", "0.31", "", "3", "37.0", "0.33"]} +{"pcdb_id": 500004, "raw": ["500004", "020004", "0", "2014/Jun/18 12:30", "Greenwood Air Management Ltd", "Greenwood", "CMFe", "", "", "current", "", "1", "0", "2", "1", "", "3", "1", "21.0", "0.50", "", "2", "29.0", "0.34", "", "3", "37.0", "0.38"]} +{"pcdb_id": 500005, "raw": ["500005", "020004", "0", "2016/May/18 18:36", "Greenwood Air Management Ltd", "Greenwood", "MVHR 90R", "", "2007", "2013", "", "3", "0", "2", "1", "3", "3", "1", "15.0", "0.78", "81", "2", "21.0", "0.84", "81", "3", "27.0", "0.97", "80"]} +{"pcdb_id": 500007, "raw": ["500007", "020002", "0", "2015/Nov/11 10:34", "Vent Axia Ltd", "Vent Axia", "HRE 350", "", "", "2010", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.96", "90", "2", "21.0", "0.96", "90", "3", "27.0", "0.99", "86"]} +{"pcdb_id": 500011, "raw": ["500011", "020083", "0", "2015/Nov/11 10:34", "Applied Energy Products Ltd", "Xpelair", "Xcell 270 DC", "", "", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.06", "91", "2", "21.0", "1.34", "91", "3", "27.0", "1.30", "90", "4", "33.0", "1.84", "89"]} +{"pcdb_id": 500012, "raw": ["500012", "020004", "0", "2016/May/18 18:34", "Greenwood Air Management Ltd", "Greenwood", "Fusion HRV1", "", "2007", "2013", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.72", "63", "2", "21.0", "0.78", "63"]} +{"pcdb_id": 500013, "raw": ["500013", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "NU-MVHR 70", "", "2007", "2007", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.79", "69", "2", "21.0", "0.89", "69", "3", "27.0", "1.07", "65"]} +{"pcdb_id": 500021, "raw": ["500021", "020008", "0", "2016/Jul/14 15:48", "Passivent Ltd", "Passivent", "A151 DC", "", "2007", "2013", "", "1", "0", "2", "1", "", "3", "1", "21.0", "0.36", "", "2", "29.0", "0.30", "", "3", "37.0", "0.29"]} +{"pcdb_id": 500022, "raw": ["500022", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "WHR 350", "", "2007", "2007", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "1.10", "89", "2", "21.0", "1.16", "89", "3", "27.0", "1.30", "87", "4", "33.0", "1.48", "86", "5", "39.0", "1.68", "85", "6", "45.0", "1.89", "83"]} +{"pcdb_id": 500023, "raw": ["500023", "020002", "0", "2015/Nov/11 10:34", "Vent Axia Ltd", "Vent Axia", "LoWatt HR204", "", "", "2011", "", "3", "0", "2", "2", "1", "1", "1", "15.0", "1.05", "57"]} +{"pcdb_id": 500024, "raw": ["500024", "020009", "0", "2013/Oct/24 09:06", "Vortice Ltd", "Vortice", "CVE ECO 2", "", "2007", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.22", "", "2", "29.0", "0.20", "", "3", "37.0", "0.21", "", "4", "45.0", "0.24"]} +{"pcdb_id": 500025, "raw": ["500025", "020009", "0", "2015/Nov/11 10:34", "Vortice Ltd", "Vortice", "HRU ECO 3 RF", "", "", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.56", "92", "2", "21.0", "0.62", "93", "3", "27.0", "0.72", "88", "4", "33.0", "0.86", "87", "5", "39.0", "1.00", "85", "6", "45.0", "1.16", "84"]} +{"pcdb_id": 500026, "raw": ["500026", "020078", "0", "2015/Nov/11 10:34", "Villavent Ltd", "Villavent", "VM2", "", "", "current", "", "3", "0", "2", "1", "1", "4", "2", "21.0", "0.91", "84", "3", "27.0", "0.91", "84", "4", "33.0", "1.02", "82", "5", "39.0", "1.18", "83"]} +{"pcdb_id": 500027, "raw": ["500027", "020003", "0", "2007/Oct/18 08:53", "The Nuaire Group", "Nuaire", "MEVDC-ES", "", "2007", "current", "", "1", "0", "2", "2", "", "3", "1", "21.0", "0.34", "", "2", "29.0", "0.31", "", "3", "37.0", "0.33"]} +{"pcdb_id": 500028, "raw": ["500028", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX70", "", "2007", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.79", "69", "2", "21.0", "0.89", "69", "3", "27.0", "1.07", "65"]} +{"pcdb_id": 500029, "raw": ["500029", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX90L", "", "2007", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "1.12", "89", "2", "21.0", "1.13", "89", "3", "27.0", "1.26", "86", "4", "33.0", "1.44", "85", "5", "39.0", "1.65", "85"]} +{"pcdb_id": 500030, "raw": ["500030", "020007", "0", "2016/Feb/22 15:19", "Titon Hardware Ltd", "Titon", "Q Plus WCME 100", "", "2007", "2009", "", "1", "0", "2", "1", "", "3", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.18"]} +{"pcdb_id": 500031, "raw": ["500031", "020011", "3", "2022/Jan/27 07:28", "Vectaire Ltd", "Vectaire", "MBOX 125/2DC", "", "", "obsolete", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.38", "", "2", "29.0", "0.41", "", "3", "37.0", "0.49", "", "4", "45.0", "0.65"]} +{"pcdb_id": 500032, "raw": ["500032", "020083", "0", "2016/Jun/20 12:41", "Applied Energy Products Ltd", "Xpelair", "Xplus 340", "", "", "current", "", "1", "0", "2", "1", "", "3", "1", "21.0", "0.38", "", "2", "29.0", "0.30", "", "3", "37.0", "0.24"]} +{"pcdb_id": 500033, "raw": ["500033", "020083", "0", "2016/Jun/20 12:41", "Applied Energy Products Ltd", "Xpelair", "Xplus 250DC 8CV", "", "", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.52", "", "2", "29.0", "0.27", "", "3", "37.0", "0.34", "", "4", "45.0", "0.50"]} +{"pcdb_id": 500034, "raw": ["500034", "020007", "0", "2016/Feb/22 14:32", "Titon Hardware Ltd", "Titon", "Q Plus WHR 350", "", "", "2007", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "1.10", "89", "2", "21.0", "1.16", "89", "3", "27.0", "1.30", "87", "4", "33.0", "1.48", "86", "5", "39.0", "1.68", "85", "6", "45.0", "1.89", "83"]} +{"pcdb_id": 500035, "raw": ["500035", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "Q Plus WHR 180", "", "", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "1.00", "84", "2", "21.0", "1.06", "84"]} +{"pcdb_id": 500036, "raw": ["500036", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 140 L", "", "2007", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "1.03", "81", "2", "21.0", "1.48", "81"]} +{"pcdb_id": 500037, "raw": ["500037", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 350", "", "2007", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.70", "85", "2", "21.0", "0.71", "84", "3", "27.0", "0.81", "83", "4", "33.0", "0.94", "82", "5", "39.0", "1.08", "82"]} +{"pcdb_id": 500038, "raw": ["500038", "020024", "0", "2015/Nov/11 10:34", "Itho BV", "Xpelair", "Xcell 301DC", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.55", "91", "2", "21.0", "0.61", "91", "3", "27.0", "0.72", "87", "4", "33.0", "0.84", "86", "5", "39.0", "0.97", "84", "6", "45.0", "1.11", "83"]} +{"pcdb_id": 500039, "raw": ["500039", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX90S", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.06", "83", "2", "21.0", "1.13", "85", "3", "27.0", "1.37", "82", "4", "33.0", "1.69", "79"]} +{"pcdb_id": 500040, "raw": ["500040", "020013", "0", "2013/Oct/24 09:06", "Johnson & Starley Ltd", "Johnson & Starley", "CE300 Premier", "", "2007", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.26", "", "2", "29.0", "0.24", "", "3", "37.0", "0.29", "", "4", "45.0", "0.36", "", "5", "53.0", "0.46"]} +{"pcdb_id": 500041, "raw": ["500041", "020013", "0", "2008/May/03 10:29", "Johnson & Starley Ltd", "Johnson & Starley", "CE180 Premier", "", "2007", "current", "", "1", "0", "2", "2", "", "4", "1", "21.0", "0.40", "", "2", "29.0", "0.45", "", "3", "37.0", "0.75", "", "4", "45.0", "1.03"]} +{"pcdb_id": 500042, "raw": ["500042", "020014", "0", "2013/Oct/24 09:06", "Itho Ventilation Ltd", "Itho", "CVE ECO 2 HP", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.18", "", "2", "29.0", "0.17", "", "3", "37.0", "0.18", "", "4", "45.0", "0.20", "", "5", "53.0", "0.22", "", "6", "61.0", "0.27"]} +{"pcdb_id": 500043, "raw": ["500043", "020014", "0", "2013/Oct/24 09:06", "Itho Ventilation Ltd", "Itho", "CVE ECO 2", "", "2008", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.18", "", "2", "29.0", "0.17", "", "3", "37.0", "0.18", "", "4", "45.0", "0.20", "", "5", "53.0", "0.22"]} +{"pcdb_id": 500044, "raw": ["500044", "020014", "0", "2015/Nov/11 10:34", "Itho Ventilation Ltd", "Itho", "HRU ECO 4", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.47", "90", "2", "21.0", "0.53", "90", "3", "27.0", "0.65", "87", "4", "33.0", "0.79", "87", "5", "39.0", "0.94", "87", "6", "45.0", "1.10", "88"]} +{"pcdb_id": 500047, "raw": ["500047", "020016", "0", "2015/Nov/11 10:34", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "PA600", "", "2007", "2012", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.97", "90", "2", "21.0", "0.90", "90", "3", "27.0", "1.00", "89", "4", "33.0", "1.18", "88"]} +{"pcdb_id": 500048, "raw": ["500048", "020017", "0", "2013/Oct/24 09:06", "Airflow Developments Ltd", "Airflow", "HVS-10", "", "2008", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.26", "", "2", "29.0", "0.21", "", "3", "37.0", "0.24", "", "4", "45.0", "0.26"]} +{"pcdb_id": 500049, "raw": ["500049", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent 230 EC", "", "2007", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.20", "83", "2", "21.0", "1.63", "83", "3", "27.0", "1.62", "78", "4", "33.0", "1.90", "75"]} +{"pcdb_id": 500050, "raw": ["500050", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 300", "", "2007", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "1.10", "85", "2", "21.0", "0.97", "85", "3", "27.0", "0.97", "86", "4", "33.0", "1.04", "85", "5", "39.0", "1.15", "85"]} +{"pcdb_id": 500051, "raw": ["500051", "020016", "0", "2015/Nov/11 10:34", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "PA300", "", "2007", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "1.00", "74", "2", "21.0", "1.07", "74"]} +{"pcdb_id": 500052, "raw": ["500052", "020004", "0", "2016/May/18 18:37", "Greenwood Air Management Ltd", "Greenwood", "HRV95", "", "2007", "2012", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.73", "86", "2", "21.0", "0.76", "86", "3", "27.0", "0.87", "85", "4", "33.0", "1.00", "83", "5", "39.0", "1.15", "81", "6", "45.0", "1.32", "78"]} +{"pcdb_id": 500053, "raw": ["500053", "020013", "0", "2015/Nov/11 10:34", "Johnson & Starley Ltd", "Johnson & Starley", "LE155", "125 mm ducts", "2007", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.65", "62", "2", "21.0", "0.84", "62", "3", "27.0", "1.14", "58", "4", "33.0", "1.48", "55"]} +{"pcdb_id": 500054, "raw": ["500054", "020013", "0", "2015/Nov/11 10:34", "Johnson & Starley Ltd", "Johnson & Starley", "LE155", "100 mm ducts", "2007", "current", "", "3", "0", "2", "2", "1", "3", "1", "15.0", "0.94", "62", "2", "21.0", "1.64", "62", "3", "27.0", "2.60", "60"]} +{"pcdb_id": 500055, "raw": ["500055", "020017", "0", "2013/Oct/24 09:06", "Airflow Developments Ltd", "Airflow", "WHV-8", "", "2008", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.55", "", "2", "29.0", "0.83", "", "3", "37.0", "0.65", "", "4", "45.0", "0.54", "", "5", "53.0", "0.45"]} +{"pcdb_id": 500056, "raw": ["500056", "020018", "0", "2013/Oct/24 09:06", "National Ventilation", "National Ventilation", "MVS10", "", "2008", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.41", "", "2", "29.0", "0.33", "", "3", "37.0", "0.29", "", "4", "45.0", "0.27"]} +{"pcdb_id": 500059, "raw": ["500059", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "150 Effect Duplexvent", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.88", "79", "2", "21.0", "0.90", "79", "3", "27.0", "0.99", "82", "4", "33.0", "1.13", "82", "5", "39.0", "1.28", "83"]} +{"pcdb_id": 500060, "raw": ["500060", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "90 Duplexvent", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.75", "82", "2", "21.0", "0.86", "82", "3", "27.0", "1.07", "82", "4", "33.0", "1.32", "82", "5", "39.0", "1.59", "80"]} +{"pcdb_id": 500061, "raw": ["500061", "020019", "0", "2015/Nov/11 10:34", "Intervent Ventilation Systems Ltd", "Intervent Ventilation Systems", "HRC300-4B", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "1.04", "86", "2", "21.0", "0.91", "86", "3", "27.0", "0.93", "85", "4", "33.0", "1.02", "85", "5", "39.0", "1.15", "86", "6", "45.0", "1.30", "87"]} +{"pcdb_id": 500062, "raw": ["500062", "020015", "0", "2015/Nov/11 10:34", "Helios Ventilation Systems Ltd", "Helios", "KWL EC 450", "", "2008", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.99", "77", "2", "21.0", "1.01", "77", "3", "27.0", "1.10", "80", "4", "33.0", "1.22", "81", "5", "39.0", "1.35", "81", "6", "45.0", "1.49", "82", "7", "51.0", "1.64", "82"]} +{"pcdb_id": 500063, "raw": ["500063", "020015", "0", "2015/Nov/11 10:34", "Helios Ventilation Systems Ltd", "Helios", "KWL EC 300", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.79", "79", "2", "21.0", "0.93", "79", "3", "27.0", "1.15", "78", "4", "33.0", "1.42", "77", "5", "39.0", "1.72", "76"]} +{"pcdb_id": 500064, "raw": ["500064", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "LPXBOX DC-2", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.71", "65", "2", "21.0", "0.80", "65", "3", "27.0", "1.07", "62", "4", "33.0", "1.43", "60", "5", "39.0", "1.84", "59", "6", "45.0", "2.27", "57"]} +{"pcdb_id": 500065, "raw": ["500065", "020020", "0", "2015/Nov/11 10:34", "Orcon B.V.", "Orcon", "HRC300-4B", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "1.04", "86", "2", "21.0", "0.91", "86", "3", "27.0", "0.93", "85", "4", "33.0", "1.02", "85", "5", "39.0", "1.15", "86", "6", "45.0", "1.30", "87"]} +{"pcdb_id": 500066, "raw": ["500066", "020021", "0", "2016/May/18 18:30", "Paul Waermerueckgewinnung Gmbh", "Paul", "Atmos 175 DC", "", "2006", "2008", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.55", "86", "2", "21.0", "0.53", "86", "3", "27.0", "0.61", "86", "4", "33.0", "0.73", "87", "5", "39.0", "0.88", "87", "6", "45.0", "1.04", "88"]} +{"pcdb_id": 500067, "raw": ["500067", "020021", "0", "2016/May/18 18:30", "Paul Waermerueckgewinnung Gmbh", "Paul", "Thermos 200 DC", "", "2006", "2012", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.52", "90", "2", "21.0", "0.55", "90", "3", "27.0", "0.65", "89", "4", "33.0", "0.78", "88", "5", "39.0", "0.92", "88", "6", "45.0", "1.08", "88"]} +{"pcdb_id": 500068, "raw": ["500068", "020021", "0", "2016/May/18 18:30", "Paul Waermerueckgewinnung Gmbh", "Paul", "Thermos 300 DC", "", "2006", "2012", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.49", "91", "2", "21.0", "0.53", "91", "3", "27.0", "0.62", "90", "4", "33.0", "0.75", "89", "5", "39.0", "0.90", "89", "6", "45.0", "1.05", "88"]} +{"pcdb_id": 500069, "raw": ["500069", "020021", "0", "2016/May/18 18:30", "Paul Waermerueckgewinnung Gmbh", "Paul", "Multi 100 DC", "", "2006", "2014", "", "3", "0", "2", "1", "1", "1", "1", "15.0", "0.94", "87"]} +{"pcdb_id": 500070, "raw": ["500070", "020022", "0", "2015/Nov/11 10:34", "Vallox Oy", "Vallox", "150 Effect", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.87", "79", "2", "21.0", "0.88", "79", "3", "27.0", "0.97", "82", "4", "33.0", "1.11", "82", "5", "39.0", "1.26", "83", "6", "45.0", "1.42", "83"]} +{"pcdb_id": 500071, "raw": ["500071", "020022", "0", "2015/Nov/11 10:34", "Vallox Oy", "Vallox", "90", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.73", "82", "2", "21.0", "0.85", "82", "3", "27.0", "1.05", "82", "4", "33.0", "1.29", "82", "5", "39.0", "1.56", "80", "6", "45.0", "1.84", "78"]} +{"pcdb_id": 500072, "raw": ["500072", "020083", "0", "2015/Nov/11 10:34", "Applied Energy Products Ltd", "Xpelair", "Xcell 350V EC BP", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.72", "93", "2", "21.0", "0.75", "93", "3", "27.0", "0.85", "91", "4", "33.0", "0.98", "91", "5", "39.0", "1.13", "90"]} +{"pcdb_id": 500073, "raw": ["500073", "020083", "0", "2015/Nov/11 10:34", "Applied Energy Products Ltd", "Xpelair", "Xcell 150U EC BP", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.17", "87", "2", "21.0", "1.26", "87", "3", "27.0", "1.54", "85", "4", "33.0", "1.90", "85"]} +{"pcdb_id": 500074, "raw": ["500074", "020023", "0", "2026/Feb/25 10:50", "Ubbink (UK) Ltd", "Ubbink", "HRV Compact", "", "2008", "obsolete", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.09", "86", "2", "21.0", "1.23", "86", "3", "27.0", "1.48", "84", "4", "33.0", "1.77", "82"]} +{"pcdb_id": 500075, "raw": ["500075", "020023", "0", "2026/Feb/25 10:50", "Ubbink (UK) Ltd", "Ubbink", "HRV Medio", "", "2008", "obsolete", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.80", "84", "2", "21.0", "0.82", "84", "3", "27.0", "0.94", "83", "4", "33.0", "1.09", "81", "5", "39.0", "1.27", "79", "6", "45.0", "1.46", "77"]} +{"pcdb_id": 500076, "raw": ["500076", "020023", "0", "2026/Feb/25 10:50", "Ubbink (UK) Ltd", "Ubbink", "HVR Grande", "", "2008", "obsolete", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.97", "82", "2", "21.0", "0.99", "83", "3", "27.0", "1.11", "82", "4", "33.0", "1.28", "81", "5", "39.0", "1.48", "80", "6", "45.0", "1.69", "79"]} +{"pcdb_id": 500077, "raw": ["500077", "020002", "0", "2013/Oct/02 17:18", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent", "", "2008", "2012", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.19", "", "2", "29.0", "0.20", "", "3", "37.0", "0.24", "", "4", "45.0", "0.27", "", "5", "53.0", "0.32", "", "6", "61.0", "0.38"]} +{"pcdb_id": 500082, "raw": ["500082", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "HRV1 Q Plus", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.73", "90", "2", "21.0", "0.84", "89", "3", "27.0", "1.01", "87", "4", "33.0", "1.21", "87"]} +{"pcdb_id": 500083, "raw": ["500083", "020026", "0", "2015/Nov/11 10:34", "EDPAC International Ltd", "Edpac", "HR200 EC", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.74", "79", "2", "21.0", "0.83", "79", "3", "27.0", "0.97", "79", "4", "33.0", "1.12", "78"]} +{"pcdb_id": 500084, "raw": ["500084", "020002", "0", "2015/Nov/11 10:34", "Vent Axia Ltd", "Vent Axia", "Air Minder Plus Maxi FB", "", "2007", "2010", "", "3", "0", "2", "1", "1", "2", "2", "21.0", "1.01", "86", "3", "27.0", "1.04", "86"]} +{"pcdb_id": 500085, "raw": ["500085", "020009", "0", "2015/Nov/11 10:34", "Vortice Ltd", "Vortice", "Vort Prometeo HR400", "", "2008", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.55", "91", "2", "21.0", "0.59", "91", "3", "27.0", "0.68", "89", "4", "33.0", "0.80", "88", "5", "39.0", "0.93", "88", "6", "45.0", "1.08", "87", "7", "51.0", "1.23", "86"]} +{"pcdb_id": 500086, "raw": ["500086", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "Energivent Q", "", "2008", "2013", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.69", "87", "2", "21.0", "0.71", "88", "3", "27.0", "0.83", "88", "4", "33.0", "0.98", "87", "5", "39.0", "1.15", "86", "6", "45.0", "1.34", "85"]} +{"pcdb_id": 500087, "raw": ["500087", "020028", "0", "2015/Nov/11 10:34", "Enervent Oy Ab", "Enervent", "Plaza", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "1.31", "77", "2", "21.0", "1.36", "78", "3", "27.0", "1.60", "77", "4", "33.0", "1.93", "76", "5", "39.0", "2.30", "73"]} +{"pcdb_id": 500096, "raw": ["500096", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "HR180Q", "", "2008", "2012", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.09", "86", "2", "21.0", "1.23", "86", "3", "27.0", "1.48", "84", "4", "33.0", "1.77", "82"]} +{"pcdb_id": 500097, "raw": ["500097", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "HR300Q", "", "2008", "2012", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.80", "84", "2", "21.0", "0.82", "84", "3", "27.0", "0.94", "83", "4", "33.0", "1.09", "81", "5", "39.0", "1.27", "79", "6", "45.0", "1.46", "77"]} +{"pcdb_id": 500098, "raw": ["500098", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "HR400Q", "", "2008", "2012", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.97", "82", "2", "21.0", "0.99", "83", "3", "27.0", "1.11", "82", "4", "33.0", "1.28", "81", "5", "39.0", "1.48", "80", "6", "45.0", "1.69", "79"]} +{"pcdb_id": 500099, "raw": ["500099", "020030", "0", "2015/Nov/11 10:34", "Aldes", "Aldes", "DeeFly 90 Microwatt", "", "2007", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "0.88", "85", "3", "27.0", "0.97", "85", "4", "33.0", "1.16", "84", "5", "39.0", "1.39", "82", "6", "45.0", "1.66", "79"]} +{"pcdb_id": 500101, "raw": ["500101", "020031", "0", "2013/Oct/24 09:06", "NIBE Energy Systems Ltd", "NIBE", "Fighter 360P", "", "2008", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "2.00", "", "2", "29.0", "1.05", "", "3", "37.0", "0.82", "", "4", "45.0", "0.86", "", "5", "53.0", "0.83", "", "6", "61.0", "0.81"]} +{"pcdb_id": 500102, "raw": ["500102", "020013", "0", "2015/Nov/11 10:34", "Johnson & Starley Ltd", "Johnson & Starley", "LE250-1", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.62", "58", "2", "21.0", "0.83", "57", "3", "27.0", "1.13", "54", "4", "33.0", "1.48", "54"]} +{"pcdb_id": 500103, "raw": ["500103", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR100/90DC", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.79", "79", "2", "21.0", "0.97", "79", "3", "27.0", "1.19", "78", "4", "33.0", "1.45", "77", "5", "39.0", "1.71", "77"]} +{"pcdb_id": 500104, "raw": ["500104", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR150DC", "", "2007", "current", "", "3", "0", "2", "2", "1", "3", "1", "15.0", "0.94", "62", "2", "21.0", "1.64", "62", "3", "27.0", "2.60", "60"]} +{"pcdb_id": 500105, "raw": ["500105", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR250DC", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.61", "58", "2", "21.0", "0.81", "57", "3", "27.0", "1.11", "54", "4", "33.0", "1.45", "54"]} +{"pcdb_id": 500106, "raw": ["500106", "020004", "0", "2014/Jun/18 12:30", "Greenwood Air Management Ltd", "Greenwood", "CMEV.4", "", "2009", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.80", "", "2", "29.0", "0.59", "", "3", "37.0", "0.72", "", "4", "45.0", "0.60", "", "5", "53.0", "0.51"]} +{"pcdb_id": 500107, "raw": ["500107", "020004", "0", "2014/Jun/18 12:30", "Greenwood Air Management Ltd", "Greenwood", "CMEV.4e", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.22", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500108, "raw": ["500108", "020002", "0", "2015/Nov/11 10:34", "Vent Axia Ltd", "Vent Axia", "Air Minder Plas Midi FB", "", "2007", "2010", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "1.16", "84", "2", "21.0", "1.28", "82", "3", "27.0", "1.41", "84", "4", "33.0", "1.54", "88", "5", "39.0", "1.67", "94"]} +{"pcdb_id": 500109, "raw": ["500109", "020032", "0", "2015/Nov/11 10:34", "Utek srl", "Utek", "HRE 350-EC-V CTR", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.93", "89", "2", "21.0", "0.98", "89", "3", "27.0", "1.12", "88", "4", "33.0", "1.31", "87", "5", "39.0", "1.51", "85"]} +{"pcdb_id": 500110, "raw": ["500110", "020028", "0", "2015/Nov/11 10:34", "Enervent Oy Ab", "Enervent", "LTR-3 eco EC", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "1.11", "76", "2", "21.0", "1.05", "76", "3", "27.0", "1.09", "79", "4", "33.0", "1.18", "80", "5", "39.0", "1.30", "81"]} +{"pcdb_id": 500113, "raw": ["500113", "020033", "0", "2015/Nov/11 10:34", "Vaillant Group UK Ltd", "Vaillant", "recoVAIR VAR 275/3", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.90", "83", "2", "21.0", "0.91", "85", "3", "27.0", "1.04", "86", "4", "33.0", "1.23", "85", "5", "39.0", "1.44", "83", "6", "45.0", "1.68", "79"]} +{"pcdb_id": 500114, "raw": ["500114", "020033", "0", "2015/Nov/11 10:34", "Vaillant Group UK Ltd", "Vaillant", "recoVAIR VAR 350/3", "", "2008", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.88", "81", "2", "21.0", "0.87", "81", "3", "27.0", "1.00", "83", "4", "33.0", "1.19", "84", "5", "39.0", "1.42", "84", "6", "45.0", "1.68", "84", "7", "51.0", "1.94", "83"]} +{"pcdb_id": 500117, "raw": ["500117", "020027", "0", "2009/May/16 16:32", "EnviroVent Ltd", "EnviroVent", "Spider MEV", "", "2009", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.45", "", "2", "29.0", "0.34", "", "3", "37.0", "0.33", "", "4", "45.0", "0.34", "", "5", "53.0", "0.37", "", "6", "61.0", "0.40"]} +{"pcdb_id": 500118, "raw": ["500118", "020009", "0", "2009/Jun/18 08:37", "Vortice Ltd", "Vortice", "Vort Platt ES", "", "2009", "current", "", "1", "0", "2", "2", "", "3", "1", "21.0", "0.20", "", "2", "29.0", "0.20", "", "3", "37.0", "0.21"]} +{"pcdb_id": 500119, "raw": ["500119", "020009", "0", "2011/Sep/12 08:31", "Vortice Ltd", "Vortice", "Vort Penta ES", "", "2009", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.19", "", "4", "45.0", "0.20", "", "5", "53.0", "0.23"]} +{"pcdb_id": 500122, "raw": ["500122", "020034", "0", "2015/Nov/11 10:34", "REC Indovent AB", "Rec Indovent", "RT250S-EC", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.85", "79", "2", "21.0", "0.85", "79", "3", "27.0", "0.95", "76", "4", "33.0", "1.09", "75", "5", "39.0", "1.25", "75", "6", "45.0", "1.43", "75"]} +{"pcdb_id": 500123, "raw": ["500123", "020034", "0", "2015/Nov/11 10:34", "REC Indovent AB", "Rec Indovent", "RT400S-EC", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.98", "78", "2", "21.0", "0.92", "80", "3", "27.0", "0.98", "75", "4", "33.0", "1.08", "74", "5", "39.0", "1.21", "74", "6", "45.0", "1.36", "74"]} +{"pcdb_id": 500124, "raw": ["500124", "020035", "0", "2015/Nov/11 10:34", "Rega Ventilation Ltd", "RegaVent", "325DC", "", "2009", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.82", "71", "2", "21.0", "0.62", "71", "3", "27.0", "0.65", "70", "4", "33.0", "0.78", "68", "5", "39.0", "0.96", "65", "6", "45.0", "1.19", "61"]} +{"pcdb_id": 500125, "raw": ["500125", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR100/60DC", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.71", "61", "2", "21.0", "0.78", "61", "3", "27.0", "0.93", "54", "4", "33.0", "1.12", "52", "5", "39.0", "1.33", "50"]} +{"pcdb_id": 500126, "raw": ["500126", "020002", "0", "2016/May/18 14:58", "Vent Axia Ltd", "Vent Axia", "Sentinel 200", "", "2009", "current", "", "3", "0", "2", "1", "3", "3", "1", "15.0", "0.78", "85", "2", "21.0", "0.89", "85", "3", "27.0", "1.03", "82"]} +{"pcdb_id": 500127, "raw": ["500127", "020078", "0", "2015/Nov/11 10:34", "Villavent Ltd", "Villavent", "VR400 DC", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "3", "27.0", "1.38", "83", "4", "33.0", "1.45", "82", "5", "39.0", "1.56", "84", "6", "45.0", "1.70", "84", "7", "51.0", "1.86", "84"]} +{"pcdb_id": 500128, "raw": ["500128", "020078", "0", "2015/Nov/11 10:34", "Villavent Ltd", "Villavent", "VR700 DC", "", "2009", "current", "", "3", "0", "2", "1", "1", "4", "4", "33.0", "1.99", "84", "5", "39.0", "2.01", "83", "6", "45.0", "2.03", "83", "7", "51.0", "2.03", "82"]} +{"pcdb_id": 500129, "raw": ["500129", "020036", "0", "2015/Nov/11 10:34", "Glow-worm", "Glow-worm", "Atmosorb HRD 275", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.90", "83", "2", "21.0", "0.91", "85", "3", "27.0", "1.04", "86", "4", "33.0", "1.23", "85", "5", "39.0", "1.44", "83", "6", "45.0", "1.68", "79"]} +{"pcdb_id": 500130, "raw": ["500130", "020036", "0", "2015/Nov/11 10:34", "Glow-worm", "Glow-worm", "Atmosorb HRD 350", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.88", "81", "2", "21.0", "0.87", "81", "3", "27.0", "1.00", "83", "4", "33.0", "1.19", "84", "5", "39.0", "1.42", "84", "6", "45.0", "1.68", "84"]} +{"pcdb_id": 500131, "raw": ["500131", "020007", "0", "2016/Feb/22 15:19", "Titon Hardware Ltd", "Titon", "CME 1 Q Plus", "125 mm ducts", "2009", "2015", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.18", "", "4", "45.0", "0.19", "", "5", "53.0", "0.22", "", "6", "61.0", "0.25"]} +{"pcdb_id": 500132, "raw": ["500132", "020007", "0", "2016/Feb/22 15:19", "Titon Hardware Ltd", "Titon", "CME 1 Q Plus", "100 mm ducts", "2009", "2015", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.25", "", "2", "29.0", "0.22", "", "3", "37.0", "0.23", "", "4", "45.0", "0.25", "", "5", "53.0", "0.30"]} +{"pcdb_id": 500133, "raw": ["500133", "020009", "0", "2013/Oct/24 09:06", "Vortice Ltd", "Vortice", "Vort Leto MEV", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.24", "", "2", "29.0", "0.21", "", "3", "37.0", "0.20", "", "4", "45.0", "0.23", "", "5", "53.0", "0.25", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500138, "raw": ["500138", "020004", "0", "2016/May/18 18:35", "Greenwood Air Management Ltd", "Greenwood", "Fusion HRV2", "", "2009", "2015", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.47", "93", "2", "21.0", "0.55", "93", "3", "27.0", "0.70", "91", "4", "33.0", "0.88", "90", "5", "39.0", "1.07", "88"]} +{"pcdb_id": 500139, "raw": ["500139", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR125DC", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.71", "90", "2", "21.0", "0.82", "89", "3", "27.0", "0.99", "87", "4", "33.0", "1.19", "87"]} +{"pcdb_id": 500140, "raw": ["500140", "020002", "0", "2016/May/18 14:54", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic B", "", "2009", "current", "", "3", "0", "2", "1", "3", "5", "1", "15.0", "0.76", "91", "2", "21.0", "0.88", "91", "3", "27.0", "1.07", "88", "4", "33.0", "1.28", "86", "5", "39.0", "1.50", "84"]} +{"pcdb_id": 500141, "raw": ["500141", "020004", "0", "2016/May/18 18:40", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 200", "", "2009", "current", "", "3", "0", "2", "1", "3", "5", "1", "15.0", "0.91", "93", "2", "21.0", "1.02", "93", "3", "27.0", "1.20", "91", "4", "33.0", "1.42", "90", "5", "39.0", "1.66", "88"]} +{"pcdb_id": 500142, "raw": ["500142", "020004", "0", "2016/May/18 18:40", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 350", "", "2009", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.71", "88", "2", "21.0", "0.71", "88", "3", "27.0", "0.80", "87", "4", "33.0", "0.93", "86", "5", "39.0", "1.07", "86", "6", "45.0", "1.23", "85"]} +{"pcdb_id": 500143, "raw": ["500143", "020004", "0", "2016/May/18 18:40", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 550", "", "2009", "current", "", "3", "0", "2", "1", "3", "7", "1", "15.0", "0.83", "88", "2", "21.0", "0.79", "88", "3", "27.0", "0.83", "88", "4", "33.0", "0.90", "88", "5", "39.0", "0.99", "86", "6", "45.0", "1.08", "84", "7", "51.0", "1.19", "82"]} +{"pcdb_id": 500144, "raw": ["500144", "020083", "0", "2015/Nov/11 10:34", "Applied Energy Products Ltd", "Xpelair", "Xcell 150 QV", "", "2009", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.72", "89", "2", "21.0", "0.84", "89", "3", "27.0", "1.01", "86"]} +{"pcdb_id": 500145, "raw": ["500145", "020083", "0", "2015/Nov/11 10:34", "Applied Energy Products Ltd", "Xpelair", "Xcell 300 QV", "", "2009", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.61", "91", "2", "21.0", "0.65", "91", "3", "27.0", "0.76", "88", "4", "33.0", "0.90", "87", "5", "39.0", "1.07", "86", "6", "45.0", "1.24", "85"]} +{"pcdb_id": 500146, "raw": ["500146", "020007", "0", "2016/Feb/22 14:32", "Titon Hardware Ltd", "Titon", "HRV1.5 Q Plus", "", "2009", "2014", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.64", "90", "2", "21.0", "0.72", "90", "3", "27.0", "0.85", "89", "4", "33.0", "1.01", "88", "5", "39.0", "1.18", "87"]} +{"pcdb_id": 500147, "raw": ["500147", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "HRV2 Q Plus", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "0.72", "89", "3", "27.0", "0.89", "90", "4", "33.0", "1.11", "88", "5", "39.0", "1.36", "87", "6", "45.0", "1.63", "86"]} +{"pcdb_id": 500148, "raw": ["500148", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 365", "", "2007", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.69", "85", "2", "21.0", "0.70", "84", "3", "27.0", "0.79", "83", "4", "33.0", "0.92", "82", "5", "39.0", "1.06", "82", "6", "45.0", "1.22", "81"]} +{"pcdb_id": 500149, "raw": ["500149", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 365", "", "2007", "current", "", "3", "0", "1", "1", "1", "6", "1", "15.0", "0.67", "85", "2", "21.0", "0.70", "84", "3", "27.0", "0.80", "83", "4", "33.0", "0.94", "82", "5", "39.0", "1.10", "82", "6", "45.0", "1.27", "81"]} +{"pcdb_id": 500150, "raw": ["500150", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 350", "", "2007", "current", "", "3", "0", "1", "1", "1", "6", "1", "15.0", "0.67", "85", "2", "21.0", "0.70", "84", "3", "27.0", "0.80", "83", "4", "33.0", "0.94", "82", "5", "39.0", "1.10", "82", "6", "45.0", "1.27", "81"]} +{"pcdb_id": 500151, "raw": ["500151", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 600", "", "2007", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.81", "88", "2", "21.0", "0.79", "88", "3", "27.0", "0.84", "86", "4", "33.0", "0.91", "85", "5", "39.0", "1.01", "85", "6", "45.0", "1.11", "84"]} +{"pcdb_id": 500152, "raw": ["500152", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 600", "", "2007", "current", "", "3", "0", "1", "1", "1", "6", "1", "15.0", "0.78", "88", "2", "21.0", "0.75", "88", "3", "27.0", "0.82", "86", "4", "33.0", "0.92", "85", "5", "39.0", "1.04", "85", "6", "45.0", "1.18", "84"]} +{"pcdb_id": 500153, "raw": ["500153", "020038", "0", "2015/Nov/11 10:34", "AB C.A. Östberg", "Beam", "AXCO HERU 62 B", "", "2009", "current", "", "3", "0", "2", "1", "1", "3", "2", "21.0", "1.90", "83", "3", "27.0", "1.71", "82", "4", "33.0", "1.93", "85"]} +{"pcdb_id": 500154, "raw": ["500154", "020038", "0", "2015/Nov/11 10:34", "AB C.A. Östberg", "Beam", "AXCO HERU 90 S", "", "2009", "current", "", "3", "0", "2", "1", "1", "4", "3", "27.0", "1.24", "84", "4", "33.0", "1.32", "83", "5", "39.0", "1.43", "85", "6", "45.0", "1.57", "85"]} +{"pcdb_id": 500155, "raw": ["500155", "020038", "0", "2015/Nov/11 10:34", "AB C.A. Östberg", "Beam", "AXCO HERU 130 S", "", "2009", "current", "", "3", "0", "2", "1", "1", "3", "4", "33.0", "1.08", "81", "5", "39.0", "1.16", "80", "6", "45.0", "1.26", "84"]} +{"pcdb_id": 500156, "raw": ["500156", "020038", "0", "2015/Nov/11 10:34", "AB C.A. Östberg", "Beam", "AXCO HERU 180 S", "", "2009", "current", "", "3", "0", "2", "1", "1", "2", "5", "39.0", "1.16", "79", "6", "45.0", "1.15", "76"]} +{"pcdb_id": 500157, "raw": ["500157", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX95-Loft", "", "2009", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.79", "92", "2", "21.0", "0.92", "92"]} +{"pcdb_id": 500158, "raw": ["500158", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "HRV3 Q Plus", "", "2009", "current", "", "3", "0", "2", "1", "1", "4", "3", "27.0", "0.90", "88", "4", "33.0", "1.10", "89", "5", "39.0", "1.32", "87", "6", "45.0", "1.56", "87"]} +{"pcdb_id": 500159, "raw": ["500159", "020083", "0", "2015/Nov/11 10:34", "Applied Energy Products Ltd", "Xpelair", "Xcell 200 QV", "", "2009", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.68", "91", "2", "21.0", "0.77", "91", "3", "27.0", "0.91", "88", "4", "33.0", "1.07", "86"]} +{"pcdb_id": 500160, "raw": ["500160", "020083", "0", "2016/Jun/20 12:41", "Applied Energy Products Ltd", "Xpelair", "Xplus 2-EC", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.21", "", "2", "29.0", "0.19", "", "3", "37.0", "0.19", "", "4", "45.0", "0.21", "", "5", "53.0", "0.24", "", "6", "61.0", "0.27"]} +{"pcdb_id": 500163, "raw": ["500163", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent DV 75EC", "", "2008", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.68", "61", "2", "21.0", "0.80", "62", "3", "27.0", "0.96", "59", "4", "33.0", "1.15", "57", "5", "39.0", "1.34", "54"]} +{"pcdb_id": 500164, "raw": ["500164", "020040", "0", "2015/Nov/11 10:34", "Itho Ventilation Ltd", "Kair", "IECO 4", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.47", "90", "2", "21.0", "0.53", "90", "3", "27.0", "0.65", "87", "4", "33.0", "0.79", "87", "5", "39.0", "0.94", "87", "6", "45.0", "1.10", "88"]} +{"pcdb_id": 500165, "raw": ["500165", "020012", "0", "2015/Nov/11 10:34", "MTD Solutions Ltd", "MTD Solutions", "MTD-ERV 200", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.91", "93", "2", "21.0", "1.02", "93", "3", "27.0", "1.20", "91", "4", "33.0", "1.42", "90", "5", "39.0", "1.66", "88"]} +{"pcdb_id": 500166, "raw": ["500166", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX95-Wall", "", "2009", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.71", "91", "2", "21.0", "0.92", "91"]} +{"pcdb_id": 500167, "raw": ["500167", "020002", "0", "2016/May/26 14:24", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic Plus B", "", "2009", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.52", "92", "2", "21.0", "0.55", "92", "3", "27.0", "0.63", "90", "4", "33.0", "0.74", "89", "5", "39.0", "0.86", "89", "6", "45.0", "0.99", "88"]} +{"pcdb_id": 500168, "raw": ["500168", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR180DC", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.09", "86", "2", "21.0", "1.23", "86", "3", "27.0", "1.48", "84", "4", "33.0", "1.77", "82"]} +{"pcdb_id": 500169, "raw": ["500169", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR300DC", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.80", "84", "2", "21.0", "0.82", "84", "3", "27.0", "0.94", "83", "4", "33.0", "1.09", "81", "5", "39.0", "1.27", "79", "6", "45.0", "1.46", "77"]} +{"pcdb_id": 500170, "raw": ["500170", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR400DC", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.97", "82", "2", "21.0", "0.99", "83", "3", "27.0", "1.11", "82", "4", "33.0", "1.28", "81", "5", "39.0", "1.48", "80", "6", "45.0", "1.69", "79"]} +{"pcdb_id": 500179, "raw": ["500179", "020041", "0", "2015/Nov/11 10:34", "Polypipe Ltd", "Polypipe Ventilation", "HR01L", "", "2009", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.79", "92", "2", "21.0", "0.92", "92"]} +{"pcdb_id": 500180, "raw": ["500180", "020041", "0", "2015/Nov/11 10:34", "Polypipe Ltd", "Polypipe Ventilation", "HR01W", "", "2009", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.71", "91", "2", "21.0", "0.92", "91"]} +{"pcdb_id": 500181, "raw": ["500181", "020042", "0", "2015/Nov/11 10:34", "Brink Climate Systems B.V.", "Brink", "Renovent HR 4/0 R Medium", "", "2009", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "90", "2", "21.0", "0.66", "90", "3", "27.0", "0.80", "87", "4", "33.0", "0.98", "86", "5", "39.0", "1.19", "84", "6", "45.0", "1.41", "82"]} +{"pcdb_id": 500182, "raw": ["500182", "020042", "0", "2015/Nov/11 10:34", "Brink Climate Systems B.V.", "Brink", "Renovent HR 4/0 R Small", "", "2009", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.85", "88", "2", "21.0", "1.03", "88", "3", "27.0", "1.31", "85"]} +{"pcdb_id": 500183, "raw": ["500183", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "energiVent FLOW", "", "2010", "2012", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.73", "89", "2", "21.0", "0.83", "90", "3", "27.0", "1.02", "87", "4", "33.0", "1.24", "85", "5", "39.0", "1.48", "83"]} +{"pcdb_id": 500184, "raw": ["500184", "020013", "0", "2015/Nov/11 10:34", "Johnson & Starley Ltd", "Johnson & Starley", "Q Vent HR160", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.85", "91", "2", "21.0", "1.04", "91", "3", "27.0", "1.33", "88"]} +{"pcdb_id": 500185, "raw": ["500185", "020023", "0", "2026/Feb/25 10:50", "Ubbink (UK) Ltd", "Ubbink", "HRV C180", "", "2009", "obsolete", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.85", "88", "2", "21.0", "1.03", "88", "3", "27.0", "1.31", "85"]} +{"pcdb_id": 500186, "raw": ["500186", "020023", "0", "2026/Feb/25 10:50", "Ubbink (UK) Ltd", "Ubbink", "HRV M300", "", "2009", "obsolete", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "90", "2", "21.0", "0.66", "90", "3", "27.0", "0.80", "87", "4", "33.0", "0.98", "86", "5", "39.0", "1.19", "84", "6", "45.0", "1.41", "82"]} +{"pcdb_id": 500187, "raw": ["500187", "020078", "0", "2015/Nov/11 10:34", "Villavent Ltd", "Villavent", "VR400 DCV/BR", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "1.54", "77", "3", "27.0", "1.51", "77", "4", "33.0", "1.56", "80", "5", "39.0", "1.66", "81", "6", "45.0", "1.78", "82"]} +{"pcdb_id": 500188, "raw": ["500188", "020004", "0", "2016/May/18 18:29", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir ZHRV2", "", "2009", "2015", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.47", "93", "2", "21.0", "0.55", "93", "3", "27.0", "0.70", "91", "4", "33.0", "0.88", "90", "5", "39.0", "1.07", "88"]} +{"pcdb_id": 500189, "raw": ["500189", "020078", "0", "2015/Nov/11 10:34", "Villavent Ltd", "Villavent", "Combini EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.73", "62", "2", "21.0", "0.90", "63", "3", "27.0", "1.19", "55", "4", "33.0", "1.53", "50"]} +{"pcdb_id": 500190, "raw": ["500190", "020009", "0", "2015/Nov/11 10:34", "Vortice Ltd", "Vortice", "Vort HR200", "", "2010", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.73", "91", "2", "21.0", "0.88", "91"]} +{"pcdb_id": 500191, "raw": ["500191", "020043", "0", "2014/Jun/11 13:30", "Genvex A/S", "Genvex", "GES Energy BP DS LG", "", "2009", "current", "", "3", "0", "2", "1", "3", "5", "1", "15.0", "0.81", "91", "2", "21.0", "0.88", "91", "3", "27.0", "1.01", "88", "4", "33.0", "1.16", "86", "5", "39.0", "1.33", "84"]} +{"pcdb_id": 500194, "raw": ["500194", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent DV71", "", "2010", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "3.18", "92", "2", "21.0", "3.05", "92"]} +{"pcdb_id": 500195, "raw": ["500195", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent DV72", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.75", "90", "2", "21.0", "0.87", "90", "3", "27.0", "1.05", "87", "4", "33.0", "1.27", "86"]} +{"pcdb_id": 500196, "raw": ["500196", "020002", "0", "2015/Nov/11 10:34", "Vent Axia Ltd", "Vent Axia", "Lo-Carbon Astra", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.75", "90", "2", "21.0", "0.87", "90", "3", "27.0", "1.05", "87", "4", "33.0", "1.27", "86"]} +{"pcdb_id": 500199, "raw": ["500199", "020014", "0", "2015/Nov/11 10:34", "Itho Ventilation Ltd", "Itho", "Advance", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.50", "89", "2", "21.0", "0.73", "89", "3", "27.0", "1.00", "86"]} +{"pcdb_id": 500200, "raw": ["500200", "020048", "0", "2015/Nov/11 10:34", "Manrose Manufacturing Ltd", "Manrose", "Windsor MANHR4500DC", "", "2010", "2012", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.75", "90", "2", "21.0", "0.87", "90", "3", "27.0", "1.05", "87", "4", "33.0", "1.27", "86"]} +{"pcdb_id": 500201, "raw": ["500201", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX95-LH1", "", "2010", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "91", "2", "21.0", "0.66", "91", "3", "27.0", "0.80", "91", "4", "33.0", "0.96", "90", "5", "39.0", "1.15", "90", "6", "45.0", "1.35", "89"]} +{"pcdb_id": 500202, "raw": ["500202", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX95-LH2", "", "2010", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.78", "91", "2", "21.0", "0.83", "91", "3", "27.0", "0.97", "91", "4", "33.0", "1.14", "90", "5", "39.0", "1.33", "90", "6", "45.0", "1.54", "89"]} +{"pcdb_id": 500203, "raw": ["500203", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR100/90DC-A", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.99", "91", "2", "21.0", "1.14", "91", "3", "27.0", "1.34", "88"]} +{"pcdb_id": 500204, "raw": ["500204", "020031", "0", "2013/Oct/24 09:06", "NIBE Energy Systems Ltd", "NIBE", "F205P", "", "2010", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.65", "", "2", "29.0", "0.60", "", "3", "37.0", "0.65", "", "4", "45.0", "0.78", "", "5", "53.0", "0.87", "", "6", "61.0", "1.02"]} +{"pcdb_id": 500205, "raw": ["500205", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR100/90DC-B", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.78", "91", "2", "21.0", "0.94", "91", "3", "27.0", "1.16", "88"]} +{"pcdb_id": 500206, "raw": ["500206", "020049", "0", "2015/Nov/11 10:34", "Kingspan Century Ltd", "Kingspan", "Air Recovery 4", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.47", "90", "2", "21.0", "0.53", "90", "3", "27.0", "0.65", "87", "4", "33.0", "0.79", "87", "5", "39.0", "0.94", "87", "6", "45.0", "1.10", "88"]} +{"pcdb_id": 500207, "raw": ["500207", "020047", "0", "2015/Nov/11 10:34", "Mitsubishi Electric Europe B.V.", "Mitsubishi", "LGH-50RSDC-E1", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.70", "85", "2", "21.0", "0.76", "85", "3", "27.0", "0.89", "82", "4", "33.0", "0.93", "81"]} +{"pcdb_id": 500208, "raw": ["500208", "020050", "0", "2013/Oct/24 09:06", "Aereco Ltd", "Aereco", "V2A 032EX", "", "2009", "current", "", "1", "0", "2", "1", "", "2", "1", "21.0", "0.37", "", "2", "29.0", "0.42"]} +{"pcdb_id": 500209, "raw": ["500209", "020050", "0", "2013/Oct/24 09:06", "Aereco Ltd", "Aereco", "V4A 053EX", "", "2009", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.45", "", "2", "29.0", "0.40", "", "3", "37.0", "0.36", "", "4", "45.0", "0.34"]} +{"pcdb_id": 500210, "raw": ["500210", "020050", "0", "2013/Oct/24 09:06", "Aereco Ltd", "Aereco", "VAM 767EX", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.71", "", "2", "29.0", "0.51", "", "3", "37.0", "0.50", "", "4", "45.0", "0.46", "", "5", "53.0", "0.50", "", "6", "61.0", "0.61"]} +{"pcdb_id": 500211, "raw": ["500211", "020038", "0", "2015/Nov/11 10:34", "Beam Vacuum Systems Ltd", "Beam", "AXCO HERU 130T", "", "2009", "current", "", "3", "0", "2", "1", "1", "3", "4", "33.0", "1.33", "81", "5", "39.0", "1.38", "81", "6", "45.0", "1.46", "80"]} +{"pcdb_id": 500212, "raw": ["500212", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR125DC AERA", "", "2010", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.61", "93", "2", "21.0", "0.69", "93", "3", "27.0", "0.84", "91", "4", "33.0", "1.01", "90", "5", "39.0", "1.20", "89"]} +{"pcdb_id": 500213, "raw": ["500213", "020013", "0", "2015/Nov/11 10:34", "Johnson & Starley Ltd", "Johnson & Starley", "Q-Vent HR260", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.92", "89", "2", "21.0", "1.09", "89", "3", "27.0", "1.33", "88", "4", "33.0", "1.62", "88"]} +{"pcdb_id": 500214, "raw": ["500214", "020021", "0", "2016/May/18 18:41", "Paul Waermerueckgewinnung Gmbh", "Paul", "Novus 300", "", "2010", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.67", "93", "2", "21.0", "0.66", "93", "3", "27.0", "0.73", "93", "4", "33.0", "0.83", "92", "5", "39.0", "0.96", "91", "6", "45.0", "1.09", "90"]} +{"pcdb_id": 500215, "raw": ["500215", "020021", "0", "2015/Nov/11 10:34", "Paul Waermerueckgewinnung Gmbh", "Paul", "Focus F200", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.82", "94", "2", "21.0", "0.92", "94", "3", "27.0", "1.14", "93", "4", "33.0", "1.40", "91"]} +{"pcdb_id": 500216, "raw": ["500216", "020078", "0", "2015/Nov/11 10:34", "Villavent Ltd", "Villavent", "VR 300 ECV / B", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "1.77", "79", "2", "21.0", "1.74", "80", "3", "27.0", "1.72", "80"]} +{"pcdb_id": 500217, "raw": ["500217", "020052", "0", "2011/May/04 13:42", "Brook Design Hardware Ltd", "Brookvent", "Airstream 1.1AC", "", "2009", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.80", "", "2", "29.0", "0.59", "", "3", "37.0", "0.72", "", "4", "45.0", "0.60", "", "5", "53.0", "0.51"]} +{"pcdb_id": 500218, "raw": ["500218", "020052", "0", "2011/May/04 13:41", "Brook Design Hardware Ltd", "Brookvent", "Airstream 1.1AC", "", "2009", "current", "", "1", "0", "2", "2", "", "3", "1", "21.0", "0.81", "", "2", "29.0", "0.91", "", "3", "37.0", "0.72"]} +{"pcdb_id": 500219, "raw": ["500219", "020052", "0", "2011/May/04 13:42", "Brook Design Hardware Ltd", "Brookvent", "Airstream 1.1E", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.22", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500220, "raw": ["500220", "020052", "0", "2011/May/04 13:42", "Brook Design Hardware Ltd", "Brookvent", "Airstream 1.1E", "", "2009", "current", "", "1", "0", "2", "2", "", "4", "1", "21.0", "0.23", "", "2", "29.0", "0.20", "", "3", "37.0", "0.24", "", "4", "45.0", "0.27"]} +{"pcdb_id": 500221, "raw": ["500221", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHRC180DC", "", "2009", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.85", "88", "2", "21.0", "1.03", "88", "3", "27.0", "1.31", "85"]} +{"pcdb_id": 500222, "raw": ["500222", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHRM300DC", "", "2009", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "90", "2", "21.0", "0.66", "90", "3", "27.0", "0.80", "87", "4", "33.0", "0.98", "86", "5", "39.0", "1.19", "84", "6", "45.0", "1.41", "82"]} +{"pcdb_id": 500223, "raw": ["500223", "020083", "0", "2015/Nov/11 10:34", "Applied Energy Products Ltd", "Xpelair", "Xcell 400 QV", "", "2010", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.67", "90", "2", "21.0", "0.72", "90", "3", "27.0", "0.84", "90", "4", "33.0", "1.00", "89", "5", "39.0", "1.17", "88", "6", "45.0", "1.36", "87", "7", "51.0", "1.56", "86"]} +{"pcdb_id": 500224, "raw": ["500224", "020002", "0", "2016/May/26 14:29", "Vent Axia Ltd", "Vent Axia", "Kinetic E", "", "2010", "2016", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.60", "90", "2", "21.0", "0.79", "90"]} +{"pcdb_id": 500225, "raw": ["500225", "020031", "0", "2013/Oct/24 09:06", "NIBE Energy Systems Ltd", "NIBE", "F370", "", "2010", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.66", "", "2", "29.0", "0.62", "", "3", "37.0", "0.65", "", "4", "45.0", "0.71", "", "5", "53.0", "0.79", "", "6", "61.0", "0.90"]} +{"pcdb_id": 500226, "raw": ["500226", "020048", "0", "2010/Dec/18 08:49", "Manrose Manufacturing Ltd", "Manrose", "MANI2000", "", "", "current", "", "1", "0", "2", "1", "", "3", "1", "21.0", "0.24", "", "2", "29.0", "0.18", "", "3", "37.0", "0.21"]} +{"pcdb_id": 500227, "raw": ["500227", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "Compakt", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.50", "89", "2", "21.0", "0.73", "89", "3", "27.0", "1.00", "86"]} +{"pcdb_id": 500228, "raw": ["500228", "020008", "0", "2016/Jul/14 15:51", "Passivent Ltd", "Passivent", "1Hybrid HR 3", "", "2008", "2013", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.47", "90", "2", "21.0", "0.53", "90", "3", "27.0", "0.65", "87", "4", "33.0", "0.79", "87", "5", "39.0", "0.94", "87", "6", "45.0", "1.10", "88"]} +{"pcdb_id": 500231, "raw": ["500231", "020052", "0", "2015/Nov/11 10:34", "Brookvent", "Brookvent", "AirCycle", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.73", "89", "2", "21.0", "0.83", "89", "3", "27.0", "0.98", "86"]} +{"pcdb_id": 500232, "raw": ["500232", "020050", "0", "2011/Feb/22 17:22", "Aereco Ltd", "Aereco", "V4A Premium", "V4A336EX", "2010", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.33", "", "2", "29.0", "0.29", "", "3", "37.0", "0.27", "", "4", "45.0", "0.26"]} +{"pcdb_id": 500233, "raw": ["500233", "020003", "0", "2013/Oct/24 09:06", "The Nuaire Group", "Nuaire", "MEVDC", "", "2011", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.35", "", "2", "29.0", "0.30", "", "3", "37.0", "0.31", "", "4", "45.0", "0.33", "", "5", "53.0", "0.38"]} +{"pcdb_id": 500235, "raw": ["500235", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX90M", "", "2010", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "89", "2", "21.0", "0.66", "90", "3", "27.0", "0.80", "87", "4", "33.0", "0.98", "89", "5", "39.0", "1.19", "93", "6", "45.0", "1.41", "99"]} +{"pcdb_id": 500236, "raw": ["500236", "020059", "0", "2015/Nov/11 10:34", "Ventilation Systems PrJSC", "VENTS", "VUT 600 WH EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "2", "21.0", "1.35", "89", "3", "27.0", "1.38", "89", "4", "33.0", "1.48", "87", "5", "39.0", "1.60", "86"]} +{"pcdb_id": 500237, "raw": ["500237", "020059", "0", "2015/Nov/11 10:34", "Ventilation Systems PrJSC", "VENTS", "VUT 300 WH EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.99", "89", "2", "21.0", "0.96", "89", "3", "27.0", "1.01", "88", "4", "33.0", "1.09", "87"]} +{"pcdb_id": 500238, "raw": ["500238", "020059", "0", "2015/Nov/11 10:34", "Ventilation Systems PrJSC", "VENTS", "VUT 300V mini EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.76", "59", "2", "21.0", "0.91", "59", "3", "27.0", "1.13", "54", "4", "33.0", "1.38", "53"]} +{"pcdb_id": 500239, "raw": ["500239", "020059", "0", "2015/Nov/11 10:34", "Ventilation Systems PrJSC", "VENTS", "VUT 400 WH EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.90", "89", "2", "21.0", "0.89", "89", "3", "27.0", "0.97", "87", "4", "33.0", "1.09", "87", "5", "39.0", "1.22", "86"]} +{"pcdb_id": 500240, "raw": ["500240", "020059", "0", "2015/Nov/11 10:34", "Ventilation Systems PrJSC", "VENTS", "VUT 300H mini EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.90", "63", "2", "21.0", "1.10", "63", "3", "27.0", "1.37", "58"]} +{"pcdb_id": 500241, "raw": ["500241", "020078", "0", "2013/Nov/25 09:40", "Villavent Ltd", "Systemair", "K 160 EC", "", "2011", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.24", "", "2", "29.0", "0.27", "", "3", "37.0", "0.30", "", "4", "45.0", "0.36", "", "5", "53.0", "0.43", "", "6", "61.0", "0.57"]} +{"pcdb_id": 500242, "raw": ["500242", "020078", "0", "2013/Nov/25 09:40", "Villavent Ltd", "Systemair", "KVKE 125 EC", "", "2011", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.25", "", "2", "29.0", "0.27", "", "3", "37.0", "0.30", "", "4", "45.0", "0.35", "", "5", "53.0", "0.42", "", "6", "61.0", "0.53"]} +{"pcdb_id": 500243, "raw": ["500243", "020041", "0", "2015/Nov/11 10:34", "Polypipe Ltd", "Polypipe Ventilation", "HR02L", "", "2010", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "91", "2", "21.0", "0.66", "91", "3", "27.0", "0.80", "91", "4", "33.0", "0.96", "90", "5", "39.0", "1.15", "90", "6", "45.0", "1.35", "89"]} +{"pcdb_id": 500244, "raw": ["500244", "020041", "0", "2015/Nov/11 10:34", "Polypipe Ltd", "Polypipe Ventilation", "HR03L", "", "2010", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.78", "91", "2", "21.0", "0.83", "91", "3", "27.0", "0.97", "91", "4", "33.0", "1.14", "90", "5", "39.0", "1.33", "90", "6", "45.0", "1.54", "89"]} +{"pcdb_id": 500245, "raw": ["500245", "020060", "0", "2011/Mar/21 08:56", "Addvent", "Addvent", "AVWH2N", "", "2006", "current", "", "1", "0", "2", "1", "", "3", "1", "21.0", "0.24", "", "2", "29.0", "0.18", "", "3", "37.0", "0.21"]} +{"pcdb_id": 500246, "raw": ["500246", "020060", "0", "2015/Nov/11 10:34", "Addvent", "Addvent", "AVHRU4", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.75", "90", "2", "21.0", "0.87", "90", "3", "27.0", "1.05", "87", "4", "33.0", "1.27", "86"]} +{"pcdb_id": 500247, "raw": ["500247", "020035", "0", "2015/Nov/11 10:34", "Rega Ventilation Ltd", "RegaVent", "200 DC", "", "2011", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.69", "77", "2", "21.0", "0.84", "77", "3", "27.0", "1.03", "71", "4", "33.0", "1.26", "69"]} +{"pcdb_id": 500248, "raw": ["500248", "020035", "0", "2015/Nov/11 10:34", "Rega Ventilation Ltd", "RegaVent", "650 DC", "", "2011", "current", "", "3", "0", "2", "1", "1", "4", "3", "27.0", "0.63", "70", "4", "33.0", "0.77", "71", "5", "39.0", "1.00", "67", "6", "45.0", "1.27", "65"]} +{"pcdb_id": 500249, "raw": ["500249", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX95-WH1", "", "2011", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.42", "91", "2", "21.0", "0.50", "91", "3", "27.0", "0.61", "89", "4", "33.0", "0.75", "88", "5", "39.0", "0.90", "87", "6", "45.0", "1.05", "85"]} +{"pcdb_id": 500250, "raw": ["500250", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "HRV1.75 Q Plus", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.42", "90", "2", "21.0", "0.54", "90", "3", "27.0", "0.72", "89", "4", "33.0", "0.93", "88", "5", "39.0", "1.16", "87"]} +{"pcdb_id": 500251, "raw": ["500251", "020007", "0", "2016/Feb/22 15:16", "Titon Hardware Ltd", "Titon", "HRV2.75 Q Plus", "", "2009", "2016", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.44", "90", "2", "21.0", "0.56", "90", "3", "27.0", "0.73", "89", "4", "33.0", "0.92", "88", "5", "39.0", "1.12", "87"]} +{"pcdb_id": 500252, "raw": ["500252", "020058", "0", "2015/Nov/11 10:34", "Ferrob Ltd", "Ferrob", "Infinity 9000", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.75", "90", "2", "21.0", "0.87", "90", "3", "27.0", "1.05", "87", "4", "33.0", "1.27", "86"]} +{"pcdb_id": 500253, "raw": ["500253", "020061", "0", "2015/Nov/11 10:34", "Maico Ventilation UK Ltd", "Maico", "WRG 180 EC", "", "2009", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.85", "88", "2", "21.0", "1.03", "88", "3", "27.0", "1.31", "85"]} +{"pcdb_id": 500254, "raw": ["500254", "020061", "0", "2015/Nov/11 10:34", "Maico Ventilation UK Ltd", "Maico", "Aeronom WS 250", "", "2011", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "1.05", "87", "2", "21.0", "1.12", "87", "3", "27.0", "1.35", "87", "4", "33.0", "1.64", "87"]} +{"pcdb_id": 500255, "raw": ["500255", "020061", "0", "2015/Nov/11 10:34", "Maico Ventilation UK Ltd", "Maico", "Aeronom WR 300", "", "2011", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.76", "83", "2", "21.0", "0.80", "84", "3", "27.0", "0.89", "83", "4", "33.0", "0.98", "81"]} +{"pcdb_id": 500256, "raw": ["500256", "020061", "0", "2015/Nov/11 10:34", "Maico Ventilation UK Ltd", "Maico", "Aeronom WR 400", "", "2011", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.85", "84", "2", "21.0", "0.83", "84", "3", "27.0", "0.92", "84", "4", "33.0", "1.06", "83"]} +{"pcdb_id": 500257, "raw": ["500257", "020061", "0", "2015/Nov/11 10:34", "Maico Ventilation UK Ltd", "Maico", "Aeronom WR 600", "", "2011", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.96", "81", "2", "21.0", "1.00", "81", "3", "27.0", "1.08", "83", "4", "33.0", "1.19", "83", "5", "39.0", "1.31", "81"]} +{"pcdb_id": 500258, "raw": ["500258", "020031", "0", "2013/Oct/24 09:06", "NIBE Energy Systems Ltd", "NIBE", "Fighter 470", "", "2011", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.65", "", "2", "29.0", "0.60", "", "3", "37.0", "0.65", "", "4", "45.0", "0.78", "", "5", "53.0", "0.87", "", "6", "61.0", "1.02"]} +{"pcdb_id": 500259, "raw": ["500259", "020027", "0", "2011/Jul/25 14:11", "EnviroVent Ltd", "EnviroVent", "OZEO", "", "2011", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.30", "", "2", "29.0", "0.28", "", "3", "37.0", "0.25", "", "4", "45.0", "0.24", "", "5", "53.0", "0.25", "", "6", "61.0", "0.27"]} +{"pcdb_id": 500260, "raw": ["500260", "020011", "0", "2011/Jul/25 14:44", "Vectaire Ltd", "Vectaire", "MBOX 125/2DC-B", "", "2011", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.20", "", "2", "29.0", "0.26", "", "3", "37.0", "0.34", "", "4", "45.0", "0.44", "", "5", "53.0", "0.55"]} +{"pcdb_id": 500261, "raw": ["500261", "020042", "0", "2015/Nov/11 10:34", "Brink Climate Systems B.V.", "Brink", "Renovent HR 4/0 R Large", "", "2008", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.97", "82", "2", "21.0", "0.99", "83", "3", "27.0", "1.11", "82", "4", "33.0", "1.28", "81", "5", "39.0", "1.48", "80", "6", "45.0", "1.69", "79"]} +{"pcdb_id": 500262, "raw": ["500262", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "IDEO", "", "2011", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "0.98", "87", "3", "27.0", "0.97", "87", "4", "33.0", "1.12", "87", "5", "39.0", "1.37", "86", "6", "45.0", "1.67", "85"]} +{"pcdb_id": 500263, "raw": ["500263", "020009", "0", "2015/Nov/11 10:34", "Vortice Ltd", "Vortice", "Vort Prometeo HR 400 M", "", "2008", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.55", "91", "2", "21.0", "0.59", "91", "3", "27.0", "0.68", "89", "4", "33.0", "0.80", "88", "5", "39.0", "0.93", "88", "6", "45.0", "1.08", "87", "7", "51.0", "1.23", "86"]} +{"pcdb_id": 500266, "raw": ["500266", "020003", "0", "2013/Oct/24 09:06", "The Nuaire Group", "Nuaire", "MEVDC2", "", "2011", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.35", "", "2", "29.0", "0.30", "", "3", "37.0", "0.31", "", "4", "45.0", "0.33", "", "5", "53.0", "0.38"]} +{"pcdb_id": 500267, "raw": ["500267", "020066", "0", "2013/Nov/25 09:40", "J Pichler GmbH", "Pichler", "08 LG 180", "", "2010", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.91", "89", "2", "21.0", "1.01", "90", "3", "27.0", "1.20", "86", "4", "33.0", "1.44", "84", "5", "39.0", "1.70", "82", "6", "45.0", "1.98", "80"]} +{"pcdb_id": 500268, "raw": ["500268", "020066", "0", "2013/Nov/25 09:40", "J Pichler GmbH", "Pichler", "08 LG 250", "", "2010", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.69", "88", "2", "21.0", "0.67", "88", "3", "27.0", "0.73", "88", "4", "33.0", "0.84", "87", "5", "39.0", "0.96", "86", "6", "45.0", "1.09", "85"]} +{"pcdb_id": 500269, "raw": ["500269", "020066", "0", "2013/Nov/25 09:40", "J Pichler GmbH", "Pichler", "08 LG 500", "", "2010", "current", "", "3", "0", "2", "1", "3", "7", "1", "15.0", "0.76", "89", "2", "21.0", "0.73", "89", "3", "27.0", "0.79", "89", "4", "33.0", "0.89", "89", "5", "39.0", "1.01", "88", "6", "45.0", "1.14", "87", "7", "51.0", "1.28", "85"]} +{"pcdb_id": 500270, "raw": ["500270", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent DV50", "", "2011", "current", "", "3", "0", "2", "2", "1", "4", "1", "15.0", "0.99", "80", "2", "21.0", "1.21", "80", "3", "27.0", "1.51", "82", "4", "33.0", "1.86", "82"]} +{"pcdb_id": 500271, "raw": ["500271", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent DV80", "", "2011", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.81", "90", "2", "21.0", "0.94", "91", "3", "27.0", "1.14", "90", "4", "33.0", "1.38", "89", "5", "39.0", "1.63", "89", "6", "45.0", "1.90", "88"]} +{"pcdb_id": 500272, "raw": ["500272", "020011", "0", "2015/Nov/11 10:34", "Vectaire Ltd", "Vectaire", "WHHR100/90DC-B Plus", "", "2011", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.76", "91", "2", "21.0", "0.90", "91", "3", "27.0", "1.05", "88"]} +{"pcdb_id": 500273, "raw": ["500273", "020002", "0", "2015/Nov/11 10:34", "Vent Axia Ltd", "Vent Axia", "Astra", "", "2011", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.67", "90", "2", "21.0", "0.85", "90", "3", "27.0", "1.09", "87", "4", "33.0", "1.36", "86"]} +{"pcdb_id": 500274, "raw": ["500274", "020048", "0", "2015/Nov/11 10:34", "Manrose Manufacturing Ltd", "Manrose", "MANHR4500DC2", "", "2011", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.67", "90", "2", "21.0", "0.85", "90", "3", "27.0", "1.09", "87", "4", "33.0", "1.36", "86"]} +{"pcdb_id": 500276, "raw": ["500276", "020002", "0", "2016/May/26 14:27", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic VS", "", "2011", "2016", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.61", "90", "2", "21.0", "0.74", "90", "3", "27.0", "0.95", "90", "4", "33.0", "1.19", "90"]} +{"pcdb_id": 500277, "raw": ["500277", "020042", "0", "2016/Apr/12 16:01", "Brink Climate Systems B.V.", "Brink", "Renovent Excellent 400", "", "2011", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.63", "89", "2", "21.0", "0.61", "89", "3", "27.0", "0.66", "87", "4", "33.0", "0.74", "86", "5", "39.0", "0.84", "85", "6", "45.0", "0.95", "84"]} +{"pcdb_id": 500278, "raw": ["500278", "020070", "0", "2012/Feb/27 12:02", "Soler & Palau Ltd", "S & P", "OZEO e ECOWATT", "", "2011", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.30", "", "2", "29.0", "0.28", "", "3", "37.0", "0.25", "", "4", "45.0", "0.24", "", "5", "53.0", "0.25", "", "6", "61.0", "0.27"]} +{"pcdb_id": 500281, "raw": ["500281", "020071", "0", "2015/Nov/11 10:34", "Maico Italia s.p.a.", "Elicent", "AERA IN LINE", "", "2011", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.97", "91", "2", "21.0", "1.10", "91", "3", "27.0", "1.26", "88"]} +{"pcdb_id": 500282, "raw": ["500282", "020043", "0", "2015/Nov/11 10:34", "Total Home Environment Ltd", "Genvex", "GE Premium 2 H OPT 300", "", "2009", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.92", "82", "2", "21.0", "0.86", "83", "3", "27.0", "0.93", "84", "4", "33.0", "1.07", "84", "5", "39.0", "1.24", "85", "6", "45.0", "1.43", "84"]} +{"pcdb_id": 500283, "raw": ["500283", "020043", "0", "2015/Nov/11 10:34", "Total Home Environment Ltd", "Genvex", "GE Premium 1 H OPT 300", "", "2009", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.96", "82", "2", "21.0", "0.93", "83", "3", "27.0", "1.01", "83", "4", "33.0", "1.16", "82", "5", "39.0", "1.34", "82", "6", "45.0", "1.54", "80"]} +{"pcdb_id": 500284, "raw": ["500284", "020043", "0", "2015/Nov/11 10:34", "Total Home Environment Ltd", "Genvex", "Combi 185 S OPT 310", "", "2009", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.95", "80", "2", "21.0", "0.92", "80", "3", "27.0", "1.00", "82", "4", "33.0", "1.13", "82", "5", "39.0", "1.29", "80", "6", "45.0", "1.47", "78"]} +{"pcdb_id": 500285, "raw": ["500285", "020072", "0", "2016/Mar/17 11:16", "Nilan A/S", "Nilan", "Comfort 300T", "", "2010", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.72", "89", "2", "21.0", "0.72", "88", "3", "27.0", "0.83", "88", "4", "33.0", "1.00", "88", "5", "39.0", "1.21", "88", "6", "45.0", "1.43", "89"]} +{"pcdb_id": 500286, "raw": ["500286", "020008", "0", "2016/Jul/14 15:51", "Passivent Ltd", "Passivent", "iHybrid HR1", "", "2010", "2013", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.50", "89", "2", "21.0", "0.73", "89", "3", "27.0", "1.00", "86"]} +{"pcdb_id": 500287, "raw": ["500287", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "HRV 10 Q Plus", "", "", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.48", "92", "2", "21.0", "0.52", "92", "3", "27.0", "0.63", "91", "4", "33.0", "0.78", "90", "5", "39.0", "0.94", "89", "6", "45.0", "1.11", "87", "7", "51.0", "1.29", "85"]} +{"pcdb_id": 500288, "raw": ["500288", "020070", "0", "2015/Nov/11 10:34", "Soler & Palau Ltd", "S & P", "IDEO 325 ECOWATT", "", "2011", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "0.98", "87", "3", "27.0", "0.97", "87", "4", "33.0", "1.12", "87", "5", "39.0", "1.37", "86", "6", "45.0", "1.67", "85"]} +{"pcdb_id": 500289, "raw": ["500289", "020002", "0", "2016/May/26 14:20", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic Plus BS", "", "2012", "current", "", "3", "0", "2", "1", "3", "7", "1", "15.0", "0.42", "91", "2", "21.0", "0.44", "91", "3", "27.0", "0.52", "90", "4", "33.0", "0.63", "90", "5", "39.0", "0.76", "90", "6", "45.0", "0.90", "91", "7", "51.0", "1.05", "91"]} +{"pcdb_id": 500290, "raw": ["500290", "020058", "0", "2012/Apr/23 18:47", "Ferrob Ltd", "Ferrob", "Infinity 9050", "", "2008", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.55", "", "2", "29.0", "0.83", "", "3", "37.0", "0.65", "", "4", "45.0", "0.54", "", "5", "53.0", "0.45"]} +{"pcdb_id": 500291, "raw": ["500291", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent DV72 E", "", "2012", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.70", "90", "2", "21.0", "0.92", "90", "3", "27.0", "1.20", "87", "4", "33.0", "1.50", "86"]} +{"pcdb_id": 500292, "raw": ["500292", "020073", "0", "2015/Nov/11 10:34", "Dantherm Air Handling A/S", "Dantherm", "HCH 5", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "2", "21.0", "0.58", "82", "3", "27.0", "0.65", "82", "4", "33.0", "0.76", "83", "5", "39.0", "0.90", "84"]} +{"pcdb_id": 500293, "raw": ["500293", "020073", "0", "2015/Nov/11 10:34", "Dantherm Air Handling A/S", "Dantherm", "HCH 8", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "3", "27.0", "0.70", "82", "4", "33.0", "0.82", "81", "5", "39.0", "0.98", "82", "6", "45.0", "1.15", "82"]} +{"pcdb_id": 500294, "raw": ["500294", "020073", "0", "2015/Nov/11 10:34", "Dantherm Air Handling A/S", "Dantherm", "HCV 5", "", "2010", "current", "", "3", "0", "2", "1", "1", "4", "2", "21.0", "0.64", "82", "3", "27.0", "0.72", "82", "4", "33.0", "0.85", "83", "5", "39.0", "1.01", "84"]} +{"pcdb_id": 500295, "raw": ["500295", "020002", "0", "2013/Oct/02 17:18", "Vent Axia Ltd", "Vent Axia", "MVDC-MS A", "", "", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.16", "", "2", "29.0", "0.15", "", "3", "37.0", "0.17", "", "4", "45.0", "0.20", "", "5", "53.0", "0.24", "", "6", "61.0", "0.28"]} +{"pcdb_id": 500296, "raw": ["500296", "020002", "0", "2013/Oct/02 17:18", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent A", "", "2012", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.16", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.21", "", "5", "53.0", "0.24", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500297, "raw": ["500297", "020003", "0", "2013/Sep/20 15:22", "The Nuaire Group", "Nuaire", "MRXBOX95B-LP1", "", "2012", "current", "", "3", "0", "2", "1", "3", "5", "1", "15.0", "0.59", "76", "2", "21.0", "0.71", "76", "3", "27.0", "0.91", "78", "4", "33.0", "1.15", "79", "5", "39.0", "1.40", "79"]} +{"pcdb_id": 500298, "raw": ["500298", "020002", "0", "2015/Nov/11 10:34", "Vent Axia Ltd", "Vent Axia", "Kinetic Plus E", "", "2012", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.40", "94", "2", "21.0", "0.43", "94", "3", "27.0", "0.53", "94", "4", "33.0", "0.65", "93", "5", "39.0", "0.78", "93", "6", "45.0", "0.93", "92"]} +{"pcdb_id": 500299, "raw": ["500299", "020009", "0", "2015/Nov/11 10:34", "Vortice Ltd", "Vortice", "Vort Prometeo Plus HR400", "", "2012", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.48", "91", "2", "21.0", "0.54", "91", "3", "27.0", "0.66", "89", "4", "33.0", "0.80", "88", "5", "39.0", "0.95", "88", "6", "45.0", "1.11", "87"]} +{"pcdb_id": 500300, "raw": ["500300", "020076", "0", "2015/Nov/11 10:34", "Elek-Trends Productions", "Veneco", "V4275", "", "2012", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "0.73", "90", "3", "27.0", "0.82", "90", "4", "33.0", "0.95", "89", "5", "39.0", "1.11", "88", "6", "45.0", "1.27", "87"]} +{"pcdb_id": 500301, "raw": ["500301", "020076", "0", "2015/Nov/11 10:34", "Elek-Trends Productions", "Veneco", "V4375", "", "2012", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "0.81", "90", "3", "27.0", "0.90", "90", "4", "33.0", "1.03", "89", "5", "39.0", "1.17", "88", "6", "45.0", "1.33", "87"]} +{"pcdb_id": 500302, "raw": ["500302", "020076", "0", "2015/Nov/11 10:34", "Elek-Trends Productions", "Veneco", "V4450", "", "2012", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "1.01", "90", "2", "21.0", "1.04", "91", "3", "27.0", "1.11", "90", "4", "33.0", "1.20", "89", "5", "39.0", "1.31", "88", "6", "45.0", "1.42", "86", "7", "51.0", "1.54", "85"]} +{"pcdb_id": 500303, "raw": ["500303", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX95-WM2", "", "2012", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.56", "87", "2", "21.0", "0.68", "87", "3", "27.0", "0.85", "86", "4", "33.0", "1.06", "86"]} +{"pcdb_id": 500304, "raw": ["500304", "020078", "0", "2015/Nov/11 10:34", "Villavent Ltd", "Systemair", "SAVE VTC 300", "", "2012", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.82", "76", "2", "21.0", "0.82", "76", "3", "27.0", "0.92", "79", "4", "33.0", "1.06", "80", "5", "39.0", "1.22", "81", "6", "45.0", "1.40", "81"]} +{"pcdb_id": 500305, "raw": ["500305", "020038", "0", "2012/Aug/21 07:53", "Titon Hardware Ltd", "Axco", "CEV 130", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.18", "", "4", "45.0", "0.19", "", "5", "53.0", "0.22", "", "6", "61.0", "0.25"]} +{"pcdb_id": 500306, "raw": ["500306", "020038", "0", "2012/Aug/28 20:31", "Titon Hardware Ltd", "Axco", "CEV 130", "", "2009", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.25", "", "2", "29.0", "0.22", "", "3", "37.0", "0.23", "", "4", "45.0", "0.25", "", "5", "53.0", "0.30"]} +{"pcdb_id": 500308, "raw": ["500308", "020038", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Beam", "AXCO MVHR C50", "", "2008", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.71", "90", "2", "21.0", "0.82", "89", "3", "27.0", "0.99", "87", "4", "33.0", "1.19", "87"]} +{"pcdb_id": 500309, "raw": ["500309", "020038", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Beam", "AXCO MVHR C75", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.42", "90", "2", "21.0", "0.54", "90", "3", "27.0", "0.72", "89", "4", "33.0", "0.93", "88", "5", "39.0", "1.16", "87"]} +{"pcdb_id": 500310, "raw": ["500310", "020038", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Beam", "AXCO MVHR C100", "", "2009", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.44", "90", "2", "21.0", "0.56", "90", "3", "27.0", "0.73", "89", "4", "33.0", "0.92", "88", "5", "39.0", "1.12", "87"]} +{"pcdb_id": 500311, "raw": ["500311", "020038", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Beam", "AXCO MVHR C130", "", "2011", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.48", "92", "2", "21.0", "0.52", "92", "3", "27.0", "0.63", "91", "4", "33.0", "0.78", "90", "5", "39.0", "0.94", "89", "6", "45.0", "1.11", "87"]} +{"pcdb_id": 500312, "raw": ["500312", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "energiSava 280", "", "2011", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.57", "88", "2", "21.0", "0.68", "88", "3", "27.0", "0.87", "84", "4", "33.0", "1.11", "83", "5", "39.0", "1.37", "83", "6", "45.0", "1.64", "83"]} +{"pcdb_id": 500313, "raw": ["500313", "020043", "0", "2015/Nov/11 10:34", "Genvex A/S", "Genvex", "GES Energy 2 BP", "", "2012", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "1.22", "92", "2", "21.0", "1.03", "92", "3", "27.0", "1.10", "90", "4", "33.0", "1.28", "89", "5", "39.0", "1.54", "88", "6", "45.0", "1.84", "88", "7", "51.0", "2.16", "87"]} +{"pcdb_id": 500314, "raw": ["500314", "020027", "0", "2015/Nov/11 10:34", "EnviroVent Ltd", "EnviroVent", "energiSava 380", "", "2012", "2019", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.41", "93", "2", "21.0", "0.47", "93", "3", "27.0", "0.59", "91", "4", "33.0", "0.73", "90", "5", "39.0", "0.89", "88", "6", "45.0", "1.05", "86"]} +{"pcdb_id": 500315, "raw": ["500315", "020035", "0", "2015/Nov/11 10:34", "Rega Ventilation Ltd", "RegaVent", "650P-DC", "", "2012", "current", "", "3", "0", "2", "1", "1", "6", "2", "21.0", "0.70", "81", "3", "27.0", "0.69", "81", "4", "33.0", "0.83", "82", "5", "39.0", "1.06", "81", "6", "45.0", "1.34", "81", "7", "51.0", "1.65", "80"]} +{"pcdb_id": 500316, "raw": ["500316", "020078", "0", "2015/Nov/11 10:34", "Systemair Fans & Spares Ltd", "Villavent", "VTC Low E", "", "2012", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.80", "85", "2", "21.0", "0.93", "85", "3", "27.0", "1.07", "83", "4", "33.0", "1.21", "82"]} +{"pcdb_id": 500317, "raw": ["500317", "020042", "0", "2015/Nov/11 10:34", "Brink Climate Systems B.V.", "Brink", "Renovent Sky 300", "", "2012", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "90", "2", "21.0", "0.62", "90", "3", "27.0", "0.71", "87", "4", "33.0", "0.83", "86", "5", "39.0", "0.97", "85", "6", "45.0", "1.12", "84"]} +{"pcdb_id": 500318, "raw": ["500318", "020041", "0", "2013/Sep/20 17:51", "Polypipe Ltd", "Polypipe Ventilation", "Silavent HRX", "", "2012", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.63", "87", "2", "21.0", "0.75", "86", "3", "27.0", "0.93", "85", "4", "33.0", "1.14", "84", "5", "39.0", "1.37", "84"]} +{"pcdb_id": 500319, "raw": ["500319", "020007", "0", "2015/Nov/11 10:34", "Titon Hardware Ltd", "Titon", "HRV1.25 Q Plus", "", "2012", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.61", "89", "2", "21.0", "0.86", "89", "3", "27.0", "1.19", "88"]} +{"pcdb_id": 500320, "raw": ["500320", "020002", "0", "2016/May/18 15:30", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic 300Z", "", "2012", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.54", "78", "2", "21.0", "0.61", "78", "3", "27.0", "0.75", "78", "4", "33.0", "0.93", "78", "5", "39.0", "1.13", "77", "6", "45.0", "1.35", "76"]} +{"pcdb_id": 500321, "raw": ["500321", "020072", "0", "2015/Nov/11 10:34", "Nilan A/S", "Nilan", "Compact P", "7512404", "2012", "current", "", "3", "1", "2", "1", "1", "6", "1", "15.0", "0.73", "85", "2", "21.0", "0.76", "86", "3", "27.0", "0.88", "86", "4", "33.0", "1.03", "85", "5", "39.0", "1.20", "84", "6", "45.0", "1.38", "83"]} +{"pcdb_id": 500322, "raw": ["500322", "020072", "0", "2015/Nov/11 10:34", "Nilan A/S", "Nilan", "Compact P", "7512305", "2012", "current", "", "3", "1", "2", "1", "1", "6", "1", "15.0", "0.73", "85", "2", "21.0", "0.76", "86", "3", "27.0", "0.88", "86", "4", "33.0", "1.03", "85", "5", "39.0", "1.20", "84", "6", "45.0", "1.38", "83"]} +{"pcdb_id": 500323, "raw": ["500323", "020002", "0", "2016/May/18 15:25", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic 200Z", "", "2012", "current", "", "3", "0", "2", "1", "3", "4", "1", "15.0", "0.73", "81", "2", "21.0", "0.89", "81", "3", "27.0", "1.12", "79", "4", "33.0", "1.39", "78"]} +{"pcdb_id": 500324, "raw": ["500324", "020013", "0", "2015/Nov/11 10:34", "Johnson & Starley Ltd", "Johnson & Starley", "Q-Vent HR400", "", "2011", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.48", "92", "2", "21.0", "0.52", "92", "3", "27.0", "0.63", "91", "4", "33.0", "0.78", "90", "5", "39.0", "0.94", "89", "6", "45.0", "1.11", "87"]} +{"pcdb_id": 500325, "raw": ["500325", "020082", "0", "2015/Nov/11 10:34", "Emmeti SpA", "Emmeti", "RECUPERA SLIM (ER02011-ER + ER02011-EC)", "", "2011", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.97", "91", "2", "21.0", "1.10", "91", "3", "27.0", "1.26", "88"]} +{"pcdb_id": 500326, "raw": ["500326", "020082", "0", "2015/Nov/11 10:34", "Emmeti SpA", "Emmeti", "RECUPERA-MED-ER02511-EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.61", "93", "2", "21.0", "0.69", "93", "3", "27.0", "0.84", "91", "4", "33.0", "1.01", "90", "5", "39.0", "1.20", "89"]} +{"pcdb_id": 500327, "raw": ["500327", "020071", "0", "2015/Nov/11 10:34", "Maico Italia s.p.a.", "Elicent", "AERA 280 EC", "", "2010", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.61", "93", "2", "21.0", "0.69", "93", "3", "27.0", "0.84", "91", "4", "33.0", "1.01", "90", "5", "39.0", "1.20", "89"]} +{"pcdb_id": 500328, "raw": ["500328", "020083", "0", "2015/Nov/11 10:34", "Redring Xpelair Group Ltd", "Xpelair", "Xcell Compact XR", "", "2012", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.49", "90", "2", "21.0", "0.61", "90", "3", "27.0", "0.77", "87"]} +{"pcdb_id": 500329, "raw": ["500329", "020084", "0", "2021/Oct/20 09:58", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 250 touch", "", "2012", "obsolete", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.74", "90", "2", "21.0", "0.78", "90", "3", "27.0", "0.92", "88", "4", "33.0", "1.11", "87", "5", "39.0", "1.32", "86", "6", "45.0", "1.55", "85"]} +{"pcdb_id": 500330, "raw": ["500330", "020084", "0", "2021/Oct/20 09:58", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 400 touch", "", "2012", "obsolete", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.88", "86", "2", "21.0", "0.89", "86", "3", "27.0", "0.99", "86", "4", "33.0", "1.13", "85", "5", "39.0", "1.30", "85", "6", "45.0", "1.48", "84", "7", "51.0", "1.66", "83"]} +{"pcdb_id": 500332, "raw": ["500332", "020041", "0", "2017/Apr/26 11:15", "Polypipe Ltd", "Polypipe Ventilation", "Silavent HRX2", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.48", "94", "2", "21.0", "0.50", "94", "3", "27.0", "0.59", "93", "4", "33.0", "0.71", "92", "5", "39.0", "0.85", "91", "6", "45.0", "1.00", "90", "7", "51.0", "1.15", "89"]} +{"pcdb_id": 500333, "raw": ["500333", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent BV300", "", "2013", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.60", "90", "2", "21.0", "0.78", "90", "3", "27.0", "1.02", "87", "4", "33.0", "1.29", "86"]} +{"pcdb_id": 500334, "raw": ["500334", "020017", "0", "2015/Nov/11 10:34", "Airflow Developments Ltd", "Airflow", "Duplexvent BV400", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.46", "92", "2", "21.0", "0.49", "92", "3", "27.0", "0.60", "91", "4", "33.0", "0.74", "90", "5", "39.0", "0.89", "90", "6", "45.0", "1.05", "89", "7", "51.0", "1.23", "88"]} +{"pcdb_id": 500335, "raw": ["500335", "020004", "0", "2014/Aug/04 15:30", "Greenwood Air Management Ltd", "Greenwood", "Vireo HR155CM", "", "2013", "current", "", "3", "0", "2", "1", "3", "3", "1", "15.0", "0.73", "92", "2", "21.0", "0.83", "91", "3", "27.0", "1.00", "90"]} +{"pcdb_id": 500336, "raw": ["500336", "020004", "0", "2014/Aug/04 15:30", "Greenwood Air Management Ltd", "Greenwood", "Vireo HR155WM", "", "2013", "current", "", "3", "0", "2", "1", "3", "3", "1", "15.0", "0.77", "92", "2", "21.0", "0.90", "91", "3", "27.0", "1.10", "90"]} +{"pcdb_id": 500337, "raw": ["500337", "020004", "0", "2014/Aug/04 15:30", "Greenwood Air Management Ltd", "Greenwood", "Vireo HR185WM", "", "2013", "current", "", "3", "0", "2", "1", "3", "4", "1", "15.0", "0.57", "92", "2", "21.0", "0.64", "91", "3", "27.0", "0.76", "91", "4", "33.0", "0.96", "91"]} +{"pcdb_id": 500341, "raw": ["500341", "020052", "0", "2015/Nov/11 10:34", "Brookvent", "Brookvent", "AirCycle 1.2", "", "2013", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.59", "91", "2", "21.0", "0.74", "91", "3", "27.0", "0.96", "88", "4", "33.0", "1.20", "87", "5", "39.0", "1.46", "86"]} +{"pcdb_id": 500342, "raw": ["500342", "020052", "0", "2015/Nov/11 10:34", "Brookvent", "Brookvent", "AirCycle 2.2", "", "2013", "current", "", "3", "0", "2", "1", "1", "5", "2", "21.0", "0.62", "91", "3", "27.0", "0.76", "91", "4", "33.0", "0.96", "89", "5", "39.0", "1.20", "88", "6", "45.0", "1.45", "88"]} +{"pcdb_id": 500343, "raw": ["500343", "020035", "0", "2015/Nov/11 10:34", "Rega Ventilation Ltd", "RegaVent", "300R-DC", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.70", "89", "2", "21.0", "0.77", "89", "3", "27.0", "0.92", "88", "4", "33.0", "1.11", "87", "5", "39.0", "1.32", "86", "6", "45.0", "1.55", "85", "7", "51.0", "1.78", "84"]} +{"pcdb_id": 500344, "raw": ["500344", "020035", "0", "2015/Nov/11 10:34", "Rega Ventilation Ltd", "RegaVent", "600R-DC", "", "2013", "current", "", "3", "0", "2", "1", "1", "3", "4", "33.0", "0.71", "89", "5", "39.0", "0.86", "90", "6", "45.0", "1.04", "88"]} +{"pcdb_id": 500345, "raw": ["500345", "020031", "0", "2014/Jun/11 13:30", "Genvex A/S", "NIBE", "GV-HR110-250", "", "2013", "current", "", "3", "0", "2", "1", "3", "5", "1", "15.0", "0.81", "91", "2", "21.0", "0.88", "91", "3", "27.0", "1.01", "88", "4", "33.0", "1.16", "86", "5", "39.0", "1.33", "84"]} +{"pcdb_id": 500346, "raw": ["500346", "020031", "0", "2015/Nov/11 10:34", "Genvex A/S", "NIBE", "GV-HR110-400", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "1.22", "92", "2", "21.0", "1.03", "92", "3", "27.0", "1.10", "90", "4", "33.0", "1.28", "89", "5", "39.0", "1.54", "88", "6", "45.0", "1.84", "88", "7", "51.0", "2.16", "87"]} +{"pcdb_id": 500347, "raw": ["500347", "020003", "0", "2015/Nov/11 10:34", "The Nuaire Group", "Nuaire", "MRXBOX95-WM1", "", "2013", "current", "", "3", "0", "2", "1", "1", "2", "1", "15.0", "0.72", "92", "2", "21.0", "0.99", "91"]} +{"pcdb_id": 500349, "raw": ["500349", "020016", "0", "2015/Nov/11 10:34", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "PA600LI", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.59", "93", "2", "21.0", "0.64", "92", "3", "27.0", "0.75", "91", "4", "33.0", "0.89", "90", "5", "39.0", "1.04", "89", "6", "45.0", "1.20", "87", "7", "51.0", "1.37", "86"]} +{"pcdb_id": 500350, "raw": ["500350", "020013", "0", "2015/Nov/11 10:34", "Johnson & Starley Ltd", "Johnson & Starley", "Q Vent 160 MK3", "", "2010", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.65", "91", "2", "21.0", "0.84", "90", "3", "27.0", "1.08", "90"]} +{"pcdb_id": 500351, "raw": ["500351", "020041", "0", "2013/Oct/03 13:32", "Polypipe Ltd", "Polypipe Ventilation", "Silavent CMX", "", "2013", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.24", "", "2", "29.0", "0.25", "", "3", "37.0", "0.29", "", "4", "45.0", "0.35", "", "5", "53.0", "0.43", "", "6", "61.0", "0.54"]} +{"pcdb_id": 500352, "raw": ["500352", "020011", "0", "2025/Jul/07 12:00", "Vectaire Ltd", "Vectaire", "WHHR MIDI", "", "2013", "obsolete", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.58", "93", "2", "21.0", "0.70", "91", "3", "27.0", "0.87", "90", "4", "33.0", "1.06", "89", "5", "39.0", "1.27", "87"]} +{"pcdb_id": 500353, "raw": ["500353", "020038", "0", "2014/Apr/28 10:00", "AB C.A. Östberg", "Beam", "AXCO HERU 100 S", "", "2013", "current", "", "3", "0", "2", "1", "3", "3", "3", "27.0", "1.11", "83", "4", "33.0", "1.12", "83", "5", "39.0", "1.17", "83"]} +{"pcdb_id": 500354, "raw": ["500354", "020038", "0", "2014/Apr/28 10:00", "AB C.A. Östberg", "Beam", "AXCO HERU 100 T", "", "2013", "current", "", "3", "0", "2", "1", "3", "3", "3", "27.0", "1.31", "83", "4", "33.0", "1.36", "83", "5", "39.0", "1.43", "83"]} +{"pcdb_id": 500355, "raw": ["500355", "020038", "0", "2014/Apr/28 10:00", "AB C.A. Östberg", "Beam", "AXCO HERU 160 T", "", "2013", "current", "", "3", "0", "2", "1", "3", "4", "4", "33.0", "1.08", "81", "5", "39.0", "1.17", "81", "6", "45.0", "1.29", "80", "7", "51.0", "1.43", "79"]} +{"pcdb_id": 500356, "raw": ["500356", "020078", "0", "2014/Apr/28 10:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VSR 300", "", "2013", "current", "", "3", "0", "2", "1", "3", "4", "3", "27.0", "1.09", "83", "4", "33.0", "1.17", "84", "5", "39.0", "1.30", "84", "6", "45.0", "1.48", "84"]} +{"pcdb_id": 500357, "raw": ["500357", "020078", "0", "2014/Apr/28 10:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VSR 500", "", "2013", "current", "", "3", "0", "2", "1", "3", "3", "4", "33.0", "1.14", "84", "5", "39.0", "1.20", "85", "6", "45.0", "1.29", "87"]} +{"pcdb_id": 500358, "raw": ["500358", "020009", "0", "2013/Nov/25 09:50", "Vortice Ltd", "Vortice", "VORT EVO HR 200", "", "2013", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.61", "90", "2", "21.0", "0.80", "89", "3", "27.0", "1.06", "87"]} +{"pcdb_id": 500360, "raw": ["500360", "020083", "0", "2013/Dec/04 09:50", "Redring Xpelair Group Ltd", "Xpelair", "Xcell S120Q Stratum", "92923AW", "2013", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.87", "87", "2", "21.0", "1.17", "85", "3", "27.0", "1.61", "84"]} +{"pcdb_id": 500361, "raw": ["500361", "020003", "0", "2014/Feb/18 14:00", "The Nuaire Group", "Nuaire", "MRXBOX95AB-WH1", "", "2013", "current", "", "3", "0", "2", "1", "0", "7", "1", "15.0", "0.52", "88", "2", "21.0", "0.56", "88", "3", "27.0", "0.67", "88", "4", "33.0", "0.78", "86", "5", "39.0", "0.95", "86", "6", "45.0", "1.16", "85", "7", "51.0", "1.40", "85"]} +{"pcdb_id": 500362, "raw": ["500362", "020003", "0", "2014/Feb/18 14:00", "The Nuaire Group", "Nuaire", "MRXBOX95AB-WH2", "", "2013", "current", "", "3", "0", "2", "1", "0", "7", "1", "15.0", "0.60", "89", "2", "21.0", "0.61", "88", "3", "27.0", "0.68", "88", "4", "33.0", "0.79", "87", "5", "39.0", "0.95", "86", "6", "45.0", "1.15", "85", "7", "51.0", "1.38", "85"]} +{"pcdb_id": 500363, "raw": ["500363", "020003", "0", "2014/Feb/18 14:00", "The Nuaire Group", "Nuaire", "MRXBOX95AB-WM1", "", "2013", "current", "", "3", "0", "2", "1", "0", "2", "1", "15.0", "0.91", "86", "2", "21.0", "1.26", "85"]} +{"pcdb_id": 500364, "raw": ["500364", "020003", "0", "2014/Feb/18 14:00", "The Nuaire Group", "Nuaire", "MRXBOX95AB-WM2", "", "2013", "current", "", "3", "0", "2", "1", "0", "6", "1", "15.0", "0.56", "86", "2", "21.0", "0.66", "85", "3", "27.0", "0.79", "84", "4", "33.0", "1.02", "83", "5", "39.0", "1.23", "82", "6", "45.0", "1.52", "82"]} +{"pcdb_id": 500365, "raw": ["500365", "020027", "0", "2014/Feb/18 14:00", "EnviroVent Ltd", "EnviroVent", "OZEO ECOWATT CP", "", "2013", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.43", "", "2", "29.0", "0.37", "", "3", "37.0", "0.36", "", "4", "45.0", "0.36", "", "5", "53.0", "0.35", "", "6", "61.0", "0.34"]} +{"pcdb_id": 500366, "raw": ["500366", "020011", "0", "2014/Feb/18 14:00", "Vectaire Ltd", "Vectaire", "WHHR Maxi", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.45", "92", "2", "21.0", "0.47", "92", "3", "27.0", "0.54", "91", "4", "33.0", "0.66", "90", "5", "39.0", "0.80", "90", "6", "45.0", "0.99", "89", "7", "51.0", "1.21", "89"]} +{"pcdb_id": 500367, "raw": ["500367", "020011", "0", "2014/Feb/18 14:00", "Vectaire Ltd", "Vectaire", "WHHR MIDI Plus", "", "2013", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.51", "93", "2", "21.0", "0.61", "91", "3", "27.0", "0.75", "90", "4", "33.0", "0.92", "89"]} +{"pcdb_id": 500370, "raw": ["500370", "020017", "0", "2014/Apr/24 12:00", "Airflow Developments Ltd", "Airflow", "Duplexvent DV96SE", "", "2013", "current", "", "3", "0", "2", "1", "1", "5", "1", "15.0", "0.96", "87", "2", "21.0", "1.06", "87", "3", "27.0", "1.21", "86", "4", "33.0", "1.42", "85", "5", "39.0", "1.73", "85"]} +{"pcdb_id": 500371, "raw": ["500371", "020017", "0", "2014/Apr/24 12:00", "Airflow Developments Ltd", "Airflow", "Duplexvent DV110SE", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.85", "90", "2", "21.0", "0.82", "89", "3", "27.0", "0.90", "88", "4", "33.0", "1.05", "88", "5", "39.0", "1.22", "87", "6", "45.0", "1.42", "87", "7", "51.0", "1.71", "87"]} +{"pcdb_id": 500372, "raw": ["500372", "020017", "0", "2014/Apr/24 12:00", "Airflow Developments Ltd", "Airflow", "Duplexvent DV145SE", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "1.04", "80", "2", "21.0", "0.95", "82", "3", "27.0", "0.97", "83", "4", "33.0", "1.07", "83", "5", "39.0", "1.19", "84", "6", "45.0", "1.35", "84", "7", "51.0", "1.55", "84"]} +{"pcdb_id": 500373, "raw": ["500373", "020027", "0", "2014/Apr/24 12:00", "EnviroVent Ltd", "EnviroVent", "energiSava 250", "", "2013", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.66", "90", "2", "21.0", "0.81", "89", "3", "27.0", "1.01", "87"]} +{"pcdb_id": 500374, "raw": ["500374", "020090", "0", "2014/Apr/28 10:00", "Elta UK Ltd", "Elta Fans", "VIGO 200M", "ISSUE A", "2013", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.86", "86", "2", "21.0", "1.03", "85", "3", "27.0", "1.22", "83", "4", "33.0", "1.58", "81"]} +{"pcdb_id": 500375, "raw": ["500375", "020090", "0", "2014/May/21 13:00", "Elta UK Ltd", "Elta Fans", "VIGO 200A", "ISSUE A", "2013", "current", "", "3", "0", "2", "1", "1", "4", "1", "15.0", "0.86", "86", "2", "21.0", "1.03", "85", "3", "27.0", "1.22", "83", "4", "33.0", "1.58", "81"]} +{"pcdb_id": 500376, "raw": ["500376", "020090", "0", "2014/Apr/28 10:00", "EnviroVent Ltd", "Elta Fans", "VIGO 250", "ISSUE A", "2013", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.66", "90", "2", "21.0", "0.81", "89", "3", "27.0", "1.01", "87"]} +{"pcdb_id": 500377, "raw": ["500377", "020078", "0", "2014/Jul/01 11:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTR 300/B R", "19592", "2014", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "1.39", "79", "2", "21.0", "1.18", "81", "3", "27.0", "1.12", "83", "4", "33.0", "1.20", "84", "5", "39.0", "1.30", "84", "6", "45.0", "1.46", "83", "7", "51.0", "1.65", "82"]} +{"pcdb_id": 500378, "raw": ["500378", "020078", "0", "2014/Jul/01 11:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTR 300/B L", "19593", "2014", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "1.39", "79", "2", "21.0", "1.18", "81", "3", "27.0", "1.12", "83", "4", "33.0", "1.20", "84", "5", "39.0", "1.30", "84", "6", "45.0", "1.46", "83", "7", "51.0", "1.65", "82"]} +{"pcdb_id": 500379, "raw": ["500379", "020078", "0", "2014/Jun/11 14:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTR 200/B R", "14882", "2014", "current", "", "3", "0", "2", "1", "3", "4", "1", "15.0", "1.66", "70", "2", "21.0", "1.56", "71", "3", "27.0", "1.62", "72", "4", "33.0", "1.81", "72"]} +{"pcdb_id": 500380, "raw": ["500380", "020078", "0", "2014/Jun/11 14:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTR 200/B L", "14883", "2014", "current", "", "3", "0", "2", "1", "3", "4", "1", "15.0", "1.66", "70", "2", "21.0", "1.56", "71", "3", "27.0", "1.62", "72", "4", "33.0", "1.81", "72"]} +{"pcdb_id": 500381, "raw": ["500381", "020013", "0", "2014/Jun/11 14:00", "Johnson & Starley Ltd", "Johnson & Starley", "Q-VENT CE50", "", "2014", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.26", "", "2", "29.0", "0.28", "", "3", "37.0", "0.35", "", "4", "45.0", "0.42", "", "5", "53.0", "0.54"]} +{"pcdb_id": 500382, "raw": ["500382", "020092", "0", "2014/Jun/11 14:00", "Renson Fabrications Ltd", "Renson", "C+CUBE", "", "2014", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.23", "", "2", "29.0", "0.19", "", "3", "37.0", "0.27", "", "4", "45.0", "0.24", "", "5", "53.5", "0.23"]} +{"pcdb_id": 500383, "raw": ["500383", "020092", "0", "2014/Jun/11 14:00", "Renson Fabrications Ltd", "Renson", "CBASE", "", "2014", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.23", "", "2", "29.0", "0.19", "", "3", "37.0", "0.27", "", "4", "45.0", "0.24", "", "5", "53.5", "0.23"]} +{"pcdb_id": 500384, "raw": ["500384", "020092", "0", "2014/Jun/11 14:00", "Renson Fabrications Ltd", "Renson", "HEALTHBOX II", "", "2014", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.60", "", "2", "29.0", "0.50", "", "3", "37.0", "0.43", "", "4", "45.3", "0.40", "", "5", "53.1", "0.41"]} +{"pcdb_id": 500385, "raw": ["500385", "020090", "0", "2014/Jul/14 14:00", "Elta UK Ltd", "Elta Fans", "MORI MEV", "Issue A", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.20", "", "5", "53.0", "0.24", "", "6", "61.0", "0.28"]} +{"pcdb_id": 500386, "raw": ["500386", "020090", "0", "2014/Jul/14 14:00", "Elta UK Ltd", "Elta Fans", "VIGO 400", "ISSUE A", "2014", "current", "", "3", "0", "2", "1", "1", "7", "1", "15.0", "0.59", "92", "2", "21.0", "0.60", "90", "3", "27.0", "0.64", "89", "4", "33.0", "0.78", "89", "5", "39.0", "0.89", "88", "6", "45.0", "1.06", "87", "7", "51.0", "1.27", "86"]} +{"pcdb_id": 500387, "raw": ["500387", "020007", "0", "2014/Jul/14 14:00", "Titon Hardware Ltd", "Titon", "CME2 Q Plus A", "", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.17", "", "3", "37.0", "0.19", "", "4", "45.0", "0.21", "", "5", "53.0", "0.25", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500388, "raw": ["500388", "020007", "0", "2014/Jul/28 16:30", "Titon Hardware Ltd", "Titon", "CME2 Q Plus A", "", "2014", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.28", "", "2", "29.0", "0.25", "", "3", "37.0", "0.26", "", "4", "45.0", "0.28", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500389, "raw": ["500389", "020007", "0", "2014/Jul/14 14:00", "Titon Hardware Ltd", "Titon", "CME2 Q Plus HA", "", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.17", "", "3", "37.0", "0.19", "", "4", "45.0", "0.21", "", "5", "53.0", "0.25", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500390, "raw": ["500390", "020007", "0", "2014/Jul/28 16:30", "Titon Hardware Ltd", "Titon", "CME2 Q Plus HA", "", "2014", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.28", "", "2", "29.0", "0.25", "", "3", "37.0", "0.26", "", "4", "45.0", "0.28", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500391, "raw": ["500391", "020038", "0", "2014/Jul/14 14:00", "Titon Hardware Ltd", "Beam", "AXCO MEV130 A", "", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.17", "", "3", "37.0", "0.19", "", "4", "45.0", "0.21", "", "5", "53.0", "0.25", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500392, "raw": ["500392", "020038", "0", "2014/Jul/28 16:30", "Titon Hardware Ltd", "Beam", "AXCO MEV130 A", "", "2014", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.28", "", "2", "29.0", "0.25", "", "3", "37.0", "0.26", "", "4", "45.0", "0.28", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500393, "raw": ["500393", "020038", "0", "2014/Jul/14 14:00", "Titon Hardware Ltd", "Beam", "AXCO MEV130 HA", "", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.17", "", "3", "37.0", "0.19", "", "4", "45.0", "0.21", "", "5", "53.0", "0.25", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500394, "raw": ["500394", "020038", "0", "2014/Jul/28 16:30", "Titon Hardware Ltd", "Beam", "AXCO MEV130 HA", "", "2014", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.28", "", "2", "29.0", "0.25", "", "3", "37.0", "0.26", "", "4", "45.0", "0.28", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500397, "raw": ["500397", "020003", "0", "2014/Jul/15 15:00", "The Nuaire Group", "Nuaire", "AP-MVHR-RL", "", "2014", "current", "", "3", "0", "2", "1", "0", "6", "1", "15.0", "0.72", "86", "2", "21.0", "0.77", "86", "3", "27.0", "0.88", "85", "4", "33.0", "1.10", "85", "5", "39.0", "1.29", "85", "6", "45.0", "1.60", "84"]} +{"pcdb_id": 500398, "raw": ["500398", "020003", "0", "2014/Jul/15 15:00", "The Nuaire Group", "Nuaire", "AP-MVHR-LL", "", "2014", "current", "", "3", "0", "2", "1", "0", "6", "1", "15.0", "0.72", "86", "2", "21.0", "0.77", "86", "3", "27.0", "0.88", "85", "4", "33.0", "1.10", "85", "5", "39.0", "1.29", "85", "6", "45.0", "1.60", "84"]} +{"pcdb_id": 500402, "raw": ["500402", "020027", "0", "2014/Oct/09 13:00", "EnviroVent Ltd", "EnviroVent", "MEV Spider 2", "Issue A", "2014", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.36", "", "2", "29.0", "0.32", "", "3", "37.0", "0.33", "", "4", "45.0", "0.32", "", "5", "53.0", "0.35", "", "6", "61.0", "0.36"]} +{"pcdb_id": 500403, "raw": ["500403", "020027", "0", "2014/Oct/09 13:00", "EnviroVent Ltd", "EnviroVent", "energiSava 210", "5153582900-000", "2014", "current", "", "3", "0", "2", "1", "3", "5", "1", "15.0", "0.81", "89", "2", "21.0", "0.82", "87", "3", "27.0", "0.89", "87", "4", "33.0", "1.03", "85", "5", "39.0", "1.22", "85"]} +{"pcdb_id": 500404, "raw": ["500404", "020078", "0", "2014/Nov/12 12:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTR 500 R", "Ver A", "2014", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "1.44", "71", "2", "29.0", "1.21", "74", "3", "37.0", "1.15", "76", "4", "45.0", "1.18", "78", "5", "53.0", "1.23", "79", "6", "61.0", "1.35", "80", "7", "69.0", "1.53", "81"]} +{"pcdb_id": 500405, "raw": ["500405", "020078", "0", "2014/Nov/12 12:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTR 500 L", "Ver A", "2014", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "1.44", "71", "2", "29.0", "1.21", "74", "3", "37.0", "1.15", "76", "4", "45.0", "1.18", "78", "5", "53.0", "1.23", "79", "6", "61.0", "1.35", "80", "7", "69.0", "1.53", "81"]} +{"pcdb_id": 500406, "raw": ["500406", "020078", "0", "2014/Dec/03 12:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTC 200 R", "Ver A", "2014", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.73", "89", "2", "29.0", "0.75", "89", "3", "37.0", "0.81", "89", "4", "45.0", "0.97", "88", "5", "53.0", "1.14", "88"]} +{"pcdb_id": 500407, "raw": ["500407", "020078", "0", "2014/Dec/03 12:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTC 200 L", "Ver A", "2014", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.73", "89", "2", "29.0", "0.75", "89", "3", "37.0", "0.81", "89", "4", "45.0", "0.97", "88", "5", "53.0", "1.14", "88"]} +{"pcdb_id": 500408, "raw": ["500408", "020078", "0", "2014/Nov/12 12:00", "Systemair Fans & Spares Ltd", "Systemair", "prio 160EC", "", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.24", "", "2", "29.0", "0.24", "", "3", "37.0", "0.25", "", "4", "45.0", "0.30", "", "5", "53.0", "0.35", "", "6", "61.0", "0.40"]} +{"pcdb_id": 500409, "raw": ["500409", "020007", "0", "2015/May/18 13:50", "Titon Hardware Ltd", "Titon", "HRV1.25 Q Plus Eco", "", "2014", "current", "", "3", "0", "2", "1", "3", "3", "1", "15.0", "0.68", "87", "2", "21.0", "0.90", "85", "3", "27.0", "1.21", "84"]} +{"pcdb_id": 500410, "raw": ["500410", "020007", "0", "2015/May/18 13:50", "Titon Hardware Ltd", "Titon", "HRV2.85 Q Plus Eco", "", "2014", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.60", "91", "2", "21.0", "0.62", "90", "3", "27.0", "0.72", "89", "4", "33.0", "0.88", "88", "5", "39.0", "1.06", "87", "6", "45.0", "1.30", "87"]} +{"pcdb_id": 500411, "raw": ["500411", "020007", "0", "2015/May/18 13:50", "Titon Hardware Ltd", "Titon", "HRV2 Q Plus Eco", "", "2014", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.63", "90", "2", "29.0", "0.67", "90", "3", "37.0", "0.76", "89", "4", "45.0", "0.94", "88", "5", "53.0", "1.14", "87", "6", "61.0", "1.40", "87"]} +{"pcdb_id": 500412, "raw": ["500412", "020007", "0", "2015/May/18 13:50", "Titon Hardware Ltd", "Titon", "HRV3 Q Plus Eco", "", "2014", "current", "", "3", "0", "2", "1", "3", "5", "2", "29.0", "0.71", "90", "3", "37.0", "0.85", "88", "4", "45.0", "1.04", "87", "5", "53.0", "1.28", "87", "6", "61.0", "1.58", "87"]} +{"pcdb_id": 500413, "raw": ["500413", "020007", "0", "2015/Jul/15 14:56", "Titon Hardware Ltd", "Titon", "HRV1.35 Q Plus Eco", "", "2015", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.71", "87", "2", "29.0", "0.92", "85", "3", "37.0", "1.19", "85", "4", "45.0", "1.55", "84"]} +{"pcdb_id": 500414, "raw": ["500414", "020011", "0", "2015/Mar/17 13:10", "Vectaire Ltd", "Vectaire", "WHHR Mini", "", "2014", "current", "", "3", "0", "2", "1", "1", "1", "1", "21.0", "1.40", "82"]} +{"pcdb_id": 500420, "raw": ["500420", "020007", "0", "2015/May/18 13:50", "Titon Hardware Ltd", "Titon", "HRV2.85 Q Plus", "", "2015", "current", "", "3", "0", "2", "1", "1", "6", "1", "21.0", "0.57", "90", "2", "29.0", "0.59", "89", "3", "37.0", "0.69", "89", "4", "45.0", "0.85", "88", "5", "53.0", "1.03", "87", "6", "61.0", "1.28", "86"]} +{"pcdb_id": 500421, "raw": ["500421", "020011", "0", "2021/Nov/08 20:30", "Vectaire Ltd", "Vectaire", "EVO250DC", "", "2015", "current", "", "3", "0", "2", "1", "4", "4", "1", "21.0", "0.75", "87", "2", "29.0", "0.89", "86", "3", "37.0", "1.00", "85", "4", "45.0", "1.37", "84"]} +{"pcdb_id": 500422, "raw": ["500422", "020007", "0", "2015/Jul/15 14:56", "Titon Hardware Ltd", "Titon", "HRV1.75 Q Plus Eco", "", "2015", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.52", "89", "2", "29.0", "0.60", "89", "3", "37.0", "0.73", "88", "4", "45.0", "0.94", "87"]} +{"pcdb_id": 500423, "raw": ["500423", "020007", "0", "2015/Jul/16 10:25", "Titon Hardware Ltd", "Titon", "HRV10 Q Plus Eco", "", "2015", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.55", "90", "2", "29.0", "0.57", "90", "3", "37.0", "0.65", "89", "4", "45.0", "0.76", "88", "5", "53.0", "0.90", "87", "6", "61.0", "1.09", "86", "7", "69.0", "1.27", "85"]} +{"pcdb_id": 500424, "raw": ["500424", "020035", "0", "2015/Jul/16 10:25", "Rega Ventilation Ltd", "RegaVent", "250R DC", "", "2015", "current", "", "3", "0", "2", "1", "1", "4", "1", "21.0", "0.64", "88", "2", "29.0", "0.75", "86", "3", "37.0", "0.91", "85", "4", "45.0", "1.16", "85"]} +{"pcdb_id": 500427, "raw": ["500427", "020030", "0", "2015/Aug/17 12:08", "Aldes", "Aldes", "Dee Fly Cube 300 Micro-watt", "", "2015", "current", "", "3", "0", "2", "1", "3", "5", "3", "37.0", "0.93", "87", "4", "45.0", "1.04", "84", "5", "53.0", "1.25", "84", "6", "61.0", "1.47", "83", "7", "69.0", "1.71", "82"]} +{"pcdb_id": 500429, "raw": ["500429", "020038", "0", "2015/Sep/15 12:25", "Titon Hardware Ltd", "Beam", "AXCO MVHR C90", "", "2015", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.60", "91", "2", "21.0", "0.62", "90", "3", "27.0", "0.72", "89", "4", "33.0", "0.88", "88", "5", "39.0", "1.06", "87", "6", "45.0", "1.30", "87"]} +{"pcdb_id": 500430, "raw": ["500430", "020038", "0", "2015/Sep/15 12:25", "Titon Hardware Ltd", "Beam", "AXCO MVHR C65", "", "2015", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.71", "87", "2", "29.0", "0.92", "85", "3", "37.0", "1.19", "85", "4", "45.0", "1.55", "84"]} +{"pcdb_id": 500431, "raw": ["500431", "020009", "0", "2015/Nov/09 12:00", "Vortice Ltd", "Vortice", "VORT HR 250 NETI", "", "2015", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.60", "88", "2", "29.0", "0.72", "86", "3", "37.0", "0.86", "84", "4", "45.0", "1.12", "83"]} +{"pcdb_id": 500432, "raw": ["500432", "020009", "0", "2015/Sep/15 12:25", "Vortice Ltd", "Vortice", "VORT HR 350 AVEL", "", "2015", "current", "", "3", "0", "2", "1", "0", "7", "1", "21.0", "0.60", "91", "2", "29.0", "0.63", "90", "3", "37.0", "0.72", "89", "4", "45.0", "0.88", "89", "5", "53.0", "1.05", "88", "6", "61.0", "1.31", "88", "7", "69.0", "1.58", "88"]} +{"pcdb_id": 500433, "raw": ["500433", "020059", "0", "2015/Sep/15 12:25", "Ventilation Systems PrJSC", "VENTS", "VUT 160 PB EC A11", "", "2010", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.67", "80", "2", "29.0", "0.76", "81", "3", "37.0", "0.93", "82"]} +{"pcdb_id": 500434, "raw": ["500434", "020059", "0", "2015/Sep/15 12:25", "Ventilation Systems PrJSC", "VENTS", "VUT 350 VB EC A11", "", "2010", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "88", "2", "29.0", "0.64", "88", "3", "37.0", "0.68", "87", "4", "45.0", "0.76", "86", "5", "53.0", "0.86", "86", "6", "61.0", "1.07", "85", "7", "69.0", "1.26", "85"]} +{"pcdb_id": 500435, "raw": ["500435", "020059", "0", "2015/Sep/15 12:25", "Ventilation Systems PrJSC", "VENTS", "VUT 550 VB EC A11", "", "2010", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "87", "2", "29.0", "0.63", "88", "3", "37.0", "0.63", "88", "4", "45.0", "0.72", "88", "5", "53.0", "0.84", "88", "6", "61.0", "0.98", "87", "7", "69.0", "1.16", "87"]} +{"pcdb_id": 500436, "raw": ["500436", "020002", "0", "2015/Sep/15 12:25", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic 200ZP", "", "2014", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.67", "84", "2", "29.0", "0.82", "82", "3", "37.0", "1.07", "80"]} +{"pcdb_id": 500437, "raw": ["500437", "020104", "0", "2015/Oct/20 11:58", "Salda UAB", "Salda", "SMARTY 3X P", "", "2015", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.88", "91", "2", "29.0", "0.83", "91", "3", "37.0", "0.85", "90", "4", "45.0", "0.95", "89", "5", "53.0", "1.08", "89", "6", "61.0", "1.26", "88", "7", "69.0", "1.48", "88"]} +{"pcdb_id": 500438, "raw": ["500438", "020101", "0", "2015/Nov/11 10:34", "Itho UK Ltd", "Heatrae Sadia", "Advance", "95 060 001", "2015", "current", "", "3", "0", "2", "1", "1", "3", "1", "15.0", "0.50", "89", "2", "21.0", "0.73", "89", "3", "27.0", "1.00", "86"]} +{"pcdb_id": 500439, "raw": ["500439", "020101", "0", "2015/Oct/20 12:34", "Itho UK Ltd", "Heatrae Sadia", "CVE ECO 2 HP", "95 060 006", "2015", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.18", "", "2", "29.0", "0.17", "", "3", "37.0", "0.18", "", "4", "45.0", "0.20", "", "5", "53.0", "0.22", "", "6", "61.0", "0.27"]} +{"pcdb_id": 500440, "raw": ["500440", "020101", "0", "2015/Oct/20 12:34", "Itho UK Ltd", "Heatrae Sadia", "CVE ECO 2", "95 060 004", "2015", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.18", "", "2", "29.0", "0.17", "", "3", "37.0", "0.18", "", "4", "45.0", "0.20", "", "5", "53.0", "0.22"]} +{"pcdb_id": 500441, "raw": ["500441", "020101", "0", "2016/Sep/22 11:45", "Itho UK Ltd", "Heatrae Sadia", "HRU ECO 4 - House", "95 060 002", "2015", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.47", "90", "2", "21.0", "0.53", "90", "3", "27.0", "0.65", "87", "4", "33.0", "0.79", "87", "5", "39.0", "0.94", "87", "6", "45.0", "1.10", "88"]} +{"pcdb_id": 500442, "raw": ["500442", "020101", "0", "2016/Sep/22 11:45", "Itho UK Ltd", "Heatrae Sadia", "HRU ECO 4 - Apartment", "95 060 003", "2015", "current", "", "3", "0", "2", "1", "3", "6", "1", "15.0", "0.47", "90", "2", "21.0", "0.53", "90", "3", "27.0", "0.65", "87", "4", "33.0", "0.79", "87", "5", "39.0", "0.94", "87", "6", "45.0", "1.10", "88"]} +{"pcdb_id": 500443, "raw": ["500443", "020007", "0", "2015/Nov/23 10:37", "Titon Hardware Ltd", "Titon", "H200 Q Plus ECO", "", "2015", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.58", "83", "2", "29.0", "0.66", "82", "3", "37.0", "0.80", "80", "4", "45.0", "1.01", "80", "5", "53.0", "1.25", "80", "6", "61.0", "1.58", "82"]} +{"pcdb_id": 500446, "raw": ["500446", "020013", "0", "2016/Feb/22 14:32", "Johnson & Starley Ltd", "Johnson & Starley", "Q-VENT COMPACT v2", "", "2015", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "1.54", "84", "2", "29.0", "2.00", "82"]} +{"pcdb_id": 500447, "raw": ["500447", "020002", "0", "2016/Feb/22 15:39", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic Advance S", "405215", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.39", "93", "2", "29.0", "0.46", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.70", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.07", "89", "7", "69.0", "1.31", "89"]} +{"pcdb_id": 500448, "raw": ["500448", "020059", "0", "2016/Feb/22 16:03", "Ventilation Systems PrJSC", "VENTS", "VUT 700 H EC eco", "", "2015", "current", "", "3", "0", "2", "1", "0", "7", "1", "21.0", "0.75", "83", "2", "29.0", "0.69", "85", "3", "37.0", "0.73", "86", "4", "45.0", "0.80", "86", "5", "53.0", "0.89", "86", "6", "61.0", "1.08", "85", "7", "69.0", "1.27", "85"]} +{"pcdb_id": 500449, "raw": ["500449", "020027", "0", "2016/Mar/17 13:49", "Brink Climate Systems B.V.", "EnviroVent", "energiSava 400", "", "2016", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.63", "89", "2", "21.0", "0.61", "89", "3", "27.0", "0.66", "87", "4", "33.0", "0.74", "86", "5", "39.0", "0.84", "85", "6", "45.0", "0.95", "84"]} +{"pcdb_id": 500450, "raw": ["500450", "020027", "0", "2016/Mar/17 13:49", "Brink Climate Systems B.V.", "EnviroVent", "Slimline 300", "", "2016", "current", "", "3", "0", "2", "1", "1", "6", "1", "15.0", "0.60", "90", "2", "21.0", "0.62", "90", "3", "27.0", "0.71", "87", "4", "33.0", "0.83", "86", "5", "39.0", "0.97", "85", "6", "45.0", "1.12", "84"]} +{"pcdb_id": 500451, "raw": ["500451", "020027", "0", "2016/Mar/17 13:49", "EnviroVent Ltd", "EnviroVent", "energiSava 200", "Issue A", "2016", "current", "", "3", "0", "2", "1", "0", "5", "1", "21.0", "0.75", "89", "2", "29.0", "0.82", "87", "3", "37.0", "0.96", "85", "4", "45.0", "1.17", "84", "5", "53.0", "1.41", "82"]} +{"pcdb_id": 500452, "raw": ["500452", "020052", "0", "2016/Apr/12 16:08", "Brook Design Hardware Ltd", "Brookvent", "AIRCYCLE 3.1", "", "2014", "current", "", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.41", "93", "2", "29.0", "0.43", "92", "3", "37.0", "0.51", "91", "4", "45.0", "0.64", "91", "5", "53.0", "0.78", "90", "6", "61.0", "0.98", "89", "7", "69.0", "1.20", "89"]} +{"pcdb_id": 500453, "raw": ["500453", "020041", "0", "2016/Apr/12 16:08", "Polypipe Ltd", "Polypipe Ventilation", "Silavent AQH240-S", "", "2016", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.69", "88", "2", "29.0", "0.84", "85"]} +{"pcdb_id": 500454, "raw": ["500454", "020041", "0", "2016/Apr/12 16:08", "Polypipe Ltd", "Polypipe Ventilation", "Silavent AQH200-B", "", "2016", "current", "", "3", "0", "2", "1", "3", "2", "1", "21.0", "0.75", "83", "2", "29.0", "0.95", "81"]} +{"pcdb_id": 500455, "raw": ["500455", "020041", "0", "2016/Apr/12 16:08", "Polypipe Ltd", "Polypipe Ventilation", "Silavent AQH200-S", "", "2016", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.72", "87", "2", "29.0", "0.88", "84"]} +{"pcdb_id": 500456, "raw": ["500456", "020041", "0", "2016/Apr/12 16:08", "Polypipe Ltd", "Polypipe Ventilation", "Silavent AQH240-B", "", "2016", "current", "", "3", "0", "2", "1", "3", "2", "1", "21.0", "0.69", "86", "2", "29.0", "0.84", "83"]} +{"pcdb_id": 500457, "raw": ["500457", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC240 CLASSIC Droite", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.91", "87", "2", "29.0", "1.01", "87", "3", "37.0", "1.18", "85", "4", "45.0", "1.37", "85"]} +{"pcdb_id": 500458, "raw": ["500458", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC240 CLASSIC Gauche", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.91", "87", "2", "29.0", "1.01", "87", "3", "37.0", "1.18", "85", "4", "45.0", "1.37", "85"]} +{"pcdb_id": 500459, "raw": ["500459", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC240 PREMIUM Droite", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.91", "87", "2", "29.0", "1.01", "87", "3", "37.0", "1.18", "85", "4", "45.0", "1.37", "85"]} +{"pcdb_id": 500460, "raw": ["500460", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC240 PREMIUM Gauche", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.91", "87", "2", "29.0", "1.01", "87", "3", "37.0", "1.18", "85", "4", "45.0", "1.37", "85"]} +{"pcdb_id": 500461, "raw": ["500461", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC370 CLASSIC Droite", "2016", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.90", "91", "2", "29.0", "0.92", "91", "3", "37.0", "1.04", "90", "4", "45.0", "1.21", "89", "5", "53.0", "1.39", "89", "6", "61.0", "1.64", "89"]} +{"pcdb_id": 500462, "raw": ["500462", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC370 CLASSIC Gauche", "2016", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.90", "91", "2", "29.0", "0.92", "91", "3", "37.0", "1.04", "90", "4", "45.0", "1.21", "89", "5", "53.0", "1.39", "89", "6", "61.0", "1.64", "89"]} +{"pcdb_id": 500463, "raw": ["500463", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC370 PREMIUM Droite", "2016", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.90", "91", "2", "29.0", "0.92", "91", "3", "37.0", "1.04", "90", "4", "45.0", "1.21", "89", "5", "53.0", "1.39", "89", "6", "61.0", "1.64", "89"]} +{"pcdb_id": 500464, "raw": ["500464", "020030", "0", "2016/Apr/13 11:18", "Aldes", "Aldes", "INSPIRAIR HOME", "SC370 PREMIUM Gauche", "2016", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.90", "91", "2", "29.0", "0.92", "91", "3", "37.0", "1.04", "90", "4", "45.0", "1.21", "89", "5", "53.0", "1.39", "89", "6", "61.0", "1.64", "89"]} +{"pcdb_id": 500465, "raw": ["500465", "020047", "0", "2016/Apr/14 12:26", "Mitsubishi Electric Europe B.V.", "Mitsubishi", "LOSSNAY", "VL-220CZGV-EB", "2016", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.65", "85", "2", "29.0", "0.62", "84"]} +{"pcdb_id": 500466, "raw": ["500466", "020090", "0", "2016/Apr/14 12:26", "Elta UK Ltd", "Elta Fans", "VIGO 350", "Issue A", "2016", "current", "", "3", "0", "2", "1", "0", "3", "1", "21.0", "0.98", "89", "2", "29.0", "1.15", "88", "3", "37.0", "1.42", "87"]} +{"pcdb_id": 500467, "raw": ["500467", "020090", "0", "2016/Apr/14 12:26", "Elta UK Ltd", "Elta Fans", "VIGO 550", "Issue A", "2016", "current", "", "3", "0", "2", "1", "0", "7", "1", "21.0", "0.56", "93", "2", "29.0", "0.58", "93", "3", "37.0", "0.63", "91", "4", "45.0", "0.74", "91", "5", "53.0", "0.87", "90", "6", "61.0", "1.04", "89", "7", "69.0", "1.22", "89"]} +{"pcdb_id": 500468, "raw": ["500468", "020042", "0", "2016/Apr/14 12:26", "Brink Climate Systems B.V.", "Brink", "Renovent Sky 150+ EU", "", "2013", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.75", "88", "2", "29.0", "0.86", "85", "3", "37.0", "1.04", "84"]} +{"pcdb_id": 500469, "raw": ["500469", "020042", "0", "2016/Apr/14 12:26", "Brink Climate Systems B.V.", "Brink", "Renovent Excellent 300 4-0 EU", "", "2013", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.66", "89", "2", "29.0", "0.62", "87", "3", "37.0", "0.66", "86", "4", "45.0", "0.74", "85", "5", "53.0", "0.86", "84", "6", "61.0", "1.04", "83", "7", "69.0", "1.21", "83"]} +{"pcdb_id": 500470, "raw": ["500470", "020002", "0", "2016/May/18 15:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic C", "", "2016", "current", "", "3", "0", "2", "1", "3", "3", "1", "15.0", "0.78", "85", "2", "21.0", "0.89", "85", "3", "27.0", "1.03", "82"]} +{"pcdb_id": 500471, "raw": ["500471", "020002", "0", "2016/May/18 15:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic High Flow", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.58", "88", "2", "29.0", "0.55", "90", "3", "37.0", "0.60", "91", "4", "45.0", "0.69", "91", "5", "53.0", "0.78", "90", "6", "61.0", "0.92", "90", "7", "69.0", "1.09", "90"]} +{"pcdb_id": 500472, "raw": ["500472", "020002", "0", "2016/May/18 15:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic FH", "", "2016", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.53", "89", "2", "29.0", "0.60", "88", "3", "37.0", "0.71", "86", "4", "45.0", "0.88", "84", "5", "53.0", "1.07", "84"]} +{"pcdb_id": 500473, "raw": ["500473", "020002", "0", "2016/May/18 15:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent Plus", "", "2015", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.25", "", "2", "29.0", "0.22", "", "3", "37.0", "0.22", "", "4", "45.0", "0.22", "", "5", "53.0", "0.25", "", "6", "61.0", "0.27"]} +{"pcdb_id": 500476, "raw": ["500476", "020007", "0", "2016/May/19 09:16", "Titon Hardware Ltd", "Titon", "HRV10.25 Q Plus Eco", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.43", "90", "2", "29.0", "0.46", "88", "3", "37.0", "0.54", "87", "4", "45.0", "0.65", "86", "5", "53.0", "0.79", "85", "6", "61.0", "0.96", "84", "7", "69.0", "1.16", "83"]} +{"pcdb_id": 500477, "raw": ["500477", "020108", "0", "2016/Jun/20 12:52", "Aermec SpA", "Aermec", "RePuro 170", "", "2012", "current", "", "3", "0", "2", "1", "1", "3", "1", "21.0", "0.94", "88", "2", "29.0", "0.96", "86", "3", "37.0", "1.14", "84"]} +{"pcdb_id": 500478, "raw": ["500478", "020108", "0", "2016/Jun/22 11:15", "Aermec SpA", "Aermec", "RePuro 350", "", "2012", "current", "", "3", "0", "2", "1", "1", "6", "1", "21.0", "0.77", "88", "2", "29.0", "0.76", "88", "3", "37.0", "0.83", "87", "4", "45.0", "0.96", "86", "5", "53.0", "1.12", "85", "6", "61.0", "1.40", "85"]} +{"pcdb_id": 500479, "raw": ["500479", "020004", "0", "2016/Jul/18 12:36", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 180 GB Luxe PH", "", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.72", "89", "2", "29.0", "0.79", "86", "3", "37.0", "0.94", "84", "4", "45.0", "1.20", "82"]} +{"pcdb_id": 500480, "raw": ["500480", "020004", "0", "2016/Nov/16 14:15", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir Q350 GB ST", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.60", "96", "2", "29.0", "0.53", "95", "3", "37.0", "0.57", "94", "4", "45.0", "0.64", "94", "5", "53.0", "0.72", "93", "6", "61.0", "0.89", "93", "7", "69.0", "1.03", "93"]} +{"pcdb_id": 500481, "raw": ["500481", "020004", "0", "2016/Nov/16 14:15", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir Q450 GB ST", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.54", "96", "2", "29.0", "0.53", "95", "3", "37.0", "0.55", "94", "4", "45.0", "0.62", "94", "5", "53.0", "0.73", "93", "6", "61.0", "0.86", "93", "7", "69.0", "1.04", "93"]} +{"pcdb_id": 500482, "raw": ["500482", "020004", "0", "2016/Nov/16 14:15", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir Q600 GB ST", "", "2016", "current", "", "3", "0", "2", "2", "3", "7", "1", "21.0", "0.63", "96", "2", "29.0", "0.60", "95", "3", "37.0", "0.63", "94", "4", "45.0", "0.71", "94", "5", "53.0", "0.79", "93", "6", "61.0", "0.91", "93", "7", "69.0", "1.06", "93"]} +{"pcdb_id": 500483, "raw": ["500483", "020027", "0", "2016/Aug/16 14:31", "Brink Climate Systems B.V.", "EnviroVent", "Slimline 150", "", "2016", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.75", "88", "2", "29.0", "0.86", "85", "3", "37.0", "1.04", "84"]} +{"pcdb_id": 500484, "raw": ["500484", "020027", "0", "2016/Aug/16 14:31", "Brink Climate Systems B.V.", "EnviroVent", "energiSava 300", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.66", "89", "2", "29.0", "0.62", "87", "3", "37.0", "0.66", "86", "4", "45.0", "0.74", "85", "5", "53.0", "0.86", "84", "6", "61.0", "1.04", "83", "7", "69.0", "1.21", "83"]} +{"pcdb_id": 500485, "raw": ["500485", "020101", "0", "2016/Sep/26 11:26", "Baxi Heating UK Ltd", "Heatrae Sadia", "Advance Plus", "95060007", "2015", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.48", "86", "2", "29.0", "0.72", "85", "3", "37.0", "0.99", "83", "4", "45.0", "1.35", "82"]} +{"pcdb_id": 500486, "raw": ["500486", "020083", "0", "2016/Sep/26 11:23", "Redring Xpelair Group Ltd", "Xpelair", "Stratum 275Q", "", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.73", "88", "2", "29.0", "0.85", "86", "3", "37.0", "1.02", "85", "4", "45.0", "1.27", "83"]} +{"pcdb_id": 500487, "raw": ["500487", "020069", "0", "2017/Jan/16 09:30", "Earth Save Products Ltd", "ESP", "Ecocent", "ESP400-010-200L(D)", "2008", "current", "", "1", "0", "2", "1", "", "2", "1", "21.0", "0.51", "", "2", "29.0", "0.48"]} +{"pcdb_id": 500488, "raw": ["500488", "020069", "0", "2016/Oct/04 14:06", "Earth Save Products Ltd", "ESP", "Ecocent", "ESP400-010-300L(D)", "2008", "current", "", "1", "0", "2", "1", "", "6", "2", "29.0", "0.70", "", "3", "37.0", "0.70", "", "4", "45.0", "0.70", "", "5", "53.0", "0.70", "", "6", "61.0", "0.70", "", "7", "69.0", "0.70"]} +{"pcdb_id": 500489, "raw": ["500489", "020069", "0", "2016/Oct/04 14:05", "Earth Save Products Ltd", "ESP", "Ecocent Maxi", "ESP400-015-300L(D)", "2008", "current", "", "1", "0", "2", "1", "", "6", "2", "29.0", "0.70", "", "3", "37.0", "0.70", "", "4", "45.0", "0.70", "", "5", "53.0", "0.70", "", "6", "61.0", "0.70", "", "7", "69.0", "0.70"]} +{"pcdb_id": 500490, "raw": ["500490", "020017", "0", "2016/Sep/26 11:19", "Airflow Developments Ltd", "Airflow", "DV250 Entro", "", "2016", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.70", "82", "2", "29.0", "0.78", "81", "3", "37.0", "0.93", "80", "4", "45.0", "1.20", "79", "5", "53.0", "1.38", "79"]} +{"pcdb_id": 500491, "raw": ["500491", "020017", "0", "2016/Sep/26 11:11", "Airflow Developments Ltd", "Airflow", "DV300 Entro", "", "2016", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.58", "84", "2", "29.0", "0.62", "82", "3", "37.0", "0.70", "81", "4", "45.0", "0.84", "80", "5", "53.0", "0.99", "79"]} +{"pcdb_id": 500492, "raw": ["500492", "020017", "0", "2016/Sep/26 11:03", "Airflow Developments Ltd", "Airflow", "DV400 Entro", "", "2016", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.59", "84", "2", "29.0", "0.61", "82", "3", "37.0", "0.71", "81", "4", "45.0", "0.87", "80", "5", "53.0", "1.03", "79", "6", "61.0", "1.27", "79"]} +{"pcdb_id": 500493, "raw": ["500493", "020111", "0", "2016/Sep/26 11:00", "Vaventis BV", "Vaventis", "Fresh-R", "", "2014", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.87", "87", "2", "29.0", "1.11", "84"]} +{"pcdb_id": 500495, "raw": ["500495", "020078", "0", "2016/Oct/24 14:00", "Systemair Fans & Spares Ltd", "Systemair", "SAVE VTC 700", "", "2012", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.70", "71", "3", "37.0", "0.70", "73", "4", "45.0", "0.83", "75", "5", "53.0", "0.91", "77", "6", "61.0", "1.11", "78", "7", "69.0", "1.31", "79"]} +{"pcdb_id": 500496, "raw": ["500496", "020104", "0", "2016/Oct/31 12:28", "Salda UAB", "Salda", "SMARTY 2X V 1.1", "", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.89", "88", "2", "29.0", "0.98", "88", "3", "37.0", "1.17", "87", "4", "45.0", "1.50", "86"]} +{"pcdb_id": 500497, "raw": ["500497", "020104", "0", "2016/Oct/31 12:29", "Salda UAB", "Salda", "SMARTY 3X V 1.1", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.91", "87", "2", "29.0", "0.78", "88", "3", "37.0", "0.83", "87", "4", "45.0", "0.90", "87", "5", "53.0", "1.03", "86", "6", "61.0", "1.21", "86", "7", "69.0", "1.40", "85"]} +{"pcdb_id": 500498, "raw": ["500498", "020003", "0", "2017/Jan/17 12:36", "The Nuaire Group", "Nuaire", "MRXBOX-ECO3", "", "2016", "current", "", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.47", "91", "2", "29.0", "0.50", "91", "3", "37.0", "0.58", "90", "4", "45.0", "0.71", "89", "5", "53.0", "0.86", "89", "6", "61.0", "1.08", "88", "7", "69.0", "1.33", "88"]} +{"pcdb_id": 500499, "raw": ["500499", "020003", "0", "2017/Jan/17 12:51", "The Nuaire Group", "Nuaire", "MRXBOX-ECO2", "", "2016", "current", "", "3", "0", "2", "1", "1", "6", "1", "21.0", "0.47", "89", "2", "29.0", "0.54", "88", "3", "37.0", "0.66", "87", "4", "45.0", "0.85", "87", "5", "53.0", "1.05", "86", "6", "61.0", "1.34", "86"]} +{"pcdb_id": 500500, "raw": ["500500", "020003", "0", "2017/Jan/17 13:02", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO2", "", "2016", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.52", "90", "2", "29.0", "0.59", "89", "3", "37.0", "0.77", "87", "4", "45.0", "1.00", "86", "5", "53.0", "1.23", "86"]} +{"pcdb_id": 500501, "raw": ["500501", "020003", "0", "2017/Jan/17 13:06", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO3", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.50", "90", "2", "29.0", "0.53", "90", "3", "37.0", "0.60", "89", "4", "45.0", "0.75", "88", "5", "53.0", "0.92", "88", "6", "61.0", "1.10", "87", "7", "69.0", "1.36", "87"]} +{"pcdb_id": 500502, "raw": ["500502", "020003", "0", "2017/Jan/17 16:12", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO4", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.62", "94", "2", "29.0", "0.62", "93", "3", "37.0", "0.66", "93", "4", "45.0", "0.79", "92", "5", "53.0", "0.94", "91", "6", "61.0", "1.15", "91", "7", "69.0", "1.41", "91"]} +{"pcdb_id": 500503, "raw": ["500503", "020104", "0", "2017/Mar/14 16:41", "Salda UAB", "Salda", "SMARTY 2X P 1.1", "", "2016", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.91", "86", "2", "29.0", "0.88", "84", "3", "37.0", "0.96", "82", "4", "45.0", "1.10", "81", "5", "53.0", "1.26", "81", "6", "61.0", "1.50", "80"]} +{"pcdb_id": 500504, "raw": ["500504", "020108", "0", "2017/Mar/20 14:18", "Aermec SpA", "Aermec", "RePuro 100", "", "2012", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.91", "88", "2", "29.0", "0.96", "86"]} +{"pcdb_id": 500505, "raw": ["500505", "020108", "0", "2017/Mar/20 14:18", "Aermec SpA", "Aermec", "RePuro 250", "", "2012", "current", "", "3", "0", "2", "1", "1", "6", "1", "21.0", "0.75", "88", "2", "29.0", "0.73", "88", "3", "37.0", "0.80", "87", "4", "45.0", "0.96", "86", "5", "53.0", "1.12", "85", "6", "61.0", "1.36", "85"]} +{"pcdb_id": 500506, "raw": ["500506", "020108", "0", "2017/Mar/20 14:18", "Aermec SpA", "Aermec", "RePuro 450", "", "2012", "current", "", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.84", "90", "2", "29.0", "0.77", "90", "3", "37.0", "0.80", "89", "4", "45.0", "0.90", "88", "5", "53.0", "1.03", "88", "6", "61.0", "1.22", "87", "7", "69.0", "1.42", "87"]} +{"pcdb_id": 500507, "raw": ["500507", "020108", "0", "2017/Mar/20 14:19", "Aermec SpA", "Aermec", "RePuro 550", "", "2012", "current", "", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.83", "90", "2", "29.0", "0.78", "90", "3", "37.0", "0.82", "89", "4", "45.0", "0.91", "88", "5", "53.0", "1.07", "88", "6", "61.0", "1.24", "87", "7", "69.0", "1.44", "87"]} +{"pcdb_id": 500508, "raw": ["500508", "020108", "0", "2017/Mar/20 14:19", "Aermec SpA", "Aermec", "RePuro 650", "", "2012", "current", "", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.85", "90", "2", "29.0", "0.79", "90", "3", "37.0", "0.84", "89", "4", "45.0", "0.92", "88", "5", "53.0", "1.04", "88", "6", "61.0", "1.28", "87", "7", "69.0", "1.45", "87"]} +{"pcdb_id": 500509, "raw": ["500509", "020017", "0", "2017/Mar/14 16:31", "Airflow Developments Ltd", "Airflow", "DV96 Adroit", "", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.87", "89", "2", "29.0", "0.98", "88", "3", "37.0", "1.13", "87", "4", "45.0", "1.40", "86"]} +{"pcdb_id": 500510, "raw": ["500510", "020017", "0", "2017/Mar/14 16:31", "Airflow Developments Ltd", "Airflow", "DV110 Adroit", "", "2016", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.83", "91", "2", "29.0", "0.85", "90", "3", "37.0", "0.97", "89", "4", "45.0", "1.16", "89", "5", "53.0", "1.40", "88"]} +{"pcdb_id": 500511, "raw": ["500511", "020017", "0", "2017/Mar/14 16:31", "Airflow Developments Ltd", "Airflow", "DV145 Adroit", "", "2016", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "1.11", "92", "2", "29.0", "1.05", "92", "3", "37.0", "1.05", "91", "4", "45.0", "1.19", "90", "5", "53.0", "1.31", "90", "6", "61.0", "1.48", "90"]} +{"pcdb_id": 500512, "raw": ["500512", "020041", "0", "2017/Mar/14 16:33", "Polypipe Ltd", "Domus Ventilation", "HRX2D", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.61", "94", "2", "29.0", "0.57", "94", "3", "37.0", "0.62", "92", "4", "45.0", "0.70", "92", "5", "53.0", "0.82", "91", "6", "61.0", "0.98", "90", "7", "69.0", "1.15", "90"]} +{"pcdb_id": 500513, "raw": ["500513", "020041", "0", "2017/Mar/14 16:33", "Polypipe Ltd", "Domus Ventilation", "HRXD", "", "2017", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.66", "87", "2", "29.0", "0.73", "86", "3", "37.0", "0.85", "85", "4", "45.0", "1.05", "84", "5", "53.0", "1.27", "83"]} +{"pcdb_id": 500514, "raw": ["500514", "020083", "0", "2017/Apr/26 09:20", "Xpelair", "Xpelair", "Natural Air 180", "93256AW", "2017", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.68", "86", "2", "29.0", "0.83", "84", "3", "37.0", "1.04", "84"]} +{"pcdb_id": 500515, "raw": ["500515", "020115", "0", "2017/May/17 16:57", "Vectaire Ltd", "AVT", "HRH250", "", "2015", "current", "", "3", "0", "2", "1", "1", "4", "1", "21.0", "0.75", "87", "2", "29.0", "0.89", "86", "3", "37.0", "1.00", "85", "4", "45.0", "1.37", "84"]} +{"pcdb_id": 500516, "raw": ["500516", "020115", "0", "2017/May/17 16:54", "Vectaire Ltd", "AVT", "HRH/MOD", "", "2014", "current", "", "3", "0", "2", "1", "1", "1", "1", "21.0", "1.40", "82"]} +{"pcdb_id": 500517, "raw": ["500517", "020115", "0", "2017/May/17 16:52", "Vectaire Ltd", "AVT", "HRV130", "", "2013", "current", "", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.45", "92", "2", "29.0", "0.47", "92", "3", "37.0", "0.54", "91", "4", "45.0", "0.66", "90", "5", "53.0", "0.80", "90", "6", "61.0", "0.99", "89", "7", "69.0", "1.21", "89"]} +{"pcdb_id": 500518, "raw": ["500518", "020115", "0", "2017/May/17 17:14", "Vectaire Ltd", "AVT", "SL125/2EC", "", "2011", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.20", "", "2", "29.0", "0.26", "", "3", "37.0", "0.34", "", "4", "45.0", "0.44", "", "5", "53.0", "0.55"]} +{"pcdb_id": 500519, "raw": ["500519", "020003", "0", "2017/May/17 17:27", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO-LP1", "", "2017", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.59", "76", "2", "29.0", "0.71", "76", "3", "37.0", "0.91", "78", "4", "45.0", "1.15", "79", "5", "53.0", "1.40", "79"]} +{"pcdb_id": 500520, "raw": ["500520", "020041", "0", "2017/May/22 09:52", "Polypipe Ltd", "Domus Ventilation", "CMX", "", "2013", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.24", "", "2", "29.0", "0.25", "", "3", "37.0", "0.29", "", "4", "45.0", "0.35", "", "5", "53.0", "0.43", "", "6", "61.0", "0.54"]} +{"pcdb_id": 500521, "raw": ["500521", "020041", "0", "2017/May/17 17:49", "Polypipe Ltd", "Domus Ventilation", "AQH200-B", "", "2016", "current", "", "3", "0", "2", "1", "3", "2", "1", "21.0", "0.75", "83", "2", "29.0", "0.95", "81"]} +{"pcdb_id": 500522, "raw": ["500522", "020041", "0", "2017/May/22 09:45", "Polypipe Ltd", "Domus Ventilation", "AQH200-S", "", "2016", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.72", "87", "2", "29.0", "0.88", "84"]} +{"pcdb_id": 500523, "raw": ["500523", "020041", "0", "2017/May/17 17:40", "Polypipe Ltd", "Domus Ventilation", "AQH240-B", "", "2016", "current", "", "3", "0", "2", "1", "3", "2", "1", "21.0", "0.69", "86", "2", "29.0", "0.84", "83"]} +{"pcdb_id": 500524, "raw": ["500524", "020041", "0", "2017/May/17 17:37", "Polypipe Ltd", "Domus Ventilation", "AQH240-S", "", "2016", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.69", "88", "2", "29.0", "0.84", "85"]} +{"pcdb_id": 500525, "raw": ["500525", "020030", "0", "2017/May/17 17:33", "Aldes", "Aldes", "COMPACT MICRO-WATT SP", "", "2015", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.29", "", "2", "29.0", "0.26", "", "3", "37.0", "0.24", "", "4", "45.0", "0.34"]} +{"pcdb_id": 500526, "raw": ["500526", "020030", "0", "2017/May/17 17:31", "Aldes", "Aldes", "SFP300", "", "2015", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.30", "", "2", "29.0", "0.25", "", "3", "37.0", "0.22", "", "4", "45.0", "0.33", "", "5", "53.0", "0.32", "", "6", "61.0", "0.33"]} +{"pcdb_id": 500527, "raw": ["500527", "020115", "0", "2017/May/17 16:48", "Vectaire Ltd", "AVT", "HRV100", "", "2013", "current", "", "3", "0", "2", "1", "1", "4", "1", "21.0", "0.51", "93", "2", "29.0", "0.61", "91", "3", "37.0", "0.75", "90", "4", "45.0", "0.92", "89"]} +{"pcdb_id": 500528, "raw": ["500528", "020092", "0", "2017/Jul/24 12:31", "Renson Fabrications Ltd", "Renson", "Endura Delta 330", "", "2017", "current", "", "3", "0", "2", "2", "3", "7", "1", "21.0", "0.75", "91", "2", "29.0", "0.72", "90", "3", "37.0", "0.75", "89", "4", "45.0", "0.86", "88", "5", "53.0", "0.96", "88", "6", "61.0", "1.16", "87", "7", "69.0", "1.43", "87"]} +{"pcdb_id": 500529, "raw": ["500529", "020092", "0", "2017/Jul/24 12:45", "Renson Fabrications Ltd", "Renson", "Endura Delta 380", "", "2017", "current", "", "3", "0", "2", "2", "3", "7", "1", "21.0", "0.64", "91", "2", "29.0", "0.59", "90", "3", "37.0", "0.62", "89", "4", "45.0", "0.71", "88", "5", "53.0", "0.81", "87", "6", "61.0", "0.98", "87", "7", "69.0", "1.16", "87"]} +{"pcdb_id": 500530, "raw": ["500530", "020011", "0", "2022/Sep/23 15:41", "Vectaire Ltd", "Vectaire", "WHHR-Maxi Plus BY", "", "2017", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "0.56", "89", "2", "29.0", "0.47", "89", "3", "37.0", "0.50", "88", "4", "45.0", "0.56", "87", "5", "53.0", "0.66", "86", "6", "61.0", "0.78", "85", "7", "69.0", "0.94", "84"]} +{"pcdb_id": 500531, "raw": ["500531", "020092", "0", "2017/Aug/24 11:41", "Renson Fabrications Ltd", "Renson", "E+ndura 300", "", "2015", "current", "", "1", "1", "2", "2", "", "6", "1", "21.0", "0.38", "", "2", "29.0", "0.32", "", "3", "37.0", "0.29", "", "4", "45.0", "0.25", "", "5", "53.0", "0.28", "", "6", "61.0", "0.28"]} +{"pcdb_id": 500533, "raw": ["500533", "020043", "0", "2017/Oct/11 15:42", "Total Home Environment Ltd", "Genvex", "ECO 375 TS OPT251 PET", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.65", "86", "2", "29.0", "0.63", "86", "3", "37.0", "0.64", "87", "4", "45.0", "0.74", "86", "5", "53.0", "0.85", "86", "6", "61.0", "1.02", "86", "7", "69.0", "1.19", "85"]} +{"pcdb_id": 500534, "raw": ["500534", "020043", "0", "2017/Oct/26 14:15", "Total Home Environment Ltd", "Genvex", "ECO 190 CS", "", "2016", "current", "", "3", "0", "2", "1", "0", "3", "1", "21.0", "0.67", "90", "2", "29.0", "0.71", "88", "3", "37.0", "0.80", "86"]} +{"pcdb_id": 500539, "raw": ["500539", "020065", "0", "2017/Dec/19 13:17", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "LWZ 280", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.52", "90", "2", "29.0", "0.55", "90", "3", "37.0", "0.62", "90", "4", "45.0", "0.72", "89", "5", "53.0", "0.88", "88", "6", "61.0", "1.07", "89", "7", "69.0", "1.28", "88"]} +{"pcdb_id": 500540, "raw": ["500540", "020065", "0", "2017/Dec/19 13:14", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "LWZ 280 Enthalpie", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.48", "84", "2", "29.0", "0.46", "84", "3", "37.0", "0.55", "82", "4", "45.0", "0.64", "80", "5", "53.0", "0.78", "79", "6", "61.0", "0.99", "77", "7", "69.0", "1.12", "77"]} +{"pcdb_id": 500541, "raw": ["500541", "020065", "0", "2017/Dec/19 13:11", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "LWZ 180", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.52", "90", "2", "29.0", "0.55", "90", "3", "37.0", "0.62", "90", "4", "45.0", "0.72", "89", "5", "53.0", "0.88", "88", "6", "61.0", "1.07", "89", "7", "69.0", "1.28", "88"]} +{"pcdb_id": 500542, "raw": ["500542", "020065", "0", "2017/Dec/19 13:05", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "LWZ 180 Enthalpie", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.48", "84", "2", "29.0", "0.46", "84", "3", "37.0", "0.55", "82", "4", "45.0", "0.64", "80", "5", "53.0", "0.78", "79", "6", "61.0", "0.99", "77", "7", "69.0", "1.12", "77"]} +{"pcdb_id": 500543, "raw": ["500543", "020031", "0", "2018/Feb/08 13:45", "Genvex AB", "NIBE", "ERS 20-250", "", "2017", "current", "", "3", "0", "2", "1", "0", "5", "1", "21.0", "0.81", "81", "2", "29.0", "0.83", "81", "3", "37.0", "0.97", "81", "4", "45.0", "1.18", "80", "5", "53.0", "1.45", "80"]} +{"pcdb_id": 500544, "raw": ["500544", "020031", "0", "2018/Jan/15 15:22", "NIBE Energy Systems Ltd", "NIBE", "F730", "", "2017", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.70", "", "2", "29.0", "0.64", "", "3", "37.0", "0.62", "", "4", "45.0", "0.68", "", "5", "53.0", "0.72", "", "6", "61.0", "0.81"]} +{"pcdb_id": 500545, "raw": ["500545", "020031", "0", "2018/Jan/15 15:17", "GENVEX AB", "NIBE", "ERS 10-400", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.79", "89", "2", "29.0", "0.73", "89", "3", "37.0", "0.75", "88", "4", "45.0", "0.83", "87", "5", "53.0", "0.94", "87", "6", "61.0", "1.09", "86", "7", "69.0", "1.28", "86"]} +{"pcdb_id": 500546, "raw": ["500546", "020084", "0", "2018/Jan/15 15:08", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 180 flat", "", "2016", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.92", "92", "2", "29.0", "0.98", "91", "3", "37.0", "1.13", "90", "4", "45.0", "1.41", "89"]} +{"pcdb_id": 500547, "raw": ["500547", "020084", "0", "2021/Oct/20 09:58", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 180 sensor", "", "2016", "obsolete", "", "3", "0", "2", "1", "1", "4", "1", "21.0", "0.85", "82", "2", "29.0", "0.92", "82", "3", "37.0", "1.05", "81", "4", "45.0", "1.23", "81"]} +{"pcdb_id": 500548, "raw": ["500548", "020084", "0", "2021/Oct/20 09:58", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 300 sensor", "", "2016", "obsolete", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.59", "82", "2", "29.0", "0.58", "82", "3", "37.0", "0.66", "83", "4", "45.0", "0.83", "83", "5", "53.0", "0.94", "83", "6", "61.0", "1.14", "83"]} +{"pcdb_id": 500549, "raw": ["500549", "020066", "0", "2025/Sep/15 16:33", "J Pichler GmbH", "Pichler", "PKOM4A", "", "2015", "current", "", "3", "1", "2", "1", "3", "6", "1", "21.0", "1.10", "89", "2", "29.0", "0.99", "89", "3", "37.0", "1.00", "90", "4", "45.0", "1.11", "90", "5", "53.0", "1.24", "90", "6", "61.0", "1.45", "89"]} +{"pcdb_id": 500550, "raw": ["500550", "020112", "0", "2018/Mar/20 15:06", "Blauberg UK Ltd", "Blauberg", "KOMFORT EC SB 350", "", "2015", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.66", "85", "2", "29.0", "0.60", "84", "3", "37.0", "0.63", "84", "4", "45.0", "0.71", "83", "5", "53.0", "0.82", "83", "6", "61.0", "0.97", "83", "7", "69.0", "1.16", "82"]} +{"pcdb_id": 500551, "raw": ["500551", "020112", "0", "2018/Mar/20 15:01", "Blauberg UK Ltd", "Blauberg", "KOMFORT EC SB 550 S14", "", "2015", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "84", "2", "29.0", "0.65", "85", "3", "37.0", "0.68", "85", "4", "45.0", "0.76", "85", "5", "53.0", "0.88", "85", "6", "61.0", "1.02", "85", "7", "69.0", "1.20", "84"]} +{"pcdb_id": 500552, "raw": ["500552", "020112", "0", "2018/Mar/20 14:46", "Blauberg UK Ltd", "Blauberg", "KOMFORT EC S5B 270 S14", "", "2017", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.72", "81", "2", "29.0", "0.83", "80", "3", "37.0", "1.01", "80", "4", "45.0", "1.28", "79"]} +{"pcdb_id": 500553, "raw": ["500553", "020030", "0", "2018/Apr/23 14:00", "Aldes", "Aldes", "EasyHOME PureAIR COMPACT PREMIUM", "", "2018", "current", "", "1", "0", "2", "2", "", "4", "1", "21.0", "0.28", "", "2", "29.0", "0.24", "", "3", "37.0", "0.24", "", "4", "45.0", "0.24"]} +{"pcdb_id": 500554, "raw": ["500554", "020030", "0", "2018/Apr/23 13:45", "Aldes", "Aldes", "EasyHOME PureAIR COMPACT CLASSIC", "", "2018", "current", "", "1", "0", "2", "2", "", "3", "1", "21.0", "0.59", "", "2", "29.0", "0.44", "", "3", "37.0", "0.38"]} +{"pcdb_id": 500555, "raw": ["500555", "020030", "0", "2018/Apr/23 13:48", "Aldes", "Aldes", "EasyHOME HYGRO COMPACT PREMIUM SP", "", "2018", "current", "", "1", "0", "2", "2", "", "4", "1", "21.0", "0.26", "", "2", "29.0", "0.33", "", "3", "37.0", "0.27", "", "4", "45.0", "0.25"]} +{"pcdb_id": 500556, "raw": ["500556", "020122", "0", "2018/Apr/25 12:44", "Vortice Ltd", "INVAVENT", "MAXI", "", "2009", "current", "", "1", "0", "2", "1", "", "6", "1", "15.0", "0.24", "", "2", "21.0", "0.21", "", "3", "27.0", "0.20", "", "4", "33.0", "0.23", "", "5", "39.0", "0.25", "", "6", "45.0", "0.29"]} +{"pcdb_id": 500557, "raw": ["500557", "020122", "0", "2018/Apr/25 12:42", "Vortice Ltd.", "INVAVENT", "200", "", "2013", "current", "", "3", "0", "2", "1", "1", "3", "1", "21.0", "0.61", "90", "2", "29.0", "0.80", "89", "3", "37.0", "1.06", "87"]} +{"pcdb_id": 500558, "raw": ["500558", "020122", "0", "2018/May/21 09:20", "Vortice", "INVAVENT", "350", "", "2015", "current", "", "3", "0", "2", "1", "0", "7", "1", "21.0", "0.60", "91", "2", "29.0", "0.63", "90", "3", "37.0", "0.72", "89", "4", "45.0", "0.88", "89", "5", "53.0", "1.05", "88", "6", "61.0", "1.31", "88", "7", "69.0", "1.58", "88"]} +{"pcdb_id": 500559, "raw": ["500559", "020065", "0", "2018/Apr/23 14:14", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "LWZ 130", "237805", "2017", "current", "", "3", "0", "2", "1", "1", "3", "1", "21.0", "0.82", "91", "2", "29.0", "0.99", "89", "3", "37.0", "1.13", "87"]} +{"pcdb_id": 500560, "raw": ["500560", "020065", "0", "2018/Apr/23 14:11", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "LWZ 130 Enthalpie", "237806", "2017", "current", "", "3", "0", "2", "1", "1", "3", "1", "21.0", "0.78", "79", "2", "29.0", "0.89", "75", "3", "37.0", "1.07", "72"]} +{"pcdb_id": 500561, "raw": ["500561", "020122", "0", "2018/Apr/25 12:45", "Blauberg", "INVAVENT", "550", "", "2010", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "84", "2", "29.0", "0.65", "85", "3", "37.0", "0.68", "85", "4", "45.0", "0.76", "85", "5", "53.0", "0.88", "85", "6", "61.0", "1.02", "85", "7", "69.0", "1.20", "84"]} +{"pcdb_id": 500562, "raw": ["500562", "020002", "0", "2018/May/25 08:35", "Vent Axia Ltd", "Vent Axia", "MVHR 75 R", "475695", "2018", "current", "", "3", "0", "2", "1", "1", "5", "1", "21.0", "0.58", "90", "2", "29.0", "0.70", "88", "3", "37.0", "0.83", "86", "4", "45.0", "1.05", "85", "5", "53.0", "1.32", "84"]} +{"pcdb_id": 500563, "raw": ["500563", "020030", "0", "2018/May/15 13:10", "Aldes", "Aldes", "EasyHOME HYGRO PREMIUM SP", "", "2018", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.26", "", "2", "29.0", "0.23", "", "3", "37.0", "0.33", "", "4", "45.0", "0.31", "", "5", "51.0", "0.31"]} +{"pcdb_id": 500564, "raw": ["500564", "020013", "0", "2018/May/25 08:09", "Johnson & Starley Ltd", "Johnson & Starley", "Q-VENT MEZZO", "", "2018", "current", "", "3", "0", "2", "1", "1", "4", "1", "21.0", "0.72", "88", "2", "29.0", "0.83", "85", "3", "37.0", "1.02", "83", "4", "45.0", "1.27", "81"]} +{"pcdb_id": 500565, "raw": ["500565", "020002", "0", "2018/May/25 08:26", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic Advance S", "405215A", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "94", "2", "29.0", "0.61", "93", "3", "37.0", "0.66", "93", "4", "45.0", "0.83", "92", "5", "53.0", "0.96", "91", "6", "61.0", "1.18", "90", "7", "69.0", "1.39", "90"]} +{"pcdb_id": 500566, "raw": ["500566", "020011", "0", "2018/Jun/25 11:08", "Vectaire Ltd", "Vectaire", "MBOX200/2DC", "", "2017", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.28", "", "2", "29.0", "0.27", "", "3", "37.0", "0.28", "", "4", "45.0", "0.36", "", "5", "53.0", "0.42", "", "6", "61.0", "0.52"]} +{"pcdb_id": 500567, "raw": ["500567", "020007", "0", "2018/Jun/25 11:12", "Titon Hardware Ltd", "Titon", "HRV1.6 Q Plus Eco", "", "2018", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.51", "89", "2", "29.0", "0.58", "87", "3", "37.0", "0.70", "86", "4", "45.0", "0.90", "84", "5", "53.0", "1.07", "83", "6", "61.0", "1.34", "82"]} +{"pcdb_id": 500568, "raw": ["500568", "020027", "0", "2018/Jul/24 10:20", "Titon Hardware Ltd", "EnviroVent", "energiSava 325", "", "2018", "current", "", "3", "0", "2", "1", "1", "6", "1", "21.0", "0.60", "91", "2", "29.0", "0.62", "90", "3", "37.0", "0.72", "89", "4", "45.0", "0.88", "88", "5", "53.0", "1.06", "87", "6", "61.0", "1.30", "87"]} +{"pcdb_id": 500569, "raw": ["500569", "020003", "0", "2018/Jul/24 10:24", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO-LP2", "", "2018", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.48", "78", "2", "29.0", "0.61", "79", "3", "37.0", "0.77", "79", "4", "45.0", "1.01", "79", "5", "53.0", "1.26", "79"]} +{"pcdb_id": 500570, "raw": ["500570", "020027", "0", "2018/Jul/24 10:19", "Titon Hardware Ltd", "EnviroVent", "energiSava 500", "", "2016", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.43", "90", "2", "29.0", "0.46", "88", "3", "37.0", "0.54", "87", "4", "45.0", "0.65", "86", "5", "53.0", "0.79", "85", "6", "61.0", "0.96", "84", "7", "69.0", "1.16", "83"]} +{"pcdb_id": 500571, "raw": ["500571", "020050", "0", "2018/Aug/07 10:19", "Aereco Ltd", "Aereco", "DXA Exclusive with condensate pump", "DXA1240EX", "2017", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.69", "92", "2", "29.0", "0.69", "91", "3", "37.0", "0.74", "90", "4", "45.0", "0.90", "90", "5", "53.0", "1.05", "89", "6", "61.0", "1.27", "89"]} +{"pcdb_id": 500572, "raw": ["500572", "020050", "0", "2018/Aug/07 10:19", "Aereco Ltd", "Aereco", "DXA Exclusive with siphon", "DXA1247EX", "2017", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.69", "92", "2", "29.0", "0.69", "91", "3", "37.0", "0.74", "90", "4", "45.0", "0.90", "90", "5", "53.0", "1.05", "89", "6", "61.0", "1.27", "89"]} +{"pcdb_id": 500573, "raw": ["500573", "020004", "0", "2018/Sep/20 13:33", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 160 EXP", "", "2012", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.84", "85", "2", "29.0", "0.96", "83", "3", "37.0", "1.15", "82"]} +{"pcdb_id": 500574, "raw": ["500574", "020083", "0", "2018/Sep/20 13:27", "Redring Xpelair Group Ltd", "Xpelair", "Natural Air 350", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.40", "90", "2", "29.0", "0.43", "89", "3", "37.0", "0.49", "88", "4", "45.0", "0.61", "87", "5", "53.0", "0.75", "86", "6", "61.0", "0.93", "86", "7", "69.0", "1.16", "85"]} +{"pcdb_id": 500575, "raw": ["500575", "020011", "0", "2021/Apr/30 19:13", "Vectaire Ltd", "Vectaire", "EVO350-BY", "", "2018", "current", "", "3", "0", "2", "1", "4", "5", "1", "21.0", "0.72", "87", "2", "29.0", "0.75", "85", "3", "37.0", "0.85", "84", "4", "45.0", "1.04", "83", "5", "53.0", "1.23", "82"]} +{"pcdb_id": 500576, "raw": ["500576", "020041", "0", "2018/Oct/24 07:36", "Polypipe Ltd", "Domus Ventilation", "CMX-MULTI", "", "2018", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.14", "", "2", "29.0", "0.16", "", "3", "37.0", "0.20", "", "4", "45.0", "0.25", "", "5", "53.0", "0.31", "", "6", "61.0", "0.37"]} +{"pcdb_id": 500577, "raw": ["500577", "020011", "0", "2021/Apr/30 19:13", "Vectaire Ltd", "Vectaire", "STUDIO-BY", "", "2018", "current", "", "3", "0", "2", "1", "4", "3", "1", "21.0", "0.93", "79", "2", "29.0", "1.09", "78", "3", "37.0", "1.36", "77"]} +{"pcdb_id": 500578, "raw": ["500578", "020041", "0", "2018/Oct/29 11:29", "Polypipe Ltd", "Domus Ventilation", "HRXE", "", "2018", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.57", "90", "2", "29.0", "0.64", "88", "3", "37.0", "0.81", "87", "4", "45.0", "1.05", "86", "5", "53.0", "1.29", "86"]} +{"pcdb_id": 500579, "raw": ["500579", "020018", "0", "2018/Nov/26 10:35", "Volution Ventilation UK Ltd", "National Ventilation", "MON-HRU/330-100", "", "2018", "current", "", "3", "0", "2", "1", "1", "6", "1", "21.0", "0.59", "91", "2", "29.0", "0.63", "89", "3", "37.0", "0.72", "88", "4", "45.0", "0.88", "86", "5", "53.0", "1.05", "85", "6", "61.0", "1.28", "84"]} +{"pcdb_id": 500580, "raw": ["500580", "020018", "0", "2018/Nov/27 10:58", "Volution Ventilation UK Ltd", "National Ventilation", "MON-HRU/350-150i", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.39", "93", "2", "29.0", "0.46", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.70", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.07", "89", "7", "69.0", "1.31", "89"]} +{"pcdb_id": 500581, "raw": ["500581", "020018", "0", "2018/Nov/26 10:50", "Volution Ventilation UK Ltd", "National Ventilation", "MON-HRU/230-100", "", "2018", "current", "", "3", "0", "2", "1", "1", "5", "1", "21.0", "0.58", "90", "2", "29.0", "0.70", "88", "3", "37.0", "0.83", "86", "4", "45.0", "1.05", "85", "5", "53.0", "1.32", "84"]} +{"pcdb_id": 500582, "raw": ["500582", "020018", "0", "2018/Nov/27 10:56", "Volution Ventilation UK Ltd", "National Ventilation", "MON-HRU/400-150", "", "2018", "current", "", "3", "0", "2", "1", "1", "6", "1", "21.0", "0.40", "94", "2", "29.0", "0.43", "94", "3", "37.0", "0.53", "94", "4", "45.0", "0.65", "93", "5", "53.0", "0.78", "93", "6", "61.0", "0.93", "92"]} +{"pcdb_id": 500583, "raw": ["500583", "020130", "0", "2018/Nov/27 11:25", "Vero Duco N.V.", "Duco", "DucoBox Silent UK", "00004229", "2013", "current", "", "1", "0", "2", "1", "", "6", "1", "21.4", "0.31", "", "2", "29.0", "0.26", "", "3", "37.0", "0.25", "", "4", "45.0", "0.26", "", "5", "53.6", "0.29", "", "6", "61.1", "0.32"]} +{"pcdb_id": 500584, "raw": ["500584", "020130", "0", "2018/Nov/27 11:25", "Vero Duco N.V.", "Duco", "DucoBox Focus UK", "00004448", "2013", "current", "", "1", "0", "2", "1", "", "6", "1", "21.4", "0.31", "", "2", "29.0", "0.26", "", "3", "37.0", "0.25", "", "4", "45.0", "0.26", "", "5", "53.6", "0.29", "", "6", "61.1", "0.32"]} +{"pcdb_id": 500585, "raw": ["500585", "020092", "0", "2018/Nov/26 12:23", "Renson Fabrications Ltd", "Renson", "Healthbox 3.0", "", "2018", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.46", "", "2", "29.0", "0.36", "", "3", "37.0", "0.31", "", "4", "45.0", "0.28", "", "5", "53.0", "0.28", "", "6", "61.0", "0.28"]} +{"pcdb_id": 500591, "raw": ["500591", "020092", "0", "2018/Dec/13 09:10", "Renson Fabrications Ltd", "Renson", "Endura Delta 450", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.62", "91", "2", "29.0", "0.59", "90", "3", "37.0", "0.62", "89", "4", "45.0", "0.71", "88", "5", "53.0", "0.83", "87", "6", "61.0", "0.99", "86", "7", "69.0", "1.18", "86"]} +{"pcdb_id": 500592, "raw": ["500592", "020050", "0", "2018/Dec/13 09:09", "Aereco Ltd", "Aereco", "V5S Premium", "V5S 1131", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.40", "", "2", "29.0", "0.33", "", "3", "37.0", "0.38", "", "4", "45.0", "0.34", "", "5", "53.0", "0.36", "", "6", "61.0", "0.36"]} +{"pcdb_id": 500593, "raw": ["500593", "020122", "0", "2019/Jan/23 15:04", "Inventum BV", "JOULE", "Victorum", "62010020", "2016", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.32", "", "2", "29.0", "0.29", "", "3", "37.0", "0.29", "", "4", "45.0", "0.33", "", "5", "53.0", "0.39", "", "6", "61.0", "0.45"]} +{"pcdb_id": 500594, "raw": ["500594", "020052", "0", "2019/Feb/25 12:50", "Brook Design Hardware Ltd", "Brookvent", "Aircycle 1.3", "", "2018", "current", "", "3", "0", "2", "1", "4", "4", "1", "21.0", "0.57", "90", "2", "29.0", "0.68", "87", "3", "37.0", "0.85", "85", "4", "45.0", "1.10", "84"]} +{"pcdb_id": 500595, "raw": ["500595", "020052", "0", "2019/Feb/25 12:49", "Brook Design Hardware Ltd", "Brookvent", "Aircycle 1.3+", "", "2018", "current", "", "3", "0", "2", "1", "4", "4", "1", "21.0", "0.45", "87", "2", "29.0", "0.54", "84", "3", "37.0", "0.67", "83", "4", "45.0", "0.87", "81"]} +{"pcdb_id": 500596, "raw": ["500596", "020020", "0", "2019/Feb/22 09:25", "Orcon B.V.", "Orcon", "HRC-350-MaxComfort", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.54", "93", "2", "29.0", "0.52", "93", "3", "37.0", "0.55", "92", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "92", "6", "61.0", "0.84", "92", "7", "69.0", "1.00", "91"]} +{"pcdb_id": 500597, "raw": ["500597", "020020", "0", "2019/Feb/22 09:19", "Orcon B.V.", "Orcon", "HRC-450-MaxComfort", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.47", "92", "2", "29.0", "0.46", "93", "3", "37.0", "0.50", "93", "4", "45.0", "0.57", "92", "5", "53.0", "0.67", "92", "6", "61.0", "0.79", "92", "7", "69.0", "0.93", "91"]} +{"pcdb_id": 500598, "raw": ["500598", "020007", "0", "2019/Feb/22 09:43", "Titon Hardware Ltd", "Titon", "HRV20 Q Plus Eco", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.48", "88", "2", "29.0", "0.49", "88", "3", "37.0", "0.53", "87", "4", "45.0", "0.63", "86", "5", "53.0", "0.74", "85", "6", "61.0", "0.90", "83", "7", "69.0", "1.08", "82"]} +{"pcdb_id": 500599, "raw": ["500599", "020130", "0", "2022/Jul/26 10:18", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 400 - 2ZS - L", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500600, "raw": ["500600", "020130", "0", "2022/Jul/26 10:18", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 325 - 1ZS - L", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500601, "raw": ["500601", "020007", "0", "2019/Feb/22 09:37", "Titon Hardware Ltd", "Titon", "CME3 Q Plus A", "", "2019", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.18", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.19", "", "5", "53.0", "0.21", "", "6", "61.0", "0.25"]} +{"pcdb_id": 500602, "raw": ["500602", "020007", "0", "2019/Feb/22 09:37", "Titon Hardware Ltd", "Titon", "CME3 Q Plus A", "", "2019", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.27", "", "2", "29.0", "0.25", "", "3", "37.0", "0.27", "", "4", "45.0", "0.29", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500603, "raw": ["500603", "020007", "0", "2019/Feb/22 09:35", "Titon Hardware Ltd", "Titon", "CME3 Q Plus HA", "", "2019", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.18", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.19", "", "5", "53.0", "0.21", "", "6", "61.0", "0.25"]} +{"pcdb_id": 500604, "raw": ["500604", "020007", "0", "2019/Feb/22 09:35", "Titon Hardware Ltd", "Titon", "CME3 Q Plus HA", "", "2019", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.27", "", "2", "29.0", "0.25", "", "3", "37.0", "0.27", "", "4", "45.0", "0.29", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500605, "raw": ["500605", "020007", "0", "2019/Feb/22 09:32", "Titon Hardware Ltd", "Titon", "CME3 Q Plus HA LS", "", "2019", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.18", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.19", "", "5", "53.0", "0.21", "", "6", "61.0", "0.25"]} +{"pcdb_id": 500606, "raw": ["500606", "020007", "0", "2019/Feb/22 09:32", "Titon Hardware Ltd", "Titon", "CME3 Q Plus HA LS", "", "2019", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.27", "", "2", "29.0", "0.25", "", "3", "37.0", "0.27", "", "4", "45.0", "0.29", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500607, "raw": ["500607", "020070", "0", "2019/Feb/20 13:45", "Soler and Palau Ltd", "S & P", "OZEO FLAT H Ecowatt", "9050344601 - 31/2018", "2018", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.42", "", "2", "29.0", "0.35", "", "3", "37.0", "0.34", "", "4", "45.0", "0.34", "", "5", "53.0", "0.35"]} +{"pcdb_id": 500608, "raw": ["500608", "020070", "0", "2019/Feb/20 13:44", "Soler and Palau Ltd", "S & P", "OZEO H Ecowatt 2", "9050473300 - 41/2018", "2018", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.26", "", "2", "29.0", "0.31", "", "3", "37.0", "0.33", "", "4", "45.0", "0.37", "", "5", "53.0", "0.41", "", "6", "61.0", "0.42"]} +{"pcdb_id": 500609, "raw": ["500609", "020073", "0", "2019/Apr/25 12:49", "Dantherm Air Handling A/S", "Dantherm", "HCV400 P1 – A – BP – RH", "", "2018", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.84", "93", "2", "29.0", "0.80", "93", "3", "37.0", "0.87", "92", "4", "45.0", "1.01", "92", "5", "53.0", "1.18", "91", "6", "61.0", "1.42", "91"]} +{"pcdb_id": 500610, "raw": ["500610", "020133", "0", "2019/Mar/26 12:25", "Comfortzone AB", "Comfortzone", "EX35", "", "2013", "current", "", "1", "1", "2", "1", "", "4", "1", "21.0", "0.75", "", "2", "29.0", "0.68", "", "3", "37.0", "0.71", "", "4", "45.0", "0.82"]} +{"pcdb_id": 500611, "raw": ["500611", "020133", "0", "2019/Mar/26 12:16", "Comfortzone AB", "Comfortzone", "EX50", "", "2013", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.75", "", "2", "29.0", "0.68", "", "3", "37.0", "0.71", "", "4", "45.0", "0.82", "", "5", "53.0", "0.92", "", "6", "61.0", "1.13"]} +{"pcdb_id": 500612, "raw": ["500612", "020042", "0", "2019/Apr/25 12:56", "Brink Climate Systems B.V.", "Brink", "Flair 325 4/0 R EU", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.55", "92", "2", "29.0", "0.52", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.63", "90", "5", "53.0", "0.73", "90", "6", "61.0", "0.86", "89", "7", "69.0", "1.00", "89"]} +{"pcdb_id": 500613, "raw": ["500613", "020042", "0", "2019/Apr/25 14:11", "Brink Climate Systems B.V.", "Brink", "Flair 400 4/0 R EU", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.48", "92", "2", "29.0", "0.49", "91", "3", "37.0", "0.53", "90", "4", "45.0", "0.63", "90", "5", "53.0", "0.74", "90", "6", "61.0", "0.90", "89", "7", "69.0", "1.08", "89"]} +{"pcdb_id": 500614, "raw": ["500614", "020003", "0", "2019/Apr/15 14:49", "The Nuaire Group", "Nuaire", "MEV-X", "", "2019", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.25", "", "2", "29.0", "0.22", "", "3", "37.0", "0.23", "", "4", "45.0", "0.29", "", "5", "53.0", "0.35", "", "6", "61.0", "0.43"]} +{"pcdb_id": 500615, "raw": ["500615", "020027", "0", "2019/May/02 12:07", "Brink Climate Systems BV", "EnviroVent", "EnergiSava 300B", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.66", "89", "2", "29.0", "0.62", "87", "3", "37.0", "0.66", "86", "4", "45.0", "0.74", "85", "5", "53.0", "0.86", "84", "6", "61.0", "1.04", "83", "7", "69.0", "1.21", "83"]} +{"pcdb_id": 500616, "raw": ["500616", "020007", "0", "2019/May/08 11:44", "Titon Hardware Ltd", "Titon", "HRV20 HE Q Plus Eco", "", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.52", "91", "2", "29.0", "0.53", "91", "3", "37.0", "0.58", "90", "4", "45.0", "0.68", "90", "5", "53.0", "0.79", "89", "6", "61.0", "0.95", "89", "7", "69.0", "1.15", "88"]} +{"pcdb_id": 500617, "raw": ["500617", "020038", "0", "2019/Jun/10 13:52", "Titon Hardware Ltd", "Beam", "AXCO MVHR C170", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.52", "91", "2", "29.0", "0.53", "91", "3", "37.0", "0.58", "90", "4", "45.0", "0.68", "90", "5", "53.0", "0.79", "89", "6", "61.0", "0.95", "89", "7", "69.0", "1.15", "88"]} +{"pcdb_id": 500618, "raw": ["500618", "020084", "0", "2019/Aug/12 11:52", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 250 flex", "", "2019", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.84", "93", "2", "29.0", "0.80", "93", "3", "37.0", "0.87", "92", "4", "45.0", "1.01", "92", "5", "53.0", "1.18", "91", "6", "61.0", "1.42", "91"]} +{"pcdb_id": 500619, "raw": ["500619", "020124", "0", "2019/Aug/08 14:47", "Orca Energija d.o.o.", "ORCA", "MAXI 350", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.62", "80", "2", "29.0", "0.56", "79", "3", "37.0", "0.61", "78", "4", "45.0", "0.72", "77", "5", "53.0", "0.87", "76", "6", "61.0", "1.07", "75", "7", "69.0", "1.32", "72"]} +{"pcdb_id": 500620, "raw": ["500620", "020124", "0", "2019/Aug/08 14:42", "Orca Energija d.o.o.", "ORCA", "MAXI 550", "", "2017", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.63", "88", "3", "37.0", "0.63", "88", "4", "45.0", "0.72", "88", "5", "53.0", "0.84", "88", "6", "61.0", "0.98", "87", "7", "69.0", "1.16", "87"]} +{"pcdb_id": 500621, "raw": ["500621", "020004", "0", "2019/Sep/12 11:47", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 155 WM", "", "2019", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.56", "92", "2", "29.0", "0.69", "91", "3", "37.0", "0.88", "90", "4", "45.0", "1.15", "89", "5", "53.0", "1.41", "88"]} +{"pcdb_id": 500622, "raw": ["500622", "020004", "0", "2019/Sep/12 11:51", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 155 WMe", "", "2019", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.50", "90", "2", "29.0", "0.62", "88", "3", "37.0", "0.80", "86", "4", "45.0", "1.05", "85", "5", "53.0", "1.31", "84"]} +{"pcdb_id": 500623, "raw": ["500623", "020004", "0", "2019/Sep/12 11:55", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 155 CM", "", "2019", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.57", "92", "2", "29.0", "0.71", "91", "3", "37.0", "0.92", "90"]} +{"pcdb_id": 500624, "raw": ["500624", "020004", "0", "2019/Sep/13 09:35", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir 185 WM", "", "2019", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.57", "92", "2", "29.0", "0.64", "91", "3", "37.0", "0.76", "91", "4", "45.0", "0.96", "91"]} +{"pcdb_id": 500625, "raw": ["500625", "020003", "0", "2019/Nov/27 11:11", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO5", "", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.63", "90", "2", "29.0", "0.61", "89", "3", "37.0", "0.68", "88", "4", "45.0", "0.79", "87", "5", "53.0", "0.91", "86", "6", "61.0", "1.09", "85", "7", "69.0", "1.27", "85"]} +{"pcdb_id": 500626, "raw": ["500626", "020078", "0", "2020/Jan/03 10:55", "Systemair Fans & Spares Ltd", "Systemair", "VTR 100/B LITE", "", "2019", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "1.18", "77", "2", "29.0", "1.16", "77", "3", "37.0", "1.27", "76", "4", "45.0", "1.50", "75"]} +{"pcdb_id": 500627, "raw": ["500627", "020139", "0", "2024/Dec/11 17:35", "Brink Climate Systems", "Ubbink", "Vigor W400 4/0 R GB", "", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.48", "92", "2", "29.0", "0.49", "91", "3", "37.0", "0.53", "90", "4", "45.0", "0.63", "90", "5", "53.0", "0.74", "90", "6", "61.0", "0.90", "89", "7", "69.0", "1.08", "89"]} +{"pcdb_id": 500628, "raw": ["500628", "020139", "0", "2024/Dec/11 17:36", "Brink Climate Systems", "Ubbink", "Vigor W325 4/0 R GB", "", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.55", "92", "2", "29.0", "0.52", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.63", "90", "5", "53.0", "0.73", "90", "6", "61.0", "0.86", "89", "7", "69.0", "1.00", "89"]} +{"pcdb_id": 500629, "raw": ["500629", "020139", "0", "2025/Jan/06 11:36", "Brink Climate Systems", "Ubbink", "F150", "", "2013", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.75", "88", "2", "29.0", "0.86", "85", "3", "37.0", "1.04", "84"]} +{"pcdb_id": 500630, "raw": ["500630", "020139", "0", "2024/Nov/14 16:05", "Ubbink UK Ltd", "Ubbink", "F300", "", "2013", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.60", "90", "2", "29.0", "0.62", "90", "3", "37.0", "0.71", "87", "4", "45.0", "0.83", "86", "5", "53.0", "0.97", "85", "6", "61.0", "1.12", "84"]} +{"pcdb_id": 500631, "raw": ["500631", "020003", "0", "2020/Mar/05 13:59", "The Nuaire Group", "Nuaire", "MEV-ECO", "", "2019", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.17", "", "3", "37.0", "0.20", "", "4", "45.0", "0.24", "", "5", "53.0", "0.30", "", "6", "61.0", "0.35"]} +{"pcdb_id": 500632, "raw": ["500632", "020141", "0", "2020/Mar/13 15:54", "Blauberg", "WiseAir", "EC 350", "", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.66", "85", "2", "29.0", "0.60", "84", "3", "37.0", "0.63", "84", "4", "45.0", "0.71", "83", "5", "53.0", "0.82", "83", "6", "61.0", "0.97", "83", "7", "69.0", "1.16", "82"]} +{"pcdb_id": 500633, "raw": ["500633", "020141", "0", "2020/Mar/13 15:51", "Blauberg", "WiseAir", "EC 550", "", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "84", "2", "29.0", "0.65", "85", "3", "37.0", "0.68", "85", "4", "45.0", "0.76", "85", "5", "53.0", "0.88", "85", "6", "61.0", "1.02", "85", "7", "69.0", "1.20", "84"]} +{"pcdb_id": 500634, "raw": ["500634", "020050", "0", "2020/May/05 12:13", "Aereco", "Aereco", "V5S Premium", "V5S1131 Semi-Rigid", "2014", "current", "", "1", "0", "2", "1", "", "5", "1", "21.0", "0.40", "", "2", "29.0", "0.35", "", "3", "37.0", "0.38", "", "4", "45.0", "0.36", "", "5", "53.0", "0.36"]} +{"pcdb_id": 500635, "raw": ["500635", "020143", "0", "2020/May/11 12:37", "Thessla Green Sp. z o. o.", "THESSLAGREEN", "AirPack 4", "300h", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.78", "89", "2", "29.0", "0.76", "89", "3", "37.0", "0.78", "89", "4", "45.0", "0.87", "88", "5", "53.0", "0.99", "87", "6", "61.0", "1.15", "86", "7", "69.0", "1.36", "84"]} +{"pcdb_id": 500636, "raw": ["500636", "020143", "0", "2020/May/11 12:33", "Thessla Green Sp. z o. o.", "THESSLAGREEN", "AirPack 4", "400h", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.79", "90", "2", "29.0", "0.75", "90", "3", "37.0", "0.78", "89", "4", "45.0", "0.89", "89", "5", "53.0", "1.01", "88", "6", "61.0", "1.19", "87", "7", "69.0", "1.40", "86"]} +{"pcdb_id": 500637, "raw": ["500637", "020143", "0", "2020/May/11 12:28", "Thessla Green Sp. z o. o.", "THESSLAGREEN", "AirPack 4", "500h", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.81", "90", "2", "29.0", "0.73", "89", "3", "37.0", "0.78", "89", "4", "45.0", "0.88", "88", "5", "53.0", "0.99", "88", "6", "61.0", "1.14", "87", "7", "69.0", "1.37", "87"]} +{"pcdb_id": 500638, "raw": ["500638", "020031", "0", "2020/Jun/23 10:32", "GENVEX AB", "NIBE", "ERS S10-400", "", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.79", "89", "2", "29.0", "0.73", "89", "3", "37.0", "0.75", "88", "4", "45.0", "0.83", "87", "5", "53.0", "0.94", "87", "6", "61.0", "1.09", "86", "7", "69.0", "1.28", "86"]} +{"pcdb_id": 500639, "raw": ["500639", "020146", "0", "2020/Aug/17 10:51", "BUVA rationele bouwproducten BV", "BUVA Ratione Bouwprodukten BV", "EcoStream", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.57", "93", "2", "29.0", "0.53", "92", "3", "37.0", "0.56", "92", "4", "45.0", "0.64", "91", "5", "53.0", "0.73", "91", "6", "61.0", "0.87", "90", "7", "69.0", "1.02", "89"]} +{"pcdb_id": 500640, "raw": ["500640", "020145", "0", "2023/Aug/25 15:17", "KERS Innovations UK Ltd", "KERS", "KERS MEV-W230", "", "2014", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.51", "", "2", "29.0", "0.57", "", "3", "37.0", "0.65", "", "4", "45.0", "0.84", "", "5", "53.0", "1.01", "", "6", "61.0", "1.26"]} +{"pcdb_id": 500641, "raw": ["500641", "020017", "0", "2020/Sep/16 10:06", "Airflow Developments Ltd", "Airflow", "Duplexvent DV65 Entro-V", "", "2019", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "1.07", "81", "2", "29.0", "1.12", "81", "3", "37.0", "1.24", "81"]} +{"pcdb_id": 500642, "raw": ["500642", "020017", "0", "2020/Sep/16 09:48", "Airflow Developments Ltd", "Airflow", "Duplexvent DV82 Entro-V", "", "2019", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.95", "79", "2", "29.0", "0.92", "80", "3", "37.0", "0.98", "80", "4", "45.0", "1.13", "80", "5", "53.0", "1.35", "80", "6", "61.0", "1.64", "80"]} +{"pcdb_id": 500643, "raw": ["500643", "020017", "0", "2020/Sep/16 09:40", "Airflow Developments Ltd", "Airflow", "Duplexvent DV130 Entro-V", "", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.68", "83", "2", "29.0", "0.64", "83", "3", "37.0", "0.67", "83", "4", "45.0", "0.79", "84", "5", "53.0", "0.92", "83", "6", "61.0", "1.11", "83", "7", "69.0", "1.35", "83"]} +{"pcdb_id": 500644, "raw": ["500644", "020002", "0", "2020/Nov/03 14:46", "Vent Axia Ltd", "Vent Axia", "MVDC - MS (2020)", "437634C", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.15", "", "2", "29.0", "0.14", "", "3", "37.0", "0.16", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500645, "raw": ["500645", "020002", "0", "2020/Nov/03 14:57", "Vent Axia Ltd", "Vent Axia", "MVDC - MSH (2020)", "443298B", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.15", "", "2", "29.0", "0.14", "", "3", "37.0", "0.16", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500646, "raw": ["500646", "020002", "0", "2020/Oct/22 15:10", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent H", "445655B", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500647, "raw": ["500647", "020002", "0", "2020/Oct/22 15:10", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent HX", "495360", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500648, "raw": ["500648", "020002", "0", "2020/Oct/22 15:12", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent HXCO2", "495361", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500649, "raw": ["500649", "020002", "0", "2020/Oct/22 15:13", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent Plus H", "407849A", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500650, "raw": ["500650", "020002", "0", "2020/Oct/22 14:53", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent Plus HX", "495362", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500651, "raw": ["500651", "020002", "0", "2020/Oct/22 15:05", "Vent Axia Ltd", "Vent Axia", "Sentinel Multivent Plus HXCO2", "495363", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500674, "raw": ["500674", "020018", "0", "2020/Dec/08 14:21", "Vent-Axia", "National Ventilation", "MON-MEVDC400", "", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.15", "", "2", "29.0", "0.14", "", "3", "37.0", "0.16", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500675, "raw": ["500675", "020018", "0", "2020/Dec/08 14:46", "Vent-Axia", "National Ventilation", "MON-MEVDCH400", "", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.15", "", "2", "29.0", "0.14", "", "3", "37.0", "0.16", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500676, "raw": ["500676", "020048", "0", "2020/Dec/08 14:55", "Vent-Axia", "Manrose", "MANI2000B", "", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.15", "", "2", "29.0", "0.14", "", "3", "37.0", "0.16", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500677, "raw": ["500677", "020041", "0", "2020/Dec/10 10:26", "Polypipe Ltd", "Domus Ventilation", "HRXE-HERA", "", "2020", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.52", "90", "2", "29.0", "0.59", "89", "3", "37.0", "0.77", "87", "4", "45.0", "1.00", "86", "5", "53.0", "1.23", "86"]} +{"pcdb_id": 500678, "raw": ["500678", "020041", "0", "2020/Dec/10 10:39", "Polypipe Ltd", "Domus Ventilation", "HRXE-AURA", "", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.50", "90", "2", "29.0", "0.53", "90", "3", "37.0", "0.60", "89", "4", "45.0", "0.75", "88", "5", "53.0", "0.92", "88", "6", "61.0", "1.10", "87", "7", "69.0", "1.36", "87"]} +{"pcdb_id": 500679, "raw": ["500679", "020042", "0", "2020/Nov/20 10:43", "Brink Climate Systems B.V.", "Brink", "Renovent Excellent P300", "", "2017", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.66", "89", "2", "29.0", "0.62", "87", "3", "37.0", "0.66", "86", "4", "45.0", "0.74", "85", "5", "53.0", "0.86", "84", "6", "61.0", "1.04", "83", "7", "69.0", "1.21", "83"]} +{"pcdb_id": 500680, "raw": ["500680", "020027", "0", "2020/Dec/23 11:35", "EnviroVent Ltd", "EnviroVent", "MEVS2", "", "2020", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.30", "", "2", "29.0", "0.28", "", "3", "37.0", "0.29", "", "4", "45.0", "0.32", "", "5", "53.0", "0.36", "", "6", "61.0", "0.38"]} +{"pcdb_id": 500681, "raw": ["500681", "020070", "0", "2021/Jan/25 19:03", "S&P UK Ventilation Systems Ltd", "S & P", "OZEO E Ecowatt 2", "9050514500 - 38/2020", "2018", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.19", "", "2", "29.0", "0.17", "", "3", "37.0", "0.17", "", "4", "45.0", "0.19", "", "5", "53.0", "0.21", "", "6", "61.0", "0.24"]} +{"pcdb_id": 500682, "raw": ["500682", "020150", "0", "2020/Dec/23 10:06", "RDZ SpA", "RDZ by Altecnic", "CHR 100 FC", "", "2020", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "1.08", "86", "2", "29.0", "1.17", "85", "3", "37.0", "1.48", "83"]} +{"pcdb_id": 500683, "raw": ["500683", "020150", "0", "2020/Dec/23 10:02", "RDZ SpA", "RDZ by Altecnic", "CHR 200 FC", "", "2020", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.78", "87", "2", "29.0", "0.79", "86", "3", "37.0", "0.95", "84", "4", "45.0", "1.14", "83", "5", "53.0", "1.47", "82"]} +{"pcdb_id": 500684, "raw": ["500684", "020150", "0", "2020/Dec/23 09:58", "RDZ SpA", "RDZ by Altecnic", "CHR 400 FC", "", "2020", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.66", "91", "2", "29.0", "0.70", "90", "3", "37.0", "0.81", "89", "4", "45.0", "0.95", "87", "5", "53.0", "1.18", "87", "6", "61.0", "1.44", "86"]} +{"pcdb_id": 500685, "raw": ["500685", "020150", "0", "2020/Dec/23 09:31", "RDZ SpA", "RDZ by Altecnic", "WHR 200", "", "2020", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.76", "84", "2", "29.0", "0.79", "84", "3", "37.0", "0.88", "83", "4", "45.0", "1.05", "82", "5", "53.0", "1.23", "81", "6", "61.0", "1.48", "81"]} +{"pcdb_id": 500686, "raw": ["500686", "020047", "0", "2021/Mar/25 09:26", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-350CZPVU-R-E", "", "2020", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.86", "90", "2", "29.0", "0.80", "90", "3", "37.0", "0.84", "89", "4", "45.0", "0.96", "89", "5", "53.0", "1.08", "88", "6", "61.0", "1.28", "87"]} +{"pcdb_id": 500687, "raw": ["500687", "020047", "0", "2021/Mar/25 09:26", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-350CZPVU-L-E", "", "2020", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.86", "90", "2", "29.0", "0.80", "90", "3", "37.0", "0.84", "89", "4", "45.0", "0.96", "89", "5", "53.0", "1.08", "88", "6", "61.0", "1.28", "87"]} +{"pcdb_id": 500688, "raw": ["500688", "020047", "0", "2021/Mar/25 09:26", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-250CZPVU-R-E", "", "2020", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "90", "2", "29.0", "0.67", "89", "3", "37.0", "0.79", "88", "4", "45.0", "1.00", "87", "5", "53.0", "1.19", "87"]} +{"pcdb_id": 500689, "raw": ["500689", "020047", "0", "2021/Mar/25 09:26", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-250CZPVU-L-E", "", "2020", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "90", "2", "29.0", "0.67", "89", "3", "37.0", "0.79", "88", "4", "45.0", "1.00", "87", "5", "53.0", "1.19", "87"]} +{"pcdb_id": 500690, "raw": ["500690", "020070", "0", "2022/Aug/30 11:23", "Soler and Palau Ltd", "S & P", "OZEO E Ecowatt 2", "9050514500 38/2020 Semi-Rigid", "2018", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.25", "", "2", "29.0", "0.25", "", "3", "37.0", "0.26", "", "4", "45.0", "0.29", "", "5", "53.0", "0.32", "", "6", "61.0", "0.39"]} +{"pcdb_id": 500691, "raw": ["500691", "020030", "0", "2021/Mar/16 17:29", "Aldes", "Aldes", "EASYHOME HYGRO COMPACT PREMIUM MW SP 90 semi rigid", "", "2019", "current", "", "1", "0", "2", "2", "", "4", "1", "21.0", "0.24", "", "2", "29.0", "0.19", "", "3", "37.0", "0.19", "", "4", "45.0", "0.28"]} +{"pcdb_id": 500692, "raw": ["500692", "020030", "0", "2021/Mar/17 11:07", "Aldes", "Aldes", "EASYHOME HYGRO COMPACT PREMIUM MW SP 75 semi rigid", "", "2019", "current", "", "1", "0", "2", "2", "", "4", "1", "21.0", "0.24", "", "2", "29.0", "0.20", "", "3", "37.0", "0.20", "", "4", "45.0", "0.28"]} +{"pcdb_id": 500693, "raw": ["500693", "020011", "0", "2021/Nov/11 10:29", "Vectaire Ltd", "Vectaire", "MIDIBY-AT", "", "2018", "current", "", "3", "0", "2", "1", "4", "4", "1", "21.0", "0.51", "93", "2", "29.0", "0.61", "91", "3", "37.0", "0.75", "90", "4", "45.0", "0.92", "89"]} +{"pcdb_id": 500694, "raw": ["500694", "020011", "0", "2021/Nov/15 09:54", "Vectaire Ltd", "Vectaire", "MAXIBY-AT", "", "2018", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "0.45", "92", "2", "29.0", "0.47", "92", "3", "37.0", "0.54", "91", "4", "45.0", "0.66", "90", "5", "53.0", "0.80", "90", "6", "61.0", "0.99", "89", "7", "69.0", "1.21", "89"]} +{"pcdb_id": 500695, "raw": ["500695", "020011", "0", "2021/Mar/01 17:24", "Vectaire Ltd", "Vectaire", "MAXIPLUS/BY/AT", "", "2018", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "0.56", "89", "2", "29.0", "0.47", "89", "3", "37.0", "0.50", "88", "4", "45.0", "0.56", "87", "5", "53.0", "0.66", "86", "6", "61.0", "0.78", "85", "7", "69.0", "0.94", "84"]} +{"pcdb_id": 500696, "raw": ["500696", "020030", "0", "2021/Mar/17 11:07", "Aldes", "Aldes", "EASYHOME HYGRO PREMIUM SP 75 semi rgid", "", "2018", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.23", "", "2", "29.0", "0.20", "", "3", "37.0", "0.19", "", "4", "45.0", "0.28", "", "5", "53.0", "0.28", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500697, "raw": ["500697", "020030", "0", "2021/Mar/17 11:08", "Aldes", "Aldes", "EASYHOME HYGRO PREMIUM SP 90 semi rigid", "", "2018", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.23", "", "2", "29.0", "0.20", "", "3", "37.0", "0.19", "", "4", "45.0", "0.28", "", "5", "53.0", "0.28", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500698, "raw": ["500698", "020042", "0", "2021/Feb/25 08:52", "Brink Climate Systems B.V.", "Brink", "Flair 225 4/0L EU", "", "2020", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.62", "92", "2", "29.0", "0.64", "91", "3", "37.0", "0.69", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.96", "88", "6", "61.0", "1.17", "88"]} +{"pcdb_id": 500699, "raw": ["500699", "020066", "0", "2021/Apr/26 08:53", "J Pichler GmbH", "Pichler", "08LG350V", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.65", "95", "2", "29.0", "0.60", "94", "3", "37.0", "0.62", "93", "4", "45.0", "0.70", "93", "5", "53.0", "0.76", "92", "6", "61.0", "0.90", "91", "7", "69.0", "1.06", "90"]} +{"pcdb_id": 500700, "raw": ["500700", "020066", "0", "2021/Apr/26 08:53", "J Pichler GmbH", "Pichler", "08LG450V", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.74", "94", "2", "29.0", "0.67", "95", "3", "37.0", "0.67", "94", "4", "45.0", "0.74", "93", "5", "53.0", "0.82", "92", "6", "61.0", "0.96", "91", "7", "69.0", "1.11", "90"]} +{"pcdb_id": 500701, "raw": ["500701", "020154", "0", "2021/Apr/26 12:14", "Vectaire", "Flakt Group", "EIS - 163", "", "2013", "current", "500366", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.45", "92", "2", "29.0", "0.47", "92", "3", "37.0", "0.54", "91", "4", "45.0", "0.66", "90", "5", "53.0", "0.80", "90", "6", "61.0", "0.99", "89", "7", "69.0", "1.21", "89"]} +{"pcdb_id": 500702, "raw": ["500702", "020154", "0", "2021/Apr/26 12:16", "Vectaire", "Flakt Group", "EIS-230", "", "2017", "current", "500530", "3", "1", "2", "1", "4", "7", "1", "21.0", "0.56", "89", "2", "29.0", "0.47", "89", "3", "37.0", "0.50", "88", "4", "45.0", "0.56", "87", "5", "53.0", "0.66", "86", "6", "61.0", "0.78", "85", "7", "69.0", "0.94", "84"]} +{"pcdb_id": 500703, "raw": ["500703", "020154", "0", "2021/Apr/26 12:17", "Vectaire", "Flakt Group", "EIS - Flakt Master 55", "", "2018", "current", "500577", "3", "0", "2", "1", "4", "3", "1", "21.0", "0.93", "79", "2", "29.0", "1.09", "78", "3", "37.0", "1.36", "77"]} +{"pcdb_id": 500704, "raw": ["500704", "020154", "0", "2021/Apr/26 12:17", "Vectaire", "Flakt Group", "EIS Flakt Master 80", "", "2015", "current", "500421", "3", "0", "2", "1", "1", "4", "1", "21.0", "0.75", "87", "2", "29.0", "0.89", "86", "3", "37.0", "1.00", "85", "4", "45.0", "1.37", "84"]} +{"pcdb_id": 500705, "raw": ["500705", "020154", "0", "2021/Apr/26 12:18", "Vectaire", "Flakt Group", "EIS Flakt Master 107", "", "2018", "current", "500575", "3", "0", "2", "1", "4", "5", "1", "21.0", "0.72", "87", "2", "29.0", "0.75", "85", "3", "37.0", "0.85", "84", "4", "45.0", "1.04", "83", "5", "53.0", "1.23", "82"]} +{"pcdb_id": 500706, "raw": ["500706", "020154", "0", "2021/Apr/26 12:18", "Vectaire", "Flakt Group", "EIS - MEV 100", "", "2011", "current", "500260", "4", "0", "2", "1", "", "5", "1", "21.0", "0.20", "", "2", "29.0", "0.26", "", "3", "37.0", "0.34", "", "4", "45.0", "0.44", "", "5", "53.0", "0.55"]} +{"pcdb_id": 500707, "raw": ["500707", "020154", "0", "2021/Apr/26 12:18", "Vectaire", "Flakt Group", "EIS - MEV 230", "", "2017", "current", "500566", "1", "0", "2", "1", "", "6", "1", "21.0", "0.28", "", "2", "29.0", "0.27", "", "3", "37.0", "0.28", "", "4", "45.0", "0.36", "", "5", "53.0", "0.42", "", "6", "61.0", "0.52"]} +{"pcdb_id": 500708, "raw": ["500708", "020154", "0", "2021/Apr/26 12:19", "Vectaire", "Flakt Group", "EIS-95", "", "2013", "current", "500367", "3", "0", "2", "1", "1", "4", "1", "21.0", "0.51", "93", "2", "29.0", "0.61", "91", "3", "37.0", "0.75", "90", "4", "45.0", "0.92", "89"]} +{"pcdb_id": 500709, "raw": ["500709", "020156", "0", "2021/Apr/26 10:25", "Passive House Systems (PHS) Gmbh", "Passive House Systems", "PHS MEV_H", "Model H", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.19", "", "2", "29.0", "0.18", "", "3", "37.0", "0.18", "", "4", "45.0", "0.22", "", "5", "53.0", "0.25", "", "6", "61.0", "0.30"]} +{"pcdb_id": 500710, "raw": ["500710", "020027", "0", "2021/Apr/26 10:37", "EnviroVent Ltd", "EnviroVent", "MEV160", "", "2021", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.19", "", "2", "29.0", "0.23", "", "3", "37.0", "0.27", "", "4", "45.0", "0.35", "", "5", "53.0", "0.44", "", "6", "61.0", "0.59"]} +{"pcdb_id": 500711, "raw": ["500711", "020027", "0", "2021/Apr/26 10:40", "EnviroVent Ltd", "EnviroVent", "MEV300", "", "2021", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.19", "", "2", "29.0", "0.20", "", "3", "37.0", "0.22", "", "4", "45.0", "0.25", "", "5", "53.0", "0.31", "", "6", "61.0", "0.36"]} +{"pcdb_id": 500712, "raw": ["500712", "020156", "0", "2021/Apr/26 10:25", "Passive House systems (PHS) Gmbh", "Passive House Systems", "PHS MEV_H", "+75mm Semi-rigid", "2014", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.22", "", "2", "29.0", "0.21", "", "3", "37.0", "0.21", "", "4", "45.0", "0.23", "", "5", "53.0", "0.26", "", "6", "61.0", "0.30"]} +{"pcdb_id": 500716, "raw": ["500716", "020112", "0", "2021/May/24 10:24", "Blauberg UK Ltd", "Blauberg", "Komfort EC LB400", "S25", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.89", "91", "2", "29.0", "0.77", "91", "3", "37.0", "0.79", "90", "4", "45.0", "0.87", "90", "5", "53.0", "0.99", "89", "6", "61.0", "1.18", "89", "7", "69.0", "1.38", "88"]} +{"pcdb_id": 500717, "raw": ["500717", "020073", "0", "2021/Jun/22 17:02", "Dantherm A/S", "Dantherm", "HCV460 P2", "", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "89", "2", "29.0", "0.70", "88", "3", "37.0", "0.76", "88", "4", "45.0", "0.89", "87", "5", "53.0", "1.04", "85", "6", "61.0", "1.27", "85", "7", "69.0", "1.52", "84"]} +{"pcdb_id": 500718, "raw": ["500718", "020156", "0", "2021/Jun/22 17:04", "Passive House Systems (PHS) Gmbh", "Passive House Systems", "PHS MEV_H", "+90mm Semi-Rigid", "2021", "current", "500710", "1", "0", "2", "1", "", "6", "1", "21.0", "0.21", "", "2", "29.0", "0.20", "", "3", "37.0", "0.20", "", "4", "45.0", "0.22", "", "5", "53.0", "0.24", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500719, "raw": ["500719", "020156", "0", "2021/Jun/22 17:03", "Passive House Systems (PHS) Gmbh", "Passive House Systems", "PHS", "HRV550-H", "2021", "current", "500468", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.57", "93", "2", "29.0", "0.58", "92", "3", "37.0", "0.63", "91", "4", "45.0", "0.73", "90", "5", "53.0", "0.86", "90", "6", "61.0", "1.04", "89", "7", "69.0", "1.23", "88"]} +{"pcdb_id": 500720, "raw": ["500720", "020047", "0", "2021/Jul/26 17:20", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-500CZPVU-R-E", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "91", "2", "29.0", "0.72", "90", "3", "37.0", "0.74", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.91", "88", "6", "61.0", "1.09", "88", "7", "69.0", "1.24", "88"]} +{"pcdb_id": 500721, "raw": ["500721", "020047", "0", "2021/Jul/26 17:20", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-500CZPVU-L-E", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "91", "2", "29.0", "0.72", "90", "3", "37.0", "0.74", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.91", "88", "6", "61.0", "1.09", "88", "7", "69.0", "1.24", "88"]} +{"pcdb_id": 500722, "raw": ["500722", "020070", "0", "2021/Jul/26 13:50", "S&P UK Ventilation Systems Ltd", "S & P", "OZEO Flat H Ecowatt Semi Rigid", "9050344601", "2018", "current", "", "1", "0", "2", "2", "", "6", "1", "20.1", "0.38", "", "2", "28.0", "0.31", "", "3", "35.7", "0.27", "", "4", "43.1", "0.26", "", "5", "51.3", "0.28", "", "6", "59.3", "0.31"]} +{"pcdb_id": 500723, "raw": ["500723", "020027", "0", "2021/Jul/08 09:38", "EnviroVent Ltd", "EnviroVent", "MEV160FT with 75mm semi-rigid layout", "", "2021", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.21", "", "2", "29.0", "0.26", "", "3", "37.0", "0.33", "", "4", "45.0", "0.39", "", "5", "53.0", "0.54"]} +{"pcdb_id": 500724, "raw": ["500724", "020027", "0", "2021/Jul/08 09:39", "EnviroVent Ltd", "EnviroVent", "MEV160FT with 90mm semi-rigid layout", "", "2021", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.18", "", "2", "29.0", "0.22", "", "3", "37.0", "0.28", "", "4", "45.0", "0.35", "", "5", "53.0", "0.52"]} +{"pcdb_id": 500725, "raw": ["500725", "020027", "0", "2021/Jul/08 09:41", "EnviroVent Ltd", "EnviroVent", "MEV300FT with 75mm semi-rigid layout", "", "2021", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.22", "", "2", "29.0", "0.22", "", "3", "37.0", "0.23", "", "4", "45.0", "0.27", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500726, "raw": ["500726", "020027", "0", "2021/Jul/08 09:42", "EnviroVent Ltd", "EnviroVent", "MEV300FT with 90mm semi-rigid layout", "", "2021", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.20", "", "2", "29.0", "0.20", "", "3", "37.0", "0.23", "", "4", "45.0", "0.25", "", "5", "53.0", "0.34"]} +{"pcdb_id": 500727, "raw": ["500727", "020052", "0", "2021/Aug/18 15:40", "Brook Design Hardware Ltd", "Brookvent", "AirCycle 4.1", "", "2021", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "0.45", "93", "2", "29.0", "0.45", "92", "3", "37.0", "0.49", "91", "4", "45.0", "0.58", "90", "5", "53.0", "0.67", "88", "6", "61.0", "0.83", "87", "7", "69.0", "0.99", "86"]} +{"pcdb_id": 500730, "raw": ["500730", "020039", "0", "2021/Sep/28 15:17", "H. Östberg AB", "Beam", "AXCO HERU 160", "", "2020", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "1.30", "73", "2", "29.0", "1.13", "77", "3", "37.0", "1.08", "79", "4", "45.0", "1.09", "80", "5", "53.0", "1.15", "81", "6", "61.0", "1.25", "81", "7", "69.0", "1.38", "81"]} +{"pcdb_id": 500731, "raw": ["500731", "020039", "0", "2021/Sep/28 15:16", "H. Östberg AB", "Beam", "AXCO HERU 200", "", "2020", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "1.43", "69", "2", "29.0", "1.18", "72", "3", "37.0", "1.08", "74", "4", "45.0", "1.10", "76", "5", "53.0", "1.08", "78", "6", "61.0", "1.17", "79", "7", "69.0", "1.27", "79"]} +{"pcdb_id": 500732, "raw": ["500732", "020016", "0", "2021/Sep/29 10:22", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "FRPRO", "", "2020", "current", "090850", "1", "0", "2", "2", "", "6", "1", "21.0", "0.31", "", "2", "29.0", "0.30", "", "3", "37.0", "0.28", "", "4", "45.0", "0.29", "", "5", "53.0", "0.30", "", "6", "61.0", "0.34"]} +{"pcdb_id": 500733, "raw": ["500733", "020007", "0", "2021/Oct/15 10:13", "Titon Hardware Ltd", "Titon", "HRV1.65 Q Plus Eco", "", "2021", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.46", "89", "2", "29.0", "0.54", "87", "3", "37.0", "0.65", "86", "4", "45.0", "0.84", "84", "5", "53.0", "1.05", "83", "6", "61.0", "1.27", "82"]} +{"pcdb_id": 500734, "raw": ["500734", "020087", "0", "2021/Nov/18 16:39", "Panasonic HVAC UK Ltd", "Panasonic", "PAW-A2W-VENTA", "-R & - L", "2019", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "1.13", "72", "2", "29.0", "1.09", "76", "3", "37.0", "1.17", "77", "4", "45.0", "1.34", "78", "5", "53.0", "1.51", "78"]} +{"pcdb_id": 500735, "raw": ["500735", "020145", "0", "2023/Aug/25 15:19", "KERS Innovations UK Ltd", "KERS", "KERS MEV-W200SL", "", "2015", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.51", "", "2", "29.0", "0.57", "", "3", "37.0", "0.65", "", "4", "45.0", "0.84", "", "5", "53.0", "1.01", "", "6", "61.0", "1.26"]} +{"pcdb_id": 500736, "raw": ["500736", "020003", "0", "2021/Dec/16 09:23", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO2", "MRXBOXAB-ECO2B", "2021", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.55", "90", "2", "29.0", "0.65", "89", "3", "37.0", "0.80", "87", "4", "45.0", "1.01", "86", "5", "53.0", "1.26", "86"]} +{"pcdb_id": 500738, "raw": ["500738", "020163", "0", "2022/Mar/02 16:46", "Titon Hardware Ltd", "Ventiza", "HRU360VL/R", "", "2018", "current", "500567", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.51", "89", "2", "29.0", "0.58", "87", "3", "37.0", "0.70", "86", "4", "45.0", "0.90", "84", "5", "53.0", "1.07", "83", "6", "61.0", "1.34", "82"]} +{"pcdb_id": 500739, "raw": ["500739", "020163", "0", "2022/Mar/02 16:45", "Titon Hardware Ltd.", "Ventiza", "HRU220VL/R", "", "2015", "current", "500413", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.71", "87", "2", "29.0", "0.92", "85", "3", "37.0", "1.19", "85", "4", "45.0", "1.55", "84"]} +{"pcdb_id": 500740, "raw": ["500740", "020163", "0", "2022/Feb/28 09:57", "Titon Hardware Ltd.", "Ventiza", "HRU400VL", "", "2015", "current", "500423", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.55", "90", "2", "29.0", "0.57", "90", "3", "37.0", "0.65", "89", "4", "45.0", "0.76", "88", "5", "53.0", "0.90", "87", "6", "61.0", "1.09", "86", "7", "69.0", "1.27", "85"]} +{"pcdb_id": 500741, "raw": ["500741", "020007", "0", "2022/Mar/29 15:32", "Titon Hardware Ltd", "Titon", "HRV1.3 Q Plus Eco", "", "2021", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.75", "87", "2", "29.0", "0.97", "85", "3", "37.0", "1.28", "85", "4", "45.0", "1.66", "84"]} +{"pcdb_id": 500742, "raw": ["500742", "020007", "0", "2022/Mar/29 12:32", "Titon Hardware Ltd", "Titon", "CME2.1 Q Plus CH", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.20", "", "4", "45.0", "0.22", "", "5", "53.0", "0.28", "", "6", "61.0", "0.33"]} +{"pcdb_id": 500743, "raw": ["500743", "020007", "0", "2022/Mar/29 12:33", "Titon Hardware Ltd", "Titon", "CME2.1 Q Plus A", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.20", "", "4", "45.0", "0.22", "", "5", "53.0", "0.28", "", "6", "61.0", "0.33"]} +{"pcdb_id": 500744, "raw": ["500744", "020007", "0", "2022/Mar/29 12:33", "Titon Hardware Ltd", "Titon", "CME2.1 Q Plus HA", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.20", "", "4", "45.0", "0.22", "", "5", "53.0", "0.28", "", "6", "61.0", "0.33"]} +{"pcdb_id": 500745, "raw": ["500745", "020007", "0", "2022/Mar/29 12:33", "Titon Hardware Ltd", "Titon", "CME2.1 Q Plus HALS", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.18", "", "3", "37.0", "0.20", "", "4", "45.0", "0.22", "", "5", "53.0", "0.28", "", "6", "61.0", "0.33"]} +{"pcdb_id": 500746, "raw": ["500746", "020007", "0", "2022/Mar/29 12:33", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus CH", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.20", "", "5", "53.0", "0.23", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500747, "raw": ["500747", "020007", "0", "2022/Mar/29 12:33", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus CH", "", "2022", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.25", "", "2", "29.0", "0.24", "", "3", "37.0", "0.24", "", "4", "45.0", "0.25", "", "5", "53.0", "0.29"]} +{"pcdb_id": 500748, "raw": ["500748", "020007", "0", "2022/Mar/29 12:33", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus A", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.20", "", "5", "53.0", "0.23", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500749, "raw": ["500749", "020007", "0", "2022/Mar/29 12:33", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus A", "", "2022", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.25", "", "2", "29.0", "0.24", "", "3", "37.0", "0.24", "", "4", "45.0", "0.25", "", "5", "53.0", "0.29"]} +{"pcdb_id": 500750, "raw": ["500750", "020007", "0", "2022/Mar/29 12:34", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus HA", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.20", "", "5", "53.0", "0.23", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500751, "raw": ["500751", "020007", "0", "2022/Mar/29 12:34", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus HA", "", "2022", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.25", "", "2", "29.0", "0.24", "", "3", "37.0", "0.24", "", "4", "45.0", "0.25", "", "5", "53.0", "0.29"]} +{"pcdb_id": 500752, "raw": ["500752", "020007", "0", "2022/Mar/29 12:31", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus HALS", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.20", "", "5", "53.0", "0.23", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500753, "raw": ["500753", "020007", "0", "2022/Mar/29 12:31", "Titon Hardware Ltd", "Titon", "CME3.1 Q Plus HALS", "", "2022", "current", "", "1", "0", "2", "2", "", "5", "1", "21.0", "0.25", "", "2", "29.0", "0.24", "", "3", "37.0", "0.24", "", "4", "45.0", "0.25", "", "5", "53.0", "0.29"]} +{"pcdb_id": 500754, "raw": ["500754", "020017", "0", "2022/Mar/29 15:28", "Airflow Developments Ltd", "Airflow", "DV51 Adroit", "", "2020", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.78", "80", "2", "29.0", "0.84", "80", "3", "37.0", "0.99", "81", "4", "45.0", "1.24", "81"]} +{"pcdb_id": 500758, "raw": ["500758", "020130", "0", "2022/Feb/24 14:55", "Vero Duco N.V.", "Duco", "DucoBox Energy Comfort 325", "", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.59", "91", "3", "37.0", "0.63", "91", "4", "45.0", "0.77", "90", "5", "53.0", "0.89", "90", "6", "61.0", "1.07", "90", "7", "69.0", "1.30", "90"]} +{"pcdb_id": 500759, "raw": ["500759", "020016", "0", "2022/Apr/08 12:04", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "PA600HLI", "", "2014", "current", "090630", "3", "0", "2", "1", "1", "5", "1", "21.0", "0.62", "93", "2", "29.0", "0.67", "92", "3", "37.0", "0.76", "91", "4", "45.0", "0.92", "91", "5", "53.0", "1.12", "90"]} +{"pcdb_id": 500760, "raw": ["500760", "020084", "0", "2022/Apr/13 17:13", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 360 flex", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "89", "2", "29.0", "0.70", "88", "3", "37.0", "0.76", "88", "4", "45.0", "0.89", "87", "5", "53.0", "1.04", "85", "6", "61.0", "1.27", "85", "7", "69.0", "1.52", "84"]} +{"pcdb_id": 500761, "raw": ["500761", "020016", "0", "2022/Apr/29 13:30", "Jablotron Living Technology CZ s.r.o.", "ProAir", "Futura L", "", "2019", "current", "", "3", "1", "2", "1", "3", "4", "2", "29.0", "0.99", "92", "3", "37.0", "1.10", "91", "4", "45.0", "1.37", "90", "5", "53.0", "1.57", "89"]} +{"pcdb_id": 500762, "raw": ["500762", "020130", "0", "2022/Apr/28 13:51", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 325 - 1ZS - R", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500763, "raw": ["500763", "020130", "0", "2022/Apr/28 18:04", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 325 - 2ZS - L", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500764, "raw": ["500764", "020130", "0", "2022/Apr/28 18:04", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 325 - 2ZS - R", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500765, "raw": ["500765", "020130", "0", "2022/Apr/28 13:50", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 400 - 1ZS - L", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500766, "raw": ["500766", "020130", "0", "2022/Apr/28 13:50", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 400 - 1ZS - R", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500767, "raw": ["500767", "020130", "0", "2022/Apr/28 13:51", "Vero Duco N.V.", "Duco", "DucoBox Energy Premium 400 - 2ZS - R", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "91", "2", "29.0", "0.58", "91", "3", "37.0", "0.63", "90", "4", "45.0", "0.75", "89", "5", "53.0", "0.87", "89", "6", "61.0", "1.03", "88", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500768, "raw": ["500768", "020052", "0", "2022/Apr/29 13:36", "Brook Design Hardware Ltd", "Brookvent", "Airstream Ace", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.19", "", "2", "29.0", "0.19", "", "3", "37.0", "0.22", "", "4", "45.0", "0.28", "", "5", "53.0", "0.33", "", "6", "61.0", "0.41"]} +{"pcdb_id": 500770, "raw": ["500770", "020175", "0", "2022/May/10 13:26", "Brink Climate Systems B.V", "WOLF", "CWL-2-325 4/0", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.55", "92", "2", "29.0", "0.52", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.63", "90", "5", "53.0", "0.73", "90", "6", "61.0", "0.86", "89", "7", "69.0", "1.00", "89"]} +{"pcdb_id": 500771, "raw": ["500771", "020175", "0", "2022/May/10 13:31", "Brink Climate Systems B.V", "WOLF", "CWL-2-400 4/0", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.48", "92", "2", "29.0", "0.49", "91", "3", "37.0", "0.53", "90", "4", "45.0", "0.63", "90", "5", "53.0", "0.74", "90", "6", "61.0", "0.90", "89", "7", "69.0", "1.08", "89"]} +{"pcdb_id": 500772, "raw": ["500772", "020003", "0", "2022/Jan/17 14:50", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO2.5", "", "2021", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.48", "90", "2", "29.0", "0.54", "89", "3", "37.0", "0.64", "88", "4", "45.0", "0.82", "87", "5", "53.0", "1.01", "86", "6", "61.0", "1.30", "86"]} +{"pcdb_id": 500773, "raw": ["500773", "020003", "0", "2022/Jun/21 15:48", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO-LP2", "MRXBOXAB-EC0-LP2B", "2021", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.56", "78", "2", "29.0", "0.70", "79", "3", "37.0", "0.87", "79", "4", "45.0", "1.16", "79", "5", "53.0", "1.41", "79"]} +{"pcdb_id": 500774, "raw": ["500774", "020003", "0", "2022/Jun/21 15:36", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO3", "MRXBOXAB-ECO3B", "2021", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.56", "90", "2", "29.0", "0.58", "90", "3", "37.0", "0.65", "89", "4", "45.0", "0.79", "88", "5", "53.0", "0.95", "88", "6", "61.0", "1.15", "87"]} +{"pcdb_id": 500775, "raw": ["500775", "020031", "0", "2022/May/03 12:46", "Genvex AB", "NIBE", "ERS S10-500", "", "2021", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.82", "86", "2", "29.0", "0.87", "86", "3", "37.0", "0.87", "87", "4", "45.0", "1.04", "86", "5", "53.0", "1.21", "86", "6", "61.0", "1.46", "86"]} +{"pcdb_id": 500780, "raw": ["500780", "020112", "0", "2022/Jul/19 09:56", "Blauberg UK Ltd", "Blauberg", "Komfort EC D5B 180", "", "2018", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.90", "88", "2", "29.0", "1.00", "86", "3", "37.0", "1.20", "85"]} +{"pcdb_id": 500781, "raw": ["500781", "020047", "0", "2022/Aug/16 15:08", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-500CZPVU-L-E-1", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "91", "2", "29.0", "0.72", "90", "3", "37.0", "0.74", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.91", "88", "6", "61.0", "1.09", "88", "7", "69.0", "1.24", "88"]} +{"pcdb_id": 500782, "raw": ["500782", "020047", "0", "2022/Aug/16 15:08", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-500CZPVU-R-E-1", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "91", "2", "29.0", "0.72", "90", "3", "37.0", "0.74", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.91", "88", "6", "61.0", "1.09", "88", "7", "69.0", "1.24", "88"]} +{"pcdb_id": 500789, "raw": ["500789", "020041", "0", "2022/Oct/13 11:05", "The Nuaire Group", "Domus Ventilation", "HRXE-HERA", "-B", "2021", "current", "500677", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.55", "90", "2", "29.0", "0.65", "89", "3", "37.0", "0.80", "87", "4", "45.0", "1.01", "86", "5", "53.0", "1.26", "86"]} +{"pcdb_id": 500790, "raw": ["500790", "020041", "0", "2022/Oct/13 11:05", "The Nuaire Group", "Domus Ventilation", "HRXE-AURA", "-B", "2021", "current", "500678", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "90", "2", "29.0", "0.58", "90", "3", "37.0", "0.65", "89", "4", "45.0", "0.79", "88", "5", "53.0", "0.95", "88", "6", "61.0", "1.15", "87", "7", "69.0", "1.26", "87"]} +{"pcdb_id": 500791, "raw": ["500791", "020041", "0", "2022/Oct/13 11:01", "The Nuaire Group", "Domus Ventilation", "CMX-MULTI", "-B", "2021", "current", "500631", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.17", "", "3", "37.0", "0.20", "", "4", "45.0", "0.24", "", "5", "53.0", "0.30", "", "6", "61.0", "0.35"]} +{"pcdb_id": 500792, "raw": ["500792", "020041", "0", "2022/Oct/13 11:01", "The Nuaire Group", "Domus Ventilation", "HRXE-ZEUS", "", "2022", "current", "500502", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.62", "94", "2", "29.0", "0.62", "93", "3", "37.0", "0.66", "93", "4", "45.0", "0.79", "92", "5", "53.0", "0.94", "91", "6", "61.0", "1.15", "91", "7", "69.0", "1.41", "91"]} +{"pcdb_id": 500793, "raw": ["500793", "020003", "0", "2022/Oct/25 16:55", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO2", "MRXBOXAB-ECO2C", "2022", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.60", "90", "2", "29.0", "0.65", "89", "3", "37.0", "0.80", "87", "4", "45.0", "0.99", "86", "5", "53.0", "1.21", "86"]} +{"pcdb_id": 500794, "raw": ["500794", "020003", "0", "2022/Oct/25 16:56", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO3", "MRXBOXAB-ECO3C", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.54", "90", "2", "29.0", "0.55", "90", "3", "37.0", "0.62", "89", "4", "45.0", "0.76", "88", "5", "53.0", "0.89", "88", "6", "61.0", "1.11", "87", "7", "69.0", "1.32", "87"]} +{"pcdb_id": 500795, "raw": ["500795", "020003", "0", "2022/Oct/25 16:55", "The Nuaire Group", "Nuaire", "MEV-ECO", "MEV-ECO-C", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.22", "", "2", "29.0", "0.19", "", "3", "37.0", "0.23", "", "4", "45.0", "0.27", "", "5", "53.0", "0.32", "", "6", "61.0", "0.38"]} +{"pcdb_id": 500796, "raw": ["500796", "020016", "0", "2022/Oct/26 11:13", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "PA750LI", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.58", "86", "2", "29.0", "0.57", "87", "3", "37.0", "0.61", "88", "4", "45.0", "0.72", "88", "5", "53.0", "0.86", "88", "6", "61.0", "1.04", "88", "7", "69.0", "1.23", "87"]} +{"pcdb_id": 500797, "raw": ["500797", "020122", "0", "2022/Nov/16 14:10", "Joule Ltd", "JOULE", "Modul-AIR All-E", "HHH-AEHP-00001", "2021", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.41", "", "2", "29.0", "0.37", "", "3", "37.0", "0.36", "", "4", "45.0", "0.39", "", "5", "53.0", "0.44", "", "6", "61.0", "0.59"]} +{"pcdb_id": 500798, "raw": ["500798", "020070", "0", "2022/Dec/09 14:51", "S&P UK Ventilation Systems Ltd", "S & P", "SABIK 500", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.77", "84", "2", "29.0", "0.70", "84", "3", "37.0", "0.73", "85", "4", "45.0", "0.79", "84", "5", "53.0", "0.88", "85", "6", "61.0", "1.02", "85", "7", "69.0", "1.19", "85"]} +{"pcdb_id": 500799, "raw": ["500799", "020193", "0", "2023/Jan/30 17:27", "ALNOR Systemy Wentylacji Sp. z o.o.", "ALNOR", "HRU-PremAIR-450-CF", "", "2018", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.53", "80", "2", "29.0", "0.50", "82", "3", "37.0", "0.56", "83", "4", "45.0", "0.67", "83", "5", "53.0", "0.79", "83", "6", "61.0", "0.95", "83", "7", "69.0", "1.15", "84"]} +{"pcdb_id": 500800, "raw": ["500800", "020193", "0", "2023/Jan/30 17:29", "ALNOR Systemy Wentylacji Sp. z o.o.", "ALNOR", "HRU-SlimAIR-250", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "85", "2", "29.0", "0.59", "84", "3", "37.0", "0.69", "82", "4", "45.0", "0.84", "81", "5", "53.0", "1.02", "79", "6", "61.0", "1.26", "78", "7", "69.0", "1.53", "77"]} +{"pcdb_id": 500801, "raw": ["500801", "020197", "0", "2023/Feb/01 10:09", "Ebac Ltd", "Ebac Ventilation", "VA32RWH", "", "2022", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.74", "88", "2", "29.0", "0.81", "87", "3", "37.0", "0.93", "86", "4", "45.0", "1.14", "84", "5", "53.0", "1.38", "84"]} +{"pcdb_id": 500802, "raw": ["500802", "020027", "0", "2023/Feb/01 11:53", "Soler & Palau", "EnviroVent", "energiSava 200", "Issue B", "2022", "current", "", "3", "0", "2", "1", "4", "4", "1", "21.0", "0.79", "89", "2", "29.0", "0.91", "87", "3", "37.0", "1.14", "85", "4", "45.0", "1.43", "84"]} +{"pcdb_id": 500803, "raw": ["500803", "020004", "0", "2023/Feb/23 16:21", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir Flex 250", "", "2022", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.68", "95", "2", "29.0", "0.63", "94", "3", "37.0", "0.68", "92", "4", "45.0", "0.77", "91", "5", "53.0", "0.87", "90", "6", "61.0", "1.03", "88"]} +{"pcdb_id": 500804, "raw": ["500804", "020004", "0", "2023/Feb/23 16:17", "Zehnder Group UK Ltd", "Zehnder", "ComfoAir Flex 350", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "95", "2", "29.0", "0.58", "94", "3", "37.0", "0.62", "92", "4", "45.0", "0.73", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.00", "90", "7", "69.0", "1.16", "89"]} +{"pcdb_id": 500807, "raw": ["500807", "020027", "0", "2023/Mar/08 09:43", "S&P", "EnviroVent", "Sabik 350", "", "2022", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.68", "91", "2", "29.0", "0.65", "90", "3", "37.0", "0.71", "89", "4", "45.0", "0.84", "89", "5", "53.0", "0.94", "88", "6", "61.0", "1.10", "87"]} +{"pcdb_id": 500808, "raw": ["500808", "020002", "0", "2023/Mar/30 10:33", "Vent Axia Ltd", "Vent Axia", "MVDC-MSH Uniflex", "498502", "2022", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.15", "", "2", "29.0", "0.15", "", "3", "37.0", "0.16", "", "4", "45.0", "0.18"]} +{"pcdb_id": 500809, "raw": ["500809", "020042", "0", "2023/Mar/20 16:56", "Brink Climate Systems B.V.", "Brink", "Flair 600 4/0 L EU", "", "2022", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.59", "94", "3", "37.0", "0.61", "94", "4", "45.0", "0.69", "94", "5", "53.0", "0.78", "93", "6", "61.0", "0.93", "92", "7", "69.0", "1.09", "92"]} +{"pcdb_id": 500810, "raw": ["500810", "020042", "0", "2023/Mar/20 16:59", "Brink Climate Systems B.V.", "Brink", "Flair 450 4/0 L EU", "", "2022", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.59", "94", "3", "37.0", "0.61", "94", "4", "45.0", "0.69", "94", "5", "53.0", "0.78", "93", "6", "61.0", "0.93", "92", "7", "69.0", "1.09", "92"]} +{"pcdb_id": 500811, "raw": ["500811", "020002", "0", "2023/Mar/30 12:53", "Vent Axia Ltd", "Vent Axia", "Sentinel Kinetic FH", "408167A", "2023", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.47", "89", "2", "29.0", "0.54", "88", "3", "37.0", "0.65", "86", "4", "45.0", "0.84", "84", "5", "53.0", "1.01", "84"]} +{"pcdb_id": 500812, "raw": ["500812", "020060", "0", "2023/Apr/12 16:41", "Vent Axia", "Addvent", "AVWH2X", "", "2020", "current", "500644", "1", "0", "2", "1", "", "6", "1", "21.0", "0.15", "", "2", "29.0", "0.14", "", "3", "37.0", "0.16", "", "4", "45.0", "0.18", "", "5", "53.0", "0.21", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500813, "raw": ["500813", "020027", "0", "2023/Apr/13 11:24", "S&P UK Ventilation Systems Ltd", "EnviroVent", "Sabik 500", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.77", "84", "2", "29.0", "0.70", "84", "3", "37.0", "0.73", "85", "4", "45.0", "0.79", "85", "5", "53.0", "0.88", "85", "6", "61.0", "1.02", "85", "7", "69.0", "1.19", "85"]} +{"pcdb_id": 500835, "raw": ["500835", "020027", "0", "2023/May/24 16:06", "EnviroVent Ltd", "EnviroVent", "energiSava 260", "Issue A", "2023", "current", "", "3", "0", "2", "1", "4", "5", "1", "21.0", "0.66", "90", "2", "29.0", "0.75", "87", "3", "37.0", "0.94", "85", "4", "45.0", "1.21", "84", "5", "53.0", "1.49", "83"]} +{"pcdb_id": 500836, "raw": ["500836", "020027", "0", "2023/May/24 17:14", "EnviroVent Ltd", "EnviroVent", "energiSava 260", "Issue B", "2023", "current", "", "3", "0", "2", "1", "4", "5", "1", "21.0", "0.57", "88", "2", "29.0", "0.68", "85", "3", "37.0", "0.83", "83", "4", "45.0", "1.02", "82", "5", "53.0", "1.29", "81"]} +{"pcdb_id": 500837, "raw": ["500837", "020027", "0", "2023/Dec/08 10:41", "EnviroVent Ltd", "EnviroVent", "energiSava 260", "Issue C", "2023", "current", "", "3", "0", "2", "1", "4", "5", "1", "21.0", "0.58", "80", "2", "29.0", "0.66", "80", "3", "37.0", "0.82", "79", "4", "45.0", "1.06", "79", "5", "53.0", "1.28", "79"]} +{"pcdb_id": 500838, "raw": ["500838", "020122", "0", "2023/Jul/14 10:00", "Joule Ltd", "JOULE", "Modul-AIR All-E/ Green Comfort", "HHH-AEHP-00001/ HHH-GCPH-0001", "2021", "current", "", "4", "1", "2", "1", "", "7", "1", "21.0", "0.71", "", "2", "29.0", "0.65", "", "3", "37.0", "0.70", "", "4", "45.0", "0.76", "", "5", "53.0", "0.94", "", "6", "61.0", "1.11", "", "7", "69.0", "1.37"]} +{"pcdb_id": 500839, "raw": ["500839", "020007", "0", "2023/Jul/27 12:36", "Titon Hardware Ltd", "Titon", "HRV4 Q Plus Eco", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.42", "91", "2", "29.0", "0.43", "90", "3", "37.0", "0.50", "89", "4", "45.0", "0.60", "88", "5", "53.0", "0.73", "87", "6", "61.0", "0.90", "86", "7", "69.0", "1.08", "86"]} +{"pcdb_id": 500840, "raw": ["500840", "020007", "0", "2023/Jul/27 12:36", "Titon Hardware Ltd", "Titon", "HRV4.1 Q Plus Eco", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.43", "91", "2", "29.0", "0.47", "90", "3", "37.0", "0.55", "89", "4", "45.0", "0.68", "88", "5", "53.0", "0.83", "87", "6", "61.0", "1.03", "86", "7", "69.0", "1.24", "86"]} +{"pcdb_id": 500841, "raw": ["500841", "020007", "0", "2023/Jul/27 12:36", "Titon Hardware Ltd", "Titon", "HRV4.25 Q Plus Eco", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.47", "91", "2", "29.0", "0.49", "90", "3", "37.0", "0.54", "89", "4", "45.0", "0.65", "88", "5", "53.0", "0.75", "87", "6", "61.0", "0.91", "86", "7", "69.0", "1.09", "86"]} +{"pcdb_id": 500842, "raw": ["500842", "020211", "0", "2023/Aug/25 09:55", "Vasco bvba", "Vasco", "Vasco X350 - EasyFlow rigid oval duct system", "", "2017", "current", "", "3", "0", "2", "2", "3", "4", "1", "21.0", "0.85", "93", "2", "29.0", "0.78", "93", "3", "37.0", "0.83", "92", "4", "45.0", "0.89", "91"]} +{"pcdb_id": 500843, "raw": ["500843", "020211", "0", "2023/Aug/25 09:55", "Vasco bvba", "Vasco", "Vasco X350 - 90mm semi-rigid duct system", "", "2017", "current", "", "3", "0", "2", "2", "3", "6", "1", "21.0", "0.84", "93", "2", "29.0", "0.76", "93", "3", "37.0", "0.80", "92", "4", "45.0", "0.88", "91", "5", "53.0", "0.96", "90", "6", "61.0", "1.06", "90"]} +{"pcdb_id": 500844, "raw": ["500844", "020211", "0", "2023/Aug/25 09:54", "Vasco bvba", "Vasco", "X500 - EasyFlow rigid oval duct system", "", "2017", "current", "", "3", "0", "2", "2", "3", "4", "1", "21.0", "0.88", "92", "2", "29.0", "0.78", "91", "3", "37.0", "0.80", "90", "4", "45.0", "0.86", "90"]} +{"pcdb_id": 500845, "raw": ["500845", "020211", "0", "2023/Aug/25 09:54", "Vasco bvba", "Vasco", "X500 - 90mm semi-rigid duct system", "", "2017", "current", "", "3", "0", "2", "2", "3", "6", "1", "21.0", "0.79", "92", "2", "29.0", "0.73", "91", "3", "37.0", "0.76", "90", "4", "45.0", "0.87", "90", "5", "53.0", "0.90", "90", "6", "61.0", "1.00", "89"]} +{"pcdb_id": 500846, "raw": ["500846", "020002", "0", "2023/Sep/11 14:09", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq S", "499883", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.39", "93", "2", "29.0", "0.46", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.70", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.07", "89", "7", "69.0", "1.31", "89"]} +{"pcdb_id": 500847, "raw": ["500847", "020002", "0", "2023/Sep/11 14:08", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq M", "499632", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.41", "93", "2", "29.0", "0.41", "93", "3", "37.0", "0.46", "92", "4", "45.0", "0.55", "92", "5", "53.0", "0.66", "91", "6", "61.0", "0.81", "91", "7", "69.0", "1.00", "90"]} +{"pcdb_id": 500848, "raw": ["500848", "020002", "0", "2023/Sep/11 14:08", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq L", "499641", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "93", "2", "29.0", "0.53", "93", "3", "37.0", "0.56", "93", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "91", "6", "61.0", "0.84", "91", "7", "69.0", "1.01", "90"]} +{"pcdb_id": 500849, "raw": ["500849", "020130", "0", "2023/Sep/25 15:07", "Vero Duco N.V.", "Duco", "DucoBox Energy Comfort D400", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "90", "2", "29.0", "0.53", "90", "3", "37.0", "0.57", "90", "4", "45.0", "0.65", "89", "5", "53.0", "0.76", "89", "6", "61.0", "0.90", "88", "7", "69.0", "1.08", "88"]} +{"pcdb_id": 500850, "raw": ["500850", "020130", "0", "2023/Sep/25 15:01", "Vero Duco N.V.", "Duco", "DucoBox Energy Comfort Plus D350", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "90", "2", "29.0", "0.53", "90", "3", "37.0", "0.57", "90", "4", "45.0", "0.65", "89", "5", "53.0", "0.76", "89", "6", "61.0", "0.90", "88", "7", "69.0", "1.08", "88"]} +{"pcdb_id": 500851, "raw": ["500851", "020130", "0", "2023/Sep/25 14:53", "Vero Duco N.V.", "Duco", "DucoBox Energy Comfort Plus D450", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "90", "2", "29.0", "0.53", "90", "3", "37.0", "0.57", "90", "4", "45.0", "0.65", "89", "5", "53.0", "0.76", "89", "6", "61.0", "0.90", "88", "7", "69.0", "1.08", "88"]} +{"pcdb_id": 500852, "raw": ["500852", "020209", "0", "2023/Nov/15 08:55", "Airsmart BV", "AirSmart", "Ictus 350", "", "2021", "current", "", "3", "1", "2", "1", "3", "7", "1", "21.0", "0.68", "91", "2", "29.0", "0.66", "90", "3", "37.0", "0.70", "88", "4", "45.0", "0.81", "87", "5", "53.0", "0.96", "86", "6", "61.0", "1.12", "85", "7", "69.0", "1.31", "84"]} +{"pcdb_id": 500853, "raw": ["500853", "020209", "0", "2023/Nov/15 08:55", "Airsmart BV", "AirSmart", "Ictus 450", "", "2021", "current", "", "3", "1", "2", "1", "3", "7", "1", "21.0", "0.65", "89", "2", "29.0", "0.62", "88", "3", "37.0", "0.68", "87", "4", "45.0", "0.80", "86", "5", "53.0", "0.95", "85", "6", "61.0", "1.07", "84", "7", "69.0", "1.30", "83"]} +{"pcdb_id": 500854, "raw": ["500854", "020170", "0", "2023/Nov/30 10:08", "HEATPEX SP. Z O.O.", "HEATPEX", "ARIA VITALE 300", "", "2023", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.74", "88", "2", "29.0", "0.73", "88", "3", "37.0", "0.79", "86", "4", "45.0", "0.91", "85", "5", "53.0", "0.95", "85"]} +{"pcdb_id": 500855, "raw": ["500855", "020170", "0", "2023/Nov/30 10:28", "HEATPEX SP. Z O.O.", "HEATPEX", "ARIA VITALE 450", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.84", "89", "2", "29.0", "0.81", "88", "3", "37.0", "0.89", "86", "4", "45.0", "1.06", "85", "5", "53.0", "1.13", "85", "6", "61.0", "1.32", "84", "7", "69.0", "1.43", "84"]} +{"pcdb_id": 500856, "raw": ["500856", "020170", "0", "2023/Nov/30 10:28", "HEATPEX SP. Z O.O.", "HEATPEX", "ARIA VITALE 600", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.84", "89", "2", "29.0", "0.81", "88", "3", "37.0", "0.89", "86", "4", "45.0", "1.06", "85", "5", "53.0", "1.13", "85", "6", "61.0", "1.32", "84", "7", "69.0", "1.43", "84"]} +{"pcdb_id": 500857, "raw": ["500857", "020018", "0", "2023/Nov/28 15:22", "National Ventilation", "National Ventilation", "Monsoon Energysaver Intelli-Plus S", "MEI-Plus S", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.39", "93", "2", "29.0", "0.46", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.70", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.07", "89", "7", "69.0", "1.31", "89"]} +{"pcdb_id": 500858, "raw": ["500858", "020018", "0", "2023/Nov/28 15:23", "National Ventilation", "National Ventilation", "Monsoon Energysaver Intelli-Plus M", "MEI-Plus M", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.41", "93", "2", "29.0", "0.41", "93", "3", "37.0", "0.46", "92", "4", "45.0", "0.55", "92", "5", "53.0", "0.66", "91", "6", "61.0", "0.81", "91", "7", "69.0", "1.00", "90"]} +{"pcdb_id": 500859, "raw": ["500859", "020018", "0", "2023/Nov/28 15:23", "National Ventilation", "National Ventilation", "Monsoon Energysaver Intelli-Plus L", "MEI-Plus L", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "93", "2", "29.0", "0.53", "93", "3", "37.0", "0.56", "93", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "91", "6", "61.0", "0.84", "91", "7", "69.0", "1.01", "90"]} +{"pcdb_id": 500860, "raw": ["500860", "020223", "0", "2023/Dec/15 12:35", "Ecovolt Ltd", "Ecovolt", "EVAir", "", "2023", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.46", "", "2", "29.0", "0.33", "", "3", "37.0", "0.33", "", "4", "45.0", "0.39", "", "5", "53.0", "0.44", "", "6", "61.0", "0.54"]} +{"pcdb_id": 500871, "raw": ["500871", "020002", "0", "2024/Jan/02 17:07", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq-Cool", "412380", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.39", "93", "2", "29.0", "0.46", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.70", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.07", "89", "7", "69.0", "1.31", "89"]} +{"pcdb_id": 500872, "raw": ["500872", "020130", "0", "2024/Jan/04 12:16", "Vero Duco N.V.", "Duco", "DucoBox Energy Comfort D325", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.60", "91", "2", "29.0", "0.57", "91", "3", "37.0", "0.61", "91", "4", "45.0", "0.71", "90", "5", "53.0", "0.81", "90", "6", "61.0", "0.98", "89", "7", "69.0", "1.17", "89"]} +{"pcdb_id": 500873, "raw": ["500873", "020030", "0", "2024/Jan/30 11:22", "Aldes", "Aldes", "InspirAir Top 450 Premium", "", "2021", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.52", "92", "3", "37.0", "0.57", "92", "4", "45.0", "0.67", "92", "5", "53.0", "0.81", "92", "6", "61.0", "1.01", "92", "7", "69.0", "1.27", "91"]} +{"pcdb_id": 500874, "raw": ["500874", "020030", "0", "2024/Jan/31 12:48", "Aldes", "Aldes", "InspirAIR Top 300 Classic", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.51", "90", "2", "29.0", "0.48", "90", "3", "37.0", "0.54", "89", "4", "45.0", "0.64", "88", "5", "53.0", "0.76", "87", "6", "61.0", "0.94", "86", "7", "69.0", "1.16", "86"]} +{"pcdb_id": 500876, "raw": ["500876", "020094", "0", "2024/Mar/08 15:45", "Viessmann Ltd", "Viessmann", "Vitovent 300-W", "H32S A225", "2020", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.62", "92", "2", "29.0", "0.64", "91", "3", "37.0", "0.69", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.96", "88", "6", "61.0", "1.17", "88"]} +{"pcdb_id": 500877, "raw": ["500877", "020094", "0", "2024/Mar/08 15:46", "Viessmann Ltd", "Viessmann", "Vitovent 300-W", "H32S C325", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.55", "92", "2", "29.0", "0.52", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.63", "90", "5", "53.0", "0.73", "90", "6", "61.0", "0.86", "89", "7", "69.0", "1.00", "89"]} +{"pcdb_id": 500878, "raw": ["500878", "020094", "0", "2024/Mar/08 15:47", "Viessmann Ltd", "Viessmann", "Vitovent 300-W", "H32S C400", "2020", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.48", "92", "2", "29.0", "0.49", "91", "3", "37.0", "0.53", "90", "4", "45.0", "0.63", "90", "5", "53.0", "0.74", "90", "6", "61.0", "0.90", "89", "7", "69.0", "1.08", "89"]} +{"pcdb_id": 500879, "raw": ["500879", "020094", "0", "2024/Mar/08 15:48", "Viessmann Ltd", "Viessmann", "Vitovent 300-W", "H32S A600", "2020", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.59", "94", "3", "37.0", "0.61", "94", "4", "45.0", "0.69", "94", "5", "53.0", "0.78", "93", "6", "61.0", "0.93", "92", "7", "69.0", "1.09", "92"]} +{"pcdb_id": 500880, "raw": ["500880", "020094", "0", "2024/Mar/08 15:49", "Viessmann Ltd", "Viessmann", "Vitovent 300-C", "H32S B150", "2016", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.75", "88", "2", "29.0", "0.86", "85", "3", "37.0", "1.04", "84"]} +{"pcdb_id": 500881, "raw": ["500881", "020091", "0", "2024/Mar/20 15:10", "Sabiana", "Lindab", "RHR-CF-V", "280 PRO", "2022", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "91", "2", "29.0", "0.55", "90", "3", "37.0", "0.62", "90", "4", "45.0", "0.77", "89", "5", "53.0", "0.92", "88"]} +{"pcdb_id": 500882, "raw": ["500882", "020091", "0", "2024/Mar/20 15:09", "SABIANA", "Lindab", "RHR-CF-V", "370 PRO", "2022", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.54", "91", "2", "29.0", "0.55", "91", "3", "37.0", "0.61", "90", "4", "45.0", "0.74", "89", "5", "53.0", "0.89", "89"]} +{"pcdb_id": 500883, "raw": ["500883", "020091", "0", "2024/Mar/20 15:09", "SABIANA", "Lindab", "RHR-CF-V", "460 PRO", "2022", "current", "", "3", "0", "2", "1", "3", "5", "3", "37.0", "0.71", "90", "4", "45.0", "0.85", "89", "5", "53.0", "1.03", "89", "6", "61.0", "1.30", "89", "7", "69.0", "1.61", "89"]} +{"pcdb_id": 500884, "raw": ["500884", "020091", "0", "2024/Mar/20 15:09", "SABIANA", "Lindab", "RHR-CF-V", "600 PRO", "2022", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.72", "91", "3", "37.0", "0.78", "90", "4", "45.0", "0.92", "90", "5", "53.0", "1.08", "89", "6", "61.0", "1.31", "89", "7", "69.0", "1.58", "89"]} +{"pcdb_id": 500885, "raw": ["500885", "020091", "0", "2024/Mar/20 15:08", "SABIANA", "Lindab", "RHR-CF-V", "280 PRO EL / ER", "2022", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "91", "2", "29.0", "0.55", "90", "3", "37.0", "0.62", "90", "4", "45.0", "0.77", "89", "5", "53.0", "0.92", "88"]} +{"pcdb_id": 500886, "raw": ["500886", "020091", "0", "2024/Mar/20 15:08", "SABIANA", "Lindab", "RHR-CF-V", "370 PRO EL / ER", "2022", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.54", "91", "2", "29.0", "0.55", "91", "3", "37.0", "0.61", "90", "4", "45.0", "0.74", "89", "5", "53.0", "0.89", "89"]} +{"pcdb_id": 500887, "raw": ["500887", "020091", "0", "2024/Mar/20 15:08", "SABIANA", "Lindab", "RHR-CF-V", "460 PRO EL / ER", "2022", "current", "", "3", "0", "2", "1", "3", "5", "3", "37.0", "0.71", "90", "4", "45.0", "0.85", "89", "5", "53.0", "1.03", "89", "6", "61.0", "1.30", "89", "7", "69.0", "1.61", "89"]} +{"pcdb_id": 500888, "raw": ["500888", "020091", "0", "2024/Mar/20 15:07", "SABIANA", "Lindab", "RHR-CF-V", "600 PRO EL / ER", "2022", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.72", "91", "3", "37.0", "0.78", "90", "4", "45.0", "0.92", "90", "5", "53.0", "1.08", "89", "6", "61.0", "1.31", "89", "7", "69.0", "1.58", "89"]} +{"pcdb_id": 500889, "raw": ["500889", "020235", "0", "2024/Mar/20 09:18", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SP-280", "2017", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "91", "2", "29.0", "0.55", "90", "3", "37.0", "0.62", "90", "4", "45.0", "0.77", "89", "5", "53.0", "0.92", "88"]} +{"pcdb_id": 500890, "raw": ["500890", "020235", "0", "2024/Mar/20 09:18", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SPEL/R-280", "2017", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "91", "2", "29.0", "0.55", "90", "3", "37.0", "0.62", "90", "4", "45.0", "0.77", "89", "5", "53.0", "0.92", "88"]} +{"pcdb_id": 500891, "raw": ["500891", "020235", "0", "2024/Mar/20 09:17", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SP-370", "2017", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.54", "91", "2", "29.0", "0.55", "91", "3", "37.0", "0.61", "90", "4", "45.0", "0.74", "89", "5", "53.0", "0.89", "89"]} +{"pcdb_id": 500892, "raw": ["500892", "020235", "0", "2024/Mar/20 09:17", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SPEL/R-370", "2017", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.54", "91", "2", "29.0", "0.55", "91", "3", "37.0", "0.61", "90", "4", "45.0", "0.74", "89", "5", "53.0", "0.89", "89"]} +{"pcdb_id": 500893, "raw": ["500893", "020235", "0", "2024/Mar/20 09:17", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SP-460", "2017", "current", "", "3", "0", "2", "1", "3", "5", "3", "37.0", "0.71", "90", "4", "45.0", "0.85", "89", "5", "53.0", "1.03", "89", "6", "61.0", "1.30", "89", "7", "69.0", "1.61", "89"]} +{"pcdb_id": 500894, "raw": ["500894", "020235", "0", "2024/Mar/20 09:16", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SPEL/R-460", "2017", "current", "", "3", "0", "2", "1", "3", "5", "3", "37.0", "0.71", "90", "4", "45.0", "0.85", "89", "5", "53.0", "1.03", "89", "6", "61.0", "1.30", "89", "7", "69.0", "1.61", "89"]} +{"pcdb_id": 500895, "raw": ["500895", "020235", "0", "2024/Mar/20 09:16", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SP-600", "2021", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.72", "91", "3", "37.0", "0.78", "90", "4", "45.0", "0.92", "90", "5", "53.0", "1.08", "89", "6", "61.0", "1.31", "89", "7", "69.0", "1.58", "89"]} +{"pcdb_id": 500896, "raw": ["500896", "020235", "0", "2024/Mar/20 09:15", "Sabiana S.p.A", "Sabiana SpA", "ENERGY SMART VERTICAL PRO", "ENY-SPEL/R-600", "2021", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.72", "91", "3", "37.0", "0.78", "90", "4", "45.0", "0.92", "90", "5", "53.0", "1.08", "89", "6", "61.0", "1.31", "89", "7", "69.0", "1.58", "89"]} +{"pcdb_id": 500900, "raw": ["500900", "020002", "0", "2024/May/15 13:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq SC", "411077", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.39", "93", "2", "29.0", "0.46", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.70", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.07", "89", "7", "69.0", "1.31", "89"]} +{"pcdb_id": 500901, "raw": ["500901", "020002", "0", "2024/May/15 13:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq MC", "499638", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.41", "93", "2", "29.0", "0.41", "93", "3", "37.0", "0.46", "92", "4", "45.0", "0.55", "92", "5", "53.0", "0.66", "91", "6", "61.0", "0.81", "91", "7", "69.0", "1.00", "90"]} +{"pcdb_id": 500902, "raw": ["500902", "020002", "0", "2024/May/15 13:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq LC", "499647", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "93", "2", "29.0", "0.53", "93", "3", "37.0", "0.56", "93", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "91", "6", "61.0", "0.84", "91", "7", "69.0", "1.01", "90"]} +{"pcdb_id": 500903, "raw": ["500903", "020002", "0", "2024/May/15 13:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq SCP RH", "499890", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.39", "93", "2", "29.0", "0.46", "92", "3", "37.0", "0.55", "91", "4", "45.0", "0.70", "91", "5", "53.0", "0.85", "90", "6", "61.0", "1.07", "89", "7", "69.0", "1.31", "89"]} +{"pcdb_id": 500904, "raw": ["500904", "020002", "0", "2024/May/15 13:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq MCP RH", "499639", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.41", "93", "2", "29.0", "0.41", "93", "3", "37.0", "0.46", "92", "4", "45.0", "0.55", "92", "5", "53.0", "0.66", "91", "6", "61.0", "0.81", "91", "7", "69.0", "1.00", "90"]} +{"pcdb_id": 500905, "raw": ["500905", "020002", "0", "2024/May/15 13:33", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq LCP RH", "499648", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "93", "2", "29.0", "0.53", "93", "3", "37.0", "0.56", "93", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "91", "6", "61.0", "0.84", "91", "7", "69.0", "1.01", "90"]} +{"pcdb_id": 500906, "raw": ["500906", "020027", "0", "2024/May/24 16:47", "S&P", "EnviroVent", "Sabik 350 E", "", "2022", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.54", "83", "2", "29.0", "0.55", "81", "3", "37.0", "0.56", "80", "4", "45.0", "0.66", "78", "5", "53.0", "0.78", "76", "6", "61.0", "0.97", "75"]} +{"pcdb_id": 500907, "raw": ["500907", "020027", "0", "2024/May/24 16:47", "S&P", "EnviroVent", "Sabik 500 E", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.71", "84", "2", "29.0", "0.65", "84", "3", "37.0", "0.64", "82", "4", "45.0", "0.75", "81", "5", "53.0", "0.87", "80", "6", "61.0", "1.00", "78", "7", "69.0", "1.18", "77"]} +{"pcdb_id": 500908, "raw": ["500908", "020027", "0", "2024/May/24 16:47", "S&P", "EnviroVent", "Altair 120 H", "", "2023", "current", "", "3", "0", "2", "1", "1", "2", "1", "21.0", "0.77", "80", "2", "29.0", "1.00", "80"]} +{"pcdb_id": 500909, "raw": ["500909", "020027", "0", "2024/May/24 16:48", "S&P", "EnviroVent", "Altair 160 H", "", "2023", "current", "", "3", "0", "2", "1", "1", "3", "1", "21.0", "0.65", "83", "2", "29.0", "0.75", "79", "3", "37.0", "0.94", "75"]} +{"pcdb_id": 500910, "raw": ["500910", "020031", "0", "2024/May/31 12:13", "NIBE Energy Systems Ltd", "NIBE", "NIBE S735-7 R EM 1X230V UK", "", "2023", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.65", "", "2", "29.0", "0.56", "", "3", "37.0", "0.53", "", "4", "45.0", "0.57", "", "5", "53.0", "0.59", "", "6", "61.0", "0.66"]} +{"pcdb_id": 500911, "raw": ["500911", "020031", "0", "2024/May/31 12:12", "NIBE Energy Systems Ltd", "NIBE", "S735-7 R EM 1X230V UK + SAM S42", "", "2023", "current", "", "4", "1", "2", "1", "", "6", "1", "21.0", "1.21", "", "2", "29.0", "1.11", "", "3", "37.0", "1.13", "", "4", "45.0", "1.25", "", "5", "53.0", "1.36", "", "6", "61.0", "1.62"]} +{"pcdb_id": 500913, "raw": ["500913", "020050", "0", "2024/Jun/26 09:56", "Aereco SAS", "Aereco", "V4A2", "V4A2 Premium", "2024", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.16", "", "4", "45.0", "0.20", "", "5", "53.0", "0.23", "", "6", "61.0", "0.28"]} +{"pcdb_id": 500914, "raw": ["500914", "020050", "0", "2024/Jun/26 09:59", "Aereco Ltd", "Aereco", "V4A2", "V4A2 75mm semi-rigid", "2024", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.18", "", "4", "45.0", "0.20", "", "5", "53.0", "0.24", "", "6", "61.0", "0.29"]} +{"pcdb_id": 500915, "raw": ["500915", "020130", "0", "2024/Jul/19 08:47", "Vero Duco N.V.", "Duco", "DucoBox Energy Comfort Plus D550 UK", "", "2022", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.66", "88", "2", "29.0", "0.61", "88", "3", "37.0", "0.64", "88", "4", "45.0", "0.71", "88", "5", "53.0", "0.81", "88", "6", "61.0", "0.95", "87", "7", "69.0", "1.11", "87"]} +{"pcdb_id": 500916, "raw": ["500916", "020045", "0", "2024/Jul/31 10:01", "Daikin Europe NV", "Daikin Altherma", "EKHHEU200CV37", "", "2024", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.40", "", "2", "29.0", "0.41", "", "3", "37.0", "0.46", "", "4", "45.0", "0.58", "", "5", "53.0", "0.71", "", "6", "61.0", "0.90"]} +{"pcdb_id": 500917, "raw": ["500917", "020045", "0", "2024/Jul/31 10:03", "Daikin Europe NV", "Daikin Altherma", "EKHHEU260CV37", "", "2024", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.41", "", "2", "29.0", "0.41", "", "3", "37.0", "0.45", "", "4", "45.0", "0.55", "", "5", "53.0", "0.70", "", "6", "61.0", "0.89"]} +{"pcdb_id": 500918, "raw": ["500918", "020240", "0", "2024/Aug/14 10:09", "Systemair Ltd", "Systemair", "SAVE VTR 150/B", "", "2018", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.98", "69", "2", "29.0", "1.03", "71", "3", "37.0", "1.21", "73", "4", "45.0", "1.50", "73"]} +{"pcdb_id": 500919, "raw": ["500919", "020030", "0", "2024/Sep/24 08:30", "Aldes", "Exhausto", "VEX 40 T Classic", "", "2021", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.51", "90", "2", "29.0", "0.48", "90", "3", "37.0", "0.54", "89", "4", "45.0", "0.64", "88", "5", "53.0", "0.76", "87", "6", "61.0", "0.94", "86", "7", "69.0", "1.16", "86"]} +{"pcdb_id": 500920, "raw": ["500920", "020237", "0", "2024/Sep/16 16:05", "Siber Zone S.L.U", "SIBER", "SF ECO", "Higro", "2023", "current", "", "1", "0", "2", "1", "", "4", "1", "21.0", "0.27", "", "2", "29.0", "0.28", "", "3", "37.0", "0.30", "", "4", "45.0", "0.27"]} +{"pcdb_id": 500925, "raw": ["500925", "020002", "0", "2024/Oct/17 12:30", "Vent Axia Ltd", "Vent Axia", "Sentinel Econiq-Cool L", "413775", "2024", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "93", "2", "29.0", "0.53", "93", "3", "37.0", "0.56", "93", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "91", "6", "61.0", "0.84", "91", "7", "69.0", "1.01", "90"]} +{"pcdb_id": 500928, "raw": ["500928", "020139", "0", "2024/Dec/11 17:35", "Ubbink UK Ltd", "Ubbink", "Vigor W600 4/0 L GB", "", "", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.59", "94", "3", "37.0", "0.61", "94", "4", "45.0", "0.69", "94", "5", "53.0", "0.78", "93", "6", "61.0", "0.93", "92", "7", "69.0", "1.09", "92"]} +{"pcdb_id": 500929, "raw": ["500929", "020030", "0", "2024/Dec/09 14:05", "Aldes", "Aldes", "InspirAIR Side 180 Classic (DHU)", "", "2024", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.67", "87", "2", "29.0", "0.74", "84", "3", "37.0", "0.90", "82", "4", "45.0", "1.15", "81", "5", "53.0", "1.41", "79"]} +{"pcdb_id": 500930, "raw": ["500930", "020030", "0", "2024/Dec/09 13:30", "Aldes", "Aldes", "InspirAIR Side 300 Classic (DHU)", "", "2024", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.56", "90", "2", "29.0", "0.57", "89", "3", "37.0", "0.65", "87", "4", "45.0", "0.81", "85", "5", "53.0", "0.97", "83", "6", "61.0", "1.19", "83", "7", "69.0", "1.45", "82"]} +{"pcdb_id": 500931, "raw": ["500931", "020139", "0", "2024/Dec/11 17:34", "Brink", "Ubbink", "Vigor W450 4/0 L GB", "", "2018", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.59", "94", "3", "37.0", "0.61", "94", "4", "45.0", "0.69", "94", "5", "53.0", "0.78", "93", "6", "61.0", "0.93", "92", "7", "69.0", "1.09", "92"]} +{"pcdb_id": 500932, "raw": ["500932", "020139", "0", "2024/Dec/11 17:34", "Ubbink UK Ltd", "Ubbink", "Vigor W225 4/0 L GB", "", "", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.62", "92", "2", "29.0", "0.64", "91", "3", "37.0", "0.69", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.96", "88", "6", "61.0", "1.17", "88"]} +{"pcdb_id": 500935, "raw": ["500935", "020130", "0", "2025/Jan/31 10:17", "Vero Duco N.V.", "Duco", "DucoBox Energy Sky D275 UK", "", "2024", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.64", "93", "2", "29.0", "0.63", "92", "3", "37.0", "0.68", "91", "4", "45.0", "0.83", "90", "5", "53.0", "0.97", "90", "6", "61.0", "1.16", "90", "7", "69.0", "1.39", "89"]} +{"pcdb_id": 500936, "raw": ["500936", "020130", "0", "2025/Jan/31 10:18", "Vero Duco N.V.", "Duco", "DucoBox Silent Connect UK", "00004455", "2013", "current", "", "1", "0", "2", "1", "", "6", "1", "21.4", "0.31", "", "2", "29.0", "0.26", "", "3", "37.0", "0.25", "", "4", "45.0", "0.26", "", "5", "53.6", "0.29", "", "6", "61.1", "0.32"]} +{"pcdb_id": 500937, "raw": ["500937", "020031", "0", "2024/Sep/26 09:56", "NIBE Energy Systems Ltd", "NIBE", "NIBE S735-4 R EM 1X230V UK", "", "2023", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.65", "", "2", "29.0", "0.56", "", "3", "37.0", "0.53", "", "4", "45.0", "0.57", "", "5", "53.0", "0.59", "", "6", "61.0", "0.66"]} +{"pcdb_id": 500938, "raw": ["500938", "020031", "0", "2024/Sep/26 09:52", "NIBE Energy Systems Ltd", "NIBE", "S735-4 R EM 1X230V UK + SAM S42", "", "2023", "current", "", "4", "1", "2", "1", "", "6", "1", "21.0", "1.21", "", "2", "29.0", "1.11", "", "3", "37.0", "1.13", "", "4", "45.0", "1.25", "", "5", "53.0", "1.36", "", "6", "61.0", "1.62"]} +{"pcdb_id": 500939, "raw": ["500939", "020016", "0", "2025/Feb/24 16:38", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "PA700LI", "", "2024", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.58", "95", "2", "29.0", "0.55", "95", "3", "37.0", "0.56", "94", "4", "45.0", "0.59", "93", "5", "53.0", "0.68", "93", "6", "61.0", "0.75", "92", "7", "69.0", "0.88", "91"]} +{"pcdb_id": 500940, "raw": ["500940", "020047", "0", "2025/Feb/18 11:48", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-250CZPVU-L-E-2", "", "2024", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "90", "2", "29.0", "0.67", "89", "3", "37.0", "0.79", "88", "4", "45.0", "1.00", "87", "5", "53.0", "1.19", "87"]} +{"pcdb_id": 500941, "raw": ["500941", "020047", "0", "2025/Feb/18 11:48", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-250CZPVU-R-E-2", "", "2024", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "90", "2", "29.0", "0.67", "89", "3", "37.0", "0.79", "88", "4", "45.0", "1.00", "87", "5", "53.0", "1.19", "87"]} +{"pcdb_id": 500942, "raw": ["500942", "020047", "0", "2025/Feb/18 11:48", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-350CZPVU-L-E-2", "", "2024", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.86", "90", "2", "29.0", "0.80", "90", "3", "37.0", "0.84", "89", "4", "45.0", "0.96", "89", "5", "53.0", "1.08", "88", "6", "61.0", "1.28", "87"]} +{"pcdb_id": 500943, "raw": ["500943", "020047", "0", "2025/Feb/18 11:49", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-350CZPVU-R-E-2", "", "2024", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.86", "90", "2", "29.0", "0.80", "90", "3", "37.0", "0.84", "89", "4", "45.0", "0.96", "89", "5", "53.0", "1.08", "88", "6", "61.0", "1.28", "87"]} +{"pcdb_id": 500944, "raw": ["500944", "020047", "0", "2025/Feb/18 11:49", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-500CZPVU-L-E-2", "", "2024", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "91", "2", "29.0", "0.72", "90", "3", "37.0", "0.74", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.91", "88", "6", "61.0", "1.09", "88", "7", "69.0", "1.24", "88"]} +{"pcdb_id": 500945, "raw": ["500945", "020047", "0", "2025/Feb/18 11:49", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-500CZPVU-R-E-2", "", "2024", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "91", "2", "29.0", "0.72", "90", "3", "37.0", "0.74", "90", "4", "45.0", "0.82", "89", "5", "53.0", "0.91", "88", "6", "61.0", "1.09", "88", "7", "69.0", "1.24", "88"]} +{"pcdb_id": 500946, "raw": ["500946", "020038", "0", "2025/Feb/20 15:52", "Titon Hardware Ltd", "Beam", "Axco MEV130 AIP", "", "2022", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.17", "", "2", "29.0", "0.16", "", "3", "37.0", "0.17", "", "4", "45.0", "0.20", "", "5", "53.0", "0.23", "", "6", "61.0", "0.26"]} +{"pcdb_id": 500947, "raw": ["500947", "020016", "0", "2025/Feb/24 16:13", "ProAir Heat Recovery and Ventilation Systems Ltd", "ProAir", "PA700HLI", "", "2024", "current", "", "3", "0", "2", "1", "1", "7", "1", "21.0", "0.65", "93", "2", "29.0", "0.67", "93", "3", "37.0", "0.75", "93", "4", "45.0", "0.88", "92", "5", "53.0", "1.05", "91", "6", "61.0", "1.26", "90", "7", "69.0", "1.53", "90"]} +{"pcdb_id": 500948, "raw": ["500948", "020073", "0", "2025/Feb/27 15:42", "Dantherm Air Handling A/S", "Dantherm", "RCC220 P2", "", "2024", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "1.00", "86", "2", "29.0", "1.19", "84", "3", "37.0", "1.49", "82"]} +{"pcdb_id": 500949, "raw": ["500949", "020065", "0", "2025/Feb/18 09:40", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "VRC-W 600 Premium", "204714", "2024", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.46", "92", "3", "37.0", "0.49", "92", "4", "45.0", "0.55", "91", "5", "53.0", "0.63", "91", "6", "61.0", "0.75", "90", "7", "69.0", "0.90", "90"]} +{"pcdb_id": 500950, "raw": ["500950", "020065", "0", "2025/Feb/18 09:40", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "VRC-W 600 E Premium", "204715", "2024", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.43", "87", "3", "37.0", "0.45", "85", "4", "45.0", "0.52", "83", "5", "53.0", "0.60", "82", "6", "61.0", "0.74", "80", "7", "69.0", "0.86", "79"]} +{"pcdb_id": 500951, "raw": ["500951", "020065", "0", "2025/Feb/18 09:41", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "VRC-W 450 Premium", "204940", "2024", "current", "204940", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.46", "92", "3", "37.0", "0.49", "92", "4", "45.0", "0.55", "91", "5", "53.0", "0.63", "91", "6", "61.0", "0.75", "90", "7", "69.0", "0.90", "90"]} +{"pcdb_id": 500952, "raw": ["500952", "020065", "0", "2025/Feb/18 09:41", "Stiebel Eltron UK Ltd", "Stiebel Eltron", "VRC-W 450 E Premium", "204941", "2024", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.43", "87", "3", "37.0", "0.45", "85", "4", "45.0", "0.52", "83", "5", "53.0", "0.60", "82", "6", "61.0", "0.74", "80", "7", "69.0", "0.86", "79"]} +{"pcdb_id": 500953, "raw": ["500953", "020248", "0", "2025/Feb/24 16:54", "Qvantum Energi AB", "Qvantum", "QE4", "", "2024", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.27", "", "2", "29.0", "0.32", "", "3", "37.0", "0.41", "", "4", "45.0", "0.54", "", "5", "53.0", "0.68", "", "6", "61.0", "0.91"]} +{"pcdb_id": 500954, "raw": ["500954", "020248", "0", "2025/Feb/24 16:47", "Qvantum Energi AB", "Qvantum", "QE6", "", "2024", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.27", "", "2", "29.0", "0.32", "", "3", "37.0", "0.41", "", "4", "45.0", "0.54", "", "5", "53.0", "0.68", "", "6", "61.0", "0.91"]} +{"pcdb_id": 500955, "raw": ["500955", "020204", "0", "2025/Mar/27 16:29", "Airbot Technologies AS", "AIROBOT", "AIROBOT-V3-HRV-R-W", "", "2022", "current", "", "3", "0", "2", "1", "4", "6", "1", "21.0", "0.81", "88", "2", "29.0", "0.79", "87", "3", "37.0", "0.87", "86", "4", "45.0", "1.04", "85", "5", "53.0", "1.23", "85", "6", "61.0", "1.51", "84"]} +{"pcdb_id": 500956, "raw": ["500956", "020038", "0", "2025/Mar/28 15:05", "Titon Hardware", "Beam", "AXCO C440", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.42", "91", "2", "29.0", "0.43", "90", "3", "37.0", "0.50", "89", "4", "45.0", "0.60", "88", "5", "53.0", "0.73", "87", "6", "61.0", "0.90", "86", "7", "69.0", "1.08", "86"]} +{"pcdb_id": 500957, "raw": ["500957", "020038", "0", "2025/Sep/17 09:51", "Titon Hardware Ltd", "Beam", "AXCO C570", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.47", "91", "2", "29.0", "0.49", "90", "3", "37.0", "0.54", "89", "4", "45.0", "0.65", "88", "5", "53.0", "0.75", "87", "6", "61.0", "0.91", "86", "7", "69.0", "1.09", "86"]} +{"pcdb_id": 500958, "raw": ["500958", "020242", "0", "2025/Apr/28 15:36", "Valsir Spa", "ARIOSA HV 170 E", "ARIOSA HV 170 Enthalpy", "", "2023", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "0.61", "85", "2", "29.0", "0.68", "84", "3", "37.0", "0.82", "83"]} +{"pcdb_id": 500959, "raw": ["500959", "020242", "0", "2025/Apr/28 15:37", "Valsir Spa", "ARIOSA HV 170 S", "ARIOSA HV 170 Sensible", "", "2023", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.65", "90", "2", "29.0", "0.70", "89", "3", "37.0", "0.83", "89", "4", "45.0", "1.04", "87"]} +{"pcdb_id": 500960, "raw": ["500960", "020242", "0", "2025/Apr/28 15:38", "Valsir Spa", "ARIOSA HV 250 E", "ARIOSA HV 250 Enthalpy", "", "2023", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "85", "2", "29.0", "0.69", "84", "3", "37.0", "0.84", "83", "4", "45.0", "1.05", "83", "5", "53.0", "1.30", "82"]} +{"pcdb_id": 500961, "raw": ["500961", "020242", "0", "2025/Apr/28 15:40", "Valsir Spa", "ARIOSA HV 250 S", "ARIOSA HV 250 Sensible", "", "2023", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.64", "90", "2", "29.0", "0.70", "89", "3", "37.0", "0.88", "89", "4", "45.0", "1.10", "87", "5", "53.0", "1.38", "87"]} +{"pcdb_id": 500962, "raw": ["500962", "020248", "0", "2025/Apr/28 16:47", "Qvantum Energy Technology UK Limited", "Qvantum", "QE4 + QS", "", "2025", "current", "", "4", "1", "2", "1", "", "6", "1", "21.0", "0.55", "", "2", "29.0", "0.66", "", "3", "37.0", "0.86", "", "4", "45.0", "1.08", "", "5", "53.0", "1.35", "", "6", "61.0", "1.75"]} +{"pcdb_id": 500963, "raw": ["500963", "020248", "0", "2025/Apr/28 16:47", "Qvantum Energy Technology UK Limited", "Qvantum", "QE6 + QS", "", "2025", "current", "", "4", "1", "2", "1", "", "6", "1", "21.0", "0.55", "", "2", "29.0", "0.66", "", "3", "37.0", "0.83", "", "4", "45.0", "1.08", "", "5", "53.0", "1.35", "", "6", "61.0", "1.75"]} +{"pcdb_id": 500964, "raw": ["500964", "020130", "0", "2025/May/23 12:41", "Vero Duco N.V.", "Duco", "DucoBox Reno UK", "00004948", "2023", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.16", "", "2", "29.0", "0.19", "", "3", "37.0", "0.20", "", "4", "45.0", "0.22", "", "5", "53.0", "0.24", "", "6", "61.0", "0.27"]} +{"pcdb_id": 500966, "raw": ["500966", "020087", "0", "2025/Jun/26 11:19", "Sinergia Srl", "Panasonic", "P-VEN15XQAZE5", "", "2023", "current", "", "3", "0", "2", "1", "4", "4", "1", "21.0", "0.80", "87", "2", "29.0", "0.88", "84", "3", "37.0", "1.11", "84", "4", "45.0", "1.48", "83"]} +{"pcdb_id": 500967, "raw": ["500967", "020087", "0", "2025/Jun/26 11:20", "Sinergia Srl", "Panasonic", "P-VEN20XQAZE5", "", "2023", "current", "", "3", "0", "2", "1", "4", "6", "1", "21.0", "0.81", "88", "2", "29.0", "0.77", "87", "3", "37.0", "0.91", "85", "4", "45.0", "1.06", "85", "5", "53.0", "1.22", "84", "6", "61.0", "1.50", "83"]} +{"pcdb_id": 500968, "raw": ["500968", "020087", "0", "2025/Jun/26 11:26", "Sinergia Srl", "Panasonic", "P-VEN30XQAZE5", "", "2023", "current", "", "3", "0", "2", "1", "4", "5", "1", "21.0", "0.75", "88", "2", "29.0", "0.82", "86", "3", "37.0", "0.97", "85", "4", "45.0", "1.15", "84", "5", "53.0", "1.31", "84"]} +{"pcdb_id": 500969, "raw": ["500969", "020087", "0", "2025/Jun/26 11:28", "Sinergia Srl", "Panasonic", "P-VEN40XQAVE5", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.70", "90", "2", "29.0", "0.64", "89", "3", "37.0", "0.76", "88", "4", "45.0", "0.82", "88", "5", "53.0", "0.96", "87", "6", "61.0", "1.19", "87", "7", "69.0", "1.39", "87"]} +{"pcdb_id": 500970, "raw": ["500970", "020087", "0", "2025/Jun/26 11:31", "Sinergia Srl", "Panasonic", "P-VEN45XQAHE5", "", "2023", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "0.72", "90", "2", "29.0", "0.72", "88", "3", "37.0", "0.74", "88", "4", "45.0", "0.88", "85", "5", "53.0", "1.05", "84", "6", "61.0", "1.20", "84", "7", "69.0", "1.43", "83"]} +{"pcdb_id": 500971, "raw": ["500971", "020087", "0", "2025/Jun/26 11:36", "Sinergia Srl", "Panasonic", "P-VEN45XQAVE5", "", "2023", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.78", "91", "2", "29.0", "0.66", "89", "3", "37.0", "0.69", "90", "4", "45.0", "0.80", "88", "5", "53.0", "0.97", "88", "6", "61.0", "1.18", "87", "7", "69.0", "1.37", "87"]} +{"pcdb_id": 500972, "raw": ["500972", "020242", "0", "2025/Jun/26 15:58", "Valsir Spa", "ARIOSA 330 S", "ARIOSA HV 330 Sensible", "", "2025", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.69", "88", "2", "29.0", "0.77", "86", "3", "37.0", "0.88", "85", "4", "45.0", "1.08", "84", "5", "53.0", "1.28", "84"]} +{"pcdb_id": 500973, "raw": ["500973", "020242", "0", "2025/Jun/26 16:02", "Valsir Spa", "ARIOSA 330 E", "ARIOSA HV 330 Enthalpy", "", "2025", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.73", "84", "2", "29.0", "0.74", "83", "3", "37.0", "0.86", "82", "4", "45.0", "1.05", "82", "5", "53.0", "1.27", "81"]} +{"pcdb_id": 500974, "raw": ["500974", "020240", "0", "2025/Jun/26 10:27", "Systemair Ltd", "Systemair", "Save VTR 275/B L", "", "2023", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.84", "68", "2", "29.0", "0.84", "70", "3", "37.0", "0.91", "72", "4", "45.0", "1.08", "72", "5", "53.0", "1.24", "73", "6", "61.1", "1.47", "72"]} +{"pcdb_id": 500975, "raw": ["500975", "020112", "0", "2025/Jun/26 11:04", "Blauberg UK Ltd", "Blauberg", "Passiv-180-H", "", "2024", "current", "", "3", "0", "2", "1", "3", "4", "1", "21.0", "0.57", "89", "2", "29.0", "0.66", "87", "3", "37.0", "0.85", "86", "4", "45.0", "1.11", "85"]} +{"pcdb_id": 500976, "raw": ["500976", "020084", "0", "2025/Jul/07 15:19", "FRÄNKISCHE ROHRWERKE", "FRÄNKISCHE", "profi-air 130 flat", "", "2024", "current", "", "3", "0", "2", "1", "3", "3", "1", "21.0", "1.00", "86", "2", "29.0", "1.19", "84", "3", "37.0", "1.49", "82"]} +{"pcdb_id": 500977, "raw": ["500977", "020042", "0", "2025/Jul/23 09:00", "Brink Climate Systems B.V.", "Brink", "Ease 200", "", "2025", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "86", "2", "29.0", "0.72", "85", "3", "37.0", "0.89", "84", "4", "45.0", "1.16", "84", "5", "53.0", "1.44", "83"]} +{"pcdb_id": 500981, "raw": ["500981", "020009", "0", "2025/Aug/08 12:22", "Vortice Ltd", "Vortice", "Invisible Mini Top", "", "2020", "current", "", "3", "0", "2", "1", "3", "2", "1", "21.0", "1.14", "76", "2", "29.0", "1.48", "73"]} +{"pcdb_id": 500982, "raw": ["500982", "020287", "0", "2025/Aug/05 09:46", "Rega Ventilation", "RegaVent", "250RS", "", "2025", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.94", "87", "2", "29.0", "0.94", "86", "3", "37.0", "1.06", "84", "4", "45.0", "1.25", "83", "5", "53.0", "1.50", "81"]} +{"pcdb_id": 500983, "raw": ["500983", "020047", "0", "2025/Sep/12 11:35", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-520CZPVU-R-E", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "87", "2", "29.0", "0.74", "88", "3", "37.0", "0.76", "88", "4", "45.0", "0.84", "88", "5", "53.0", "0.94", "87", "6", "61.0", "1.10", "86", "7", "69.0", "1.27", "86"]} +{"pcdb_id": 500984, "raw": ["500984", "020047", "0", "2025/Sep/12 11:37", "Mitsubishi Electric Europe B.V.", "Mitsubishi Electric", "VL-520CZPVU-L-E", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.80", "87", "2", "29.0", "0.74", "88", "3", "37.0", "0.76", "88", "4", "45.0", "0.84", "88", "5", "53.0", "0.94", "87", "6", "61.0", "1.10", "86", "7", "69.0", "1.27", "86"]} +{"pcdb_id": 500985, "raw": ["500985", "020288", "0", "2025/Aug/04 16:16", "Airsmart", "Airsmart", "ICTUS 250 Wall", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.58", "93", "2", "29.0", "0.53", "93", "3", "37.0", "0.55", "93", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "91", "6", "61.0", "0.86", "91", "7", "69.0", "1.04", "90"]} +{"pcdb_id": 500986, "raw": ["500986", "020288", "0", "2025/Aug/04 16:16", "Airsmart", "Airsmart", "ICTUS 350 Wall", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.58", "93", "2", "29.0", "0.53", "93", "3", "37.0", "0.55", "93", "4", "45.0", "0.62", "92", "5", "53.0", "0.72", "91", "6", "61.0", "0.86", "91", "7", "69.0", "1.04", "90"]} +{"pcdb_id": 500987, "raw": ["500987", "020288", "0", "2025/Aug/04 16:17", "Airsmart", "Airsmart", "ICTUS 450 Wall", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "93", "2", "29.0", "0.56", "94", "3", "37.0", "0.57", "93", "4", "45.0", "0.65", "93", "5", "53.0", "0.75", "92", "6", "61.0", "0.88", "92", "7", "69.0", "1.02", "91"]} +{"pcdb_id": 500988, "raw": ["500988", "020288", "0", "2025/Aug/04 16:17", "Airsmart", "Airsmart", "ICTUS 550 Wall", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.59", "93", "2", "29.0", "0.56", "94", "3", "37.0", "0.57", "93", "4", "45.0", "0.65", "93", "5", "53.0", "0.75", "92", "6", "61.0", "0.88", "92", "7", "69.0", "1.02", "91"]} +{"pcdb_id": 500989, "raw": ["500989", "020145", "0", "2025/Sep/23 13:43", "KERS Innovations UK Ltd", "KERS Energy Recovery", "KERS MEV 230", "", "2020", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.34", "", "2", "29.0", "0.32", "", "3", "37.0", "0.33", "", "4", "45.0", "0.40", "", "5", "53.0", "0.47", "", "6", "61.0", "0.56"]} +{"pcdb_id": 500990, "raw": ["500990", "020145", "0", "2025/Sep/23 13:43", "KERS Innovations UK Ltd", "KERS Energy Recovery", "KERS MEV 160", "", "2020", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.34", "", "2", "29.0", "0.32", "", "3", "37.0", "0.33", "", "4", "45.0", "0.40", "", "5", "53.0", "0.47", "", "6", "61.0", "0.56"]} +{"pcdb_id": 500991, "raw": ["500991", "020145", "0", "2025/Sep/23 13:43", "KERS Innovations UK Ltd", "KERS Energy Recovery", "KERS MEV 300", "", "2020", "current", "", "1", "1", "2", "1", "", "6", "1", "21.0", "0.34", "", "2", "29.0", "0.32", "", "3", "37.0", "0.33", "", "4", "45.0", "0.40", "", "5", "53.0", "0.47", "", "6", "61.0", "0.56"]} +{"pcdb_id": 500993, "raw": ["500993", "020226", "0", "2025/Oct/31 09:18", "Airfi", "Airfi", "Model 60", "", "2019", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.96", "78", "2", "29.0", "0.95", "78", "3", "37.0", "1.07", "78", "4", "45.0", "1.23", "79", "5", "53.0", "1.46", "79"]} +{"pcdb_id": 500994, "raw": ["500994", "020226", "0", "2025/Oct/31 09:17", "Airfi", "Airfi", "Model 150", "", "2019", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.83", "73", "2", "29.0", "0.75", "75", "3", "37.0", "0.80", "77", "4", "45.0", "0.88", "78", "5", "53.0", "1.07", "78", "6", "61.0", "1.19", "79", "7", "69.0", "1.38", "79"]} +{"pcdb_id": 500995, "raw": ["500995", "020052", "0", "2025/Nov/03 16:40", "Brook Design Hardware Ltd", "Brookvent", "Airstream ACE 75mm Semi-Rigid", "", "2022", "current", "", "1", "0", "2", "2", "", "6", "1", "21.0", "0.20", "", "2", "29.0", "0.21", "", "3", "37.0", "0.25", "", "4", "45.0", "0.29", "", "5", "53.0", "0.34", "", "6", "61.0", "0.42"]} +{"pcdb_id": 500996, "raw": ["500996", "020240", "0", "2025/Nov/03 16:33", "Systemair Ltd", "Systemair", "SAVE VTR 350/B L", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.88", "73", "2", "29.0", "0.82", "76", "3", "37.0", "0.83", "78", "4", "45.0", "0.93", "79", "5", "53.0", "1.03", "79", "6", "61.0", "1.19", "80", "7", "69.0", "1.36", "80"]} +{"pcdb_id": 501001, "raw": ["501001", "020287", "0", "2025/Nov/28 09:24", "Rega Ventilation", "RegaVent", "600RS", "", "2025", "current", "", "3", "0", "2", "1", "3", "6", "2", "29.0", "0.82", "87", "3", "37.0", "0.80", "87", "4", "45.0", "0.87", "86", "5", "53.0", "1.00", "85", "6", "61.0", "1.19", "84", "7", "69.0", "1.33", "83"]} +{"pcdb_id": 501003, "raw": ["501003", "020003", "0", "2026/Jan/19 11:41", "The Nuaire Group", "Nuaire", "MRXBOXAB-ECO-VM1", "", "2025", "current", "", "3", "0", "2", "1", "3", "2", "1", "21.0", "0.75", "83", "2", "29.0", "0.95", "81"]} +{"pcdb_id": 501004, "raw": ["501004", "020204", "0", "2026/Jan/21 11:01", "Airbot Technologies AS", "AIROBOT", "V6-HRV", "", "2025", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.91", "95", "2", "29.0", "0.86", "94", "3", "37.0", "0.88", "94", "4", "45.0", "0.99", "93", "5", "53.0", "1.10", "92", "6", "61.0", "1.26", "92", "7", "69.0", "1.49", "91"]} +{"pcdb_id": 501005, "raw": ["501005", "020090", "0", "2026/Jan/19 15:26", "Aerauliqa SRL", "Elta", "HAVA 120HV", "", "2025", "current", "", "3", "0", "2", "2", "1", "1", "1", "21.0", "1.16", "82"]} +{"pcdb_id": 501006, "raw": ["501006", "020090", "0", "2026/Jan/19 15:29", "Aerauliqa SRL", "Elta", "HAVA 180H", "", "2025", "current", "", "3", "0", "2", "1", "4", "3", "1", "21.0", "0.78", "86", "2", "29.0", "0.83", "84", "3", "37.0", "0.96", "82"]} +{"pcdb_id": 501007, "raw": ["501007", "020090", "0", "2026/Jan/19 15:32", "Aerauliqa SRL", "Elta", "HAVA 280V", "", "2025", "current", "", "3", "0", "2", "1", "4", "4", "1", "21.0", "0.76", "91", "2", "29.0", "0.89", "90", "3", "37.0", "1.09", "88", "4", "45.0", "1.39", "87"]} +{"pcdb_id": 501008, "raw": ["501008", "020090", "0", "2026/Jan/19 15:34", "Aerauliqa SRL", "Elta", "HAVA 340H", "", "2025", "current", "", "3", "0", "2", "1", "4", "5", "1", "21.0", "0.78", "90", "2", "29.0", "0.83", "88", "3", "37.0", "0.96", "86", "4", "45.0", "1.18", "84", "5", "53.0", "1.42", "83"]} +{"pcdb_id": 501009, "raw": ["501009", "020090", "0", "2026/Jan/19 15:39", "Aerauliqa SRL", "Elta", "HAVA 400V", "", "2025", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "0.64", "84", "2", "29.0", "0.60", "84", "3", "37.0", "0.63", "84", "4", "45.0", "0.72", "83", "5", "53.0", "0.85", "82", "6", "61.0", "1.03", "82", "7", "69.0", "1.24", "81"]} +{"pcdb_id": 501010, "raw": ["501010", "020090", "0", "2026/Jan/19 15:42", "Aerauliqa SRL", "Elta", "HAVA 550V", "", "2025", "current", "", "3", "0", "2", "1", "4", "7", "1", "21.0", "0.62", "90", "2", "29.0", "0.61", "89", "3", "37.0", "0.64", "89", "4", "45.0", "0.78", "88", "5", "53.0", "0.92", "88", "6", "61.0", "1.08", "87", "7", "69.0", "1.28", "87"]} +{"pcdb_id": 501011, "raw": ["501011", "020139", "0", "2026/Jan/26 13:38", "Brink Climate Systems B.V.", "Ubbink", "Ubiflux Compact W200", "", "2024", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "86", "2", "29.0", "0.72", "85", "3", "37.0", "0.89", "84", "4", "45.0", "1.16", "84", "5", "53.0", "1.44", "83"]} +{"pcdb_id": 501012, "raw": ["501012", "020050", "0", "2026/Feb/25 08:59", "Aereco Ltd", "Aereco", "C4A", "C4A 75mm Semi-Rigid", "2025", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.24", "", "2", "29.0", "0.22", "", "3", "37.0", "0.22", "", "4", "45.0", "0.26", "", "5", "53.0", "0.30", "", "6", "61.0", "0.34"]} +{"pcdb_id": 501013, "raw": ["501013", "020050", "0", "2026/Feb/25 09:00", "Aereco Ltd", "Aereco", "C4A", "C4A 90mm Semi-Rigid", "2025", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.28", "", "2", "29.0", "0.25", "", "3", "37.0", "0.26", "", "4", "45.0", "0.27", "", "5", "53.0", "0.32", "", "6", "61.0", "0.37"]} +{"pcdb_id": 501014, "raw": ["501014", "020050", "0", "2026/Feb/25 09:01", "Aereco Ltd", "Aereco", "C4A", "C4A Rigid Duct", "2025", "current", "", "1", "0", "2", "1", "", "6", "1", "21.0", "0.22", "", "2", "29.0", "0.26", "", "3", "37.0", "0.24", "", "4", "45.0", "0.27", "", "5", "53.0", "0.31", "", "6", "61.0", "0.33"]} +{"pcdb_id": 501015, "raw": ["501015", "020003", "0", "2026/Feb/09 15:04", "The Nuaire Group", "Nuaire", "MRXBOX-PH350", "", "2026", "current", "", "3", "0", "2", "1", "3", "7", "1", "21.0", "0.67", "94", "2", "29.0", "0.66", "94", "3", "37.0", "0.72", "93", "4", "45.0", "0.82", "93", "5", "53.0", "0.97", "92", "6", "61.0", "1.19", "91", "7", "69.0", "1.41", "91"]} +{"pcdb_id": 501016, "raw": ["501016", "020004", "0", "2026/Mar/30 15:39", "Zehnder Group UK Ltd", "Zehnder", "EVO 2", "EVO2-HRV-Z1-RW", "2026", "current", "", "3", "0", "2", "1", "3", "5", "1", "21.0", "0.62", "89", "2", "29.0", "0.67", "86", "3", "37.0", "0.84", "85", "4", "45.0", "1.06", "83", "5", "53.0", "1.34", "83"]} +{"pcdb_id": 501017, "raw": ["501017", "020004", "0", "2026/Mar/30 15:42", "Zehnder Group UK Ltd", "Zehnder", "EVO 3", "EVO3-HRV-Z1-RW", "2026", "current", "", "3", "0", "2", "1", "3", "6", "1", "21.0", "0.50", "90", "2", "29.0", "0.54", "88", "3", "37.0", "0.65", "87", "4", "45.0", "0.80", "86", "5", "53.0", "0.97", "85", "6", "61.0", "1.25", "84"]} +{"pcdb_id": 501018, "raw": ["501018", "020004", "0", "2026/Mar/30 15:45", "Zehnder Group UK Ltd", "Zehnder", "EVO 4", "EVO4-HRV-Z1-RW", "2026", "current", "", "3", "1", "2", "1", "3", "6", "2", "29.0", "0.71", "89", "3", "37.0", "0.74", "87", "4", "45.0", "0.91", "86", "5", "53.0", "1.07", "85", "6", "61.0", "1.30", "85", "7", "69.0", "1.56", "84"]} diff --git a/domain/sap10_calculator/tables/pcdb/etl.py b/domain/sap10_calculator/tables/pcdb/etl.py index c34bc513..1f26117f 100644 --- a/domain/sap10_calculator/tables/pcdb/etl.py +++ b/domain/sap10_calculator/tables/pcdb/etl.py @@ -16,10 +16,12 @@ from pathlib import Path from domain.sap10_calculator.tables.pcdb.parser import ( DecentralisedMevRecord, GasOilBoilerRecord, + MvhrRecord, MvInUseFactorsRecord, RawPcdbRecord, parse_table_105, parse_table_322, + parse_table_323, parse_table_329, parse_table_raw, ) @@ -27,6 +29,7 @@ from domain.sap10_calculator.tables.pcdb.parser import ( _TABLE_105_OUTPUT_FILENAME: str = "pcdb_table_105_gas_oil_boilers.jsonl" _TABLE_322_OUTPUT_FILENAME: str = "pcdb_table_322_decentralised_mev.jsonl" +_TABLE_323_OUTPUT_FILENAME: str = "pcdb_table_323_centralised_mev_mvhr.jsonl" _TABLE_329_OUTPUT_FILENAME: str = "pcdb_table_329_mv_in_use_factors.jsonl" # Tables ingested as `RawPcdbRecord` (pcdb_id + raw) — per-field typing is # deferred to follow-up slices when the cert-side wiring for each table @@ -84,6 +87,14 @@ def run_etl(*, source: Path, output_dir: Path) -> None: for r in parse_table_322(dat_text) ], ) + # Table 323 (Centralised MEV and MVHR) — typed via `parse_table_323`, + # exposing the per-wet-room SFP + heat-recovery-efficiency test points + # for the SAP 10.2 §2.6.4/§2.6.6 MVHR cascade. Stored as raw row + + # typed-on-load (consistent with Table 322). + _write_ndjson( + output_path=output_dir / _TABLE_323_OUTPUT_FILENAME, + records=[_mvhr_record_to_jsonable(r) for r in parse_table_323(dat_text)], + ) # Table 329 (MV In-Use Factors) — typed via `parse_table_329`, # exposing the per-ducting-type SFP IUF multipliers for "no # approved scheme" installations (the only variant our cohort @@ -112,6 +123,13 @@ def _decentralised_mev_record_to_jsonable( return {"pcdb_id": record.pcdb_id, "raw": list(record.raw)} +def _mvhr_record_to_jsonable(record: MvhrRecord) -> dict[str, object]: + """Serialise a typed Table 323 record as `{pcdb_id, raw}` — same + shape as the other typed tables; the lookup re-decodes via + `parse_centralised_mv_row` at import time.""" + return {"pcdb_id": record.pcdb_id, "raw": list(record.raw)} + + def _mv_in_use_factors_record_to_jsonable( record: MvInUseFactorsRecord, ) -> dict[str, object]: diff --git a/domain/sap10_calculator/tables/pcdb/parser.py b/domain/sap10_calculator/tables/pcdb/parser.py index 1b4dc80e..4e34195f 100644 --- a/domain/sap10_calculator/tables/pcdb/parser.py +++ b/domain/sap10_calculator/tables/pcdb/parser.py @@ -523,10 +523,18 @@ def parse_table_322(dat_text: str) -> list[DecentralisedMevRecord]: # pcdb10.dat carries Format 432 (header `$329,432,4,2021,11,25,2`), an # extended-field version of spec Format 430. The spec's first 4 fields # (system_type + 3 SFP factors for "no approved scheme") align with the -# Format 432 layout positions 0-3 — the only positions this slice -# decodes. Trailing fields (MVHR adjustments + "with-scheme" variants + -# additional Format 432 columns) are preserved verbatim in `raw` for -# follow-up slices. +# Format 432 layout positions 0-3. Format 432 expands each "adjustment" +# block to 7 columns (3 SFP + 4 MVHR-efficiency) and carries THREE +# blocks (positions 1-7 "no scheme", 8-14 "with scheme", 15-21 a third +# variant), then the timestamp. The MVHR efficiency-IUF columns within a +# block are 4 (vs Format 430's documented 2: uninsulated/insulated); the +# 3rd (index +6 within the block) is the "ducts inside the heated +# envelope" factor — worksheet-proven against simulated case 49 (Vent +# Axia 500140, system_type 3 → 0.90) and cross-checked against the +# default-data row (system_type 10 → 0.70, = SAP 10.2 Table 4g default +# heat-recovery in-use factor 0.70). The outside-envelope efficiency +# columns (indices +4/+5/+7) and the with-scheme blocks are preserved +# verbatim in `raw` (no corpus/worksheet fixture exercises them yet). # # System types per PCDF Spec §A.20 field 1: # 1 = centralised MEV @@ -539,6 +547,10 @@ _MV_IUF_IDX_SYSTEM_TYPE: Final[int] = 0 _MV_IUF_IDX_SFP_FLEX_NO_SCHEME: Final[int] = 1 _MV_IUF_IDX_SFP_RIGID_NO_SCHEME: Final[int] = 2 _MV_IUF_IDX_SFP_NO_DUCT_NO_SCHEME: Final[int] = 3 +# MVHR heat-recovery efficiency in-use factor, "no approved scheme" block, +# ducts inside the heated envelope (Format 432 position 6 = block-1 eff +# column index +2). See the block-layout note above. +_MV_IUF_IDX_MVHR_EFF_INSIDE_NO_SCHEME: Final[int] = 6 @dataclass(frozen=True) @@ -562,6 +574,10 @@ class MvInUseFactorsRecord: sfp_iuf_flexible_no_scheme: Optional[float] sfp_iuf_rigid_no_scheme: Optional[float] sfp_iuf_no_duct_no_scheme: Optional[float] + # MVHR heat-recovery efficiency in-use factor for ducts inside the + # heated envelope, "no approved scheme" block (Format 432). None for + # system types that carry no heat-recovery efficiency (1/2/5). + mvhr_efficiency_iuf_inside_no_scheme: Optional[float] raw: tuple[str, ...] @@ -588,6 +604,11 @@ def parse_mv_in_use_factors_row(row: str) -> MvInUseFactorsRecord: sfp_iuf_no_duct_no_scheme=_parse_optional_float( fields[_MV_IUF_IDX_SFP_NO_DUCT_NO_SCHEME] ), + mvhr_efficiency_iuf_inside_no_scheme=( + _parse_optional_float(fields[_MV_IUF_IDX_MVHR_EFF_INSIDE_NO_SCHEME]) + if len(fields) > _MV_IUF_IDX_MVHR_EFF_INSIDE_NO_SCHEME + else None + ), raw=fields, ) @@ -629,3 +650,118 @@ def parse_decentralised_mev_row(row: str) -> DecentralisedMevRecord: fan_configs=tuple(configs), raw=fields, ) + + +# Table 323 (Centralised MEV and MVHR) — PCDF Spec Rev 6b §A.18 Format 426. +# pcdb10.dat carries Format 431 (header `$323,431,...`), which extends spec +# Format 426 by (a) slotting an extra blank "replacement_id" field between +# `final_year` (idx 9) and `main_type` (idx 11, mirroring Table 322's +# layout) and (b) compressing each per-configuration test group to a +# 4-field tuple `(num_wet_rooms, applicable_flow_l_per_s, sfp, efficiency)` +# — the spec's "Fan speed setting" + duplicate flow fields are dropped. +# +# Field 11 "Main type" (PCDF §A.18): 1=centralised MEV, 3=balanced +# whole-house MV WITH heat recovery, 4=...without heat recovery, +# 5=positive input ventilation. Only type 3 with a non-blank efficiency +# is MVHR (24a); the rest are extract/PIV and carry no heat-recovery +# efficiency. +# +# Each test group's leading field is the number of wet rooms; SAP 10.2 +# §2.6.4 ("MVHR ... SFP is a single value depending on the number of wet +# rooms") selects the group whose wet-room count matches the dwelling +# lodgement. Worksheet-proven on simulated case 49 (000565, 2 wet rooms, +# Vent Axia Sentinel Kinetic B 500140 → group 2 = flow 21.0, SFP 0.88, +# efficiency 91%). +_CMV_IDX_BRAND_NAME: Final[int] = 5 +_CMV_IDX_MODEL_NAME: Final[int] = 6 +_CMV_IDX_MODEL_QUALIFIER: Final[int] = 7 +_CMV_IDX_MAIN_TYPE: Final[int] = 11 +_CMV_IDX_DUCT_TESTED: Final[int] = 13 # 1=flexible, 2=rigid (PCDF §A.18 f.13) +_CMV_IDX_NUM_CONFIGS: Final[int] = 16 +_CMV_GROUP_START: Final[int] = 17 +_CMV_GROUP_STRIDE: Final[int] = 4 # (wet_rooms, flow, sfp, efficiency) +_CMV_MAIN_TYPE_MVHR: Final[int] = 3 + + +@dataclass(frozen=True) +class MvhrDataPoint: + """One per-configuration test result from a Table 323 PCDB record. + + `num_wet_rooms` is the configuration's wet-room count (the SAP 10.2 + §2.6.4 selector); `flow_rate_l_per_s` is the applicable test flow + rate; `sfp_w_per_l_per_s` is the raw Specific Fan Power; `efficiency_pct` + is the raw MVHR heat-exchanger efficiency in % (None for MEV/PIV + records that carry no heat recovery). All are pre-in-use-factor. + """ + + num_wet_rooms: int + flow_rate_l_per_s: Optional[float] + sfp_w_per_l_per_s: Optional[float] + efficiency_pct: Optional[float] + + +@dataclass(frozen=True) +class MvhrRecord: + """PCDB Table 323 (Centralised MEV and MVHR) typed record. + + SAP 10.2 §2.6.4/§2.6.6 — a balanced whole-house MVHR system lodges a + single SFP + heat-exchanger efficiency per wet-room configuration in + the PCDB. SAP selects the data point matching the dwelling's wet-room + count; the raw values are multiplied by the PCDB Table 329 in-use + factors before use (SFP → fan electricity (230a); efficiency → the + (23c) effective-air-change credit in equation (2)). + + Reference: PCDF Spec Rev 6b §A.18 (Format 426 in spec, Format 431 in + pcdb10.dat). + """ + + pcdb_id: int + brand_name: str + model_name: str + model_qualifier: str + main_type: Optional[int] # =3 for balanced whole-house MV (with/without HR) + duct_type_tested: Optional[int] # 1=flexible, 2=rigid + data_points: tuple[MvhrDataPoint, ...] + raw: tuple[str, ...] + + +def parse_centralised_mv_row(row: str) -> MvhrRecord: + """Decode one Table 323 (Centralised MEV / MVHR) Format-431 row into a + typed `MvhrRecord`. The header block holds the pcdb_id + manufacturer + identifiers + main type + tested duct type; the variable-length test + block carries one 4-field group per wet-room configuration.""" + fields = tuple(row.rstrip("\r\n").split(",")) + num_configs = _parse_optional_int(fields[_CMV_IDX_NUM_CONFIGS]) or 0 + points: list[MvhrDataPoint] = [] + for j in range(num_configs): + start = _CMV_GROUP_START + j * _CMV_GROUP_STRIDE + if start + _CMV_GROUP_STRIDE > len(fields): + break + wet_rooms_str = fields[start].strip() + if not wet_rooms_str: + continue + points.append( + MvhrDataPoint( + num_wet_rooms=int(wet_rooms_str), + flow_rate_l_per_s=_parse_optional_float(fields[start + 1]), + sfp_w_per_l_per_s=_parse_optional_float(fields[start + 2]), + efficiency_pct=_parse_optional_float(fields[start + 3]), + ) + ) + return MvhrRecord( + pcdb_id=int(fields[0]), + brand_name=fields[_CMV_IDX_BRAND_NAME], + model_name=fields[_CMV_IDX_MODEL_NAME], + model_qualifier=fields[_CMV_IDX_MODEL_QUALIFIER], + main_type=_parse_optional_int(fields[_CMV_IDX_MAIN_TYPE]), + duct_type_tested=_parse_optional_int(fields[_CMV_IDX_DUCT_TESTED]), + data_points=tuple(points), + raw=fields, + ) + + +def parse_table_323(dat_text: str) -> list[MvhrRecord]: + """Walk a PCDB dat string, yielding parsed Table 323 (Centralised MEV + and MVHR) records via `parse_centralised_mv_row`. Mirror of + `parse_table_322` for the decentralised-MEV table.""" + return [parse_centralised_mv_row(row) for row in _walk_table_records(dat_text, "323")] diff --git a/tests/domain/sap10_calculator/test_pcdb_table_323_lookup.py b/tests/domain/sap10_calculator/test_pcdb_table_323_lookup.py new file mode 100644 index 00000000..22be55c4 --- /dev/null +++ b/tests/domain/sap10_calculator/test_pcdb_table_323_lookup.py @@ -0,0 +1,105 @@ +"""Tests for the runtime PCDB Table 323 (Centralised MEV and MVHR) lookup. + +The lookup loads pcdb_table_323_centralised_mev_mvhr.jsonl at import time +and caches a typed `MvhrRecord` per pcdb_id. `cert_to_inputs` invokes +`mvhr_record(pcdb_id)` to obtain the per-wet-room test points (SFP + +heat-recovery efficiency), selects the data point matching the dwelling's +wet-room count (SAP 10.2 §2.6.4: "MVHR ... SFP is a single value +depending on the number of wet rooms"), and applies the PCDB Table 329 +in-use factors before feeding the (23c)/(230a) cascade. + +Field positions are documented in PCDF Spec Rev 6b §A.18 (Format 426); +pcdb10.dat (header `$323,431,...`) carries Format 431, which compresses +each per-configuration test group to a (num_wet_rooms, flow_l_per_s, +sfp_w_per_l_per_s, efficiency_pct) tuple. + +Reference: BRE PCDB pcdb10.dat (April 2026); PCDF Spec Rev 6b §A.18; +SAP 10.2 specification (14-03-2025) §2.6.4/§2.6.6. +""" + +from __future__ import annotations + +from domain.sap10_calculator.tables.pcdb import ( + mv_in_use_factors_record, + mvhr_record, +) + + +def test_mvhr_record_returns_verified_vent_axia_500140_header() -> None: + """Vent Axia Sentinel Kinetic B, PCDB index 500140 — the MVHR unit of + the worksheet-ground-truthed simulated case 49 (000565 semi + MVHR + + gas combi → Elmhurst Current SAP 72). + + Header fields cross-referenced against pcdb10.dat: + brand_name: "Vent Axia" + model_name: "Sentinel Kinetic B" + main_type: 3 (balanced whole-house MV per PCDF §A.18 field 11) + duct_type_tested: 2 (rigid per PCDF §A.18 field 13) + """ + # Arrange / Act + record = mvhr_record(500140) + + # Assert + assert record is not None + assert record.pcdb_id == 500140 + assert record.brand_name == "Vent Axia" + assert record.model_name == "Sentinel Kinetic B" + assert record.main_type == 3 + assert record.duct_type_tested == 2 + + +def test_mvhr_record_500140_data_point_selected_by_wet_rooms() -> None: + """SAP 10.2 §2.6.4 selects the MVHR SFP/efficiency by the dwelling's + wet-room count. Record 500140 lodges five wet-room configurations; + the case-49 dwelling (000565, 2 wet rooms) resolves to the group whose + leading field is 2 → flow 21.0 l/s, SFP 0.88 W/(l/s), efficiency 91% + (the values echoed on the U985 worksheet section 2). + """ + # Arrange / Act + record = mvhr_record(500140) + + # Assert + assert record is not None + by_wet = {p.num_wet_rooms: p for p in record.data_points} + assert set(by_wet.keys()) == {1, 2, 3, 4, 5} + # Worksheet ground truth: 2 wet rooms → SFP 0.88, efficiency 91%. + assert by_wet[2].flow_rate_l_per_s == 21.0 + assert by_wet[2].sfp_w_per_l_per_s == 0.88 + assert by_wet[2].efficiency_pct == 91.0 + # The 1-wet-room point is distinct (lower flow / SFP, same efficiency). + assert by_wet[1].flow_rate_l_per_s == 15.0 + assert by_wet[1].sfp_w_per_l_per_s == 0.76 + assert by_wet[1].efficiency_pct == 91.0 + + +def test_mvhr_record_returns_none_for_unknown_pcdb_id() -> None: + """An index not in Table 323 returns None so callers fall back to the + SAP 10.2 Table 4g default MVHR data (raw SFP 2.0, efficiency 66%).""" + # Arrange / Act + record = mvhr_record(99999999) + + # Assert + assert record is None + + +def test_table_329_decodes_mvhr_efficiency_in_use_factor_for_balanced_mv() -> None: + """PCDB Table 329 (Format 432) system_type 3 (balanced whole-house MV) + carries the MVHR heat-recovery efficiency in-use factor for ducts + inside the heated envelope = 0.90 (case-49 worksheet (23c) = 91 × 0.90 + = 81.9%), plus the rigid/flexible SFP in-use factors 1.4 / 1.7. + + The default-data row (system_type 10) carries the SAP 10.2 Table 4g + default heat-recovery in-use factor 0.70 in the same column — a + cross-check that the decoded column is the efficiency factor. + """ + # Arrange / Act + balanced = mv_in_use_factors_record(3) + default = mv_in_use_factors_record(10) + + # Assert + assert balanced is not None + assert balanced.sfp_iuf_rigid_no_scheme == 1.4 + assert balanced.sfp_iuf_flexible_no_scheme == 1.7 + assert balanced.mvhr_efficiency_iuf_inside_no_scheme == 0.9 + assert default is not None + assert default.mvhr_efficiency_iuf_inside_no_scheme == 0.7 From 7b30b464e59170dc7aa43414a5abab5b38bd7b54 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 19:45:37 +0000 Subject: [PATCH 11/19] feat(ventilation): credit MVHR (24a) heat recovery via PCDB Table 323 + 329 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MVHR (24a) heat-recovery support, part 2: the mapper + cascade wiring. Both source paths now resolve balanced whole-house MV with heat recovery to the MVHR kind: - gov-API: `_API_MECHANICAL_VENTILATION_TO_KIND` 4 → "MVHR" (was None / treated as natural — under-stated ventilation heat loss, over-rating). - Elmhurst Summary: `_ELMHURST_MV_TYPE_TO_KIND` "Mechanical ventilation with heat recovery (MVHR)" → "MVHR" (was UnmappedElmhurstLabel, which blocked the whole Summary for MVHR dwellings). cert_to_inputs resolves the in-use heat-recovery efficiency + SFP for an MVHR cert (`_mvhr_system_values`): pick the PCDB Table 323 data point by the lodged wet-room count (SAP 10.2 §2.6.4), multiply the raw efficiency by the Table 329 ducts-inside-envelope in-use factor (0.90) and the raw SFP by the per-duct-type factor (rigid 1.4), and feed: - the §2.6.6 eq (2) effective-air-change credit (23c) → (24a)/(25)m; - the (230a) fan electricity (in-use SFP × 1.22 × V), costed but NOT added to the Table 5a gains (its effect is in the efficiency). An MVHR lodged with no PCDF index falls back to the SAP 10.2 Table 4g default (raw efficiency 66% × 0.70, raw SFP 2.0 × 2.5). Worksheet-proven on simulated case 49 (000565 semi + Vent Axia Sentinel Kinetic B 500140 + gas combi → Elmhurst Current SAP 72): every MVHR line matches Elmhurst exactly — (33) fabric heat loss 100.5923, (23c) in-use efficiency 81.9% = 91 × 0.90, (25)m Jan 0.8571, (230a) fan electricity 415.9325, (231) total pumps/fans 501.9325. The residual SAP 71 vs 72 is the known 000565-family space-heating-demand artifact (same -1/-2 seen on cases 47/48), not the MVHR logic. Corpus: within-0.5 72.6% -> 72.7%, MAE 0.788 -> 0.782, PE 3.6 -> 3.5. The 3 gov-API MVHR certs: Flat 1 +6 -> 0 (Table 4g default path) and 12a Princes Gate +3 -> +1 (heat-recovery credit); Apartment 707 -4 -> -6 is a separate baseline under-rate (it under-rated as natural too — the MVHR credit correctly adds ventilation loss per Elmhurst's method). Ratcheted _MAX_SAP_MAE 0.79 -> 0.785, _MAX_PE_PER_M2_MAE 3.7 -> 3.6. Note: pyright strict type gate not run locally (pyright not installed). Co-Authored-By: Claude Opus 4.8 (1M context) --- datatypes/epc/domain/mapper.py | 18 +-- .../sap10_calculator/rdsap/cert_to_inputs.py | 117 ++++++++++++++++++ .../rdsap/test_cert_to_inputs.py | 87 ++++++++++++- .../epc_client/test_sap_accuracy_corpus.py | 13 +- 4 files changed, 220 insertions(+), 15 deletions(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 40c32b0c..7ec2cd5d 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3928,20 +3928,19 @@ def _api_sheltered_sides(built_form: object) -> Optional[int]: # loft-sourced PIV as natural # (no added system air change) # 6 positive input from outside → EXTRACT_OR_PIV_OUTSIDE (24c) -# Code 4 (MVHR, 24a) is DEFERRED: its (24a)m formula needs the lodged -# heat-recovery efficiency (`mvhr_efficiency_pct`, PCDB Table 326) which the -# API→cascade path does not yet plumb; mapping it to MVHR with a null -# efficiency would mis-model it as MV (no recovery), so it stays NATURAL -# until the efficiency is wired. The extract systems (2/3/6) carry no -# efficiency, so this slice closes the clean +1.90 SAP over-rate on the -# MEV/PIV-outside cohort (n=20, 5% within 0.5) — they were silently -# defaulting to NATURAL, under-stating ventilation heat loss. +# Code 4 (MVHR, 24a) resolves to MVHR: `cert_to_inputs` reads the lodged +# heat-recovery efficiency from PCDB Table 323 (per wet-room count) × +# Table 329 in-use factor and feeds the §2.6.6 equation (2) credit. The +# 3 corpus MVHR certs carry a PCDF index (`mechanical_ventilation_index_ +# number`) + wet-room + duct lodgements; an MVHR lodged with no PCDF index +# falls back to the SAP 10.2 Table 4g default (efficiency 66% × 0.70). +# The extract systems (2/3/6) carry no efficiency. _API_MECHANICAL_VENTILATION_TO_KIND: Final[dict[int, Optional[str]]] = { 0: None, 1: "MV", 2: "EXTRACT_OR_PIV_OUTSIDE", 3: "EXTRACT_OR_PIV_OUTSIDE", - 4: None, # MVHR — efficiency plumbing deferred; treat as natural + 4: "MVHR", # balanced whole-house MV with heat recovery (24a) 5: None, 6: "EXTRACT_OR_PIV_OUTSIDE", } @@ -7564,6 +7563,7 @@ _ELMHURST_MV_TYPE_TO_KIND: Dict[str, str] = { # formula choice; the cascade resolves enum name → enum value when # picking which (25)m formula to apply. "Mechanical extract, decentralised (MEV dc)": "EXTRACT_OR_PIV_OUTSIDE", + "Mechanical ventilation with heat recovery (MVHR)": "MVHR", } diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 72c9d4d8..be7faa47 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -75,10 +75,13 @@ from domain.sap10_calculator.tables.pcdb import ( gas_oil_boiler_record, heat_pump_record, mv_in_use_factors_record, + mvhr_record, ) from domain.sap10_calculator.tables.pcdb.parser import ( GasOilBoilerRecord, HeatPumpRecord, + MvhrDataPoint, + MvhrRecord, ) from domain.sap10_calculator.tables.pcdb.postcode_weather import ( PostcodeClimate, @@ -525,6 +528,9 @@ def _table_4f_additive_components(epc: EpcPropertyData) -> float: """ total = 0.0 total += _mev_decentralised_kwh_per_yr_from_cert(epc) + # (230a) balanced MVHR fan electricity — SFP × 1.22 × V (SAP 10.2 + # §2.6.6). Costed here but kept out of the Table 5a gains. + total += _mvhr_fan_kwh_per_yr_from_cert(epc) details = epc.sap_heating.main_heating_details if epc.sap_heating else [] if details: main_1 = details[0] @@ -709,6 +715,106 @@ def _mev_decentralised_kwh_per_yr_from_cert(epc: EpcPropertyData) -> float: sfp_av_w_per_l_per_s=sfp_av, dwelling_volume_m3=dimensions.volume_m3, ) + + +# PCDB Table 329 / 323 system_type 3 = balanced whole-house MV (with or +# without heat recovery). +_MV_BALANCED_SYSTEM_TYPE: Final[int] = 3 +# SAP 10.2 Table 4g (PDF p.176) defaults for an MVHR system NOT in the +# PCDB: raw heat-recovery efficiency 66% + raw SFP 2.0 W/(l/s). Each is +# then multiplied by the default-data in-use factor (Table 329 +# system_type 10 — efficiency 0.70 inside the envelope, SFP 2.5). +_TABLE_4G_DEFAULT_MVHR_EFFICIENCY_PCT: Final[float] = 66.0 +_TABLE_4G_DEFAULT_MVHR_SFP_W_PER_L_PER_S: Final[float] = 2.0 + + +@dataclass(frozen=True) +class _MvhrSystemValues: + """In-use SFP + heat-recovery efficiency for an MVHR dwelling, with + the PCDB Table 329 in-use factors already folded in.""" + + in_use_sfp_w_per_l_per_s: float + in_use_efficiency_pct: float + + +def _select_mvhr_data_point(record: MvhrRecord, wet_rooms_count: int) -> MvhrDataPoint: + """SAP 10.2 §2.6.4: select the MVHR data point by the dwelling's + wet-room count (each Table 323 group is keyed by wet rooms). Exact + match where lodged; otherwise clamp to the tested range. Falls back + to the smallest-wet-room group when the cert lodges 0 (unlodged).""" + points = sorted(record.data_points, key=lambda p: p.num_wet_rooms) + target = wet_rooms_count if wet_rooms_count > 0 else points[0].num_wet_rooms + for point in points: + if point.num_wet_rooms == target: + return point + if target < points[0].num_wet_rooms: + return points[0] + return points[-1] + + +def _mvhr_system_values(epc: EpcPropertyData) -> Optional[_MvhrSystemValues]: + """Resolve the in-use SFP + heat-recovery efficiency for an MVHR + dwelling, or None when the cert is not MVHR. + + PCDB path (index lodged + Table 323 hit): select the data point whose + wet-room count matches the lodgement, then apply the Table 329 + system_type-3 in-use factors — SFP per the lodged duct type (rigid + 1.4 / flexible 1.7); heat-recovery efficiency for ducts inside the + heated envelope (0.90). Worksheet-proven on simulated case 49 (000565, + 2 wet rooms, Vent Axia 500140 → raw SFP 0.88 × 1.4 = 1.232; raw + efficiency 91 × 0.90 = 81.9% = worksheet (23c)). + + Default path (no PCDB record, e.g. an MVHR lodged with no PCDF index): + SAP 10.2 Table 4g raw SFP 2.0 / efficiency 66%, × the default-data + in-use factors (Table 329 system_type 10 — SFP 2.5, efficiency 0.70). + + Duct type defaults to rigid when unlodged (the Elmhurst Summary path + captures it but not all sources do); semi-rigid maps to rigid per + SAP 10.2 §2.6.8. Only the ducts-inside-envelope efficiency factor is + worksheet-validated (all corpus + worksheet MVHR certs lodge it). + """ + sv = epc.sap_ventilation + if sv is None or sv.mechanical_ventilation_kind != MechanicalVentilationKind.MVHR.name: + return None + pcdf_id = epc.mechanical_ventilation_index_number + record = mvhr_record(pcdf_id) if pcdf_id is not None else None + if record is not None and record.data_points: + point = _select_mvhr_data_point(record, epc.wet_rooms_count) + raw_sfp = point.sfp_w_per_l_per_s + raw_efficiency_pct = point.efficiency_pct + iuf_record = mv_in_use_factors_record(_MV_BALANCED_SYSTEM_TYPE) + else: + raw_sfp = _TABLE_4G_DEFAULT_MVHR_SFP_W_PER_L_PER_S + raw_efficiency_pct = _TABLE_4G_DEFAULT_MVHR_EFFICIENCY_PCT + iuf_record = mv_in_use_factors_record(_MEV_DEFAULT_DATA_SYSTEM_TYPE) + if iuf_record is None or raw_sfp is None or raw_efficiency_pct is None: + return None + if epc.mechanical_vent_duct_type == _MV_DUCT_TYPE_FLEXIBLE: + sfp_iuf = iuf_record.sfp_iuf_flexible_no_scheme + else: + sfp_iuf = iuf_record.sfp_iuf_rigid_no_scheme + efficiency_iuf = iuf_record.mvhr_efficiency_iuf_inside_no_scheme + if sfp_iuf is None or efficiency_iuf is None: + return None + return _MvhrSystemValues( + in_use_sfp_w_per_l_per_s=raw_sfp * sfp_iuf, + in_use_efficiency_pct=raw_efficiency_pct * efficiency_iuf, + ) + + +def _mvhr_fan_kwh_per_yr_from_cert(epc: EpcPropertyData) -> float: + """SAP 10.2 §5 Table 4f line (230a) annual fan electricity for an + MVHR dwelling: in-use SFP × 1.22 × V (the same (230a) shape as + decentralised MEV). Returns 0.0 when the cert is not MVHR. Per SAP + 10.2 §2.6.6 this electricity is costed but NOT added to the Table 5a + gains (its effect is already in the heat-recovery efficiency).""" + values = _mvhr_system_values(epc) + if values is None: + return 0.0 + return mev_decentralised_kwh_per_yr( + sfp_av_w_per_l_per_s=values.in_use_sfp_w_per_l_per_s, + dwelling_volume_m3=dimensions_from_cert(epc).volume_m3, + ) # SAP10.2 Table 6d note 1: "average or unknown" overshading is the # default for existing dwellings. RdSAP doesn't lodge a per-dwelling # overshading code so §5 always uses AVERAGE → Z_L = 0.83. @@ -5101,6 +5207,16 @@ def ventilation_from_cert( # MVHR/MV kinds are left untouched pending their own worksheet. if mv_kind is MechanicalVentilationKind.EXTRACT_OR_PIV_OUTSIDE: intermittent_fans = vc.intermittent_fans + # SAP 10.2 §2.6.6 equation (2): the (24a) MVHR effective-air-change + # credit needs the in-use heat-recovery efficiency (23c) = raw PCDB + # efficiency × Table 329 in-use factor (or the Table 4g default when + # no PCDB record). None for non-MVHR kinds → ventilation_from_inputs + # leaves the heat-recovery term at zero. + mvhr_efficiency_pct: Optional[float] = None + if mv_kind is MechanicalVentilationKind.MVHR: + mvhr_values = _mvhr_system_values(epc) + if mvhr_values is not None: + mvhr_efficiency_pct = mvhr_values.in_use_efficiency_pct return ventilation_from_inputs( volume_m3=vol, storey_count=storeys, @@ -5123,6 +5239,7 @@ def ventilation_from_cert( air_permeability_ap4=ap4, mv_kind=mv_kind, mv_system_ach=mv_system_ach, + mvhr_efficiency_pct=mvhr_efficiency_pct, **wind_kwargs, ) diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index 175ec7c0..fa6dc2ba 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -3773,19 +3773,98 @@ def test_api_mechanical_ventilation_maps_extract_systems_to_cascade_kind() -> No ) # Act / Assert — extract / positive-input-from-outside systems carry - # the (24c) kind; MV-no-HR carries (24b); natural and PIV-from-loft - # stay NATURAL; MVHR (4) is deferred (efficiency not yet plumbed) and - # stays NATURAL rather than mis-modelling as MV. + # the (24c) kind; MV-no-HR carries (24b); MVHR (4) carries (24a) (its + # heat-recovery efficiency is read from PCDB Table 323 in cert_to_ + # inputs); natural and PIV-from-loft stay NATURAL. assert _api_mechanical_ventilation_kind(0) is None # natural assert _api_mechanical_ventilation_kind(1) == "MV" # MV, no HR assert _api_mechanical_ventilation_kind(2) == "EXTRACT_OR_PIV_OUTSIDE" # MEV dc assert _api_mechanical_ventilation_kind(3) == "EXTRACT_OR_PIV_OUTSIDE" # MEV c - assert _api_mechanical_ventilation_kind(4) is None # MVHR (deferred) + assert _api_mechanical_ventilation_kind(4) == "MVHR" # balanced + HR (24a) assert _api_mechanical_ventilation_kind(5) is None # PIV from loft assert _api_mechanical_ventilation_kind(6) == "EXTRACT_OR_PIV_OUTSIDE" # PIV outside assert _api_mechanical_ventilation_kind(None) is None +def test_mvhr_system_values_apply_pcdb_wet_room_point_and_table_329_iufs() -> None: + # Arrange — simulated case 49 (000565 semi + MVHR + gas combi → + # Elmhurst Current SAP 72). The dwelling lodges Vent Axia Sentinel + # Kinetic B (PCDB Table 323 index 500140), 2 wet rooms, rigid ducting. + # SAP 10.2 §2.6.4 selects the 2-wet-room data point (raw SFP 0.88, + # efficiency 91%); the Table 329 system_type-3 in-use factors are SFP + # rigid 1.4 + heat-recovery-efficiency-inside-envelope 0.90. + import dataclasses + + from domain.sap10_calculator.rdsap.cert_to_inputs import ( + _mvhr_fan_kwh_per_yr_from_cert, # pyright: ignore[reportPrivateUsage] + _mvhr_system_values, # pyright: ignore[reportPrivateUsage] + dimensions_from_cert, + ) + from tests.domain.sap10_calculator.worksheet import ( + _elmhurst_worksheet_000565 as _w000565, + ) + + base = _w000565.build_epc() + assert base.sap_ventilation is not None + epc = dataclasses.replace( + base, + mechanical_ventilation_index_number=500140, + wet_rooms_count=2, + mechanical_vent_duct_type=2, # rigid + sap_ventilation=dataclasses.replace( + base.sap_ventilation, mechanical_ventilation_kind="MVHR" + ), + ) + + # Act + values = _mvhr_system_values(epc) + fan_kwh = _mvhr_fan_kwh_per_yr_from_cert(epc) + + # Assert — worksheet (23c) = 91 × 0.90 = 81.9%; in-use SFP = 0.88 × + # 1.4 = 1.232 W/(l/s). The (230a) fan electricity is in-use SFP × + # 1.22 × V; on the case-49 Summary (V = 276.7275 m³) this is exactly + # 415.9325 kWh (verified via run_elmhurst_summary). Here we pin the + # formula against this fixture's own volume (its geometry differs). + assert values is not None + assert abs(values.in_use_efficiency_pct - 81.9) <= 1e-6 + assert abs(values.in_use_sfp_w_per_l_per_s - 1.232) <= 1e-9 + expected_fan_kwh = 1.232 * 1.22 * dimensions_from_cert(epc).volume_m3 + assert abs(fan_kwh - expected_fan_kwh) <= 1e-6 + + +def test_mvhr_system_values_fall_back_to_table_4g_defaults_without_pcdb_index() -> None: + # Arrange — an MVHR cert lodged with NO PCDF index (corpus cert + # "Flat 1"). SAP 10.2 Table 4g (PDF p.176): default raw efficiency + # 66% + raw SFP 2.0, × the default-data in-use factors (Table 329 + # system_type 10 — efficiency-inside 0.70, SFP 2.5). + import dataclasses + + from domain.sap10_calculator.rdsap.cert_to_inputs import ( + _mvhr_system_values, # pyright: ignore[reportPrivateUsage] + ) + from tests.domain.sap10_calculator.worksheet import ( + _elmhurst_worksheet_000565 as _w000565, + ) + + base = _w000565.build_epc() + assert base.sap_ventilation is not None + epc = dataclasses.replace( + base, + mechanical_ventilation_index_number=None, + sap_ventilation=dataclasses.replace( + base.sap_ventilation, mechanical_ventilation_kind="MVHR" + ), + ) + + # Act + values = _mvhr_system_values(epc) + + # Assert — efficiency 66 × 0.70 = 46.2%; SFP 2.0 × 2.5 = 5.0. + assert values is not None + assert abs(values.in_use_efficiency_pct - 46.2) <= 1e-6 + assert abs(values.in_use_sfp_w_per_l_per_s - 5.0) <= 1e-9 + + def test_api_mechanical_ventilation_unmapped_code_raises() -> None: # Arrange — an out-of-range `mechanical_ventilation` integer is a # spec-coverage gap: raise rather than silently default to NATURAL diff --git a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py index e8b47d6a..dd6e322c 100644 --- a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py +++ b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py @@ -201,9 +201,18 @@ _MIN_WITHIN_HALF_SAP = 0.72 # dMEV intermittent-fan fix: an EXTRACT_OR_PIV_OUTSIDE cert lodging 0 fans now # takes 0 (not the Table 5 age-band default) — same "case 48" worksheet closes # its space-heating demand to land SAP 57 exact. -_MAX_SAP_MAE = 0.79 +# 0.788 -> 0.782 (within-0.5 72.6% -> 72.7%, PE 3.6 -> 3.5) via MVHR (24a) heat- +# recovery support: the gov-API `mechanical_ventilation=4` cohort (balanced +# whole-house MV with heat recovery) was treated as natural; now credited via +# PCDB Table 323 efficiency × Table 329 in-use factor and the §2.6.6 eq (2) +# effective-air-change formula + (230a) fan electricity. Worksheet-proven on +# simulated case 49 (Vent Axia 500140, every MVHR line — (33) 100.5923, (23c) +# 81.9%, (230a) 415.9325, (231) 501.9325 — matching Elmhurst exactly). Corpus +# MVHR certs: Flat 1 +6 -> 0, 12a Princes Gate +3 -> +1; Apartment 707's -4 -> +# -6 is a separate baseline under-rate (it under-rated as natural too). +_MAX_SAP_MAE = 0.785 _MAX_CO2_MAE_TONNES = 0.09 # t CO2 / yr vs co2_emissions_current -_MAX_PE_PER_M2_MAE = 3.7 # kWh / m2 / yr vs energy_consumption_current +_MAX_PE_PER_M2_MAE = 3.6 # kWh / m2 / yr vs energy_consumption_current def _load_corpus() -> list[dict[str, Any]]: From aad40509157bb07278289473c6e5185b46c275de Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 19:46:41 +0000 Subject: [PATCH 12/19] fix(hyde): harden Elmhurst browser automation for the dev container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In-container Playwright runs were flaky: the chromium renderer crashed mid-build ("Target crashed") on the 64M /dev/shm, and login intermittently hung. Added `--disable-dev-shm-usage` + `--no-sandbox` launch args, a 4-attempt login retry loop (domcontentloaded + explicit selector wait), and an `ELM_GUID` env override so a per-UPRN assessment can be targeted without editing the module. Tooling only — no calculator impact. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/hyde/elmhurst_lib.py | 38 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/scripts/hyde/elmhurst_lib.py b/scripts/hyde/elmhurst_lib.py index 4f137988..9b27443d 100644 --- a/scripts/hyde/elmhurst_lib.py +++ b/scripts/hyde/elmhurst_lib.py @@ -64,8 +64,12 @@ HERE = Path(__file__).parent SESSION_DIR = HERE / ".elmhurst-session" CREDS_FILE = HERE / ".elmhurst-creds.json" # gitignored; {"access":..,"pwd":..} -# The single reusable campaign assessment ("Khalim-test"). Overwrite it per UPRN. -ASSESSMENT_GUID = "B44A0DB4-4C08-4241-B818-86F060172105" +# The reusable campaign assessment ("Khalim-test"), overwritten per UPRN. Can be +# overridden per run via the `ELM_GUID` env var (e.g. a per-UPRN assessment under +# a different account) — see Limitations "parameterize the GUID". +ASSESSMENT_GUID = os.environ.get( + "ELM_GUID", "B44A0DB4-4C08-4241-B818-86F060172105" +) ENTRY_URL = ( "https://rdsap10online.elmhurstenergy.co.uk/Processing/WebFormAddress.aspx" f"?Guid={ASSESSMENT_GUID}" @@ -98,17 +102,31 @@ def session() -> Generator[tuple[BrowserContext, Page], None, None]: headless=False, accept_downloads=True, viewport={"width": 1400, "height": 1000}, + # Container /dev/shm is tiny (64M) → chromium renderer "Target + # crashed" mid-build without this. --no-sandbox for rootless Xvfb. + args=["--disable-dev-shm-usage", "--no-sandbox"], ) page = ctx.pages[0] if ctx.pages else ctx.new_page() page.on("dialog", lambda d: d.accept()) - page.goto(ENTRY_URL, wait_until="networkidle", timeout=60_000) - if "WebFormLogin" in page.url: - access, pwd = _creds() - page.fill("#ctl00_ctl00_ContentBody_ContentBody_TextBoxAccessCode", access) - page.fill("#ctl00_ctl00_ContentBody_ContentBody_TextBoxPassword", pwd) - with page.expect_navigation(wait_until="networkidle", timeout=60_000): - page.click("#ctl00_ctl00_ContentBody_ContentBody_ImageButtonEnter") - page.wait_for_timeout(1000) + # Login can be slow/flaky; retry the whole goto+fill a few times. + for attempt in range(4): + try: + page.goto(ENTRY_URL, wait_until="domcontentloaded", timeout=60_000) + if "WebFormLogin" not in page.url: + break # already authed (server session still valid) + access, pwd = _creds() + ac = "#ctl00_ctl00_ContentBody_ContentBody_TextBoxAccessCode" + page.wait_for_selector(ac, state="visible", timeout=30_000) + page.fill(ac, access) + page.fill("#ctl00_ctl00_ContentBody_ContentBody_TextBoxPassword", pwd) + with page.expect_navigation(wait_until="networkidle", timeout=60_000): + page.click("#ctl00_ctl00_ContentBody_ContentBody_ImageButtonEnter") + page.wait_for_timeout(1000) + if "WebFormLogin" not in page.url: + break + except PlaywrightTimeoutError: + print(f" login attempt {attempt+1} timed out, retrying...", flush=True) + page.wait_for_timeout(2000) try: yield ctx, page finally: From b7455aabe5d7865a0060aec9fbfb4fb85a6e56ab Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 20:15:32 +0000 Subject: [PATCH 13/19] fix(ventilation): MVHR takes lodged 0 intermittent fans, not the Table 5 default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the dMEV intermittent-fan fix (4db05e84) to MVHR. A balanced whole-house MVHR system IS the dwelling's ventilation, so the lodged (7a) intermittent-extract-fan count is explicit — a lodged 0 means 0, not the RdSAP 10 Table 5 age-band "unknown" default. The cascade was substituting the default (here 20 m³/h) into worksheet line (8) openings, inflating (16/18) infiltration → (21) → (22b) → (25) effective ach → (38) ventilation heat loss → the space-heating demand. Worksheet-proven on simulated case 49 (000565 + Vent Axia 500140 MVHR, lodged (7a)=0): our (8) openings 0.0723 -> 0.0000, (18) 0.7223 -> 0.6500, (25)m Jan 0.9423 -> 0.8571, all now matching Elmhurst exactly; space- heating demand 7857 -> 7528 kWh (worksheet 7546). SAP 70.90 -> 71.43 continuous. (The residual to the worksheet's 72 is its own continuous SAP 71.69 rounding up, driven by a separate gas-combi water-heating-loss gap, not ventilation.) Scoped to EXTRACT_OR_PIV_OUTSIDE + MVHR only — MV-without-HR (mechanical_ventilation=1) stays on the default-substitution path (forcing its lodged 0 regressed 47 Howsman / 18 Jutland and is not worksheet-validated). Corpus within-0.5 holds 72.7%, MAE 0.782 -> 0.781. Note: pyright strict type gate not run locally (pyright not installed). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/rdsap/cert_to_inputs.py | 27 ++++++++++++------- .../epc_client/test_sap_accuracy_corpus.py | 5 ++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index be7faa47..7767c788 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -5196,16 +5196,23 @@ def ventilation_from_cert( # can override via a future plumbing slice; the spec default # is what every MEV / MV / MVHR cohort cert lodges today. mv_system_ach = 0.5 - # For a whole-house mechanical EXTRACT system (MEV / dMEV) the - # lodged intermittent extract-fan count (7a) is taken AS-IS — the - # Table 5 age-band default must NOT be substituted for a lodged 0. - # On a mechanically-ventilated dwelling the fan count is explicit - # (the dMEV is the ventilation), so 0 means 0, not "unknown". - # Worksheet-proven on two dMEV builds of 000565: "case 48" lodges - # (7a)=0 → SAP 57 exact, while the original 000565 fixture lodges - # (7a)=2 → unchanged. Scoped to EXTRACT_OR_PIV_OUTSIDE; balanced - # MVHR/MV kinds are left untouched pending their own worksheet. - if mv_kind is MechanicalVentilationKind.EXTRACT_OR_PIV_OUTSIDE: + # For a whole-house mechanical EXTRACT (MEV / dMEV) OR balanced- + # with-heat-recovery (MVHR) system the lodged intermittent extract- + # fan count (7a) is taken AS-IS — the Table 5 age-band default must + # NOT be substituted for a lodged 0. On such a dwelling the fan + # count is explicit (the mechanical system IS the ventilation), so + # 0 means 0, not "unknown". Worksheet-proven on three 000565 builds: + # "case 48" (dMEV) lodges (7a)=0 → SAP 57 exact; "case 49" (MVHR, + # Vent Axia 500140) lodges (7a)=0 → the worksheet (8) openings line + # is 0.0000 (our default had added 20 m³/h = 0.0723 ach, inflating + # (22b)/(25)/(38) and the demand). The original 000565 fixture + # lodges (7a)=2 → unchanged. MV-without-HR (mechanical_ventilation + # =1) is EXCLUDED: forcing its lodged 0 regressed 47 Howsman / 18 + # Jutland and is not worksheet-validated. + if mv_kind in ( + MechanicalVentilationKind.EXTRACT_OR_PIV_OUTSIDE, + MechanicalVentilationKind.MVHR, + ): intermittent_fans = vc.intermittent_fans # SAP 10.2 §2.6.6 equation (2): the (24a) MVHR effective-air-change # credit needs the in-use heat-recovery efficiency (23c) = raw PCDB diff --git a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py index dd6e322c..2eb5b80a 100644 --- a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py +++ b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py @@ -210,6 +210,11 @@ _MIN_WITHIN_HALF_SAP = 0.72 # 81.9%, (230a) 415.9325, (231) 501.9325 — matching Elmhurst exactly). Corpus # MVHR certs: Flat 1 +6 -> 0, 12a Princes Gate +3 -> +1; Apartment 707's -4 -> # -6 is a separate baseline under-rate (it under-rated as natural too). +# Then 0.782 -> 0.781 via extending the dMEV "lodged 0 extract fans, no Table 5 +# default" rule to MVHR (the balanced system is the ventilation, so a lodged +# (7a)=0 is explicit): case 49's (8) openings line is 0.0000 — our default had +# added 20 m3/h (0.0723 ach), inflating (22b)/(25)/(38) and demand (SAP 70.90 +# -> 71.43; the worksheet's own continuous SAP is 71.69 -> rounds to 72). _MAX_SAP_MAE = 0.785 _MAX_CO2_MAE_TONNES = 0.09 # t CO2 / yr vs co2_emissions_current _MAX_PE_PER_M2_MAE = 3.6 # kWh / m2 / yr vs energy_consumption_current From 9694650abe31fea8d9f8489461743f82c7acecd7 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 21:01:11 +0000 Subject: [PATCH 14/19] fix(water-heating): derive combi keep-hot from the PCDB record, default no-keep-hot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SAP 10.2 Table 3a (PDF p.160) additional combi loss (61)m. Two coupled defects, both surfaced by simulated case 49 (000565 + gas combi, U985 "Combi keep hot type = None") sitting at SAP 71.43 vs the worksheet's 72: 1. The cascade defaulted EVERY non-PCDB combi to the flat keep-hot time-clock row (600 × n/365). A combi WITHOUT a keep-hot facility uses row 1 (600 × fu × n/365, fu = V_d/100 when daily HW < 100 L/day) — over-counting (61)m for the no-keep-hot cohort. `water_heating_from_ cert` now defaults to the "without keep-hot" row. 2. `pcdb_combi_loss_override` returned None for keep_hot_facility=1/ timer=1, leaning on the OLD flat-600 default. So flipping the default silently turned 190 corpus PCDB keep-hot-time-clock combis into no-keep-hot. Fixed to return the flat keep-hot row EXPLICITLY. Key insight (the Summary is the input echo; the U985 keep-hot line is a computed OUTPUT, so it must be derivable): keep-hot rides on the PCDB boiler record (Table 105 keep_hot_facility/timer), resolved by `pcdb_combi_loss_override`. A generic SAP-code combi with no PCDB record (case 49, PCDF ref 0) has no keep-hot by construction → row 1. So the default is not a guess — it is the spec-correct value for non-PCDB combis. Worksheet-proven: case 49 → cost £726.696, SAP 72 — matching the accredited worksheet to the digit (continuous 71.6945 = the worksheet's own 71.6945). 000516 (keep-hot None) also exact (£860.716, SAP 63); 000490 (PCDB 10328, keep_hot_facility=1/timer=1) keeps its flat-600 via the PCDB path. Masked until now because every prior combi-loss worksheet fixture was keep-hot (000490/000474/000480 time-clock) or had V_d >= 100 every month (001431, rows coincide); case 49 is the first no-keep-hot one. Corpus within-0.5 72.7% -> 73.3%, MAE 0.781 -> 0.774, PE 3.5 -> 3.4; ratcheted _MAX_SAP_MAE 0.785 -> 0.775, _MAX_PE_PER_M2_MAE 3.6 -> 3.5. Note: pyright strict type gate not run locally (pyright not installed). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/rdsap/cert_to_inputs.py | 21 ++++-- .../worksheet/water_heating.py | 4 +- .../rdsap/test_cert_to_inputs.py | 73 ++++++++++++++----- .../worksheet/_elmhurst_worksheet_000490.py | 14 +++- .../worksheet/test_water_heating.py | 6 +- .../epc_client/test_sap_accuracy_corpus.py | 15 +++- 6 files changed, 97 insertions(+), 36 deletions(-) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 7767c788..a58dcd57 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -200,6 +200,7 @@ from domain.sap10_calculator.worksheet.water_heating import ( PIPEWORK_INSULATED_UNINSULATED, TABLE_J1_TCOLD_FROM_MAINS_C, WaterHeatingResult, + combi_loss_monthly_kwh_table_3a_keep_hot_time_clock, combi_loss_monthly_kwh_table_3a_row_1_no_keep_hot, combi_loss_monthly_kwh_table_3a_row_4_keep_hot_no_time_clock, combi_loss_monthly_kwh_table_3b_row_1_instantaneous, @@ -5385,10 +5386,11 @@ def pcdb_combi_loss_override( ) if kh == 1: if timer == 1: - # SAP 10.2 Table 3a row 3: 600 × n_m / 365. Cascade's - # `water_heating_from_cert` default — return None so the - # default fires. - return None + # SAP 10.2 Table 3a row 3: 600 × n_m / 365 (keep-hot, time + # clock). Returned EXPLICITLY — the cascade default is now + # the "without keep-hot" row, so the keep-hot value must be + # delivered here rather than leaned on the default. + return combi_loss_monthly_kwh_table_3a_keep_hot_time_clock() # SAP 10.2 Table 3a row 4: 900 × n_m / 365 (no time-clock). return combi_loss_monthly_kwh_table_3a_row_4_keep_hot_no_time_clock() # kh ∈ {2, 3} — electric or mixed keep-hot. Table 3a Note 2 routes @@ -6540,11 +6542,14 @@ def _water_heating_worksheet_and_gains( main ): # SAP 10.2 §4 line 7702 fallback: non-combi main heating → (61)m - # = 0. Without this gate the cascade falls through to `combi_ - # loss_monthly_kwh_table_3a_keep_hot_time_clock()` (600 kWh/yr) - # on every cert lacking a PCDB Table 105 boiler record — - # including all heat pump certs. + # = 0. Without this gate the cascade falls through to the Table 3a + # "without keep-hot" default on every cert lacking a PCDB Table + # 105 boiler record — including all heat pump certs. combi_loss_override = zero_monthly + # A non-PCDB combi (override still None here) has no PCDB-declared + # keep-hot facility → `water_heating_from_cert` applies the Table 3a + # "without keep-hot" row (600 × fu × n/365). Keep-hot rides only on the + # PCDB boiler record, resolved above by `pcdb_combi_loss_override`. # SAP 10.2 §4 lines 7670-7693 + Tables 2/2a/2b — cylinder storage loss # (56)m. Spec p.135 instructs entering 0 in (47) for instantaneous / # combi systems, so the override is only built when the cert explicitly diff --git a/domain/sap10_calculator/worksheet/water_heating.py b/domain/sap10_calculator/worksheet/water_heating.py index bf0dd475..dad601e2 100644 --- a/domain/sap10_calculator/worksheet/water_heating.py +++ b/domain/sap10_calculator/worksheet/water_heating.py @@ -960,7 +960,9 @@ def water_heating_from_cert( combi = ( combi_loss_monthly_kwh_override if combi_loss_monthly_kwh_override is not None - else combi_loss_monthly_kwh_table_3a_keep_hot_time_clock() + else combi_loss_monthly_kwh_table_3a_row_1_no_keep_hot( + daily_hot_water_monthly_l_per_day=daily_total, + ) ) zero12 = (0.0,) * 12 solar_storage = ( diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index fa6dc2ba..01dbdcad 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -3865,6 +3865,38 @@ def test_mvhr_system_values_fall_back_to_table_4g_defaults_without_pcdb_index() assert abs(values.in_use_sfp_w_per_l_per_s - 5.0) <= 1e-9 +def test_pcdb_combi_loss_keep_hot_time_clock_returns_explicit_flat_600() -> None: + # Arrange — SAP 10.2 Table 3a (PDF p.160): a PCDB combi declaring a + # keep-hot facility on a time clock (keep_hot_facility=1, keep_hot_ + # timer=1) uses the flat 600 × n/365 row. The keep-hot status rides on + # the PCDB boiler record, NOT a separate cert field — a non-PCDB combi + # has no keep-hot and falls to the "without keep-hot" 600 × fu default. + # pcdb_combi_loss_override must return this row EXPLICITLY (it must not + # lean on the cascade default, which is now "without keep-hot"). + # Vaillant Ecotec Pro (PCDB 10328) is the 000490 boiler. + from domain.sap10_calculator.rdsap.cert_to_inputs import pcdb_combi_loss_override + from domain.sap10_calculator.tables.pcdb import gas_oil_boiler_record + from domain.sap10_calculator.worksheet.water_heating import ( + combi_loss_monthly_kwh_table_3a_keep_hot_time_clock, + ) + + record = gas_oil_boiler_record(10328) + assert record is not None + assert record.keep_hot_facility == 1 + assert record.keep_hot_timer == 1 + monthly = (200.0,) * 12 # energy content / daily HW unused for this row + + # Act + override = pcdb_combi_loss_override( + record, + energy_content_monthly_kwh=monthly, + daily_hot_water_monthly_l_per_day=monthly, + ) + + # Assert — explicit flat keep-hot row, not None. + assert override == combi_loss_monthly_kwh_table_3a_keep_hot_time_clock() + + def test_api_mechanical_ventilation_unmapped_code_raises() -> None: # Arrange — an out-of-range `mechanical_ventilation` integer is a # spec-coverage gap: raise rather than silently default to NATURAL @@ -5599,16 +5631,15 @@ def test_pcdb_combi_loss_override_preserves_separate_dhw_tests_1_routing_to_tabl def test_pcdb_combi_loss_override_returns_none_or_raises_for_untested_or_storage_combis() -> None: - """The override gate returns None — letting the worksheet fall back - to Table 3a row 3 (600 × n/365) — whenever the PCDB record lodges - keep-hot with a time clock but has insufficient EN 13203 lab data, - or sits in a storage / FGHRS row (Table 3b/3c rows 2-5, deferred - until a fixture exercises them). + """The override gate dispatches the PCDB record's keep-hot fields to + the matching Table 3a row, or returns None for a storage / FGHRS row + (Table 3b/3c rows 2-5, deferred until a fixture exercises them). - Per Slice S0380.21: keep_hot_facility ∈ {None, 0} dispatches to - Table 3a row 1 (`600 × fu × n/365`), keep_hot_facility=1 + no - timer dispatches to row 4 (`900 × n/365`). Only the electric - keep-hot variants (keep_hot_facility ∈ {2, 3}) now raise + keep_hot_facility ∈ {None, 0} → Table 3a row 1 (`600 × fu × n/365`, + without keep-hot); keep_hot_facility=1 + timer=1 → row 3 (`600 × + n/365`, EXPLICIT — the cascade default is now "without keep-hot"); + keep_hot_facility=1 + no timer → row 4 (`900 × n/365`). Only the + electric keep-hot variants (keep_hot_facility ∈ {2, 3}) raise `UnresolvedPcdbCombiLoss` — Table 3a Note 2 fuel-split deferred.""" # Arrange — a minimal record skeleton, mutated per scenario via # dataclasses.replace. @@ -5635,7 +5666,7 @@ def test_pcdb_combi_loss_override_returns_none_or_raises_for_untested_or_storage loss_factor_f1_kwh_per_day=0.5, loss_factor_f2_kwh_per_day=0.001, rejected_factor_f3_per_litre=0.00014, - keep_hot_facility=1, # lodges keep-hot → cascade default 600 is correct + keep_hot_facility=1, # lodges keep-hot, time clock → flat 600 row keep_hot_timer=1, raw=(), ) @@ -5649,17 +5680,19 @@ def test_pcdb_combi_loss_override_returns_none_or_raises_for_untested_or_storage ) is None ) - # separate_dhw_tests=0 + keep_hot_facility=1 + timer=1 → None (no - # PCDB DHW test data, but cascade's row 3 default IS the right spec - # row → return None and let the cascade default fire). - assert ( - pcdb_combi_loss_override( - replace(base, separate_dhw_tests=0), - energy_content_monthly_kwh=energy_content, - daily_hot_water_monthly_l_per_day=daily_hw, - ) - is None + # separate_dhw_tests=0 + keep_hot_facility=1 + timer=1 → Table 3a row 3 + # (600 × n/365, keep-hot time clock) returned EXPLICITLY. The cascade + # default is now "without keep-hot" (600 × fu), so the keep-hot row + # must be delivered here, not leaned on the default. + from domain.sap10_calculator.worksheet.water_heating import ( + combi_loss_monthly_kwh_table_3a_keep_hot_time_clock, ) + + assert pcdb_combi_loss_override( + replace(base, separate_dhw_tests=0), + energy_content_monthly_kwh=energy_content, + daily_hot_water_monthly_l_per_day=daily_hw, + ) == combi_loss_monthly_kwh_table_3a_keep_hot_time_clock() # separate_dhw_tests=0 + keep_hot_facility=None → Table 3a row 1 # (600 × fu × n/365) — Slice S0380.21 dispatch. row_1 = pcdb_combi_loss_override( diff --git a/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000490.py b/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000490.py index 9bf66262..4184b799 100644 --- a/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000490.py +++ b/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000490.py @@ -42,7 +42,10 @@ from domain.sap10_ml.tests._fixtures import ( ) 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.sap10_calculator.worksheet.water_heating import ( + TABLE_J1_TCOLD_FROM_MAINS_C, + combi_loss_monthly_kwh_table_3a_keep_hot_time_clock, +) from tests.domain.sap10_calculator.worksheet._elmhurst_fixtures import ( SECTION_8C_ALL_ZERO_MONTHLY, @@ -284,9 +287,12 @@ MIXER_SHOWER_FLOW_RATES_L_PER_MIN: tuple[float, ...] = (7.0,) COLD_WATER_TEMPS_C: tuple[float, ...] = TABLE_J1_TCOLD_FROM_MAINS_C LOW_WATER_USE: bool = False # Vaillant Ecotec Pro combi, "Combi keep hot type = Gas/Oil, time clock": -# the orchestrator's default (Table 3a row "time-clock keep-hot") -# reproduces this fixture exactly. No override needed. -COMBI_LOSS_OVERRIDE: Optional[tuple[float, ...]] = None +# SAP 10.2 Table 3a flat keep-hot row (600 × n/365). The orchestrator's +# default is now "without keep-hot" (600 × fu × n/365, the modal lodging), +# so this keep-hot fixture supplies the override explicitly. +COMBI_LOSS_OVERRIDE: Optional[tuple[float, ...]] = ( + combi_loss_monthly_kwh_table_3a_keep_hot_time_clock() +) ELECTRIC_SHOWER_OVERRIDE: Optional[tuple[float, ...]] = None # §4 Water heating energy requirements diff --git a/tests/domain/sap10_calculator/worksheet/test_water_heating.py b/tests/domain/sap10_calculator/worksheet/test_water_heating.py index 5a96deb5..c7a2db79 100644 --- a/tests/domain/sap10_calculator/worksheet/test_water_heating.py +++ b/tests/domain/sap10_calculator/worksheet/test_water_heating.py @@ -970,13 +970,17 @@ def test_water_heating_from_cert_matches_elmhurst_worksheet_000490() -> None: # Arrange epc = _w000490.build_epc() - # Act + # Act — 000490 lodges "Combi keep hot type = time clock" (U985 §15), + # so the (61)m combi loss is the Table 3a flat keep-hot row, not the + # "without keep-hot" 600 × fu default that `water_heating_from_cert` + # now applies when no override is supplied. result = water_heating_from_cert( epc=epc, mixer_shower_flow_rates_l_per_min=(7.0,), has_bath=True, cold_water_temps_c=TABLE_J1_TCOLD_FROM_MAINS_C, low_water_use=False, + combi_loss_monthly_kwh_override=combi_loss_monthly_kwh_table_3a_keep_hot_time_clock(), ) # Assert — annual output equals the days-month-weighted sum of (64)m diff --git a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py index 2eb5b80a..307ebb1d 100644 --- a/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py +++ b/tests/infrastructure/epc_client/test_sap_accuracy_corpus.py @@ -215,9 +215,20 @@ _MIN_WITHIN_HALF_SAP = 0.72 # (7a)=0 is explicit): case 49's (8) openings line is 0.0000 — our default had # added 20 m3/h (0.0723 ach), inflating (22b)/(25)/(38) and demand (SAP 70.90 # -> 71.43; the worksheet's own continuous SAP is 71.69 -> rounds to 72). -_MAX_SAP_MAE = 0.785 +# Then 0.781 -> 0.774 (within-0.5 72.7% -> 73.3%, PE 3.5 -> 3.4) via the combi +# keep-hot fix: SAP 10.2 Table 3a (PDF p.160) — keep-hot rides on the PCDB +# boiler record (Table 105 keep_hot_facility/timer), NOT a separate cert field. +# (a) A non-PCDB / generic-SAP-code combi has no keep-hot → "without keep-hot" +# row 600 × fu × n/365 (fu = V_d/100 when V_d < 100), now the cascade default; +# the cascade had wrongly defaulted every non-PCDB combi to the flat keep-hot +# 600 × n/365 row. (b) `pcdb_combi_loss_override` now returns the keep-hot row +# EXPLICITLY for keep_hot_facility=1/timer=1 (was returning None to lean on the +# old flat default — so flipping the default had silently turned 190 PCDB +# keep-hot combis into no-keep-hot). Closes case 49 EXACTLY (cost £726.696, +# SAP 72 = the worksheet to the digit). +_MAX_SAP_MAE = 0.775 _MAX_CO2_MAE_TONNES = 0.09 # t CO2 / yr vs co2_emissions_current -_MAX_PE_PER_M2_MAE = 3.6 # kWh / m2 / yr vs energy_consumption_current +_MAX_PE_PER_M2_MAE = 3.5 # kWh / m2 / yr vs energy_consumption_current def _load_corpus() -> list[dict[str, Any]]: From eea5d3a5a82d85fb80a7660ca13acea3083ec117 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 21:48:59 +0000 Subject: [PATCH 15/19] fix(tariff): map electric boilers 191/193-196 to their Table 12a Grid 1 rows SAP 10.2 Table 4a electric boilers (PDF p.170) split across three distinct Table 12a Grid 1 SH rows (PDF p.191), not one "direct-acting" family as the stale TODO in `_table_12a_system_for_main` implied: - 191 Direct-acting electric boiler -> "Direct-acting electric boiler (a)" row: 7-hour 0.90, 10-hour 0.50 (NOT the 1.00/0.50 "Other direct-acting electric heating" room-heater row). - 193/194/195/196 Electric dry core / water storage boiler -> "Electric dry core or water storage boiler" row: 7-hour 0.00 (charged wholly off-peak = 100% low rate, identical to the None fallback). - 192 Electric CPSU -> Appendix F; left falling through to None (off-peak low) until the Appendix-F high-rate cascade is implemented. The enum + fractions already existed in table_12a.py; only the code->enum mapping was missing. Resolves the TODO and pins the spec-correct 0.00 for the storage boilers so 195 can't be mis-"fixed" up to a direct-acting fraction. Forward guard, 0 corpus impact: storage boilers already billed 100% low via the None fallback, and all corpus 191 certs are on standard tariff (Table 12a off-peak split never fires). Corpus gauge unchanged 73.3% / MAE 0.774. Pin: test_electric_boilers_191_195_map_to_distinct_table_12a_grid1_rows. pyright strict gate not run locally (pyright not installed in this container). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/rdsap/cert_to_inputs.py | 37 +++++++++++++++- .../rdsap/test_cert_to_inputs.py | 44 +++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index a58dcd57..8905d0b6 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -2520,6 +2520,25 @@ _ELECTRIC_ROOM_HEATER_SAP_CODES: Final[frozenset[int]] = frozenset( {691, 692, 693, 694, 699} ) +# SAP 10.2 Table 4a electric boilers (PDF p.170, codes 191-196) → their +# Table 12a Grid 1 SH rows (PDF p.191). NOTE the boiler families do NOT all +# share a row — read the spec exactly: +# 191 Direct-acting electric boiler → "Direct-acting electric boiler +# (a)" row: 7-hour 0.90, 10-hour 0.50 (NOT the "Other direct-acting +# electric heating" 1.00/0.50 room-heater row). +# 192 Electric CPSU → "Electric CPSU" row: Appendix F +# (no flat Table 12a fraction — left as a documented gap, see below). +# 193/194 Electric dry core storage boiler → "Electric dry core or water +# 195/196 Electric water storage boiler storage boiler" row: 7-hour +# 0.00 (charged wholly off-peak — identical to the 100%-low-rate the +# None fallback already gave; mapped EXPLICITLY so the spec-correct +# 0.00 is pinned and can't be "fixed" up to a wrong direct-acting 1.00). +_DIRECT_ACTING_ELECTRIC_BOILER_CODES: Final[frozenset[int]] = frozenset({191}) +_ELECTRIC_STORAGE_BOILER_CODES: Final[frozenset[int]] = frozenset( + {193, 194, 195, 196} +) +_ELECTRIC_CPSU_CODES: Final[frozenset[int]] = frozenset({192}) + def _table_12a_system_for_main( main: Optional[MainHeatingDetail], @@ -2539,8 +2558,9 @@ def _table_12a_system_for_main( - Storage heaters (cat 7): 408 → INTEGRATED_STORAGE_DIRECT (0.20), all others → OTHER_STORAGE_HEATERS (0.00) — wired - Underfloor heating (421-422) — TODO - - Direct-acting electric (191) / CPSU (192) / electric storage - boiler (193, 195) — TODO + - Direct-acting electric boiler (191) → 0.90/0.50; electric storage + boilers (193/194/195/196) → 0.00 — wired + - Electric CPSU (192) — Appendix F high-rate cascade still TODO """ if main is None: return None @@ -2601,6 +2621,19 @@ def _table_12a_system_for_main( if code == 408: return Table12aSystem.INTEGRATED_STORAGE_DIRECT return Table12aSystem.OTHER_STORAGE_HEATERS + # Electric boilers (Table 4a codes 191-196) — resolve the misleading TODO + # that lumped them as one "direct-acting" family. They split across THREE + # distinct Table 12a Grid 1 rows (see `_DIRECT_ACTING_ELECTRIC_BOILER_CODES` + # et al). 191 alone is direct-acting (0.90/0.50); 193-196 are storage + # boilers (0.00 = 100% low, the spec-correct value the None fallback gave — + # so this is a forward guard, not a corpus mover); 192 CPSU needs Appendix F + # and is left to fall through to None (the off-peak-low fallback) until the + # Appendix-F high-rate cascade is implemented. + if code is not None and _is_electric_main(main): + if code in _DIRECT_ACTING_ELECTRIC_BOILER_CODES: + return Table12aSystem.DIRECT_ACTING_ELECTRIC_BOILER + if code in _ELECTRIC_STORAGE_BOILER_CODES: + return Table12aSystem.ELECTRIC_DRY_CORE_OR_WATER_STORAGE return None diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index 01dbdcad..e7169dfe 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -65,6 +65,7 @@ from domain.sap10_calculator.rdsap.cert_to_inputs import ( _TABLE_4G_DEFAULT_MEV_SFP_W_PER_L_PER_S, # pyright: ignore[reportPrivateUsage] dimensions_from_cert, _table_12_factor_fuel_code, # pyright: ignore[reportPrivateUsage] + _table_12a_system_for_main, # pyright: ignore[reportPrivateUsage] _is_electric_main, # pyright: ignore[reportPrivateUsage] _is_heat_network_electric_main, # pyright: ignore[reportPrivateUsage] _is_electric_water, # pyright: ignore[reportPrivateUsage] @@ -911,6 +912,49 @@ def test_no_system_electric_heaters_assumed_code_699_bills_direct_acting_split() assert abs(inputs.space_heating_fuel_cost_gbp_per_kwh - 0.1109) <= 1e-9 +def test_electric_boilers_191_195_map_to_distinct_table_12a_grid1_rows() -> None: + # Arrange — SAP 10.2 Table 4a electric boilers (PDF p.170) do NOT share + # one Table 12a Grid 1 row (PDF p.191). Read exactly: code 191 + # "Direct-acting electric boiler (a)" → 7-hour 0.90 / 10-hour 0.50 (its + # OWN row, NOT the 1.00/0.50 "Other direct-acting electric heating" + # room-heater row); code 195 "Electric water storage boiler" → the + # "Electric dry core or water storage boiler" row → 7-hour 0.00 (charged + # wholly off-peak = 100% low rate, identical to the None fallback). This + # pins the spec-correct 0.00 so 195 can't be mis-"fixed" up to a wrong + # direct-acting fraction. + from domain.sap10_calculator.tables.table_12a import ( + Table12aSystem, + Tariff, + space_heating_high_rate_fraction, + ) + + def _electric_boiler_main(code: int) -> MainHeatingDetail: + return MainHeatingDetail( + has_fghrs=False, + main_fuel_type=29, # electricity + heat_emitter_type=0, + emitter_temperature=1, + main_heating_control=2106, + main_heating_category=2, # boiler + sap_main_heating_code=code, + ) + + # Act + direct_acting = _table_12a_system_for_main(_electric_boiler_main(191)) + storage = _table_12a_system_for_main(_electric_boiler_main(195)) + + # Assert — distinct rows with their published fractions. + assert direct_acting is Table12aSystem.DIRECT_ACTING_ELECTRIC_BOILER + assert storage is Table12aSystem.ELECTRIC_DRY_CORE_OR_WATER_STORAGE + assert ( + space_heating_high_rate_fraction(direct_acting, Tariff.SEVEN_HOUR) == 0.90 + ) + assert ( + space_heating_high_rate_fraction(direct_acting, Tariff.TEN_HOUR) == 0.50 + ) + assert space_heating_high_rate_fraction(storage, Tariff.SEVEN_HOUR) == 0.00 + + def test_heat_network_distribution_electricity_per_sap_10_2_appendix_c_3_2() -> None: # Arrange — heat-network main (Table 4a code 301 = community heating, # category 6). SAP 10.2 Appendix C §C3.2 (PDF p.51): distribution From cd5113abf2d8367e613daadb33a1e95e8555127f Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 22:16:58 +0000 Subject: [PATCH 16/19] fix(tariff): include MVHR fan electricity in the off-peak Grid 2 fan split MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SAP 10.2 Table 12a Grid 2 (PDF p.191) bills "Fans for mechanical ventilation systems" at 0.71 (7-hour) / 0.58 (10-hour), distinct from "All other uses" (0.90 / 0.80) which covers circulation pumps, flue fans and the solar HW pump. The cost-split mech-vent kWh (`mev_kwh_for_cost_split`) only summed the decentralised-MEV (230b) fans, not the (230a) MVHR fan electricity — even though the total pumps/fans bucket adds both. So an MVHR dwelling on an off-peak tariff billed its fan electricity at the 0.90/0.80 "all other uses" rate instead of 0.71/0.58. The comment already said "MEV/MVHR-fan portion"; only the MEV term was wired when MVHR landed. Fixed to mirror both mechanical-ventilation fan terms summed into the total. Worksheet-proven on simulated case 50 (000565 semi + MVHR Vent Axia + dual electric immersion, Unknown meter -> 7-hour via the §12 dual-immersion trigger): the fan bucket (315.64 kWh, 100% MVHR per worksheet line 230a) was billing at 14.311 p/kWh (0.90) vs Elmhurst's 12.451 p/kWh (0.71) — +£5.87/yr, -0.23 SAP. After the fix our existing-dwelling rating reconciles to Elmhurst EXACTLY: SAP value 38.8426 (=Elmhurst 38.8426 -> 39), total cost £1317.0116 (=Elmhurst £1317.0116 to the penny). Same `mev_kwh_for_cost_split` feeds the CO2 + PE cascades, so all three split consistently. 0 corpus impact (all 3 corpus MVHR certs are standard tariff); gauge unchanged 73.3% / MAE 0.774 / CO2 0.08 / PE 3.4. Pin: test_mvhr_fan_electricity_bills_at_grid2_fan_fraction_on_off_peak. pyright strict gate not run locally (pyright not installed in this container). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/rdsap/cert_to_inputs.py | 19 ++++-- .../rdsap/test_cert_to_inputs.py | 65 +++++++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 8905d0b6..779790a9 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -7318,10 +7318,21 @@ def cert_to_inputs( pumps_fans_kwh += _table_4f_circulation_pump_kwh(_pumps_main_2) pumps_fans_kwh += _table_4f_additive_components(epc) # Track the MEV/MVHR-fan portion separately so the cost cascade can - # apply Table 12a Grid 2 `FANS_FOR_MECH_VENT` (0.58 high-frac on - # 10-hour) instead of `ALL_OTHER_USES` (0.80) — see - # `_pumps_fans_fuel_cost_gbp_per_kwh`. Zero when no MEV is lodged. - mev_kwh_for_cost_split = _mev_decentralised_kwh_per_yr_from_cert(epc) + # apply Table 12a Grid 2 `FANS_FOR_MECH_VENT` (0.71 high-frac on + # 7-hour, 0.58 on 10-hour) instead of `ALL_OTHER_USES` (0.90 / 0.80) + # — see `_pumps_fans_fuel_cost_gbp_per_kwh`. Must mirror the + # mechanical-ventilation fan terms summed into the total pumps/fans + # at `_table_4f_additive_components` (230b decentralised MEV/extract) + # + the (230a) MVHR fan: both are "Fans for mechanical ventilation + # systems" in Grid 2, while flue fans / circulation pumps / solar HW + # pump are "All other uses". The MVHR term was omitted when MVHR + # landed, so an MVHR dwelling on off-peak billed its fan electricity + # at 0.90 instead of 0.71 (case 50: +£5.87/yr, -0.23 SAP). Zero when + # no mechanical-ventilation fan is lodged. + mev_kwh_for_cost_split = ( + _mev_decentralised_kwh_per_yr_from_cert(epc) + + _mvhr_fan_kwh_per_yr_from_cert(epc) + ) primary_age = _dwelling_age_band(epc) # SAP 10.2 Appendix D2.1: if the cert lodges a PCDB index number that diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index e7169dfe..4af6d3d0 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -3876,6 +3876,71 @@ def test_mvhr_system_values_apply_pcdb_wet_room_point_and_table_329_iufs() -> No assert abs(fan_kwh - expected_fan_kwh) <= 1e-6 +def test_mvhr_fan_electricity_bills_at_grid2_fan_fraction_on_off_peak() -> None: + # Arrange — an MVHR dwelling on an off-peak tariff. SAP 10.2 Table 12a + # Grid 2 (PDF p.191) bills "Fans for mechanical ventilation systems" at + # 0.71 (7-hour) / 0.58 (10-hour) high-rate fraction, distinct from "All + # other uses" (0.90 / 0.80) which covers circulation pumps + flue fans. + # The MVHR fan electricity (230a) was summed into the pumps/fans total + # but NOT into the cost-split mech-vent kWh, so on off-peak it billed at + # the 0.90/0.80 "all other uses" rate. Worksheet-proven on simulated + # case 50 (000565 + MVHR + dual immersion): +£5.87/yr, -0.23 SAP. + import dataclasses + + from domain.sap10_calculator.rdsap.cert_to_inputs import ( + _mvhr_fan_kwh_per_yr_from_cert, # pyright: ignore[reportPrivateUsage] + _tariff_high_low_rates_p_per_kwh, # pyright: ignore[reportPrivateUsage] + ) + from domain.sap10_calculator.tables.table_12a import ( + OtherUse, + Tariff, + other_use_high_rate_fraction, + ) + from tests.domain.sap10_calculator.worksheet import ( + _elmhurst_worksheet_000565 as _w000565, + ) + + base = _w000565.build_epc() + assert base.sap_ventilation is not None + epc = dataclasses.replace( + base, + mechanical_ventilation_index_number=500140, + wet_rooms_count=2, + mechanical_vent_duct_type=2, # rigid + sap_ventilation=dataclasses.replace( + base.sap_ventilation, mechanical_ventilation_kind="MVHR" + ), + sap_energy_source=dataclasses.replace( + base.sap_energy_source, meter_type="1" # off-peak (10-hour) + ), + ) + + # Act + inputs = cert_to_inputs(epc) + rate = inputs.pumps_fans_fuel_cost_gbp_per_kwh + + # Assert — the resolved rate is the kWh-weighted blend of the MVHR fan + # portion at the FANS_FOR_MECH_VENT fraction and the remaining pumps at + # ALL_OTHER_USES. The presence of the MVHR fan in the split makes it + # strictly cheaper than billing the whole bucket at ALL_OTHER_USES. + tariff = Tariff.TEN_HOUR + high, low = _tariff_high_low_rates_p_per_kwh(tariff) + fan_frac = other_use_high_rate_fraction(OtherUse.FANS_FOR_MECH_VENT, tariff) + other_frac = other_use_high_rate_fraction(OtherUse.ALL_OTHER_USES, tariff) + fan_blend = fan_frac * high + (1.0 - fan_frac) * low + other_blend = other_frac * high + (1.0 - other_frac) * low + fan_kwh = _mvhr_fan_kwh_per_yr_from_cert(epc) + total_kwh = inputs.pumps_fans_kwh_per_yr + non_fan_kwh = total_kwh - fan_kwh + expected = ( + (fan_kwh * fan_blend + non_fan_kwh * other_blend) / total_kwh / 100.0 + ) + assert rate is not None + assert fan_kwh > 0.0 + assert abs(rate - expected) <= 1e-12 + assert rate < other_blend / 100.0 # regression guard: fan portion is in the split + + def test_mvhr_system_values_fall_back_to_table_4g_defaults_without_pcdb_index() -> None: # Arrange — an MVHR cert lodged with NO PCDF index (corpus cert # "Flat 1"). SAP 10.2 Table 4g (PDF p.176): default raw efficiency From f3e3494bf70d76163b4d468bd67b007884daa57c Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 24 Jun 2026 08:50:19 +0000 Subject: [PATCH 17/19] =?UTF-8?q?test(worksheet):=20pin=20simulated=20case?= =?UTF-8?q?=2052=20=E2=80=94=20regular=20gas=20boiler=20+=20cylinder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the mapper-driven e2e cascade pin for "simulated case 52" (000565 semi + regular non-combi mains-gas boiler SAP 102 + 160 L foam cylinder heated from the main, no cylinder stat, uninsulated primary pipework, standard tariff). Routes the Summary PDF through extractor + mapper + calculator like the other 000565 / 001431_case* fixtures. This closes the last untested branch of the cylinder/water chain: the SAP 10.2 §4 cylinder storage loss (Table 2/2a/2b lines 51-55) + the Table 3 PRIMARY circuit loss (59, uninsulated pipework + no stat) that combi/immersion fixtures don't reach. All 11 SAP-result fields reconcile to the U985 worksheet EXACTLY with no calculator change — SAP 57.2904 (=57), cost £911.1973, water 3929.7635 kWh — confirming the cylinder-loss derivation is correct. Summary mirrored to the tracked fixtures dir so the test doesn't depend on the unstaged `sap worksheets/` workspace. pyright strict gate not run locally (pyright not installed in this container). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../tests/fixtures/Summary_000565_case52.pdf | Bin 0 -> 81285 bytes .../_elmhurst_worksheet_000565_case52.py | 110 ++++++++++++++++++ .../worksheet/test_e2e_elmhurst_sap_score.py | 19 +++ 3 files changed, 129 insertions(+) create mode 100644 backend/documents_parser/tests/fixtures/Summary_000565_case52.pdf create mode 100644 tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case52.py diff --git a/backend/documents_parser/tests/fixtures/Summary_000565_case52.pdf b/backend/documents_parser/tests/fixtures/Summary_000565_case52.pdf new file mode 100644 index 0000000000000000000000000000000000000000..82d1891f8696c3cafcee7d208ce43d28cde5022c GIT binary patch literal 81285 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

|DM;gGuoG^ooz95w&cm(Yh?R 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 MB8j=V@~ literal 0 HcmV?d00001 diff --git a/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case52.py b/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case52.py new file mode 100644 index 00000000..8f368476 --- /dev/null +++ b/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case52.py @@ -0,0 +1,110 @@ +"""Mapper-driven cascade pin against the Elmhurst U985-0001-000565 +"simulated case 52" worksheet — a REGULAR (non-combi) mains-gas boiler +feeding a hot-water CYLINDER, on standard tariff. + +Like 000565 / the _rr cases, this fixture does NOT hand-build the +EpcPropertyData: it routes the Summary PDF through +ElmhurstSiteNotesExtractor + from_elmhurst_site_notes so the SAP-result +pin grid exercises the WHOLE extractor + mapper + calculator pipeline. + +This case was hand-built (Khalim) to ground-truth the LAST untested +branch of the cylinder/water-heating chain that the combi/immersion +fixtures don't reach: a regular boiler + cylinder heated from the main, +exercising SAP 10.2 §4 — + - Cylinder storage loss via Table 2 loss factor (51) 0.0181, Table 2a + volume factor (52) 0.9086, Table 2b temperature factor (53) 0.7020 + (NO cylinder thermostat) → (55) 1.8466. + - PRIMARY circuit loss (59) 128.3772 (winter) — the Table 3 path for + UNINSULATED primary pipework + no cylinder stat. Case 50 (immersion, + no boiler) couldn't reach this branch. + - Combi loss (61) correctly 0 (regular boiler, not a combi). +The whole chain reconciles to the U985 worksheet EXACTLY with no +calculator change — it pins the cylinder-loss derivation as correct. + +Cert shape: 000565 semi shell, single main = mains-gas REGULAR boiler +(SAP code 102, control 2106 programmer + room stat + TRVs), water +heating from main (WHC 901) via a 160 L foam-insulated cylinder (no +cylinder stat, uninsulated primary pipework, in heated space), one +instantaneous electric shower, no secondary, no PV, standard tariff. + +Source: user-simulated PDFs at `sap worksheets/golden fixture debugging/ +simulated case 52/`. The Summary is mirrored into the tracked +`backend/documents_parser/tests/fixtures/Summary_000565_case52.pdf` so the +test runs without depending on the unstaged workspace. + +Worksheet pin targets (U985-0001-000565 block 1 — existing dwelling SAP): +- SAP rating 57 (258); continuous 57.2904; ECF 3.0616 (257) +- Total fuel cost £911.1973 (255) +- Total CO2 3834.8434 kg/year (272) +- Space heating 10563.5170 kWh/year ((98c)) +- Main 1 fuel 13371.5405 kWh/year (211) +- Secondary fuel 0.0 kWh/year (215) +- Hot water fuel 3929.7635 kWh/year (219) +- Lighting 435.3204 kWh/year (232) +- Pumps/fans 401.6384 kWh/year (231) + +Per [[feedback-zero-error-strict]] + [[feedback-e2e-validation- +philosophy]]: pins are abs=1e-4 against the worksheet PDF. The pin +values live in `test_e2e_elmhurst_sap_score._FIXTURE_PINS`. +""" + +from __future__ import annotations + +import re +import subprocess +from pathlib import Path +from typing import Final + +from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor +from datatypes.epc.domain.epc_property_data import EpcPropertyData +from datatypes.epc.domain.mapper import EpcPropertyDataMapper + +# parents[0]=worksheet/, [1]=sap10_calculator/, [2]=domain/, [3]=tests/, +# [4]=repo root. +_SUMMARY_PDF: Final[Path] = ( + Path(__file__).resolve().parents[4] + / "backend" / "documents_parser" / "tests" / "fixtures" + / "Summary_000565_case52.pdf" +) + + +def _summary_pdf_to_textract_style_pages(pdf_path: Path) -> list[str]: + """Convert a Summary PDF into the per-page text format the + ElmhurstSiteNotesExtractor expects (label\\nvalue sequences). Mirror + of the helper in the other `_elmhurst_worksheet_*` fixtures. + """ + info = subprocess.run( + ["pdfinfo", str(pdf_path)], capture_output=True, text=True, check=True, + ).stdout + m = re.search(r"Pages:\s+(\d+)", info) + if m is None: + raise RuntimeError(f"Could not parse page count from {pdf_path}") + page_count = int(m.group(1)) + pages: list[str] = [] + for i in range(1, page_count + 1): + layout = subprocess.run( + [ + "pdftotext", "-layout", "-f", str(i), "-l", str(i), + str(pdf_path), "-", + ], + capture_output=True, text=True, check=True, + ).stdout + tokens: list[str] = [] + for line in layout.splitlines(): + if not line.strip(): + tokens.append("") + continue + parts = [p for p in re.split(r"\s{2,}", line.strip()) if p] + tokens.extend(parts) + pages.append("\n".join(tokens)) + return pages + + +def build_epc() -> EpcPropertyData: + """Route the simulated case-52 Summary through extractor + mapper. + No hand-built EpcPropertyData — the extractor and mapper are part of + the test target. + """ + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_PDF) + site_notes = ElmhurstSiteNotesExtractor(pages).extract() + return EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes) diff --git a/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py b/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py index 1625bcbc..13b1b1bb 100644 --- a/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py +++ b/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py @@ -48,6 +48,7 @@ from tests.domain.sap10_calculator.worksheet import ( _elmhurst_worksheet_001431_case6 as _w001431_case6, _elmhurst_worksheet_001431_case7 as _w001431_case7, _elmhurst_worksheet_001431_case20 as _w001431_case20, + _elmhurst_worksheet_000565_case52 as _w000565_case52, ) from tests.domain.sap10_calculator.worksheet._elmhurst_fixtures import ( ALL_FIXTURES as _ELMHURST_FIXTURES, @@ -296,6 +297,23 @@ _FIXTURE_PINS: Final[dict[str, FixtureCascadePins]] = { lighting_kwh_per_yr=246.3083, pumps_fans_kwh_per_yr=0.0, ), + # Mapper-driven — Summary_000565_case52.pdf → extractor → mapper → + # calculator. Regular (non-combi) mains-gas boiler (SAP 102) + a + # 160 L foam cylinder heated from the main (WHC 901), no cylinder + # stat + uninsulated primary pipework, standard tariff. Validates the + # cylinder storage loss (51-55) + PRIMARY loss (59) chain — the + # branch immersion/combi fixtures can't reach. Reconciles to the + # worksheet EXACTLY with no calculator change. + "000565_case52": FixtureCascadePins( + sap_score=57, sap_score_continuous=57.2904, ecf=3.0616, + total_fuel_cost_gbp=911.1973, co2_kg_per_yr=3834.8434, + space_heating_kwh_per_yr=10563.5170, + main_heating_fuel_kwh_per_yr=13371.5405, + secondary_heating_fuel_kwh_per_yr=0.0, + hot_water_kwh_per_yr=3929.7635, + lighting_kwh_per_yr=435.3204, + pumps_fans_kwh_per_yr=401.6384, + ), } @@ -315,6 +333,7 @@ _FIXTURE_MODULES: Final[dict[str, ModuleType]] = { "001431_case6": _w001431_case6, "001431_case7": _w001431_case7, "001431_case20": _w001431_case20, + "000565_case52": _w000565_case52, } From 39ae2cf0c2592994cd178b4708eba4d5075b084e Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 24 Jun 2026 09:05:21 +0000 Subject: [PATCH 18/19] fix(water-heating): split whc-903 immersion HW CO2/PE on off-peak tariffs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SAP 10.2 Table 12d/12e: electric water heating on a 7-/10-hour tariff bills CO2/PE at the high-rate code (32/34) and low-rate code (31/33), kWh-weighted by the Table 13 high-rate fraction. The cost path already applied this split; the CO2/PE factors did not — they used the flat annual Table 12 figure (0.136 CO2 / 1.501 PE) for ALL dual-rate electric HW. That flat-annual behaviour (slice S0380.163) was validated only against HW-from-main "low-rate cost" certs (100% low, no high-rate split). It is NOT how Elmhurst bills a whc-903 ELECTRIC IMMERSION: the hand-built case-50 worksheet (000565 + dual immersion, 7-hour) splits HW CO2/PE into "high rate cost" (CO2 0.1475 / PE 1.5514) + "low rate cost" (CO2 0.1238 / PE 1.4429) weighted by the Table 13 fraction 0.1009. So flat-0.136 for immersion HW was a spec gap on our side, not an Elmhurst divergence. Fix: `_electric_immersion_hw_high_rate_fraction` threads the Table 13 fraction (scoped to whc-903, 7-/10-hour, cylinder data present) into the HW CO2 + PE factor helpers, which then blend the Table 12d/12e high/low codes. The flat rule is unchanged for HW-from-main and 18-/24-hour (no Table 12d split), so the S0380.163 41-variant cases and the existing pin are untouched. Case 50: rating CO2 2413.48 -> 2397.1237 = Elmhurst EXACT; demand CO2 2007.1384 EXACT; demand PE +111 -> +32.5 residual (within corpus PE noise). Corpus unchanged 73.3% / MAE 0.774 / CO2 0.08 / PE 3.4 (62 whc-903 off-peak certs; aggregate gauges hold). SAP unaffected (cost-based). Pin: test_whc903_immersion_hw_co2_pe_factors_split_high_low_on_off_peak; doc updated in SAP_CALCULATOR.md §8.1. pyright strict gate not run locally (pyright not installed in this container). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sap10_calculator/docs/SAP_CALCULATOR.md | 23 +++-- .../sap10_calculator/rdsap/cert_to_inputs.py | 86 +++++++++++++++++++ .../rdsap/test_cert_to_inputs.py | 52 +++++++++++ 3 files changed, 156 insertions(+), 5 deletions(-) diff --git a/domain/sap10_calculator/docs/SAP_CALCULATOR.md b/domain/sap10_calculator/docs/SAP_CALCULATOR.md index 7e78d99c..68555fda 100644 --- a/domain/sap10_calculator/docs/SAP_CALCULATOR.md +++ b/domain/sap10_calculator/docs/SAP_CALCULATOR.md @@ -427,12 +427,25 @@ secondary heating (1.5715) on the same certs, but flat Table 12 for the "low-rate cost" line items (SH main 1 + HW). It's an Elmhurst implementation choice, not a documented spec exception. -**Cascade rule (post-S0380.163):** +**Cascade rule (post-S0380.163, refined for whc-903 immersion):** -| Tariff | HW PE / CO2 factor source | -|---|---| -| STANDARD | Table 12e / 12d monthly, weighted by HW demand seasonality (per spec literal) | -| 7-hour / 10-hour / 18-hour / 24-hour | Table 12 annual flat (1.501 PE / 0.136 CO2) | +| Tariff | HW system | HW PE / CO2 factor source | +|---|---|---| +| STANDARD | any | Table 12e / 12d monthly, weighted by HW demand seasonality (per spec literal) | +| 7-hour / 10-hour | whc-903 electric immersion (Table 13 split) | Table 12d/12e high- + low-rate codes (32/31 or 34/33), kWh-weighted, blended by the Table 13 high-rate fraction — the SAME split the cost path applies | +| 7-hour / 10-hour / 18-hour / 24-hour | all other (HW-from-main "low-rate cost", or 18h/24h immersion) | Table 12 annual flat (1.501 PE / 0.136 CO2) | + +**whc-903 immersion carve-out (simulated case 50).** The original S0380.163 +rule applied flat annual 0.136/1.501 to *every* dual-rate electric HW. That +was validated only against HW-from-main "low-rate cost" certs (100% low, no +high-rate split). The hand-built **case-50** worksheet (whc-903 dual electric +immersion, 7-hour) bills HW CO2/PE as a split — "Water heating - high rate +cost" (CO2 0.1475 / PE 1.5514, code 32) + "low rate cost" (CO2 0.1238 / PE +1.4429, code 31), weighted by the Table 13 fraction 0.1009 — NOT flat 0.136. +So flat-0.136 for immersion HW was a *spec gap on our side*, not an Elmhurst +divergence. `_electric_immersion_hw_high_rate_fraction` threads the Table 13 +fraction into the CO2/PE helpers; the flat rule still holds for everything +else (the 41-variant cases below are unaffected — they are HW-from-main). The SH main factor (`_main_heating_primary_factor`) already matches Elmhurst by accident: for dual-rate tariffs the diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 779790a9..2fb2151d 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -3980,6 +3980,8 @@ def _hot_water_co2_factor_kg_per_kwh( epc: EpcPropertyData, hw_monthly_kwh: tuple[float, ...], tariff: Tariff, + *, + immersion_high_rate_fraction: Optional[float] = None, ) -> float: """SAP 10.2 Table 12 / 12d (p.195) per-end-use CO2 factor for the cert's lodged water-heating fuel. @@ -4043,6 +4045,25 @@ def _hot_water_co2_factor_kg_per_kwh( else _table_12_factor_fuel_code(fuel) ) if tariff is not Tariff.STANDARD: + # whc-903 electric IMMERSION with a Table 13 high-rate split: the + # Elmhurst worksheet bills HW CO2 as two lines — "Water heating - + # high rate cost" at the Table 12d high-rate code + "low rate cost" + # at the low-rate code, weighted by the SAME Table 13 fraction the + # COST path uses (simulated case 50: high 0.1475 + low 0.1238, + # frac 0.1009). The flat-annual S0380.163 rule below was validated + # only on HW-from-main "low-rate cost" certs (100% low) where no + # high-rate split exists; it does NOT hold for the immersion split. + if immersion_high_rate_fraction is not None: + codes = _TARIFF_HIGH_LOW_FUEL_CODES_TABLE_12.get(tariff) + if codes is not None: + high_code, low_code = codes + f_high = _effective_monthly_co2_factor(hw_monthly_kwh, high_code) + f_low = _effective_monthly_co2_factor(hw_monthly_kwh, low_code) + if f_high is not None and f_low is not None: + return ( + immersion_high_rate_fraction * f_high + + (1.0 - immersion_high_rate_fraction) * f_low + ) return co2_factor_kg_per_kwh(table_12_code) monthly = _effective_monthly_co2_factor(hw_monthly_kwh, table_12_code) if monthly is not None: @@ -4054,6 +4075,8 @@ def _hot_water_primary_factor( epc: EpcPropertyData, hw_monthly_kwh: tuple[float, ...], tariff: Tariff, + *, + immersion_high_rate_fraction: Optional[float] = None, ) -> float: """SAP 10.2 Table 12 / 12e (p.196) per-end-use PE factor for the cert's lodged water-heating fuel. PE-side mirror of @@ -4107,6 +4130,21 @@ def _hot_water_primary_factor( else _table_12_factor_fuel_code(fuel) ) if tariff is not Tariff.STANDARD: + # whc-903 immersion Table 13 split — PE mirror of the CO2 helper. + # Elmhurst splits HW PE into Table 12e high-/low-rate electricity + # weighted by the Table 13 fraction; the flat-annual S0380.163 rule + # only holds for HW-from-main "low-rate cost" certs (no split). + if immersion_high_rate_fraction is not None: + codes = _TARIFF_HIGH_LOW_FUEL_CODES_TABLE_12.get(tariff) + if codes is not None: + high_code, low_code = codes + f_high = _effective_monthly_pe_factor(hw_monthly_kwh, high_code) + f_low = _effective_monthly_pe_factor(hw_monthly_kwh, low_code) + if f_high is not None and f_low is not None: + return ( + immersion_high_rate_fraction * f_high + + (1.0 - immersion_high_rate_fraction) * f_low + ) return primary_energy_factor(table_12_code) monthly = _effective_monthly_pe_factor(hw_monthly_kwh, table_12_code) if monthly is not None: @@ -4114,6 +4152,43 @@ def _hot_water_primary_factor( return primary_energy_factor(fuel) +def _electric_immersion_hw_high_rate_fraction( + epc: EpcPropertyData, + tariff: Tariff, + *, + cylinder_volume_l: Optional[float], + occupancy_n: Optional[float], + immersion_single: Optional[bool], +) -> Optional[float]: + """SAP 10.2 Table 13 HW high-rate fraction for a whc-903 electric + immersion on a 7-/10-hour off-peak tariff — the SAME split the cost + path (`_hot_water_fuel_cost_gbp_per_kwh`) applies. The CO2/PE factor + helpers blend the Table 12d/12e high- and low-rate electricity factors + by this fraction, mirroring the worksheet's split "Water heating - + high/low rate cost" lines (simulated case 50). Returns None when not a + dual-rate immersion, when the cylinder/occupancy data Table 13 needs is + missing, or on 18-/24-hour tariffs (no Table 12d/12e high/low split) — + callers then keep the flat-annual S0380.163 factor.""" + if tariff is Tariff.STANDARD: + return None + if epc.sap_heating.water_heating_code != _WHC_ELECTRIC_IMMERSION: + return None + if ( + cylinder_volume_l is None + or occupancy_n is None + or immersion_single is None + ): + return None + if _TARIFF_HIGH_LOW_FUEL_CODES_TABLE_12.get(tariff) is None: + return None + return electric_dhw_high_rate_fraction( + cylinder_volume_l=cylinder_volume_l, + occupancy_n=occupancy_n, + single_immersion=immersion_single, + tariff=tariff, + ) + + def _secondary_fuel_code(epc: EpcPropertyData) -> int: """SAP 10.2 secondary fuel code, resolved through the API mapper's Appendix M Table 4a spec-fuel routing. When no `secondary_fuel_type` @@ -8027,11 +8102,22 @@ def cert_to_inputs( occupancy_n=wh_result.occupancy if wh_result is not None else None, immersion_single=_immersion_is_single(epc), ) + # whc-903 immersion Table 13 high-rate fraction — same split the + # cost path applies above; threaded into the CO2/PE factor helpers + # so the worksheet's high/low HW lines reconcile (simulated case 50). + _hw_immersion_high_frac = _electric_immersion_hw_high_rate_fraction( + epc, _rdsap_tariff(epc), + cylinder_volume_l=_hot_water_cylinder_volume_l(epc), + occupancy_n=wh_result.occupancy if wh_result is not None else None, + immersion_single=_immersion_is_single(epc), + ) hw_co2_factor = _hot_water_co2_factor_kg_per_kwh( epc, hw_monthly_kwh_for_factors, _rdsap_tariff(epc), + immersion_high_rate_fraction=_hw_immersion_high_frac, ) hw_pe_factor = _hot_water_primary_factor( epc, hw_monthly_kwh_for_factors, _rdsap_tariff(epc), + immersion_high_rate_fraction=_hw_immersion_high_frac, ) _hw_extra_standing = 0.0 _heat_network_standing = _heat_network_standing_charge_gbp(epc, main) diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index 4af6d3d0..99a2348a 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -5352,6 +5352,58 @@ def test_electric_water_heating_factors_use_annual_table_12_on_dual_rate_tariff( ) +def test_whc903_immersion_hw_co2_pe_factors_split_high_low_on_off_peak() -> None: + # Arrange — the flat-annual S0380.163 rule (preceding test) was validated + # only on HW-from-main "low-rate cost" certs (100% low, no high-rate + # split). For whc-903 ELECTRIC IMMERSION the Elmhurst worksheet bills HW + # CO2/PE as TWO lines — "Water heating - high rate cost" at the Table + # 12d/12e high-rate electricity factor + "low rate cost" at the low-rate + # factor — weighted by the SAME Table 13 fraction the COST path applies + # (simulated case 50: CO2 high 0.1475 + low 0.1238; PE high 1.5514 + low + # 1.4429; frac 0.1009). Our cascade previously applied the flat annual + # 0.136 / 1.501 to dual-rate immersion HW too — a spec gap, not a genuine + # Elmhurst divergence (case 50 worksheet proves the split). The split + # makes both factors fall BELOW the flat annual (the low-rate factor + # dominates at ~0.90 weight). Reconciled exactly by the case-50 e2e pin. + storage_main = MainHeatingDetail( + has_fghrs=False, + main_fuel_type=30, + heat_emitter_type="", + emitter_temperature="", + main_heating_control=2401, + sap_main_heating_code=402, # storage heater → off-peak + central_heating_pump_age_str="Unknown", + ) + epc = make_minimal_sap10_epc( + total_floor_area_m2=_TYPICAL_TFA_M2, + habitable_rooms_count=4, + country_code="ENG", + has_hot_water_cylinder=True, + sap_building_parts=[make_building_part(construction_age_band="D")], + sap_heating=make_sap_heating( + water_heating_code=903, # electric immersion + water_heating_fuel=29, + cylinder_size=5, + immersion_heating_type=1, # dual immersion + main_heating_details=[storage_main], + ), + ) + # Unknown meter + dual electric immersion → 7-hour off-peak (§12 trigger). + epc.sap_energy_source.meter_type = "Unknown" + + # Act + inputs = cert_to_inputs(epc) + co2 = inputs.hot_water_co2_factor_kg_per_kwh + pe = inputs.hot_water_primary_factor + + # Assert — the Table 13 split is applied, so both factors are a genuine + # high/low blend strictly BELOW the flat-annual S0380.163 figures (and + # above the all-low-rate bound — not degenerate). + assert co2 is not None and pe is not None + assert 0.1238 < co2 < 0.136, f"expected split CO2 in (0.1238, 0.136); got {co2}" + assert 1.4429 < pe < 1.501, f"expected split PE in (1.4429, 1.501); got {pe}" + + def test_gas_water_heating_co2_and_pe_factors_pass_through_annual_table_12() -> None: # Arrange — RdSAP cert with mains-gas water heating # (`water_heating_fuel=26` API mains gas → Table 12 code 1). Per From 4e2f2bdcc7272dd1998a4e2121e3a212dca21d83 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 24 Jun 2026 09:07:16 +0000 Subject: [PATCH 19/19] =?UTF-8?q?test(worksheet):=20pin=20simulated=20case?= =?UTF-8?q?=2050=20=E2=80=94=20MVHR=20+=20dual-immersion=20all-electric?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the mapper-driven e2e cascade pin for "simulated case 50" (000565 semi, electric storage main SAP 402 + portable electric secondary + MVHR + whc-903 DUAL electric immersion + 160 L cylinder, Economy-7). Routes the Summary PDF through extractor + mapper + calculator like the other 000565 fixtures. Locks in two off-peak fixes this case ground-truthed: - the Table 13 HW high/low split applied to CO2/PE (commit 39ae2cf0), and - the Table 12a Grid 2 MVHR fan fraction 0.71/0.58 (commit cd5113ab). All 11 SAP-result fields reconcile to the U985 worksheet EXACTLY, including the (272) rating CO2 2397.1237 — SAP 38.8426 (=39), cost £1317.0116, water 1668.0788 kWh, fans 315.6384 kWh. Summary mirrored to the tracked fixtures dir so the test doesn't depend on the unstaged `sap worksheets/` workspace. pyright strict gate not run locally (pyright not installed in this container). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../tests/fixtures/Summary_000565_case50.pdf | Bin 0 -> 67909 bytes .../_elmhurst_worksheet_000565_case50.py | 113 ++++++++++++++++++ .../worksheet/test_e2e_elmhurst_sap_score.py | 18 +++ 3 files changed, 131 insertions(+) create mode 100644 backend/documents_parser/tests/fixtures/Summary_000565_case50.pdf create mode 100644 tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case50.py diff --git a/backend/documents_parser/tests/fixtures/Summary_000565_case50.pdf b/backend/documents_parser/tests/fixtures/Summary_000565_case50.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ab23b99832f0c41720bb9ca96f01336fef6ae094 GIT binary patch literal 67909 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

v7W&q#sj{ooLLMo+piFNT5yjbrij^@!6JDpO`;I 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 literal 0 HcmV?d00001 diff --git a/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case50.py b/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case50.py new file mode 100644 index 00000000..e051922b --- /dev/null +++ b/tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000565_case50.py @@ -0,0 +1,113 @@ +"""Mapper-driven cascade pin against the Elmhurst U985-0001-000565 +"simulated case 50" worksheet — an all-electric Economy-7 dwelling with +MVHR + electric storage heaters + a DUAL-immersion hot-water cylinder. + +Like 000565 / the _rr cases, this fixture does NOT hand-build the +EpcPropertyData: it routes the Summary PDF through +ElmhurstSiteNotesExtractor + from_elmhurst_site_notes so the SAP-result +pin grid exercises the WHOLE extractor + mapper + calculator pipeline. + +This case was hand-built (Khalim) to ground-truth the dual-immersion +cylinder water-heating path, and it exercises two distinct off-peak +tariff mechanics that the gas/standard-tariff fixtures don't reach: + + - The Table 13 HW high-rate fraction for a whc-903 DUAL electric + immersion (0.1009 high / 0.8991 low) — the COST (6.4878 p/kWh) AND + the CO2/PE split ("Water heating - high/low rate cost", fixed in + `_electric_immersion_hw_high_rate_fraction`). + - The Table 12a Grid 2 fan fraction (0.71/0.58) for MVHR fan + electricity (315.64 kWh, 100% MVHR per worksheet line 230a) — billed + distinct from "all other uses" 0.90/0.80 (fixed by including the MVHR + fan in `mev_kwh_for_cost_split`). + +Unknown meter + dual electric immersion resolves to 7-hour off-peak via +the §12 trigger. After both fixes the existing-dwelling rating reconciles +to the U985 worksheet EXACTLY, including the (272) rating CO2. + +Cert shape: 000565 semi shell, main = electric storage heaters (SAP 402, +manual charge control), portable electric secondary (SAP 693), water +heating from a whc-903 dual electric immersion + 160 L foam cylinder (no +cylinder stat), MVHR (Vent Axia, PCDB 500140), one instantaneous electric +shower, no PV, Economy-7. + +Source: user-simulated PDFs at `sap worksheets/golden fixture debugging/ +simulated case 50/`. The Summary is mirrored into the tracked +`backend/documents_parser/tests/fixtures/Summary_000565_case50.pdf` so the +test runs without depending on the unstaged workspace. + +Worksheet pin targets (U985-0001-000565 block 1 — existing dwelling SAP): +- SAP rating 39 (258); continuous 38.8426; ECF 4.4252 (257) +- Total fuel cost £1317.0116 (255) +- Total CO2 2397.1237 kg/year (272) +- Space heating 14318.4904 kWh/year ((98c)) +- Main 1 fuel 12170.7169 kWh/year (211) +- Secondary fuel 2147.7736 kWh/year (215) +- Hot water fuel 1668.0788 kWh/year (219) +- Lighting 435.3204 kWh/year (232) +- Pumps/fans 315.6384 kWh/year (231) + +Per [[feedback-zero-error-strict]] + [[feedback-e2e-validation- +philosophy]]: pins are abs=1e-4 against the worksheet PDF. The pin +values live in `test_e2e_elmhurst_sap_score._FIXTURE_PINS`. +""" + +from __future__ import annotations + +import re +import subprocess +from pathlib import Path +from typing import Final + +from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor +from datatypes.epc.domain.epc_property_data import EpcPropertyData +from datatypes.epc.domain.mapper import EpcPropertyDataMapper + +# parents[0]=worksheet/, [1]=sap10_calculator/, [2]=domain/, [3]=tests/, +# [4]=repo root. +_SUMMARY_PDF: Final[Path] = ( + Path(__file__).resolve().parents[4] + / "backend" / "documents_parser" / "tests" / "fixtures" + / "Summary_000565_case50.pdf" +) + + +def _summary_pdf_to_textract_style_pages(pdf_path: Path) -> list[str]: + """Convert a Summary PDF into the per-page text format the + ElmhurstSiteNotesExtractor expects (label\\nvalue sequences). Mirror + of the helper in the other `_elmhurst_worksheet_*` fixtures. + """ + info = subprocess.run( + ["pdfinfo", str(pdf_path)], capture_output=True, text=True, check=True, + ).stdout + m = re.search(r"Pages:\s+(\d+)", info) + if m is None: + raise RuntimeError(f"Could not parse page count from {pdf_path}") + page_count = int(m.group(1)) + pages: list[str] = [] + for i in range(1, page_count + 1): + layout = subprocess.run( + [ + "pdftotext", "-layout", "-f", str(i), "-l", str(i), + str(pdf_path), "-", + ], + capture_output=True, text=True, check=True, + ).stdout + tokens: list[str] = [] + for line in layout.splitlines(): + if not line.strip(): + tokens.append("") + continue + parts = [p for p in re.split(r"\s{2,}", line.strip()) if p] + tokens.extend(parts) + pages.append("\n".join(tokens)) + return pages + + +def build_epc() -> EpcPropertyData: + """Route the simulated case-50 Summary through extractor + mapper. + No hand-built EpcPropertyData — the extractor and mapper are part of + the test target. + """ + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_PDF) + site_notes = ElmhurstSiteNotesExtractor(pages).extract() + return EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes) diff --git a/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py b/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py index 13b1b1bb..918329d9 100644 --- a/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py +++ b/tests/domain/sap10_calculator/worksheet/test_e2e_elmhurst_sap_score.py @@ -48,6 +48,7 @@ from tests.domain.sap10_calculator.worksheet import ( _elmhurst_worksheet_001431_case6 as _w001431_case6, _elmhurst_worksheet_001431_case7 as _w001431_case7, _elmhurst_worksheet_001431_case20 as _w001431_case20, + _elmhurst_worksheet_000565_case50 as _w000565_case50, _elmhurst_worksheet_000565_case52 as _w000565_case52, ) from tests.domain.sap10_calculator.worksheet._elmhurst_fixtures import ( @@ -297,6 +298,22 @@ _FIXTURE_PINS: Final[dict[str, FixtureCascadePins]] = { lighting_kwh_per_yr=246.3083, pumps_fans_kwh_per_yr=0.0, ), + # Mapper-driven — Summary_000565_case50.pdf → extractor → mapper → + # calculator. All-electric Economy-7: storage-heater main (SAP 402) + + # MVHR + whc-903 DUAL electric immersion + 160 L cylinder. Exercises + # the Table 13 HW high/low split (cost + CO2/PE) AND the Table 12a + # Grid 2 MVHR fan fraction (0.71/0.58). Reconciles to the worksheet + # EXACTLY after both off-peak fixes (incl. the (272) rating CO2). + "000565_case50": FixtureCascadePins( + sap_score=39, sap_score_continuous=38.8426, ecf=4.4252, + total_fuel_cost_gbp=1317.0116, co2_kg_per_yr=2397.1237, + space_heating_kwh_per_yr=14318.4904, + main_heating_fuel_kwh_per_yr=12170.7169, + secondary_heating_fuel_kwh_per_yr=2147.7736, + hot_water_kwh_per_yr=1668.0788, + lighting_kwh_per_yr=435.3204, + pumps_fans_kwh_per_yr=315.6384, + ), # Mapper-driven — Summary_000565_case52.pdf → extractor → mapper → # calculator. Regular (non-combi) mains-gas boiler (SAP 102) + a # 160 L foam cylinder heated from the main (WHC 901), no cylinder @@ -333,6 +350,7 @@ _FIXTURE_MODULES: Final[dict[str, ModuleType]] = { "001431_case6": _w001431_case6, "001431_case7": _w001431_case7, "001431_case20": _w001431_case20, + "000565_case50": _w000565_case50, "000565_case52": _w000565_case52, }