diff --git a/.gitignore b/.gitignore index b0d7e27d8..50c7fc7a1 100644 --- a/.gitignore +++ b/.gitignore @@ -28,8 +28,15 @@ profile/ *.sublime-workspace -# Qt-generated files -src/openstudio_lib/moc_*.cpp +# Qt moc/rcc-generated files, wherever CMake drops them in the tree +moc_*.cpp +moc_*.cpp_parameters +qrc_*.cpp +*.qrc.depends +**/__/ +*_autogen/ +.lupdate/ +.qt/ *.sublime-project cmake-build-debug @@ -45,3 +52,61 @@ clang_format.patch conan-cache .ccache CMakeUserPresets.json + +# This repo is built in-place at the repo root (not an out-of-source build/ dir), so +# CMake/Conan/MSBuild drop generated files alongside tracked source. Ignore those. +/CMakeCache.txt +/CMakePresets.json +/CPackConfig.cmake +/CPackSourceConfig.cmake +/cmakedeps_macros.cmake +/conan_toolchain.cmake +/conanbuild.bat +/conanbuildenv-*.bat +/conandeps_legacy.cmake +/conanrun.bat +/conanrunenv-*.bat +/deactivate_conanbuild.bat +/deactivate_conanrun.bat +/*-Target-release.cmake +/*-release-x86_64-data.cmake +/*Config.cmake +/*ConfigVersion.cmake +/*Targets.cmake +/*-config.cmake +/*-config-version.cmake +/Find*.cmake +/module-*.cmake +/get_output_vars.rb +/output_vars_list.txt +/aqtinstall.log +/build_output.txt +/Products/ +/_deps/ +/OpenStudio-3.11.0/ + +# CMake-generated Visual Studio projects/solution, wherever they land +CMakeFiles/ +cmake_install.cmake +*.vcxproj +*.vcxproj.filters +*.sln + +# Compiled translation binaries (built from the tracked .ts sources) +translations/*.qm + +__pycache__/ + +# EnergyPlus local-docs workflow output (see ENERGYPLUS_DOCS_WORKFLOW.md, itself gitignored) +/ENERGYPLUS_DOCS_WORKFLOW.md +/EnergyPlus/ +/EnergyPlusDocsArchive/ + +# configure_file()-generated from tracked .in templates; AnalyticsHelperSecrets.hxx in +# particular is populated with real secret values at configure time -- never commit it. +src/openstudio_lib/AnalyticsHelperSecrets.hxx +src/openstudio_lib/AnalyticsHelperSecrets.hxx.tmp +src/openstudio_app/AboutBox.hpp +src/model_editor/AboutBox.hpp +src/openstudio_app/OpenStudioApp.rc +src/utilities/OpenStudioApplicationPathHelpers.cxx diff --git a/src/openstudio_app/Resources/default/hvac_library.osm b/src/openstudio_app/Resources/default/hvac_library.osm index 54c76447f..f23fb1598 100644 --- a/src/openstudio_app/Resources/default/hvac_library.osm +++ b/src/openstudio_app/Resources/default/hvac_library.osm @@ -6999,6 +6999,64 @@ OS:AirLoopHVAC:UnitarySystem, , !- Heat Recovery Water Outlet Node Name ; !- Design Specification Multispeed Object Name +OS:Coil:Heating:Desuperheater, + {bdb03443-be0a-4953-8eb4-e6014bbced83}, !- Handle + Desuperheater Reheat Coil, !- Name + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + 0.25, !- Heat Reclaim Recovery Efficiency + , !- Air Inlet Node Name + , !- Air Outlet Node Name + {983b9c20-5a9e-49eb-9770-5c1545cc6b65}, !- Heating Source Name + 0; !- On Cycle Parasitic Electric Load {W} + +OS:AirLoopHVAC:UnitarySystem, + {dbc42aea-40d9-42a6-9016-87cc16813b11}, !- Handle + Unitary - Single Speed DX cooling - Elec heat - CAV - Hotgas reheat, !- Name + Load, !- Control Type + , !- Controlling Zone or Thermostat Location + CoolReheat, !- Dehumidification Control Type + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + , !- Air Inlet Node Name + , !- Air Outlet Node Name + {0c016d27-85cb-4254-8b85-9a16c4586de5}, !- Supply Fan Name + BlowThrough, !- Fan Placement + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Supply Air Fan Operating Mode Schedule Name + {ca3b3fed-58b0-4f64-85f9-257f45d033a5}, !- Heating Coil Name + 1, !- DX Heating Coil Sizing Ratio + {983b9c20-5a9e-49eb-9770-5c1545cc6b65}, !- Cooling Coil Name + No, !- Use DOAS DX Cooling Coil + 2, !- DOAS DX Cooling Coil Leaving Minimum Air Temperature {C} + LatentOrSensibleLoadControl, !- Latent Load Control + {bdb03443-be0a-4953-8eb4-e6014bbced83}, !- Supplemental Heating Coil Name + SupplyAirFlowRate, !- Supply Air Flow Rate Method During Cooling Operation + autosize, !- Supply Air Flow Rate During Cooling Operation {m3/s} + , !- Supply Air Flow Rate Per Floor Area During Cooling Operation {m3/s-m2} + , !- Fraction of Autosized Design Cooling Supply Air Flow Rate + , !- Design Supply Air Flow Rate Per Unit of Capacity During Cooling Operation {m3/s-W} + SupplyAirFlowRate, !- Supply Air Flow Rate Method During Heating Operation + autosize, !- Supply Air Flow Rate During Heating Operation {m3/s} + , !- Supply Air Flow Rate Per Floor Area during Heating Operation {m3/s-m2} + , !- Fraction of Autosized Design Heating Supply Air Flow Rate + , !- Design Supply Air Flow Rate Per Unit of Capacity During Heating Operation {m3/s-W} + None, !- Supply Air Flow Rate Method When No Cooling or Heating is Required + , !- Supply Air Flow Rate When No Cooling or Heating is Required {m3/s} + , !- Supply Air Flow Rate Per Floor Area When No Cooling or Heating is Required {m3/s-m2} + , !- Fraction of Autosized Design Cooling Supply Air Flow Rate When No Cooling or Heating is Required + , !- Fraction of Autosized Design Heating Supply Air Flow Rate When No Cooling or Heating is Required + , !- Design Supply Air Flow Rate Per Unit of Capacity During Cooling Operation When No Cooling or Heating is Required {m3/s-W} + , !- Design Supply Air Flow Rate Per Unit of Capacity During Heating Operation When No Cooling or Heating is Required {m3/s-W} + Yes, !- No Load Supply Air Flow Rate Control Set To Low Speed + 80, !- Maximum Supply Air Temperature {C} + 21, !- Maximum Outdoor Dry-Bulb Temperature for Supplemental Heater Operation {C} + , !- Outdoor Dry-Bulb Temperature Sensor Node Name + 0, !- Ancilliary On-Cycle Electric Power {W} + 0, !- Ancilliary Off-Cycle Electric Power {W} + , !- Design Heat Recovery Water Flow Rate {m3/s} + 80, !- Maximum Temperature for Heat Recovery {C} + , !- Heat Recovery Water Inlet Node Name + , !- Heat Recovery Water Outlet Node Name + ; !- Design Specification Multispeed Object Name + OS:Fan:OnOff, {e803e540-d848-409c-9bad-0c0dc9c7b50a}, !- Handle Fan On Off 14, !- Name diff --git a/src/openstudio_lib/HVACSystemsController.cpp b/src/openstudio_lib/HVACSystemsController.cpp index f5f498e8e..6e72b565c 100644 --- a/src/openstudio_lib/HVACSystemsController.cpp +++ b/src/openstudio_lib/HVACSystemsController.cpp @@ -92,6 +92,8 @@ #include #include #include +#include +#include #include #include #include @@ -126,6 +128,8 @@ #include #include +#include + #include #include #include @@ -519,6 +523,69 @@ void HVACLayoutController::addLibraryObjectToTopLevel(const OSItemId& itemId) { message.exec(); } +namespace { + // ModelObject::clone() clones each child individually and reattaches it via setParent(), so + // true parent-child edges are correctly remapped to point within the new subtree. But 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 (e.g. + // CoilHeatingDesuperheater::heatingSource() pointing at a sibling cooling coil, or a + // SetpointManager's node references) is left pointing at the original objects instead of their + // clones. The functions below fix that up generically, for any object type, rather than special + // casing each lateral-reference field as it's discovered. + + // Walks `original` and `clone` in parallel -- children() order is deterministic and preserved by + // clone() -- building a map from each original object's handle to its corresponding clone. + void buildCloneHandleMap(const model::ModelObject& original, const model::ModelObject& clone, + std::map& handleMap) { + handleMap.emplace(original.handle(), clone); + + boost::optional originalParent = original.optionalCast(); + boost::optional cloneParent = clone.optionalCast(); + if (originalParent && cloneParent) { + std::vector originalChildren = originalParent->children(); + std::vector cloneChildren = cloneParent->children(); + if (originalChildren.size() == cloneChildren.size()) { + for (size_t i = 0; i < originalChildren.size(); ++i) { + buildCloneHandleMap(originalChildren[i], cloneChildren[i], handleMap); + } + } + } + } + + // Rewrites every object-list field on clonedObject (and recursively its children) that still + // points at an object handleMap has a clone for, to point at that clone instead. + void remapLateralReferences(model::ModelObject clonedObject, const std::map& handleMap) { + IddObject iddObject = clonedObject.iddObject(); + for (unsigned index = 0; index < clonedObject.numFields(); ++index) { + if (iddObject.objectLists(index).empty()) { + continue; + } + boost::optional target = clonedObject.getTarget(index); + if (!target) { + continue; + } + auto it = handleMap.find(target->handle()); + if (it != handleMap.end() && it->second.handle() != target->handle()) { + clonedObject.setPointer(index, it->second.handle()); + } + } + + if (boost::optional parent = clonedObject.optionalCast()) { + for (const model::ModelObject& child : parent->children()) { + remapLateralReferences(child, handleMap); + } + } + } + + // `original` is the subtree that was cloned; `clonedObject` is its clone. Re-links any lateral + // reference in the clone that still points into the original subtree. + void fixupClonedReferences(const model::ModelObject& original, model::ModelObject clonedObject) { + std::map handleMap; + buildCloneHandleMap(original, clonedObject, handleMap); + remapLateralReferences(clonedObject, handleMap); + } +} // namespace + void HVACLayoutController::addLibraryObjectToModelNode(const OSItemId& itemId, model::HVACComponent& comp) { model::OptionalModelObject object; bool remove = false; @@ -527,7 +594,9 @@ void HVACLayoutController::addLibraryObjectToModelNode(const OSItemId& itemId, m object = doc->getModelObject(itemId); if (object) { if (!doc->fromModel(itemId)) { + model::ModelObject original = object.get(); object = object->clone(comp.model()); + fixupClonedReferences(original, object.get()); remove = true; } } @@ -893,6 +962,7 @@ void HVACSystemsController::onCopySystemClicked() { auto loop = currentLoop(); if (loop) { auto clone = loop->clone(loop->model()); + fixupClonedReferences(loop.get(), clone); setCurrentHandle(toQString(clone.handle())); } }