Skip to main content

idfpy

PyPI Python 3.12+ License: MIT EnergyPlus 26.2 Autoupdate Docs Ask DeepWiki

Type-safe Pydantic models for all EnergyPlus IDF object types, plus IDF file read/write and simulation execution, optimized for LLM tool calling and IDE auto-completion.

Auto-generated from Energy+.schema.epJSON version 26.2.0.

Features

  • 881 object types as Pydantic v2 models with full validation
  • 287 reference types with cross-object validation
  • Forward navigation — surface.zone resolves one reference field; surface.referenced() returns every referenced object
  • Reverse navigation — zone.referencing("Lights") finds all objects that reference a given object
  • Reference validation — idf.validate() batch-checks all cross-object references for existence and type compatibility
  • Extension plugin system — surface.area, .normal, .centroid via auto-discovered geometry mixins with full IDE support
  • Case-insensitive Literal field matching (EnergyPlus IDF is case-insensitive)
  • Extensible field support (vertices, schedule data, etc.)
  • IDF read/write with positional field ordering
  • epJSON read/write with auto-detection by file extension
  • to_dict() / from_dict() for in-memory dict conversion (ideal for LLM tool calls)
  • EnergyPlus simulation execution with ExpandObjects support
  • Accepts both snake_case and original EnergyPlus schema key names

Why idfpy over eppy?

idfpy eppy
No EnergyPlus IDD required at runtime ✅ ❌
Type-safe field validation ✅ Pydantic v2 ❌
epJSON read/write ✅ ❌
Cross-reference validation ✅ 287 ref groups ❌
Forward/reverse navigation ✅ 2921 properties ❌
Surface geometry (area/normal) ✅ ext plugin ❌
to_dict() / from_dict() for LLM ✅ ❌
Dependencies 4 (pydantic, jinja2, loguru, typer) 12+ (lxml, pyparsing...)

Installation

pip install idfpy

Quick Start

from pathlib import Path
from idfpy import IDF
from idfpy.models import Version, Building, Zone

# Create an IDF
idf = IDF()
idf.add(Version())
idf.add(Building(name='MyBuilding', north_axis=0.0))
idf.add(Zone(name='Zone1'))

# Save as IDF
idf.save(Path('output.idf'))

# Save as epJSON
idf.save(Path('output.epjson'), output_type='epjson')

# Load (auto-detects format by extension)
idf = IDF.load(Path('existing.idf'))  # IDF format
idf = IDF.load(Path('existing.epjson'))  # epJSON format
# Invalid objects raise, with the source line for IDF text;
# strict=False skips them with a warning instead
idf = IDF.load(Path('existing.idf'), strict=False)

# Run simulation
from idfpy.sim import simulate

result = simulate(
    Path('output.idf'), weather=Path('weather.epw'), output_dir=Path('results/')
)
print(result.success)  # True / False

In-memory dict conversion

from pathlib import Path
from idfpy import IDF

idf = IDF.load(Path('model.idf'))

# IDF → dict (epJSON structure)
data = idf.to_dict()
# {
#   "Building": {"MyBuilding": {"north_axis": 0.0, "terrain": "Suburbs"}},
#   "Zone": {"Zone1": {"direction_of_relative_north": 0.0}},
#   ...
# }

# dict → IDF
idf = IDF.from_dict(data)

Logging

idfpy keeps INFO and higher-level logs available through Loguru. Detailed per-object and internal progress messages use Loguru's TRACE level, so they do not appear in a handler configured at DEBUG level. To inspect these messages, configure a TRACE sink explicitly:

from loguru import logger

logger.add('idfpy.log', level='TRACE')

Object navigation

Every reference field generates a @property for direct forward navigation. referenced() returns all objects referenced by an object, while referencing() provides reverse navigation. These navigation methods and all query methods (get / has / all_of_type / remove) accept either an EnergyPlus type string, a Python class name, or the model class itself — passing the class preserves precise typing in your IDE.

from pathlib import Path
from idfpy import IDF
from idfpy.models import BuildingSurfaceDetailed, Zone

idf = IDF.load(Path('model.idf'))

# Forward navigation — resolve reference to target object
surface = idf.get(BuildingSurfaceDetailed, 'Wall1')  # → BuildingSurfaceDetailed | None
surface.zone_name  # "Zone1" (raw string, always works)
surface.zone  # Zone object (resolved via IDF)
surface.construction  # Construction object
surface.referenced()  # [Construction object, Zone object, ...]
surface.referenced(Zone)  # [Zone object]

# Reverse navigation — find all objects referencing a given object
zone = idf.get(Zone, 'Zone1')
zone.referencing(BuildingSurfaceDetailed)  # → [Wall1, Wall2, ...]
zone.referencing('Lights')  # → [OfficeLights, ...]

