Skip to content

Feature request: warn at the SDK level when a Construction has an AirGap material as its outermost/innermost layer (EnergyPlus currently fails with a FATAL error, discovered only after translate + run) #5630

Description

@jmcaamanog

Description

Summary

If a Construction's outermost layer (facing outdoors) or innermost
layer (facing the zone interior) is an OS:Material:AirGap, the model
translates to IDF without any warning, but EnergyPlus then fails with
a FATAL error very early in GetSurfaceData, well before any
useful diagnostics run:

** Severe  ** CheckAndSetConstructionProperties: Outside Layer is Air for construction EXTERIOR WALL
**   ~~~   **   Error in material F04 WALL AIR SPACE RESISTANCE
**  Fatal  ** GetSurfaceData: Errors discovered, program terminates.

EnergyPlus's own check (CheckAndSetConstructionProperties) already
knows this is invalid — but the OpenStudio SDK has no equivalent check
at authoring time (e.g. in Construction::setLayers, or as part of
Model.validityReport). Anyone building Construction objects
programmatically from an external material/layer list (import
pipelines, generative scripts, measures) can silently produce an
invalid model that only fails much later, after a full
translate-and-run cycle, with no indication of which construction or
why until they go digging in eplusout.err.

A warning (or a validityReport entry) raised directly when
setLayers is called with an AirGap in position 0 or position N-1
would let people catch this immediately, in their own script/measure,
instead of discovering it downstream in EnergyPlus.

Minimal reproduction (self-contained, no external files needed)

Built entirely from OpenStudio's own bundled example model — no
external geometry or project data involved. Python shown, same idea
applies in Ruby via OpenStudio::Model.exampleModel:

import openstudio

m = openstudio.model.exampleModel()
constructions = {c.name().get(): c for c in m.getConstructions()}
c = constructions["Exterior Wall"].to_Construction().get()

layers = list(c.layers())
# "F04 Wall air space resistance" is already an AirGap material in the
# example model, just correctly placed in the middle of the stack.
airgap = [l for l in layers if not l.to_AirGap().isNull()][0]
others = [l for l in layers if l.handle() != airgap.handle()]

# Move it to the front (outermost layer) -- this is what triggers the
# fatal error. No warning/error is raised at this point.
c.setLayers(openstudio.model.MaterialVector([airgap] + others))

m.save(openstudio.path("broken_airgap_outer.osm"), True)
ft = openstudio.energyplus.ForwardTranslator()
ft.translateModel(m).save(openstudio.path("broken_airgap_outer.idf"), True)

Then running that IDF through EnergyPlus (design-day only is enough,
no need for an annual run) with any weather file reproduces the fatal
error above.

Attached in eplus_airgap_repro.zip:

  • baseline_ok.idf / baseline_ok_eplusout.err — the unmodified
    example model, completes successfully (73 Warning, 0 Severe).
  • broken_airgap_outer.idf / broken_airgap_outer_eplusout.err — same
    model, only the "Exterior Wall" construction's layer order changed
    as shown above, fails FATAL.
  • USA_CO_Golden-NREL.724666_TMY3.epw — the weather file used (NREL's
    own standard sample EPW, bundled with the SDK/Application install).

Expected behavior

Either Construction::setLayers (C++ SDK) rejects/warns when an
AirGap-derived material ends up at index 0 or the last index, or at
minimum Model.validityReport(StrictnessLevel.Draft) (or equivalent)
flags it — so this shows up while authoring the model, not after a
full EnergyPlus run.

Actual behavior

No warning anywhere in the OpenStudio SDK (Ruby or Python) when
building/saving such a Construction, or during ForwardTranslator.
The only signal is EnergyPlus's own fatal error, several steps removed
from the actual mistake.

Versions

  • OpenStudio SDK (pip openstudio): 3.11.0
  • OpenStudio Application: 1.11.0-rc2
  • EnergyPlus: 25.2.0

Additional context

Ran into the EnergyPlus-side version of this while building an
external BIM → OpenStudio import pipeline: a construction whose layer
order came from imported data ended up with an air-gap layer at the
boundary, and the only symptom was this fatal error after a full
translate + run. Worth noting EnergyPlus's message itself is already
reasonably clear once you reach it (names the construction and the
offending material) — the gap is that nothing catches it earlier, at
the SDK level, for anyone assembling constructions programmatically.

by jmcaamanog

eplus_airgap_repro.zip

Current Behavior

No warning anywhere in the OpenStudio SDK (Ruby or Python) when
building/saving such a Construction, or during ForwardTranslator.
The only signal is EnergyPlus's own fatal error, several steps removed
from the actual mistake:

** Severe ** CheckAndSetConstructionProperties: Outside Layer is Air for construction EXTERIOR WALL
** ~~~ ** Error in material F04 WALL AIR SPACE RESISTANCE
** Fatal ** GetSurfaceData: Errors discovered, program terminates.

Expected Behavior

Either Construction::setLayers (C++ SDK) rejects/warns when an
AirGap-derived material ends up at index 0 or the last index, or at
minimum Model.validityReport (or equivalent) flags it -- so this shows
up while authoring the model, not after a full EnergyPlus run.

Steps to reproduce

  1. Take the OSM attached (broken_airgap_outer.osm.txt) -- it's
    OpenStudio's own bundled example model (openstudio.model.exampleModel()
    in the Python SDK / OpenStudio::Model.exampleModel in Ruby), with a
    single change: the "Exterior Wall" construction's layer order was
    modified so its AirGap material ("F04 Wall air space resistance",
    already present in the unmodified example model) is now the
    outermost layer instead of the middle of the stack.

  2. No warning or error appears when building/saving this model, nor
    when running it through the ForwardTranslator to IDF.

  3. Run the resulting IDF through EnergyPlus with any weather file
    (design-day-only is enough, no need for a full annual run) -- it
    fails FATAL during GetSurfaceData (see Current Behavior above).

  4. For contrast, baseline_ok.osm.txt is the exact same model with the
    layer order untouched -- it completes successfully (73 Warning, 0
    Severe).

Minimal code used to produce the difference (Python; same idea with
OpenStudio::Model.exampleModel in Ruby):

import openstudio

m = openstudio.model.exampleModel()
constructions = {c.name().get(): c for c in m.getConstructions()}
c = constructions["Exterior Wall"].to_Construction().get()

layers = list(c.layers())
airgap = [l for l in layers if not l.to_AirGap().isNull()][0]
others = [l for l in layers if l.handle() != airgap.handle()]

# Moves the air gap to the front (outermost layer) -- no warning
# raised here, but this is what makes EnergyPlus fail fatally later.
c.setLayers(openstudio.model.MaterialVector([airgap] + others))

Possible Solution

No response

Operating System affected

Windows 11

Environment

OS: Windows 11 Home, build 10.0.22631 (x86_64)
OpenStudio SDK: 3.11.0 (Python bindings, pip install openstudio)
Python: 3.13.14
EnergyPlus: 25.2.0-cf7368216c

Version of OpenStudio

3.11.0

Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    TriageIssue needs to be assessed and labeled, further information on reported might be needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions