Reference PHX Model Reference

PHX Model Reference

Complete object graph, module map, design patterns, and testing conventions for the PHX in-memory data model.

schema version PHX 1.x
maintainer bldgtyp
source PHX / docs
LLM-ready · Fetch this doc

This page is mirrored at a stable URL for programmatic retrieval. Point your LLM tool at the URL below and it will always receive the current canonical schema in plain Markdown.

GET https://docs.passivehousetools.com/llm/phx/reference/phx-model-reference.md

PHX Model Reference

PHX (Passive House Exchange) is an in-memory intermediate representation of Passive House building data. Models are created from source formats (HBJSON, WUFI XML) and consumed by exporters (WUFI XML, PHPP, PPP, METr JSON). PHX models are never serialized directly.


Object Graph

PhxProject
├── assembly_types: dict[str, PhxConstructionOpaque]
│   └── layers: list[PhxLayer]
│       ├── material: PhxMaterial
│       └── divisions: PhxLayerDivisionGrid (optional, for composite layers)
│           └── cells: list[PhxLayerDivisionCell]
├── window_types: dict[str, PhxConstructionWindow]
│   ├── frame_top/right/bottom/left: PhxWindowFrameElement
│   └── _id_num_shade → PhxWindowShade
├── shade_types: dict[str, PhxWindowShade]
├── utilization_patterns_ventilation: UtilizationPatternCollection_Ventilation
├── utilization_patterns_occupancy: UtilizationPatternCollection_Occupancy
├── utilization_patterns_lighting: UtilizationPatternCollection_Lighting
├── project_data: PhxProjectData
│   ├── customer: ProjectData_Agent
│   ├── building: ProjectData_Agent
│   ├── owner: ProjectData_Agent
│   ├── designer: ProjectData_Agent
│   └── project_date: PhxProjectDate
└── variants: list[PhxVariant]
    ├── building: PhxBuilding
    │   ├── _components: list[PhxComponentOpaque]
    │   │   ├── assembly → PhxConstructionOpaque (ref by identifier)
    │   │   ├── polygons: list[PhxPolygon]
    │   │   │   └── vertices: list[PhxVertix]
    │   │   └── apertures: list[PhxComponentAperture]
    │   │       ├── window_type → PhxConstructionWindow (ref by identifier)
    │   │       └── elements: list[PhxApertureElement]
    │   │           ├── polygon: PhxPolygonRectangular | PhxPolygon | None
    │   │           └── shading_dimensions: PhxApertureShadingDimensions
    │   └── zones: list[PhxZone]
    │       ├── spaces: list[PhxSpace]
    │       │   ├── ventilation: PhxProgramVentilation (load + schedule)
    │       │   ├── occupancy: PhxProgramOccupancy (load + schedule)
    │       │   └── lighting: PhxProgramLighting (load + schedule)
    │       ├── _thermal_bridges: dict[str, PhxComponentThermalBridge]
    │       ├── elec_equipment_collection: PhxElectricDeviceCollection
    │       │   └── _devices: dict[str, PhxElectricalDevice subclass]
    │       └── exhaust_ventilator_collection: PhxExhaustVentilatorCollection
    ├── site: PhxSite
    │   ├── location: PhxLocation
    │   ├── climate: PhxClimate
    │   │   └── peak_heating/cooling: PhxClimatePeakLoad
    │   ├── ground: PhxGround
    │   ├── phpp_codes: PhxPHPPCodes
    │   └── energy_factors: PhxSiteEnergyFactors
    │       ├── pe_factors: dict[str, PhxPEFactor]
    │       └── co2_factors: dict[str, PhxCO2Factor]
    ├── phius_cert: PhxPhiusCertification
    │   ├── phius_certification_criteria: PhxPhiusCertificationCriteria
    │   ├── phius_certification_settings: PhxPhiusCertificationSettings
    │   └── ph_building_data: PhxPhBuildingData
    │       ├── setpoints: PhxSetpoints
    │       ├── summer_ventilation: PhxSummerVentilation
    │       └── foundations: list[PhxFoundation]
    │           (subtypes: PhxHeatedBasement, PhxUnHeatedBasement,
    │            PhxSlabOnGrade, PhxVentedCrawlspace)
    ├── phi_cert: PhxPhiCertification
    │   └── phi_certification_settings: PhxPhiCertificationSettings
    └── _mech_collections: list[PhxMechanicalSystemCollection]
        ├── _devices: dict[str, AnyMechDevice]
        │   (PhxDeviceVentilator, PhxHeater*, PhxHeatPump*, PhxHotWaterTank)
        ├── _distribution_piping_trunks: dict[str, PhxPipeTrunk]
        │   └── branches: list[PhxPipeBranch]
        │       └── fixtures: list[PhxPipeElement]
        │           └── segments: dict[str, PhxPipeSegment]
        ├── _distribution_piping_recirc: dict[str, PhxPipeElement]
        ├── _distribution_ducting: dict[str, PhxDuctElement]
        │   └── segments: dict[str, PhxDuctSegment]
        ├── supportive_devices: PhxSupportiveDeviceCollection
        │   └── _devices: dict[str, PhxSupportiveDevice]
        └── renewable_devices: PhxRenewableDeviceCollection
            └── _devices: dict[str, PhxDevicePhotovoltaic]

