Skip to content

Parsing Results

The SimulationResult class provides structured access to all EnergyPlus output files with lazy loading for efficient memory usage.

SimulationResult Overview

from idfkit.simulation import simulate

result = simulate(model, weather)

# Basic info
print(f"Success: {result.success}")
print(f"Exit code: {result.exit_code}")
print(f"Runtime: {result.runtime_seconds:.1f}s")
print(f"Output dir: {result.run_dir}")

# Parsed outputs (lazy-loaded)
result.errors  # ErrorReport from .err file
result.sql  # SQLResult from .sql database
result.variables  # OutputVariableIndex from .rdd/.mdd
result.csv  # CSVResult from .csv file
result.html  # HTMLResult from HTML tabular output

Output File Paths

Access paths to specific output files:

result.sql_path  # Path to .sql database
result.err_path  # Path to .err file
result.eso_path  # Path to .eso file
result.csv_path  # Path to .csv file
result.html_path  # Path to HTML table file
result.rdd_path  # Path to .rdd file
result.mdd_path  # Path to .mdd file

Each returns None if the file wasn't produced.

Error Report

Parse warnings and errors from the .err file:

errors = result.errors

# Summary
print(errors.summary())

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

# Check for severe errors
if errors.has_severe:
    for err in errors.severe:
        print(f"SEVERE: {err.message}")

# All warnings
for warn in errors.warnings:
    print(f"Warning: {warn.message}")

# Counts
print(f"Fatal: {errors.fatal_count}")
print(f"Severe: {errors.severe_count}")
print(f"Warnings: {errors.warning_count}")

See Error Handling for detailed error parsing.

SQL Database

Query time-series and tabular data from the SQLite output:

sql = result.sql
if sql is not None:
    # Time-series data
    ts = sql.get_timeseries(
        variable_name="Zone Mean Air Temperature",
        key_value="THERMAL ZONE 1",
    )
    print(f"Max: {max(ts.values):.1f}°C")

    # Tabular reports
    rows = sql.get_tabular_data(report_name="AnnualBuildingUtilityPerformanceSummary")

See SQL Output Queries for detailed SQL parsing.

Output Variables

Discover available output variables from .rdd/.mdd files:

variables = result.variables
if variables is not None:
    # Search for variables
    matches = variables.search("Temperature")
    for var in matches:
        print(f"{var.name} [{var.units}]")

    # Add outputs to model for next run
    variables.add_all_to_model(model, filter_pattern="Zone.*Temperature")

See Output Discovery for variable discovery.

CSV Output

Parse CSV time-series output:

csv_result = result.csv
if csv_result is not None:
    # List all columns
    for col in csv_result.columns:
        print(f"{col.variable_name} ({col.key_value}) [{col.units}]")

    # Get data for a specific column
    values = csv_result.get_column_values("Zone Mean Air Temperature")

HTML Tabular Output

Parse the HTML tabular summary file (eplustbl.htm) that EnergyPlus produces alongside every simulation:

html = result.html
if html is not None:
    # Iterate all tables
    for table in html:
        print(f"{table.title}: {len(table.rows)} rows")

    # eppy-compatible (title, rows) pairs
    for title, rows in html.titletable():
        print(title)

    # Look up a table by title (case-insensitive substring match)
    table = html.tablebyname("Site and Source Energy")
    if table:
        data = table.to_dict()  # {row_key: {col_header: value}}
        print(data)

    # Get all tables from a specific report
    annual = html.tablesbyreport("Annual Building Utility Performance Summary")

    # Access by index
    first = html.tablebyindex(0)

Each HTMLTable has these attributes:

Attribute Type Description
title str Bold title preceding the table
header list[str] Column headers
rows list[list[str]] Data rows
report_name str Parent report name
for_string str The "For:" qualifier (e.g. "Entire Facility")

You can also parse a standalone HTML file without a full simulation:

from idfkit.simulation.parsers.html import HTMLResult

html = HTMLResult.from_file("eplustbl.htm")
html = HTMLResult.from_string(html_string)

This replaces eppy's readhtml module.

Lazy Loading

Output files are parsed only when accessed:

result = simulate(model, weather)

# Nothing parsed yet - only metadata stored

result.errors  # NOW parses .err file
result.sql  # NOW opens SQLite database
result.variables  # NOW parses .rdd/.mdd files
result.html  # NOW parses HTML tabular output

This keeps memory usage low, especially for batch simulations where you might only need specific outputs.

Releasing File Handles

Accessing result.sql opens a SQLite connection on first use and caches it. That connection holds an OS-level file handle on eplus.sql. On Windows, the handle locks the file, so deleting the run directory while the result is alive fails with PermissionError [WinError 32] — typically when the run directory lives inside a tempfile.TemporaryDirectory or is removed with shutil.rmtree. POSIX systems don't lock on open, so this only affects Windows.

SimulationResult is a context manager: exiting the with block calls close(), which releases the connection. Close the result before the run directory is removed:

# The result context exits first - closing the SQLite connection - then
# TemporaryDirectory cleans up, so rmtree succeeds even on Windows.
with tempfile.TemporaryDirectory() as tmp, simulate(model, weather, output_dir=Path(tmp) / "run") as result:
    temps = result.sql.get_timeseries("Zone Mean Air Temperature", "ZONE 1")

# Equivalent without a context manager:
result = simulate(model, weather)
try:
    temps = result.sql.get_timeseries("Zone Mean Air Temperature", "ZONE 1")
finally:
    result.close()  # idempotent; reopens lazily if you touch result.sql again

close() is idempotent and resets the cached connection, so touching result.sql afterwards transparently reopens it. The other accessors (errors, csv, eso, html, variables) read their files eagerly and hold no handles, so they need no cleanup.

Reconstructing from Directory

Inspect results from a previous simulation:

from idfkit.simulation import SimulationResult

# From a local directory
result = SimulationResult.from_directory("/path/to/sim_output")

# From a cloud storage location
from idfkit.simulation import S3FileSystem

fs = S3FileSystem(bucket="my-bucket")
result = SimulationResult.from_directory("runs/run-001", fs=fs)

# Query data
ts = result.sql.get_timeseries("Zone Mean Air Temperature", "ZONE 1")

Attributes Reference

Attribute Type Description
run_dir Path Directory containing output files
success bool Whether simulation succeeded
exit_code int | None Process exit code (None if timed out)
stdout str Captured standard output
stderr str Captured standard error
runtime_seconds float Wall-clock execution time
output_prefix str Output file prefix (default "eplus")

Properties Reference

Property Type Description
errors ErrorReport Parsed error/warning report
sql SQLResult | None SQL database accessor
variables OutputVariableIndex | None Variable discovery
csv CSVResult | None CSV output parser
html HTMLResult | None HTML tabular output parser
sql_path Path | None Path to .sql file
err_path Path | None Path to .err file
eso_path Path | None Path to .eso file
csv_path Path | None Path to .csv file
html_path Path | None Path to HTML file
rdd_path Path | None Path to .rdd file
mdd_path Path | None Path to .mdd file

See Also