Skip to main content

OpenPH

Core PHPP data models and table view generation

Part of the openph UV workspace - a Python implementation of Passive House Planning Package (PHPP) calculations with exact numerical fidelity to Excel PHPP.

Purpose

OpenPH provides:

  • Data Models: Python classes representing PHPP building components (areas, constructions, rooms, climate, HVAC systems)
  • Table Rendering: Generate formatted output tables (.txt, .html) matching PHPP worksheet layouts for validation
  • Plugin Architecture: Extensible table system with auto-discovery via entry points
  • HBJSON Import: Convert Honeybee-PH JSON models to OpenPH data structures

Structure

openph/
├── src/
│   └── openph/         # Main package module
│       ├── model/      # PHPP data classes
│       ├── to_table/   # Table rendering system (plugin-based)
│       ├── from_HBJSON/# Honeybee-PH JSON import
│       └── phpp.py     # Main PHPP container class
├── tests/
└── pyproject.toml

Usage

Converting a PHX Model (canonical entry point)

OpenPH's public conversion boundary accepts a live, in-memory PHX.model.project.PhxVariant — no file I/O, no serialization round trips. OpenPH does not accept native Honeybee objects; Honeybee → PHX is PHX's concern, PHX → OpenPH is OpenPH's:

Honeybee/honeybee-ph model
  → PHX.conversion.from_honeybee
  → PhxProject
  → select exactly one PhxVariant
  → openph.conversion.from_phx_variant
  → OpPhPHPP
from PHX.conversion import from_honeybee
from openph.conversion import from_phx_variant

phx_project = from_honeybee(hb_model, group_components=True)
if len(phx_project.variants) != 1:
    raise ValueError(f"OpenPH requires exactly one PHX variant; got {len(phx_project.variants)}")
phpp = from_phx_variant(phx_project.variants[0])

# Calculate through a registered solver (requires the openph-demand plugin):
heating = phpp.get_solver("energy_demand").heating_demand
annual_kwh = heating.total_yearly_heating_demand              # Heating!AF117
annual_kwh_m2a = heating.total_yearly_specific_heating_demand  # Heating!Q78

Read the annual scalars rather than summing a monthly row: each is a canonical PHPP-addressed result, so every consumer reports the same number.

Getting results out

Three surfaces, each doing one job — pick by what the caller needs:

Need Use
Audit / PHPP comparison — every input, intermediate, and final value with its worksheet address openph.results.collect_results(phpp)OpPhResults
An application payload — annual and monthly demand, warnings, provenance, small enough to return per request openph_demand.build_energy_demand_summary(phpp)EnergyDemandSummary
Human inspection / export the table views (openph[tables])

The compact summary supplements the audit document rather than replacing it; for one model it is roughly 200x smaller. Core openph needs neither pandas nor rich for the first two.

from_phx_variant preflights the variant and validates the finished model with the structured readiness diagnostics (openph.validate): it raises OpPhValidationError carrying a machine-readable OpPhValidationReport when error-severity issues exist, and returns a fully built, validated, solver-ready OpPhPHPP otherwise. Call openph.validate_phx_variant(variant) directly for report-style (non-raising) feedback.

The legacy import path openph.from_HBJSON.create_phpp.from_phx_variant remains functional and is the same single implementation.

Basic Model Creation

from openph.phpp import OpPhPHPP

# Create PHPP model
phpp = OpPhPHPP()

# Access model components
phpp.climate
phpp.areas
phpp.rooms
phpp.hvac

Table Rendering (Single Tables)

from openph.to_table import TableDisplayManager, TableNames

# Initialize table display manager
display = TableDisplayManager(phpp)

# Render individual tables
climate_table = display.get_table(TableNames.CLIMATE_ANNUAL)
climate_table.render(format="console")
climate_table.render(format="html", output_path="climate.html")

Table Grouping (Recommended)

Group related tables and render to a single file:

from openph.to_table import TableDisplayManager, TableNames

display = TableDisplayManager(phpp)

# Create logical groups
climate_group = display.create_group([
    TableNames.CLIMATE_ANNUAL,
    TableNames.CLIMATE_PEAK_LOAD,
    TableNames.CLIMATE_RADIATION_FACTORS,
])

# Render entire group to one file
climate_group.render(format="html", output_path="./climate_report.html")
climate_group.render(format="txt", output_path="./climate_report.txt")

Available Core Tables

Climate: CLIMATE_ANNUAL, CLIMATE_PEAK_LOAD, CLIMATE_RADIATION_FACTORS
Areas: AREAS_SUMMARY, AREAS_OPAQUE_SURFACE_*, AREAS_APERTURE_*, AREAS_SOLAR_REDUCTION_*
Rooms: ROOMS_VENTILATION_PROPERTIES, ROOMS_VENTILATION_SCHEDULE
Ventilation: VENTILATION_DUCT_INPUTS, VENTILATION_DUCT_RESULTS, VENTILATION_DUCT_*

See TableNames class for complete list.

Development

Part of UV workspace - see root context/ENVIRONMENT.md:

uv sync                      # Install all workspace packages
uv run pytest openph/tests/  # Run tests

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openph-0.7.0.tar.gz (161.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openph-0.7.0-py3-none-any.whl (187.3 kB view details)

Uploaded Python 3

File details

Details for the file openph-0.7.0.tar.gz.

File metadata

  • Download URL: openph-0.7.0.tar.gz
  • Upload date:
  • Size: 161.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for openph-0.7.0.tar.gz
Algorithm Hash digest
SHA256 bef96022ba603ce23542cd43d35a8b9c1cff2f8806ba772ac7601898f5ec729d
MD5 58e2c6c3d3add916e5cd6c2c82a4452e
BLAKE2b-256 3352b786bb79cb02c03d00a94e2ca94a861b1c3803664439a21fc2dfedd71af8

See more details on using hashes here.

Provenance

The following attestation bundles were made for openph-0.7.0.tar.gz:

Publisher: publish.yml on Open-PH/openph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openph-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: openph-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 187.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for openph-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2a1cf5bf459a32a8b8c38751111474bb2279c38461980ff77c69984da4613bc4
MD5 829dc805b5c71790b73f082d8e6e29ca
BLAKE2b-256 afe349c23ccf1e712ac42833ec4620eabd7bf0b5c9f02ed249bbffee0b4426b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for openph-0.7.0-py3-none-any.whl:

Publisher: publish.yml on Open-PH/openph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

This release

0.7.0 This release

2 files

0.6.0

2 files

0.5.1

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page