Quick Lookup

You want to find…Navigate to…
Wall/floor/roof surfacesPhxBuilding._components (list of PhxComponentOpaque)
Windows in a wallPhxComponentOpaque.apertures (list of PhxComponentAperture)
Construction/U-valuePhxComponentOpaque.assemblyPhxConstructionOpaque
Material layersPhxConstructionOpaque.layersPhxLayerPhxMaterial
Mixed-material layersPhxLayer.divisionsPhxLayerDivisionGridPhxLayerDivisionCell
Heat-flow pathwaysPhxConstructionOpaque.heat_flow_pathwayslist[PhxHeatFlowPathway]
Window propertiesPhxComponentAperture.window_typePhxConstructionWindow
Window framesPhxConstructionWindow.frame_top/right/bottom/leftPhxWindowFrameElement
Room ventilation ratesPhxSpace.ventilationPhxProgramVentilation.loadPhxLoadVentilation
Occupancy schedulePhxSpace.occupancyPhxProgramOccupancy.schedulePhxScheduleOccupancy
Thermal bridgesPhxZone.thermal_bridges (returns ValuesView[PhxComponentThermalBridge])
HVAC devicesPhxVariant.mech_collectionsPhxMechanicalSystemCollection.devices
Hot water pipingPhxMechanicalSystemCollection.dhw_distribution_trunksPhxPipeTrunkPhxPipeBranch
Recirculation pipingPhxMechanicalSystemCollection.dhw_recirc_pipinglist[PhxPipeElement]
Ventilation ductingPhxMechanicalSystemCollection.vent_ductinglist[PhxDuctElement]
Electrical equipmentPhxZone.elec_equipment_collectionPhxElectricDeviceCollection
Exhaust ventilatorsPhxZone.exhaust_ventilator_collectionPhxExhaustVentilatorCollection
Supportive devicesPhxMechanicalSystemCollection.supportive_devicesPhxSupportiveDeviceCollection
Renewable energy (PV)PhxMechanicalSystemCollection.renewable_devicesPhxRenewableDeviceCollection
Climate/locationPhxVariant.sitePhxSite.location, .climate
Certification dataPhxVariant.phius_cert or PhxVariant.phi_cert
Foundation dataPhxVariant.phius_cert.ph_building_data.foundations
All assembly typesPhxProject.assembly_types (dict by identifier)
All window typesPhxProject.window_types (dict by identifier)
All shade typesPhxProject.shade_types (dict by identifier)

Module Map