# Chained navigation
zone.referencing(BuildingSurfaceDetailed)[0].construction

Strict type-name validation (default)

Query methods raise UnknownObjectTypeError when the type name cannot be resolved — this surfaces typos immediately instead of returning an empty result. Pass strict=False for the legacy silent behavior.

from idfpy import UnknownObjectTypeError

try:
    idf.get('BuildingSurface:detailed', 'Wall1')  # note the lowercase 'd'
except UnknownObjectTypeError as e:
    print(e)  # → Unknown object type: 'BuildingSurface:detailed'. ...

# Opt-in legacy silent behavior
idf.get('BuildingSurface:detailed', 'Wall1', strict=False)  # → None

Reference validation

from idfpy import IDF, RefValidationError

idf = IDF.load(Path('model.idf'))

# Batch check all cross-object references
errors = idf.validate()
for e in errors:
    print(e)
# [missing] Lights/OffLights.schedule_name: "BadSched" not found in any of [ScheduleNames]

# Or raise on first broken reference set
try:
    idf.validate_or_raise()
except RefValidationError as exc:
    print(f'{len(exc.errors)} broken reference(s)')

Real-world Example

from pathlib import Path
from idfpy import IDF

# Load a DOE reference building
idf = IDF.load(Path('LargeOffice.idf'))

# Modify all exterior walls' insulation
for con_name, con in idf.all_of_type('Construction').items():
    layer = con.outside_layer_ref
    if layer and hasattr(layer, 'conductivity'):
        print(f'{con.name}: k={layer.conductivity} W/m·K')

# Validate all references
errors = idf.validate()
print(f'{len(errors)} broken references')

Geometry extensions

Surface models include geometry properties via the built-in ext.geometry plugin — area, normal vector, and centroid are computed from vertices using Newell's method, with full IDE autocompletion.

from idfpy import IDF
from pathlib import Path

idf = IDF.load(Path('model.idf'))

surface = idf.get('BuildingSurface:Detailed', 'Wall1')
surface.area  # 30.0 (m²)
surface.normal  # (0.0, -1.0, 0.0) — outward unit normal
surface.centroid  # (5.0, 0.0, 1.5)
surface.vertices_as_tuples  # [(0,0,3), (0,0,0), (10,0,0), (10,0,3)]

window = idf.get('FenestrationSurface:Detailed', 'Win1')
window.area  # 16.0 (m²)

Supported surface types: BuildingSurface:Detailed, FenestrationSurface:Detailed, Floor:Detailed, RoofCeiling:Detailed, Wall:Detailed, Shading:Building:Detailed, Shading:Site:Detailed, Shading:Zone:Detailed.

Creating custom plugins

Extensions live in idfpy/ext/ as sub-packages. Each plugin exposes a MIXIN_MAP that the code generator auto-discovers:

# idfpy/ext/thermal/__init__.py
from .mixins import ThermalPropertyMixin

MIXIN_MAP: dict[str, type] = {
    'BuildingSurfaceDetailed': ThermalPropertyMixin,
}
# idfpy/ext/thermal/mixins.py
class ThermalPropertyMixin:
    @property
    def u_value(self) -> float:
        """Compute U-value from construction layers."""
        ...

After adding a plugin, re-run idfpy codegen to regenerate models — the mixin is injected into the class hierarchy and IDE autocompletion works immediately.

Container mutation

from idfpy.models import Zone

idf.remove(Zone, 'Zone1')  # unbinds + unregisters references
idf.remove('Zone', 'Zone1')  # string form (EnergyPlus or Python class name)

License

MIT

Metadata

Release files for idfpy 26.2.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for idfpy 26.2.6
File Size Uploaded
idfpy-26.2.6.tar.gz 663.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for idfpy 26.2.6
File Interpreter ABI Platform
idfpy-26.2.6-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / idfpy-26.2.6.tar.gz

Download URL idfpy-26.2.6.tar.gz
Size 663.4 kB
Tags Source
SHA-256 checksum
How to use checksums
5e4d8bbc9d4e0d1604ec641685f26bfb580a36a5c3dba62fae89875f044e7a26
BLAKE2b-256 checksum
How to use checksums
9cce1a2cfbecb0044d2f60bef911619f4131d670d0445f2109cc6bcb1119131b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / idfpy-26.2.6-py3-none-any.whl

Download URL idfpy-26.2.6-py3-none-any.whl
Size 662.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
55b499a1a24f3ec04601d49da9fa926948d91d28dcacc309a4a58f5b7f025267
BLAKE2b-256 checksum
How to use checksums
85123c44611c598899bfd96aaebf259dd3e56f64a0ad348c5302f3fe31ab7944
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log
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