Skip to content

Quick Start

Get up and running with idfkit in 5 minutes. This guide covers the essential operations you'll use every day.

Load a Model

from idfkit import load_idf

# Load an existing IDF file
model = load_idf("building.idf")
print(f"Loaded {len(model)} objects")

# For migration-only tolerant loading of legacy/noisy files:
model = load_idf("legacy_building.idf", strict_parsing=False)
print(f"Tolerant load parsed {len(model)} objects")

load_idf() uses strict parsing by default (strict_parsing=True) and raises IDFParseError for malformed objects. Use strict_parsing=False only as a migration/compatibility fallback for legacy or noisy files.

Query Objects

Access objects with O(1) dictionary lookups:

# Get all zones
for zone in model["Zone"]:
    print(f"Zone: {zone.name}")

# Get a specific zone by name
office = model["Zone"]["Office"]
print(f"Origin: ({office.x_origin}, {office.y_origin}, {office.z_origin})")

Modify Fields

Change field values with attribute assignment:

# Update a field
office.x_origin = 10.0

# See what references this zone
for obj in model.get_referencing("Office"):
    print(f"  {obj.obj_type}: {obj.name}")

Discover Available Fields

Not sure what fields an object type has? Use describe() to see all available fields:

# See all fields for a Zone
print(model.describe("Zone"))
# === Zone ===
# ...
# Fields (9):
#   direction_of_relative_north (number) [deg] default=0
#   x_origin (number) [m] default=0
#   ...

# See required fields for a Material
desc = model.describe("Material")
print(f"Required: {desc.required_fields}")
# Required: ['roughness', 'thickness', 'conductivity', 'density', 'specific_heat']

In REPL/Jupyter, use tab completion to explore object fields:

zone = model["Zone"]["Office"]
zone.<TAB>  # Shows: x_origin, y_origin, z_origin, multiplier, ...

Validation is enabled by default, so typos are caught immediately:

model.add("Zone", "Office", x_orgin=0)  # Raises: unknown field 'x_orgin'

# Disable validation for bulk operations where performance matters
model.add("Zone", "Office", x_origin=0, validate=False)

IDE Support

idfkit ships type stubs for all 858 EnergyPlus object types — your IDE will autocomplete field names, show inline documentation, and catch typos. See Type-Safe Development for details.

Create a New Model

from idfkit import new_document

# Create a baseline model for EnergyPlus 24.1
model = new_document(version=(24, 1, 0))

# Update pre-seeded singleton objects
building = model["Building"].first()
if building is not None:
    building.name = "My Building"
    building.north_axis = 0
    building.terrain = "City"

geometry_rules = model["GlobalGeometryRules"].first()
if geometry_rules is not None:
    geometry_rules.starting_vertex_position = "UpperLeftCorner"
    geometry_rules.vertex_entry_direction = "Counterclockwise"
    geometry_rules.coordinate_system = "Relative"

# Add additional named objects
model.add("Zone", "Office", x_origin=0, y_origin=0, z_origin=0)

# Add an additional unnamed singleton object
model.add("Timestep", number_of_timesteps_per_hour=4)

Write Output

from idfkit import write_idf, write_epjson

# Write to IDF format
write_idf(model, "output.idf")

# Or write to epJSON format
write_epjson(model, "output.epJSON")

# Get as string (no file path)
idf_string = write_idf(model)

Run a Simulation

from idfkit.simulation import simulate

result = simulate(
    model,
    weather="weather.epw",
    design_day=True,  # Fast design-day run
)

print(f"Success: {result.success}")
print(f"Runtime: {result.runtime_seconds:.1f}s")

# Check for errors
if result.errors.has_fatal:
    for err in result.errors.fatal:
        print(f"Error: {err.message}")

Query Results

# Get time-series data from SQLite output
ts = result.sql.get_timeseries(
    variable_name="Zone Mean Air Temperature",
    key_value="Office",
)
print(f"Variable: {ts.variable_name}")
print(f"Temperature range: {min(ts.values):.1f}°C to {max(ts.values):.1f}°C")

# Filter by environment if needed
ts_sizing = result.sql.get_timeseries(
    variable_name="Zone Mean Air Temperature",
    key_value="Office",
    environment="sizing",  # Design day data only
)

# Get tabular data
tables = result.sql.get_tabular_data(report_name="AnnualBuildingUtilityPerformanceSummary")

Find Weather Stations

from idfkit.weather import StationIndex, geocode

# Load the station index (instant, no network needed)
index = StationIndex.load()

# Search by name
results = index.search("chicago ohare")
print(results[0].station.display_name)

# Find nearest station to an address
results = index.nearest(*geocode("Willis Tower, Chicago, IL"))
station = results[0].station
print(f"{station.display_name}: {results[0].distance_km:.0f} km away")

Apply Design Days

from idfkit.weather import DesignDayManager

# Parse a DDY file and apply design days to your model
ddm = DesignDayManager("weather.ddy")
added = ddm.apply_to_model(
    model,
    heating="99.6%",
    cooling="1%",
    update_location=True,
)
print(f"Added {len(added)} design days")

Lossless Round-Trip

Pass preserve_formatting=True to build a Concrete Syntax Tree (CST) so that write_idf reproduces the original formatting, comments, and whitespace for unmodified objects:

from idfkit import load_idf, write_idf

# Build a CST to preserve original formatting
model = load_idf("building.idf", preserve_formatting=True)

# Modify a zone ceiling height
model["Zone"]["Office"].ceiling_height = 3.5

# Unmodified objects keep their original formatting, comments,
# and whitespace; only the changed object is re-serialised.
write_idf(model, "building_updated.idf")

Next Steps