ModuleKey ClassesPurpose
model/project.pyPhxProject, PhxVariant, PhxProjectData, ProjectData_Agent, PhxProjectDate, WufiPluginTop-level containers
model/building.pyPhxBuilding, PhxZoneBuilding geometry container, thermal zones
model/components.pyPhxComponentBase, PhxComponentOpaque, PhxComponentAperture, PhxApertureElement, PhxApertureShadingDimensions, PhxComponentThermalBridgeSurfaces, windows, thermal bridges
model/constructions.pyPhxConstructionOpaque, PhxConstructionWindow, PhxWindowFrameElement, PhxLayer, PhxLayerDivisionGrid, PhxLayerDivisionCell, PhxMaterial, PhxColorAssembly/material definitions
model/assembly_pathways.pyPhxHeatFlowPathway, identify_heat_flow_pathways(), compute_r_value_from_pathways()ISO 6946 heat-flow pathway analysis for composite assemblies
model/geometry.pyPhxPolygon, PhxPolygonRectangular, PhxVertix, PhxVertix2D, PhxVector, PhxPlane, PhxLineSegment, PhxGraphics3D3D geometry primitives
model/spaces.pyPhxSpaceIndividual room/subzone with programs
model/certification.pyPhxPhiCertification, PhxPhiCertificationSettings, PhxPhiusCertification, PhxPhiusCertificationCriteria, PhxPhiusCertificationSettings, PhxPhBuildingData, PhxSetpoints, PhxSummerVentilationPassive house certification data
model/elec_equip.pyPhxElectricalDevice (base), PhxElectricDeviceCollection, + device subclasses (see below)Household electrical devices
model/ground.pyPhxFoundation (base), PhxHeatedBasement, PhxUnHeatedBasement, PhxSlabOnGrade, PhxVentedCrawlspaceGround/foundation models
model/phx_site.pyPhxSite, PhxLocation, PhxClimate, PhxClimatePeakLoad, PhxClimateIterOutput, PhxGround, PhxPEFactor, PhxCO2Factor, PhxSiteEnergyFactors, PhxPHPPCodesLocation and climate data
model/shades.pyPhxWindowShadeWindow shading devices
model/utilization_patterns.pyUtilizationPatternCollection_Ventilation, UtilizationPatternCollection_Occupancy, UtilizationPatternCollection_LightingSchedule collections
model/enums/Various enumsbuilding.py, hvac.py, elec_equip.py, foundations.py, phx_site.py, phi_certification_phpp_9.py, phi_certification_phpp_10.py, phius_certification.py
model/schedules/PhxScheduleVentilation (+ Vent_UtilPeriods, Vent_OperatingPeriod), PhxScheduleOccupancy, PhxScheduleLightingTime-based operating patterns
model/loads/PhxLoadVentilation, PhxLoadOccupancy, PhxLoadLightingNumeric load definitions
model/programs/PhxProgramVentilation, PhxProgramOccupancy, PhxProgramLightingLoad + Schedule pairs

Electrical Equipment Devices (model/elec_equip.py)

All subclass PhxElectricalDevice:

ClassDevice Type
PhxDeviceDishwasherKitchen dishwasher
PhxDeviceClothesWasherLaundry washer
PhxDeviceClothesDryerLaundry dryer
PhxDeviceRefrigeratorRefrigerator
PhxDeviceFreezerFreezer
PhxDeviceFridgeFreezerFridge/freezer combo
PhxDeviceCooktopKitchen cooking
PhxDeviceMELMisc. electric loads
PhxDeviceLightingInteriorInterior lighting
PhxDeviceLightingExteriorExterior lighting
PhxDeviceLightingGarageGarage lighting
PhxDeviceCustomElecUser-defined electric
PhxDeviceCustomLightingUser-defined lighting
PhxDeviceCustomMELUser-defined MEL
PhxElevatorHydraulicHydraulic elevator
PhxElevatorGearedTractionGeared traction elevator
PhxElevatorGearlessTractionGearless traction elevator

HVAC Subsystem (model/hvac/)

ModuleKey Classes
_base.pyPhxMechanicalDevice (base), PhxMechanicalDeviceParams (base), PhxUsageProfile
collection.pyPhxMechanicalSystemCollection, PhxExhaustVentilatorCollection, PhxSupportiveDeviceCollection, PhxRenewableDeviceCollection, PhxZoneCoverage
ventilation.pyPhxDeviceVentilation (base), PhxDeviceVentilator, PhxDeviceVentilatorParams, PhxExhaustVentilatorBase, PhxExhaustVentilatorRangeHood, PhxExhaustVentilatorDryer, PhxExhaustVentilatorUserDefined, PhxExhaustVentilatorParams
heating.pyPhxHeatingDevice (base), PhxHeaterElectric, PhxHeaterBoilerFossil, PhxHeaterBoilerWood, PhxHeaterDistrictHeat, + corresponding *Params classes
heat_pumps.pyPhxHeatPumpDevice (base), PhxHeatPumpAnnual, PhxHeatPumpMonthly, PhxHeatPumpHotWater, PhxHeatPumpCombined, + corresponding *Params classes
water.pyPhxHotWaterDevice (base), PhxHotWaterTank, PhxHotWaterTankParams
piping.pyPhxPipeTrunk, PhxPipeBranch, PhxPipeElement, PhxPipeSegment, PhxRecirculationParameters
ducting.pyPhxDuctElement, PhxDuctSegment
renewable_devices.pyPhxDevicePhotovoltaic, PhxDevicePhotovoltaicParams
supportive_devices.pyPhxSupportiveDevice, PhxSupportiveDeviceParams
cooling_params.pyPhxCoolingParams (collection), PhxCoolingVentilationParams, PhxCoolingRecirculationParams, PhxCoolingDehumidificationParams, PhxCoolingPanelParams

