When openstudio.energyplus.ForwardTranslator translates an .osm model containing a large
number (~49, confirmed) of ZoneHVAC:PackagedTerminalHeatPump objects, it writes an invalid,
apparently-uninitialized value (4.94065645841247e-324, i.e. std::numeric_limits<double>::denorm_min()
— a classic "read of unset/garbage memory" pattern) into the optional field "DX Heating Coil
Sizing Ratio" of the resulting IDF ZoneHVAC:PackagedTerminalHeatPump object. EnergyPlus then
rejects the value outright ("is not a valid Object Type") and the simulation terminates with a
Fatal Error before running.
This reproduces consistently and only with the pip openstudio 3.11.0 wheel
(pip install openstudio==3.11.0). It does not reproduce with the openstudio-3.10.0 CLI
(openstudio.exe, bundled EnergyPlus 25.1.0) translating the exact same model. Both environments
bundle the same EnergyPlus release (25.1.0), so the difference is in the OpenStudio
ForwardTranslator/model library itself between these two builds, not in EnergyPlus.
Environment
- Failing:
openstudio 3.11.0 installed via pip on Windows 11, Python 3.11.
- Not failing (same model, same EnergyPlus release):
C:\openstudio-3.10.0\bin\openstudio.exe
(the official 3.10.0 CLI distribution), which bundles EnergyPlus 25.1.0-1c11a3d85f — the
exact same EnergyPlus version string reported by the pip 3.11.0 install when it does run.
- OS: Windows 11.
Symptom (exact messages observed)
During ForwardTranslator.translateModel(model) (pip 3.11.0), repeated for every affected PTHP
object:
[BOOST_ASSERT] <2> Assertion value failed on line 362 of double __cdecl
openstudio::model::detail::ZoneHVACPackagedTerminalHeatPump_Impl::dXHeatingCoilSizingRatio(void)
const in file D:\a\OpenStudio\OpenStudio\src\model\ZoneHVACPackagedTerminalHeatPump.cpp.
The resulting IDF ZoneHVAC:PackagedTerminalHeatPump object then contains:
, !- Capacity Control Method
, !- Minimum Supply Air Temperature in Cooling Mode {C}
, !- Maximum Supply Air Temperature in Heating Mode {C}
4.94065645841247e-324; !- DX Heating Coil Sizing Ratio
EnergyPlus then fails at the GetSurfaceData/IDD-parsing stage (or later, depending on which tool
wrote the file) with:
** Severe ** Object contains more field values than maximum number of IDD fields and is not extensible.
** Severe ** Error parsing "ZoneHVAC:PackagedTerminalHeatPump". Error in following line.
** Severe ** ~~~ 4.94065645841247e-324; !- DX Heating Coil Sizing Ratio
** Severe ** "4.94065645841247e-324" is not a valid Object Type.
repeated once per affected PTHP object (49 times in our case), followed by:
EnergyPlus Terminated--Error(s) Detected.
Confirmed NOT autosize-related: the source .osm template had this field genuinely
unset/blank (isEmpty on the underlying IDF field is True, confirmed via
ZoneHVACPackagedTerminalHeatPump.getString(<field index>)), and the bug reproduces identically
whether the heating/cooling coil capacities are left in Autosize or explicitly hard-sized (we
tried both, to rule out an autosizing-calculation code path as the trigger — same failure either
way, same garbage value).
Confirmed scale-dependent, not just "any PTHP": translating a model with 1-5
ZoneHVAC:PackagedTerminalHeatPump objects (built by cloning the exact same source object
repeatedly into a small test model) does not reproduce the bug — the field is correctly
omitted (not written at all, as expected for an unset optional trailing field). It only appears
once the model reaches the scale of our real building (73 total thermal zones, 49 of them
equipped with a ZoneHVAC:PackagedTerminalHeatPump). We were not able to narrow down the exact
zone/object count threshold between "small test model, works" and "49 zones, fails" due to time
constraints — this would be useful information for whoever picks this up.
A second, related but distinct observation (model/IDD schema mismatch)
Separately, we found that saving a model containing a ZoneHVAC:PackagedTerminalHeatPump object
that was originally clone()-d from a .osm file declaring a newer OpenStudio version (3.11.0)
into a model being saved by the 3.10.0 CLI can result in the saved .osm text containing
one extra field for that specific object type beyond what OpenStudio 3.10.0's own IDD defines
for OS:ZoneHVAC:PackagedTerminalHeatPump (25 fields) — i.e. the saved file has 26 raw fields for
that object. Reloading that file with a stricter loader (openstudio.exe run -w model.osw, as
opposed to a plain script calling VersionTranslator().loadModel() + ForwardTranslator, which
tolerates it silently) then fails with:
[utilities.idf.IdfObject] <Error> IdfObject of type 'OS:ZoneHVAC:PackagedTerminalHeatPump' cannot
have field index of 25. Cutting off IdfObject field parsing here, with the following text
remaining:
We are not certain whether this second issue shares a root cause with the first (both involve the
same object type and the same trailing "DX Heating Coil Sizing Ratio"-adjacent field region), or
is a separate clone()/cross-version-schema issue. Flagging both together since they were found
in the same investigation and both revolve around the same object type/field.
Steps to reproduce (best available — no fully minimal isolated repro yet)
We don't have a small, shareable, from-scratch repro script yet (our case involves a real building
model we can't publish), but the pattern that triggers it reliably is:
- Build (or load) an OpenStudio model with a genuinely large number (order of 40-50+) of
ThermalZones, each with its own ZoneHVAC:PackagedTerminalHeatPump (Fan:SystemModel supply
fan, Coil:Heating:DX:SingleSpeed, Coil:Cooling:DX:SingleSpeed, Coil:Heating:Electric
supplemental heater) — in our case, all 49 were .clone()-d from a single real template PTHP
object (with populated schedules/curves) into the target model, once per zone.
- Leave the "DX Heating Coil Sizing Ratio" field unset (its default state after cloning from a
template that also never had it explicitly set).
- Translate the model with
openstudio.energyplus.ForwardTranslator under the pip openstudio
3.11.0 wheel.
- Observe the repeated
BOOST_ASSERT on dXHeatingCoilSizingRatio() and the garbage value
written into the IDF.
If someone from the OpenStudio team wants to build a minimal repro: a loop that creates N
ZoneHVACPackagedTerminalHeatPump objects (with real Fan:SystemModel + DX coils + electric
backup coil, not defaults) attached to N zones, then runs ForwardTranslator, scaling N up from a
handful to 40-50, would likely find the exact threshold.
Workaround we are using
Translate/simulate this specific model using the OpenStudio 3.10.0 CLI
(C:\openstudio-3.10.0\bin\openstudio.exe, ForwardTranslator + EnergyPlus.exe directly) instead
of the pip 3.11.0 SDK. This avoids the bug entirely for the same model — the field is correctly
never written the CLI 3.10.0 build reproduces it, IDF is well-formed, EnergyPlus runs to
completion.
Expected behavior
ForwardTranslator should either (a) correctly omit the "DX Heating Coil Sizing Ratio" field when
it is unset on the model object (as it already does correctly for models with few PTHP objects),
or (b) if some internal autosizing-related computation needs a value for it, fall back to the IDD
default (1.0) instead of writing whatever uninitialized memory the getter happens to return.
Additional context
- This was found while building a tool that merges real building materials/HVAC (
ZoneHVAC:PackagedTerminalHeatPump,
cloned from a real reference model of the building) onto a much finer-grained geometry (73
thermal zones) exported from a Revit BIM model — a legitimate, real-world use case for having
many PTHP-equipped zones in one model.
- Happy to provide more detail, the exact IDF fragment (without the real building's proprietary
geometry), or test further scale thresholds if useful for triage.
by jmcaamnog
When
openstudio.energyplus.ForwardTranslatortranslates an.osmmodel containing a largenumber (~49, confirmed) of
ZoneHVAC:PackagedTerminalHeatPumpobjects, it writes an invalid,apparently-uninitialized value (
4.94065645841247e-324, i.e.std::numeric_limits<double>::denorm_min()— a classic "read of unset/garbage memory" pattern) into the optional field "DX Heating Coil
Sizing Ratio" of the resulting IDF
ZoneHVAC:PackagedTerminalHeatPumpobject. EnergyPlus thenrejects the value outright ("is not a valid Object Type") and the simulation terminates with a
Fatal Error before running.
This reproduces consistently and only with the pip
openstudio3.11.0 wheel(
pip install openstudio==3.11.0). It does not reproduce with theopenstudio-3.10.0CLI(
openstudio.exe, bundled EnergyPlus 25.1.0) translating the exact same model. Both environmentsbundle the same EnergyPlus release (25.1.0), so the difference is in the OpenStudio
ForwardTranslator/model library itself between these two builds, not in EnergyPlus.Environment
openstudio3.11.0 installed viapipon Windows 11, Python 3.11.C:\openstudio-3.10.0\bin\openstudio.exe(the official 3.10.0 CLI distribution), which bundles EnergyPlus 25.1.0-1c11a3d85f — the
exact same EnergyPlus version string reported by the pip 3.11.0 install when it does run.
Symptom (exact messages observed)
During
ForwardTranslator.translateModel(model)(pip 3.11.0), repeated for every affected PTHPobject:
The resulting IDF
ZoneHVAC:PackagedTerminalHeatPumpobject then contains:EnergyPlus then fails at the
GetSurfaceData/IDD-parsing stage (or later, depending on which toolwrote the file) with:
repeated once per affected PTHP object (49 times in our case), followed by:
Confirmed NOT autosize-related: the source
.osmtemplate had this field genuinelyunset/blank (
isEmptyon the underlying IDF field isTrue, confirmed viaZoneHVACPackagedTerminalHeatPump.getString(<field index>)), and the bug reproduces identicallywhether the heating/cooling coil capacities are left in
Autosizeor explicitly hard-sized (wetried both, to rule out an autosizing-calculation code path as the trigger — same failure either
way, same garbage value).
Confirmed scale-dependent, not just "any PTHP": translating a model with 1-5
ZoneHVAC:PackagedTerminalHeatPumpobjects (built by cloning the exact same source objectrepeatedly into a small test model) does not reproduce the bug — the field is correctly
omitted (not written at all, as expected for an unset optional trailing field). It only appears
once the model reaches the scale of our real building (73 total thermal zones, 49 of them
equipped with a
ZoneHVAC:PackagedTerminalHeatPump). We were not able to narrow down the exactzone/object count threshold between "small test model, works" and "49 zones, fails" due to time
constraints — this would be useful information for whoever picks this up.
A second, related but distinct observation (model/IDD schema mismatch)
Separately, we found that saving a model containing a
ZoneHVAC:PackagedTerminalHeatPumpobjectthat was originally
clone()-d from a.osmfile declaring a newer OpenStudio version (3.11.0)into a model being saved by the 3.10.0 CLI can result in the saved
.osmtext containingone extra field for that specific object type beyond what OpenStudio 3.10.0's own IDD defines
for
OS:ZoneHVAC:PackagedTerminalHeatPump(25 fields) — i.e. the saved file has 26 raw fields forthat object. Reloading that file with a stricter loader (
openstudio.exe run -w model.osw, asopposed to a plain script calling
VersionTranslator().loadModel()+ForwardTranslator, whichtolerates it silently) then fails with:
We are not certain whether this second issue shares a root cause with the first (both involve the
same object type and the same trailing "DX Heating Coil Sizing Ratio"-adjacent field region), or
is a separate
clone()/cross-version-schema issue. Flagging both together since they were foundin the same investigation and both revolve around the same object type/field.
Steps to reproduce (best available — no fully minimal isolated repro yet)
We don't have a small, shareable, from-scratch repro script yet (our case involves a real building
model we can't publish), but the pattern that triggers it reliably is:
ThermalZones, each with its ownZoneHVAC:PackagedTerminalHeatPump(Fan:SystemModelsupplyfan,
Coil:Heating:DX:SingleSpeed,Coil:Cooling:DX:SingleSpeed,Coil:Heating:Electricsupplemental heater) — in our case, all 49 were
.clone()-d from a single real template PTHPobject (with populated schedules/curves) into the target model, once per zone.
template that also never had it explicitly set).
openstudio.energyplus.ForwardTranslatorunder the pipopenstudio3.11.0 wheel.
BOOST_ASSERTondXHeatingCoilSizingRatio()and the garbage valuewritten into the IDF.
If someone from the OpenStudio team wants to build a minimal repro: a loop that creates N
ZoneHVACPackagedTerminalHeatPumpobjects (with realFan:SystemModel+ DX coils + electricbackup coil, not defaults) attached to N zones, then runs
ForwardTranslator, scaling N up from ahandful to 40-50, would likely find the exact threshold.
Workaround we are using
Translate/simulate this specific model using the OpenStudio 3.10.0 CLI
(
C:\openstudio-3.10.0\bin\openstudio.exe,ForwardTranslator+EnergyPlus.exedirectly) insteadof the pip 3.11.0 SDK. This avoids the bug entirely for the same model — the field is correctly
never written the CLI 3.10.0 build reproduces it, IDF is well-formed, EnergyPlus runs to
completion.
Expected behavior
ForwardTranslatorshould either (a) correctly omit the "DX Heating Coil Sizing Ratio" field whenit is unset on the model object (as it already does correctly for models with few PTHP objects),
or (b) if some internal autosizing-related computation needs a value for it, fall back to the IDD
default (
1.0) instead of writing whatever uninitialized memory the getter happens to return.Additional context
ZoneHVAC:PackagedTerminalHeatPump,cloned from a real reference model of the building) onto a much finer-grained geometry (73
thermal zones) exported from a Revit BIM model — a legitimate, real-world use case for having
many PTHP-equipped zones in one model.
geometry), or test further scale thresholds if useful for triage.
by jmcaamnog