How to run a simulation¶
The simulate() function executes EnergyPlus as a subprocess and returns
a structured SimulationResult with access to all output files.
If you have not run a model before, Simulate an office block walks the whole path from geometry to a heating load. This page is the reference for the one step.
Python only, permanently
Running a locally installed EnergyPlus and reading its results belongs to Python alone. JavaScript is not waiting on a port and will not gain a counterpart: this is a permanent boundary, so no issue tracks it, and moving the capability out of that state takes a constitutional amendment rather than a ledger edit.
Requires an EnergyPlus installation on the machine and a subprocess to drive it, then
reads the eplusout files that run leaves on disk. Neither the installation nor the
subprocess is available in a browser, which is the runtime the JavaScript library
targets. JavaScript reaches EnergyPlus by the other mechanism instead: see
browser-simulation, which is not a
workaround for this entry but a different capability.
The full entry, including the vocabulary this capability owns, is on the capability parity page.
Basic Usage¶
from idfkit import load_idf
from idfkit.simulation import simulate
model = load_idf("building.idf")
result = simulate(model, "weather.epw")
print(f"Success: {result.success}")
print(f"Runtime: {result.runtime_seconds:.1f}s")
print(f"Output directory: {result.run_dir}")
Parameters¶
For the full simulate() signature, with every keyword argument, its type, and
its default, see the generated
simulate() reference. The sections below cover
the arguments you'll reach for most often.
Simulation Modes¶
Design-Day Only¶
Fast simulation using only design day conditions:
Annual Simulation¶
Full-year simulation:
Default Mode¶
Without flags, EnergyPlus uses whatever run periods are defined in the model:
Preprocessing¶
Some EnergyPlus models contain high-level template objects that must be expanded into their low-level equivalents before simulation. idfkit provides standalone preprocessing functions for this.
Expanding HVAC Templates¶
HVACTemplate:* objects are shorthand for complex HVAC systems.
expand_objects() converts them into their fully specified equivalents:
from idfkit.simulation import expand_objects
expanded = expand_objects(model)
# HVACTemplate:Zone:IdealLoadsAirSystem → ZoneHVAC:IdealLoadsAirSystem + ...
Note
simulate() runs ExpandObjects automatically when expand_objects=True
(the default). Call expand_objects() directly only when you need to
inspect or modify the expanded model before simulation.
Ground Heat Transfer (Slab & Basement)¶
Models with GroundHeatTransfer:Slab:* or GroundHeatTransfer:Basement:*
objects need the Slab or Basement preprocessor to compute ground temperatures.
Note
simulate() automatically runs the Slab and/or Basement
preprocessors when expand_objects=True (the default) and the model
contains the corresponding ground heat-transfer objects. In most
cases you do not need to call these functions yourself.
Preprocessor Timeout¶
Each preprocessor stage (ExpandObjects, Slab, Basement) gets its own
subprocess timeout, separate from the EnergyPlus timeout. Override it
per call with preprocessor_timeout:
Or set a process-wide default with the IDFKIT_PREPROCESSOR_TIMEOUT
environment variable — useful for slow shared hardware (raise it) or
fast CI where you want to catch hangs quickly (lower it):
export IDFKIT_PREPROCESSOR_TIMEOUT=600 # slow shared hardware
export IDFKIT_PREPROCESSOR_TIMEOUT=30 # fail fast in CI
The default is 120 seconds per subprocess. timeout and
preprocessor_timeout are independent budgets — there is no shared
wall-clock cap across the pipeline, so a long preprocessor_timeout
will not trip the simulation's timeout and vice versa.
For cases where you need to inspect or modify the preprocessed model before simulation, standalone functions are available:
from idfkit.simulation import run_slab_preprocessor, run_basement_preprocessor
# Slab-on-grade foundation
expanded = run_slab_preprocessor(model, weather="weather.epw")
# Basement walls and floors
expanded = run_basement_preprocessor(model, weather="weather.epw")
Each function runs ExpandObjects first (to extract the ground heat-transfer
input), then the Fortran solver, and returns a new IDFDocument with the
computed temperature schedules appended.
All preprocessing functions raise
ExpandObjectsError on failure, with
structured preprocessor, exit_code, and stderr fields for
programmatic error handling.
See the Preprocessing API reference for full details.
EnergyPlus Discovery¶
By default, simulate() auto-discovers EnergyPlus:
# Auto-discovery
result = simulate(model, weather)
# Explicit path
from idfkit.simulation import find_energyplus
config = find_energyplus(path="/custom/path/EnergyPlus-24-1-0")
result = simulate(model, weather, energyplus=config)
# Environment variable
# Set ENERGYPLUS_DIR=/path/to/EnergyPlus before running
result = simulate(model, weather)
Discovery priority:
- Explicit
energyplusparameter ENERGYPLUS_DIRenvironment variable- System PATH
- Platform default directories
Output Directory¶
Automatic Temporary Directory¶
By default, outputs go to an auto-generated temp directory:
Explicit Directory¶
Specify where to store outputs:
The directory is created if it doesn't exist.
Error Handling¶
Simulation Errors¶
from idfkit.exceptions import SimulationError
try:
result = simulate(model, weather)
except SimulationError as e:
print(f"Simulation failed: {e}")
print(f"Exit code: {e.exit_code}")
print(f"Stderr: {e.stderr}")
Timeout¶
try:
result = simulate(model, weather, timeout=60.0) # 1 minute max
except SimulationError as e:
if e.exit_code is None:
print("Simulation timed out")
Checking Success¶
result = simulate(model, weather)
if not result.success:
print(f"Exit code: {result.exit_code}")
print(f"Stderr: {result.stderr}")
for err in result.errors.fatal:
print(f"Error: {err.message}")
Model Safety¶
simulate() copies your model before running — the original is never modified:
model = load_idf("building.idf")
original_count = len(model)
result = simulate(model, weather)
# Model unchanged
assert len(model) == original_count
assert "Output:SQLite" not in model
Command-Line Options¶
Output Suffix Modes¶
| Value | Description |
|---|---|
"C" |
Combined table files (default) |
"L" |
Legacy separate table files |
"D" |
Timestamped separate files |
Extra Arguments¶
Pass additional EnergyPlus flags:
Cloud Storage¶
For remote storage backends (S3, etc.):
from idfkit.simulation import S3FileSystem
fs = S3FileSystem(bucket="my-bucket", prefix="runs/")
result = simulate(
model,
weather,
output_dir="run-001", # Required with fs
fs=fs,
)
See how to use cloud and remote storage for details.
Caching¶
Enable content-addressed caching to avoid redundant simulations:
from idfkit.simulation import SimulationCache
cache = SimulationCache()
# First run: executes simulation
result1 = simulate(model, weather, cache=cache)
# Second run: instant cache hit
result2 = simulate(model, weather, cache=cache)
See how to cache simulation results for details.
Version Migration¶
When model.version differs from the installed EnergyPlus,
simulate() raises VersionMismatchError
by default. Pass auto_migrate=True to forward-migrate the model
transparently before the run; the resulting
MigrationReport is attached to
SimulationResult.migration_report.
See how to migrate models between versions for the
full workflow.
See Also¶
- How to migrate models between EnergyPlus versions — Forward-migrate IDF models across EnergyPlus releases
- How to track simulation progress — Real-time progress with
on_progress - How to access simulation results — Working with
SimulationResult - How to run batch simulations — Running multiple simulations
- How to handle simulation errors — Understanding error reports