Design Patterns

ClassVar Counters

Every model class has _count: ClassVar[int] = 0 auto-incremented in __post_init__ or __init__, assigning id_num. Tests must reset these for predictable IDs.

UUID + id_num Dual Identity

Constructions and devices carry both a uuid.UUID | str identifier and an integer id_num. The UUID is for lookup/deduplication; id_num is for sequential output numbering.

__add__ Merging

The following model classes support + for consolidation (merging coplanar surfaces, combining spaces by ERV, etc.):

  • PhxComponentOpaque — merge surfaces with same assembly
  • PhxComponentAperture — merge windows with same type
  • PhxComponentThermalBridge — merge TBs with same psi/type (length-weighted psi recalculation)
  • PhxSpace — merge spaces by ERV assignment
  • PhxLoadVentilation — combine airflow values
  • PhxUsageProfile — combine coverage percentages
  • PhxMechanicalDevice (and subclasses) — merge device quantities/coverage
  • PhxMechanicalDeviceParams (and subclasses) — merge device parameters
  • Various exhaust ventilator types, PhxHotWaterTank, PhxSupportiveDevice, PhxDevicePhotovoltaic

Program = Load + Schedule

Ventilation, occupancy, and lighting each follow: PhxProgram* = PhxLoad* + PhxSchedule*. The load holds numeric values (airflow, people, watts); the schedule holds operating periods and hours.

Dict-Keyed vs List Collections

  • Dict-keyed (by identifier/key): assembly_types, window_types, shade_types, _devices, _thermal_bridges — O(1) lookup
  • List-ordered: variants, zones, spaces, _components — ordered iteration

Component Classes Use __init__ (not dataclass)

PhxComponentBase subclasses (PhxComponentOpaque, PhxComponentAperture, PhxApertureShadingDimensions, PhxComponentThermalBridge) use plain __init__ with a shared _count on PhxComponentBase, unlike most other model classes which use @dataclass.

Library References

Constructions/windows/shades live in project-level dicts; components reference them by identifier, not by embedding.

Piping Hierarchy

DHW piping uses a three-level hierarchy: PhxPipeTrunkPhxPipeBranchPhxPipeElement (fixtures). Each PhxPipeElement contains PhxPipeSegment objects. Recirculation piping is stored separately as flat PhxPipeElement entries.

unique_key Property

Classes that may be grouped by construction or type expose a unique_key property. Components with the same unique_key can be merged via __add__. For example, PhxComponentOpaque instances sharing the same assembly identifier have the same unique_key and can be combined into a single component.

Enum Runtime Extension

Some enums use _missing_() to handle unknown values at runtime rather than raising. For example, ComponentExposureExterior returns a fallback member for unrecognized integer values from WUFI XML imports. This prevents deserialization failures on non-standard model files.


Honeybee to PHX Concept Mapping

HoneybeePHXNotes
ModelPhxProjectTop-level container. One HB Model → one PhxProject.
RoomPhxZone (via PhxBuilding)HB Rooms grouped by ph_bldg_segment into zones.
Room (sub-space)PhxSpaceEach HB Room → one or more PhxSpaces within a zone.
Face (Wall/Floor/Roof)PhxComponentOpaqueEach HB Face → component with geometry + assembly ref.
AperturePhxComponentAperture / PhxApertureElementWindows within opaque components.
OpaqueConstructionPhxConstructionOpaqueReusable assembly in PhxProject.assembly_types.
WindowConstructionPhxConstructionWindowReusable window type in PhxProject.window_types.
EnergyMaterialPhxMaterialPart of construction layers.
IdealAirSystem / HVACPhxMechanicalSystemCollectionHB HVAC → PHX device collections.
SchedulePhxSchedule*Operating patterns with utilization periods.
ph_bldg_segmentPhxVariantRooms sharing a segment become one variant.

Key Structural Differences

Libraries vs inline: Honeybee stores constructions/schedules inline on rooms and faces. PHX extracts them into project-level libraries and components reference by identifier.

Room → Zone + Space split: A single HB Room may become one PhxZone with one PhxSpace, or multiple rooms may be grouped into a single zone with multiple spaces (grouped by ph_bldg_segment).

