Skip to content

ModelObject::clone() doesn't remap lateral (sibling-to-sibling) object-list references within the cloned subtree #5636

Description

@Ski90Moo

Summary

ModelObject::clone() (and by extension Loop::clone() / AirLoopHVAC::clone()) clones each child individually and reattaches it via setParent(), so true parent-child ownership edges are correctly remapped to point within the new subtree. However, children are cloned one at a time rather than as a batch sharing a single old->new handle table, so any lateral object-list reference between two siblings in the cloned subtree is left broken on the clone.

Concretely:

  • CoilHeatingDesuperheater::heatingSource() — when the source is a sibling coil on the same loop, the field is cleared to empty outright on the clone, rather than pointing at the cloned sibling.
  • SetpointManager node references on an AirLoopHVACOutdoorAirSystem's outdoor-air/relief branch equipment — these still point at the original node objects rather than the clone's.

Both cause EnergyPlus to fail with a fatal error when the resulting model is simulated, since the referenced field is either blank or points at an object outside the new subtree.

To reproduce

  1. Build an AirLoopHVAC with a CoilCoolingDXSingleSpeed and a CoilHeatingDesuperheater whose heatingSource() is set to that cooling coil.
  2. Clone the loop: AirLoopHVAC newLoop = loop.clone(model).cast<AirLoopHVAC>();
  3. Find the cloned desuperheater coil and inspect heatingSource() — it will be empty (or, for other field/type combinations, still point at the original object) instead of pointing at the cloned cooling coil.

Suggested fix

clone() would need to batch-clone the whole subtree into one shared old->new handle map first (rather than cloning children one at a time via setParent()), then do a second pass remapping any object-list field whose target has an entry in that handle map. This is a bigger structural change to clone()'s orchestration, so it likely needs its own design discussion rather than a quick patch.

Context

We hit this in openstudiocoalition/OpenStudioApplication#891 (adding hot-gas-reheat desuperheater support) and worked around it at the application level with a generic post-clone fixup pass (see src/utilities/CloneFixup.cpp in that PR) rather than touching clone() itself. Filing this so the underlying SDK gap is tracked — the app-level fix could be removed if this is addressed upstream.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions