mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Slice S0380.99: PCDB Table 329 (MV In-Use Factors) ETL + parser + lookup (PCDF Spec §A.20)
PCDF Spec Rev 6b §A.20 (May 2021) Format 430 — Mechanical Ventilation
In-Use Factors Table. Pcdb10.dat carries Format 432 (header
`$329,432,4,2021,11,25,2`), an extended-field version where Format
430 fields 1-4 (system_type + 3 SFP factors for the "no approved
scheme" variant) align at positions 0..3. The remainder of Format
432 carries MVHR adjustments + "with approved scheme" variants +
additional Format 432 columns, preserved verbatim in `raw` for
follow-up slices.
Per PCDF Spec §A.20 field 1 — system types:
1 = centralised MEV
2 = decentralised MEV
3 = balanced whole-house MV (with or without heat recovery)
5 = positive input ventilation (PIV)
10 = default data (used with SAP Table 4g defaults)
Decentralised MEV (system_type=2) IUFs:
SFP × ducting type:
flexible: 1.45 (field 2)
rigid: 1.30 (field 3)
no-duct: 1.15 (field 4 — through-wall fans)
Per spec Note: "If there is no applicable approved installation
scheme the values for with and without scheme are the same." Cert
000565 lodges "Approved Installation: No" → use the "no scheme"
IUFs.
Validation for cert 000565 against worksheet line (230a):
Σ(SFP_j × FR_j × IUF_j) for the 4 lodged fans:
in-room kitchen: 1×0.15×13×1.45 = 2.8275
in-room other wet: 1×0.15× 8×1.45 = 1.7400
through-wall kitchen: 2×0.11×13×1.15 = 3.2890
through-wall other wet: 3×0.14× 8×1.15 = 3.8640
Σ = 11.7205 W (matches worksheet "total watage = 11.7205")
Σ(FR_j) = 92.0 l/s (matches worksheet "total flow = 92.0000")
SFPav = 11.7205 / 92.0 = 0.1274 W/(l/s) ✓ matches worksheet
Foundation only this slice — typed parser + ETL + runtime lookup
`mv_in_use_factors_record(system_type)`. No cascade integration; no
behavioural change on any cert. Next slice S0380.100 wires the
SFPav formula.
5 Table 329 records ingested. Pyright net-zero per touched file.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
b3330821e7
commit
433f4a49ce
6 changed files with 234 additions and 0 deletions
|
|
@ -32,8 +32,10 @@ from domain.sap10_calculator.tables.pcdb.parser import (
|
||||||
GasOilBoilerRecord,
|
GasOilBoilerRecord,
|
||||||
HeatPumpRecord,
|
HeatPumpRecord,
|
||||||
MevFanConfig,
|
MevFanConfig,
|
||||||
|
MvInUseFactorsRecord,
|
||||||
parse_decentralised_mev_row,
|
parse_decentralised_mev_row,
|
||||||
parse_heat_pump_row_raw,
|
parse_heat_pump_row_raw,
|
||||||
|
parse_mv_in_use_factors_row,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|
@ -41,9 +43,11 @@ __all__ = [
|
||||||
"GasOilBoilerRecord",
|
"GasOilBoilerRecord",
|
||||||
"HeatPumpRecord",
|
"HeatPumpRecord",
|
||||||
"MevFanConfig",
|
"MevFanConfig",
|
||||||
|
"MvInUseFactorsRecord",
|
||||||
"decentralised_mev_record",
|
"decentralised_mev_record",
|
||||||
"gas_oil_boiler_record",
|
"gas_oil_boiler_record",
|
||||||
"heat_pump_record",
|
"heat_pump_record",
|
||||||
|
"mv_in_use_factors_record",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -54,6 +58,9 @@ _TABLE_105_JSONL: Final[Path] = (
|
||||||
_TABLE_322_JSONL: Final[Path] = (
|
_TABLE_322_JSONL: Final[Path] = (
|
||||||
_PCDB_DATA_DIR / "pcdb_table_322_decentralised_mev.jsonl"
|
_PCDB_DATA_DIR / "pcdb_table_322_decentralised_mev.jsonl"
|
||||||
)
|
)
|
||||||
|
_TABLE_329_JSONL: Final[Path] = (
|
||||||
|
_PCDB_DATA_DIR / "pcdb_table_329_mv_in_use_factors.jsonl"
|
||||||
|
)
|
||||||
_TABLE_362_JSONL: Final[Path] = (
|
_TABLE_362_JSONL: Final[Path] = (
|
||||||
_PCDB_DATA_DIR / "pcdb_table_362_heat_pumps.jsonl"
|
_PCDB_DATA_DIR / "pcdb_table_362_heat_pumps.jsonl"
|
||||||
)
|
)
|
||||||
|
|
@ -172,3 +179,36 @@ def decentralised_mev_record(pcdb_id: int) -> Optional[DecentralisedMevRecord]:
|
||||||
for MEV centralised or decentralised) per the spec's first-tier
|
for MEV centralised or decentralised) per the spec's first-tier
|
||||||
cascade rule (§2.6.3 / Table 4g note 1)."""
|
cascade rule (§2.6.3 / Table 4g note 1)."""
|
||||||
return _TABLE_322_BY_ID.get(pcdb_id)
|
return _TABLE_322_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
|
||||||
|
jsonl is missing (ETL bootstrap concession; production callers
|
||||||
|
always observe the committed file)."""
|
||||||
|
records_by_type: dict[int, MvInUseFactorsRecord] = {}
|
||||||
|
if not _TABLE_329_JSONL.exists():
|
||||||
|
return records_by_type
|
||||||
|
with _TABLE_329_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_mv_in_use_factors_row(",".join(raw_fields))
|
||||||
|
records_by_type[record.system_type] = record
|
||||||
|
return records_by_type
|
||||||
|
|
||||||
|
|
||||||
|
_TABLE_329_BY_SYSTEM_TYPE: Final[dict[int, MvInUseFactorsRecord]] = _load_table_329()
|
||||||
|
|
||||||
|
|
||||||
|
def mv_in_use_factors_record(system_type: int) -> Optional[MvInUseFactorsRecord]:
|
||||||
|
"""Table 329 lookup by SAP 10.2 ventilation system type (1, 2, 3,
|
||||||
|
5, 10 per PCDF Spec §A.20). Returns None when the system_type is
|
||||||
|
not in Table 329 — caller can fall back to SAP 10.2 Table 4g
|
||||||
|
defaults (system_type=10) or skip the IUF adjustment per spec
|
||||||
|
Note: "If there is no applicable approved installation scheme the
|
||||||
|
values for with and without scheme are the same"."""
|
||||||
|
return _TABLE_329_BY_SYSTEM_TYPE.get(system_type)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{"system_type": 1, "raw": ["1", "1.7", "1.4", "", "", "", "", "", "1.6", "1.3", "", "", "", "", "", "", "", "", "", "", "", "", "2021/Nov/22 14:55"]}
|
||||||
|
{"system_type": 2, "raw": ["2", "1.45", "1.3", "1.15", "", "", "", "", "1.45", "1.3", "1.15", "", "", "", "", "", "", "", "", "", "", "", "2021/Nov/22 14:55"]}
|
||||||
|
{"system_type": 5, "raw": ["5", "1.7", "1.4", "", "", "", "", "", "1.6", "1.25", "", "", "", "", "", "1.1", "1.1", "", "", "", "", "", "2021/Nov/22 14:55"]}
|
||||||
|
{"system_type": 3, "raw": ["3", "1.7", "1.4", "", "0.25", "0.5", "0.9", "0.8", "1.6", "1.25", "", "0.25", "0.5", "0.9", "0.8", "1.1", "1.1", "", "0.25", "0.5", "0.9", "0.8", "2021/Nov/22 14:55"]}
|
||||||
|
{"system_type": 10, "raw": ["10", "2.5", "2.5", "2.5", "0.25", "0.25", "0.7", "0.7", "2.5", "2.5", "2.5", "0.25", "0.25", "0.7", "0.7", "2.5", "2.5", "2.5", "0.25", "0.25", "0.7", "0.7", "2021/Nov/22 14:55"]}
|
||||||
|
|
@ -16,15 +16,18 @@ from pathlib import Path
|
||||||
from domain.sap10_calculator.tables.pcdb.parser import (
|
from domain.sap10_calculator.tables.pcdb.parser import (
|
||||||
DecentralisedMevRecord,
|
DecentralisedMevRecord,
|
||||||
GasOilBoilerRecord,
|
GasOilBoilerRecord,
|
||||||
|
MvInUseFactorsRecord,
|
||||||
RawPcdbRecord,
|
RawPcdbRecord,
|
||||||
parse_table_105,
|
parse_table_105,
|
||||||
parse_table_322,
|
parse_table_322,
|
||||||
|
parse_table_329,
|
||||||
parse_table_raw,
|
parse_table_raw,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
_TABLE_105_OUTPUT_FILENAME: str = "pcdb_table_105_gas_oil_boilers.jsonl"
|
_TABLE_105_OUTPUT_FILENAME: str = "pcdb_table_105_gas_oil_boilers.jsonl"
|
||||||
_TABLE_322_OUTPUT_FILENAME: str = "pcdb_table_322_decentralised_mev.jsonl"
|
_TABLE_322_OUTPUT_FILENAME: str = "pcdb_table_322_decentralised_mev.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
|
# Tables ingested as `RawPcdbRecord` (pcdb_id + raw) — per-field typing is
|
||||||
# deferred to follow-up slices when the cert-side wiring for each table
|
# deferred to follow-up slices when the cert-side wiring for each table
|
||||||
# lands.
|
# lands.
|
||||||
|
|
@ -81,6 +84,17 @@ def run_etl(*, source: Path, output_dir: Path) -> None:
|
||||||
for r in parse_table_322(dat_text)
|
for r in parse_table_322(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
|
||||||
|
# exercises). Stored as raw row + typed-on-load.
|
||||||
|
_write_ndjson(
|
||||||
|
output_path=output_dir / _TABLE_329_OUTPUT_FILENAME,
|
||||||
|
records=[
|
||||||
|
_mv_in_use_factors_record_to_jsonable(r)
|
||||||
|
for r in parse_table_329(dat_text)
|
||||||
|
],
|
||||||
|
)
|
||||||
for table_id, filename in _RAW_TABLES.items():
|
for table_id, filename in _RAW_TABLES.items():
|
||||||
_write_ndjson(
|
_write_ndjson(
|
||||||
output_path=output_dir / filename,
|
output_path=output_dir / filename,
|
||||||
|
|
@ -98,6 +112,16 @@ def _decentralised_mev_record_to_jsonable(
|
||||||
return {"pcdb_id": record.pcdb_id, "raw": list(record.raw)}
|
return {"pcdb_id": record.pcdb_id, "raw": list(record.raw)}
|
||||||
|
|
||||||
|
|
||||||
|
def _mv_in_use_factors_record_to_jsonable(
|
||||||
|
record: MvInUseFactorsRecord,
|
||||||
|
) -> dict[str, object]:
|
||||||
|
"""Serialise a typed Table 329 record. Table 329 is keyed by
|
||||||
|
`system_type` rather than `pcdb_id`, so this dict uses `system_type`
|
||||||
|
as the primary identifier; lookup callers `mv_in_use_factors(
|
||||||
|
system_type)` resolve via the same key."""
|
||||||
|
return {"system_type": record.system_type, "raw": list(record.raw)}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": # pragma: no cover — manual ETL invocation
|
if __name__ == "__main__": # pragma: no cover — manual ETL invocation
|
||||||
data_dir = Path(__file__).resolve().parent / "data"
|
data_dir = Path(__file__).resolve().parent / "data"
|
||||||
run_etl(
|
run_etl(
|
||||||
|
|
|
||||||
|
|
@ -519,6 +519,79 @@ def parse_table_322(dat_text: str) -> list[DecentralisedMevRecord]:
|
||||||
return [parse_decentralised_mev_row(row) for row in _walk_table_records(dat_text, "322")]
|
return [parse_decentralised_mev_row(row) for row in _walk_table_records(dat_text, "322")]
|
||||||
|
|
||||||
|
|
||||||
|
# Table 329 (MV In-Use Factors) — PCDF Spec Rev 6b §A.20 Format 430.
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# System types per PCDF Spec §A.20 field 1:
|
||||||
|
# 1 = centralised MEV
|
||||||
|
# 2 = decentralised MEV
|
||||||
|
# 3 = balanced whole-house MV (with or without heat recovery)
|
||||||
|
# 5 = positive input ventilation (PIV)
|
||||||
|
# 10 = default data (used when SFP / efficiency are taken from SAP
|
||||||
|
# Table 4g rather than the PCDB)
|
||||||
|
_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
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class MvInUseFactorsRecord:
|
||||||
|
"""PCDB Table 329 (MV In-Use Factors) typed record.
|
||||||
|
|
||||||
|
SAP 10.2 §2.6 + §2.6.4 — in-use factors (IUF) are multiplied into
|
||||||
|
the PCDB SFP per equation (1) to allow for additional ductwork
|
||||||
|
losses encountered in practice. Per PCDF Spec §A.20 Note 1: "If
|
||||||
|
there is no applicable approved installation scheme the values
|
||||||
|
for with and without scheme are the same" — so this slice exposes
|
||||||
|
the "no scheme" SFP IUFs only; with-scheme variants are deferred
|
||||||
|
until a fixture lodges an approved installation.
|
||||||
|
|
||||||
|
Fields are Optional because each system_type populates a subset
|
||||||
|
(e.g. centralised MEV lodges the flex / rigid IUFs but no
|
||||||
|
through-wall — the no-duct field is blank).
|
||||||
|
"""
|
||||||
|
|
||||||
|
system_type: int
|
||||||
|
sfp_iuf_flexible_no_scheme: Optional[float]
|
||||||
|
sfp_iuf_rigid_no_scheme: Optional[float]
|
||||||
|
sfp_iuf_no_duct_no_scheme: Optional[float]
|
||||||
|
raw: tuple[str, ...]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_table_329(dat_text: str) -> list[MvInUseFactorsRecord]:
|
||||||
|
"""Walk a PCDB dat string, yielding parsed Table 329 (MV In-Use
|
||||||
|
Factors) records. One record per `system_type`; SFP IUFs decoded
|
||||||
|
for the "no scheme" variant per PCDF Spec §A.20 Note 1."""
|
||||||
|
return [parse_mv_in_use_factors_row(row) for row in _walk_table_records(dat_text, "329")]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_mv_in_use_factors_row(row: str) -> MvInUseFactorsRecord:
|
||||||
|
"""Decode one Table 329 (MV In-Use Factors) Format-432 row into a
|
||||||
|
typed `MvInUseFactorsRecord`. Positions 0..3 align with PCDF Spec
|
||||||
|
Format 430 fields 1..4 — the "no approved scheme" SFP IUFs."""
|
||||||
|
fields = tuple(row.rstrip("\r\n").split(","))
|
||||||
|
return MvInUseFactorsRecord(
|
||||||
|
system_type=int(fields[_MV_IUF_IDX_SYSTEM_TYPE]),
|
||||||
|
sfp_iuf_flexible_no_scheme=_parse_optional_float(
|
||||||
|
fields[_MV_IUF_IDX_SFP_FLEX_NO_SCHEME]
|
||||||
|
),
|
||||||
|
sfp_iuf_rigid_no_scheme=_parse_optional_float(
|
||||||
|
fields[_MV_IUF_IDX_SFP_RIGID_NO_SCHEME]
|
||||||
|
),
|
||||||
|
sfp_iuf_no_duct_no_scheme=_parse_optional_float(
|
||||||
|
fields[_MV_IUF_IDX_SFP_NO_DUCT_NO_SCHEME]
|
||||||
|
),
|
||||||
|
raw=fields,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_decentralised_mev_row(row: str) -> DecentralisedMevRecord:
|
def parse_decentralised_mev_row(row: str) -> DecentralisedMevRecord:
|
||||||
"""Decode one Table 322 (Decentralised MEV) Format-428 row into a
|
"""Decode one Table 322 (Decentralised MEV) Format-428 row into a
|
||||||
typed `DecentralisedMevRecord`.
|
typed `DecentralisedMevRecord`.
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,7 @@ def test_run_etl_writes_all_pcdb_table_jsonl_files(tmp_path: Path) -> None:
|
||||||
"pcdb_table_143_micro_cogen.jsonl",
|
"pcdb_table_143_micro_cogen.jsonl",
|
||||||
"pcdb_table_313_flue_gas_heat_recovery.jsonl",
|
"pcdb_table_313_flue_gas_heat_recovery.jsonl",
|
||||||
"pcdb_table_322_decentralised_mev.jsonl",
|
"pcdb_table_322_decentralised_mev.jsonl",
|
||||||
|
"pcdb_table_329_mv_in_use_factors.jsonl",
|
||||||
"pcdb_table_353_waste_water_heat_recovery.jsonl",
|
"pcdb_table_353_waste_water_heat_recovery.jsonl",
|
||||||
"pcdb_table_362_heat_pumps.jsonl",
|
"pcdb_table_362_heat_pumps.jsonl",
|
||||||
"pcdb_table_391_high_heat_retention_storage_heaters.jsonl",
|
"pcdb_table_391_high_heat_retention_storage_heaters.jsonl",
|
||||||
|
|
|
||||||
91
domain/sap10_calculator/tests/test_pcdb_table_329_lookup.py
Normal file
91
domain/sap10_calculator/tests/test_pcdb_table_329_lookup.py
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
"""Tests for the runtime PCDB Table 329 (MV In-Use Factors) lookup.
|
||||||
|
|
||||||
|
Table 329 maps each ventilation system_type (1=centralised MEV,
|
||||||
|
2=decentralised MEV, 3=balanced w/wo HR, 5=PIV, 10=default) to a set
|
||||||
|
of multiplicative in-use factors (IUF) that adjust the PCDB-lodged
|
||||||
|
Specific Fan Power for the ducting type actually installed.
|
||||||
|
|
||||||
|
The cascade applies the IUFs via SAP 10.2 §2.6.4 equation (1) — in
|
||||||
|
the SFPav numerator the PCDB SFP for each fan is multiplied by the
|
||||||
|
applicable in-use factor before the flow-weighted average is taken.
|
||||||
|
|
||||||
|
Reference: BRE PCDB pcdb10.dat (header `$329,432,4,2021,11,25,2`);
|
||||||
|
PCDF Spec Rev 6b §A.20 Format 430; SAP 10.2 specification §2.6 +
|
||||||
|
§2.6.4.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from domain.sap10_calculator.tables.pcdb import mv_in_use_factors_record
|
||||||
|
|
||||||
|
|
||||||
|
def test_mv_in_use_factors_for_decentralised_mev_returns_per_ducting_iufs() -> None:
|
||||||
|
"""SAP 10.2 §2.6.4 — decentralised MEV (system_type=2) lodges
|
||||||
|
three per-ducting SFP in-use factors at PCDF Spec §A.20 fields
|
||||||
|
2, 3, 4 (Format 432 positions 1, 2, 3):
|
||||||
|
|
||||||
|
flexible ducting: 1.45
|
||||||
|
rigid ducting: 1.30
|
||||||
|
no ducting (TW): 1.15
|
||||||
|
|
||||||
|
The "no scheme" variants are exercised by cert 000565 (Approved
|
||||||
|
Installation: No); the with-scheme variants are deferred until a
|
||||||
|
cohort cert lodges an approved installation.
|
||||||
|
|
||||||
|
Cert 000565 validation:
|
||||||
|
ws line (230a) = IUF × SFPav × 1.22 × V → 127.5159 kWh
|
||||||
|
where SFPav = Σ(SFP_j × FR_j × IUF_j) / Σ(FR_j) = 11.7205 / 92 = 0.1274
|
||||||
|
and IUF_j ∈ {1.45 (in-room flex), 1.15 (through-wall)}
|
||||||
|
"""
|
||||||
|
# Arrange / Act
|
||||||
|
record = mv_in_use_factors_record(2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert record is not None
|
||||||
|
assert record.system_type == 2
|
||||||
|
assert record.sfp_iuf_flexible_no_scheme == 1.45
|
||||||
|
assert record.sfp_iuf_rigid_no_scheme == 1.30
|
||||||
|
assert record.sfp_iuf_no_duct_no_scheme == 1.15
|
||||||
|
|
||||||
|
|
||||||
|
def test_mv_in_use_factors_for_centralised_mev_has_no_through_wall_iuf() -> None:
|
||||||
|
"""Centralised MEV (system_type=1) doesn't include "through-the-
|
||||||
|
wall" / no-duct as a valid fan location — fans are centrally
|
||||||
|
located with ducting to each wet room. The PCDB record reflects
|
||||||
|
this by leaving field 4 (no-duct IUF) blank."""
|
||||||
|
# Arrange / Act
|
||||||
|
record = mv_in_use_factors_record(1)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert record is not None
|
||||||
|
assert record.system_type == 1
|
||||||
|
assert record.sfp_iuf_flexible_no_scheme == 1.7
|
||||||
|
assert record.sfp_iuf_rigid_no_scheme == 1.4
|
||||||
|
assert record.sfp_iuf_no_duct_no_scheme is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_mv_in_use_factors_for_default_data_system_type_10() -> None:
|
||||||
|
"""System type 10 = default data per PCDF Spec §A.20: used when SFP
|
||||||
|
and efficiency are taken from SAP Table 4g rather than the PCDB.
|
||||||
|
All three SFP IUFs are 2.5 (per the pcdb10.dat record), reflecting
|
||||||
|
a conservative spread for un-lodged products."""
|
||||||
|
# Arrange / Act
|
||||||
|
record = mv_in_use_factors_record(10)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert record is not None
|
||||||
|
assert record.system_type == 10
|
||||||
|
assert record.sfp_iuf_flexible_no_scheme == 2.5
|
||||||
|
assert record.sfp_iuf_rigid_no_scheme == 2.5
|
||||||
|
assert record.sfp_iuf_no_duct_no_scheme == 2.5
|
||||||
|
|
||||||
|
|
||||||
|
def test_mv_in_use_factors_returns_none_for_unknown_system_type() -> None:
|
||||||
|
"""A system_type not in Table 329 returns None so callers can
|
||||||
|
fall back to default (system_type=10) or skip the IUF adjustment
|
||||||
|
entirely."""
|
||||||
|
# Arrange / Act
|
||||||
|
record = mv_in_use_factors_record(99)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert record is None
|
||||||
Loading…
Add table
Reference in a new issue