Program composition: HB stores loads and schedules separately. PHX pairs them: PhxProgramVentilation = PhxLoadVentilation + PhxScheduleVentilation.

HVAC disaggregation: HB uses high-level IdealAirSystem. PHX disaggregates into specific device types (ventilators, heaters, heat pumps, hot water tanks, piping) with usage profiles specifying coverage percentages.

Conversion Entry Points

The PHX repo’s from_HBJSON/ package handles Honeybee → PHX conversion:

  • create_project.convert_hb_model_to_PhxProject() — main entry point
  • create_variant.py — build PhxVariant from HB model
  • create_building.py, create_rooms.py, create_geometry.py — geometry conversion
  • create_assemblies.py — construction/material conversion
  • create_hvac.py — HVAC device conversion
  • create_schedules.py — schedule conversion
  • create_elec_equip.py — electrical equipment conversion
  • create_shades.py — shade device conversion
  • create_shw_devices.py — service hot water device conversion
  • create_foundations.py — foundation/ground conversion
  • cleanup.py, cleanup_merge_faces.py — post-conversion cleanup (vertex welding, face merging, component grouping)

Testing Patterns

Class Counter Reset

Tests must reset _count ClassVars for predictable IDs:

@pytest.fixture
def reset_class_counters():
    _reset_phx_class_counters()
    try:
        yield
    finally:
        _reset_phx_class_counters()

When adding a new model class with _count, add it to _reset_phx_class_counters() in conftest.py.

Module Reload (End-to-End Tests)

For reference-case tests that compare full XML output, modules must be reloaded (not just counters reset) to guarantee clean state:

importlib.reload(building)
importlib.reload(components)
# ... all model modules

Add new modules to _reload_phx_classes() in conftest.py if needed for reference-case tests.

Test Organization

Tests mirror the source structure under tests/:

DirectoryCoverage
test_model/Unit tests for model classes (building, components, constructions, geometry, hvac, spaces, etc.)
test_from_HBJSON/HBJSON → PHX conversion tests
test_to_WUFI_xml/PHX → XML export tests (includes end-to-end reference cases)
test_from_WUFI/XML → PHX reverse conversion tests
test_PHPP/PHPP Excel export tests
test_to_PPP/PPP export tests

Writing New Tests

  1. Place tests in the directory that mirrors the source module path
  2. Use reset_class_counters fixture for any test that creates model objects
  3. Test __str__/__repr__ to catch serialization issues early
  4. Test __add__ if the class supports merging
  5. Test unique_key if the class supports grouping
  6. For new HVAC device types, test both standalone creation and addition to PhxMechanicalSystemCollection

Common Test Patterns

Unit test (model class):

def test_blank_project(reset_class_counters):
    proj = project.PhxProject()
    assert str(proj)
    assert not proj.assembly_types
    assert not proj.variants

Property / behavior test:

def test_component_face_type(reset_class_counters):
    comp = PhxComponentOpaque()
    comp.face_type = ComponentFaceType.WALL
    comp.exposure_exterior = ComponentExposureExterior.EXTERIOR
    assert comp.is_above_grade_wall is True

Merge / addition test:

def test_space_addition(reset_class_counters):
    space_a = PhxSpace()
    space_a.floor_area = 100.0
    space_b = PhxSpace()
    space_b.floor_area = 50.0
    merged = space_a + space_b
    assert merged.floor_area == 150.0

End-to-end reference case:

def test_xml_output(to_xml_reference_cases):
    hbjson_file, xml_file = to_xml_reference_cases
    hb_json_dict = read_HBJSON_file.read_hb_json_from_file(hbjson_file)
    hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)
    phx_project = create_project.convert_hb_model_to_PhxProject(hb_model)
    xml_txt = xml_builder.generate_WUFI_XML_from_object(phx_project)
    expected = read_WUFI_XML_file.get_WUFI_xml_file_as_str(xml_file)
    assert xml_txt == expected

Geometry fixture:

@pytest.fixture
def polygon_1x1x0():  # 1m x 1m square at z=0
    p = PhxPolygon("no_name", 100.0, PhxVertix(1,1,0), PhxVector(0,0,1), plane)
    p.add_vertix(PhxVertix(0,0,0))
    p.add_vertix(PhxVertix(0,1,0))
    p.add_vertix(PhxVertix(1,1,0))
    p.add_vertix(PhxVertix(1,0,0))
    return p