From 7f4a419a55870a773873bed37cede1caa5a13ff8 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 15 Jun 2020 15:24:06 +0200 Subject: [PATCH 1/2] #3990: CoilCoolingDXMultiSpeed stages API * Add methods to remove stages, set by vector etc * Constrain the number to maximum 4 stages per E+ * Ensure that a CoilCoolingDXMultiSpeedStageData can only be added to ONE CoilCoolingDXMultiSpeed * When a CoilCoolingDXMultiSpeedStageData is removed, delete the corresponding extensible group in CoilCoolingDXMultiSpeed if any --- resources/model/OpenStudio.idd | 7 + src/model/CoilCoolingDXMultiSpeed.cpp | 149 +++++++++++++++++- src/model/CoilCoolingDXMultiSpeed.hpp | 50 +++++- .../CoilCoolingDXMultiSpeedStageData.cpp | 53 ++++--- .../CoilCoolingDXMultiSpeedStageData.hpp | 3 + .../CoilCoolingDXMultiSpeedStageData_Impl.hpp | 41 +++-- src/model/CoilCoolingDXMultiSpeed_Impl.hpp | 14 +- src/model/FanSystemModel.cpp | 5 +- ...CoilCoolingDXMultiSpeedStageData_GTest.cpp | 70 ++++++++ .../test/CoilCoolingDXMultiSpeed_GTest.cpp | 126 ++++++++++++++- 10 files changed, 466 insertions(+), 52 deletions(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 5fb800bcf97..8ff79dfa0cd 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -15525,6 +15525,13 @@ OS:CoilPerformance:DX:Cooling, OS:Coil:Cooling:DX:MultiSpeed, \extensible:1 + \max-fields 22 + \memo Direct expansion (DX) cooling coil and condensing unit (includes electric or + \memo engine-driven compressor and condenser fan), multi-speed (or variable-speed). + \memo Optional moisture evaporation from wet coil when compressor cycles off with continuous + \memo fan operation. Requires two to four sets of performance data and will interpolate + \memo between speeds. Modeled as a single coil (multi-speed compressor or multiple + \memo compressors with row split or intertwined coil). A1, \field Handle \type handle \required-field diff --git a/src/model/CoilCoolingDXMultiSpeed.cpp b/src/model/CoilCoolingDXMultiSpeed.cpp index 811de45c29f..2edffce038c 100644 --- a/src/model/CoilCoolingDXMultiSpeed.cpp +++ b/src/model/CoilCoolingDXMultiSpeed.cpp @@ -319,6 +319,10 @@ namespace detail { return result; } + unsigned CoilCoolingDXMultiSpeed_Impl::numberOfStages() const { + return numExtensibleGroups(); + } + std::vector CoilCoolingDXMultiSpeed_Impl::stages() const { std::vector result; auto groups = extensibleGroups(); @@ -333,10 +337,115 @@ namespace detail { return result; } - void CoilCoolingDXMultiSpeed_Impl::addStage(CoilCoolingDXMultiSpeedStageData& stage) { + boost::optional CoilCoolingDXMultiSpeed_Impl::stageIndex(const CoilCoolingDXMultiSpeedStageData& stage) const { + + boost::optional result; + + auto egs = castVector(extensibleGroups()); + auto h = openstudio::toString(stage.handle()); + auto it = std::find_if(egs.begin(), egs.end(), + [&](const WorkspaceExtensibleGroup& eg) { + return (eg.getField(OS_Coil_Cooling_DX_MultiSpeedExtensibleFields::Stage).get() == h); + }); + + // If found, we compute the index by using std::distance between the start of vector and the iterator returned by std::find_if + if (it != egs.end()) { + result = std::distance(egs.begin(), it) + 1; + } + + return result; + } + + bool CoilCoolingDXMultiSpeed_Impl::addStage(const CoilCoolingDXMultiSpeedStageData& stage) { + if (auto _c = stage.parentCoil()) { + if (this->handle() == _c->handle()) { + return true; // already the case + } else { + LOG(Error, "For " << briefDescription() << " cannot add " << stage.briefDescription() + << " since this Stage is already in use by another coil ('" << _c->nameString() << "')."); + return false; + } + } auto group = getObject().pushExtensibleGroup().cast(); - OS_ASSERT(! group.empty()); - group.setPointer(OS_Coil_Cooling_DX_MultiSpeedExtensibleFields::Stage,stage.handle()); + if (group.empty()) { + LOG(Error, "You have reached the maximum number of stages (=" << numberOfStages() << "), occurred for " << briefDescription() << "."); + return false; + } + bool result = group.setPointer(OS_Coil_Cooling_DX_MultiSpeedExtensibleFields::Stage, stage.handle()); + if (!result) { + // Something went wrong, so erase the new extensible group + getObject().eraseExtensibleGroup(group.groupIndex()); + } + return result; + } + + bool CoilCoolingDXMultiSpeed_Impl::setStageIndex(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index) + { + boost::optional idx = stageIndex(stage); + if (!idx) { + LOG(Warn, "For " << briefDescription() << " cannot set the index of stage " << stage.briefDescription() << " since it is not part of it."); + return false; + } + + // TODO: we could just set via string instead of doing a ton of typechecking below... + + std::vector stageVector = stages(); + + if (index > stageVector.size()) { + LOG(Warn, "Requested a stage index of " << index << " to be assigned to " << stage.briefDescription() << ", but " + << briefDescription() << " only has " << stageVector.size() << " stages, resetting to that."); + index = stageVector.size(); + } else if (index < 1) { + LOG(Warn, "Requested a stage index of " << index << " < 1 to be assigned to " << stage.briefDescription() << ", resetting to 1"); + index = 1; + } + + stageVector.erase(stageVector.begin() + idx.get() - 1); // stageIndex is 1-indexed, and vector is 0-indexed + + stageVector.insert(stageVector.begin() + (index - 1), stage); + + return setStages(stageVector); + } + + bool CoilCoolingDXMultiSpeed_Impl::addStage(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index) { + bool ok = addStage(stage); + if (!ok) { + return false; + } + ok = setStageIndex(stage, index); + return ok; + } + bool CoilCoolingDXMultiSpeed_Impl::setStages(const std::vector& stages) { + // Clear the extensible groups, and redo them + bool ok = true; + clearExtensibleGroups(); + for (const CoilCoolingDXMultiSpeedStageData& s : stages) { + ok &= addStage(s); + } + return ok; + } + + void CoilCoolingDXMultiSpeed_Impl::removeAllStages() { + clearExtensibleGroups(); + } + + bool CoilCoolingDXMultiSpeed_Impl::removeStage(const CoilCoolingDXMultiSpeedStageData& stage) { + boost::optional idx = stageIndex(stage); + if (!idx) { + LOG(Warn, "For " << briefDescription() << " cannot remove stage " << stage.briefDescription() << " since it is not part of it."); + return false; + } + + return removeStage(idx.get()); + } + + bool CoilCoolingDXMultiSpeed_Impl::removeStage(unsigned index) { + bool result = false; + if ((index > 0) && (index <= numberOfStages())) { + getObject().eraseExtensibleGroup(index-1); + result = true; + } + return result; } boost::optional CoilCoolingDXMultiSpeed_Impl::containingHVACComponent() const @@ -524,14 +633,46 @@ bool CoilCoolingDXMultiSpeed::setMinimumOutdoorDryBulbTemperatureforCompressorOp return getImpl()->setMinimumOutdoorDryBulbTemperatureforCompressorOperation(minimumOutdoorDryBulbTemperatureforCompressorOperation); } +unsigned CoilCoolingDXMultiSpeed::numberOfStages() const { + return getImpl()->numberOfStages(); +} + +boost::optional CoilCoolingDXMultiSpeed::stageIndex(const CoilCoolingDXMultiSpeedStageData& stage) const { + return getImpl()->stageIndex(stage); +} + std::vector CoilCoolingDXMultiSpeed::stages() const { return getImpl()->stages(); } -void CoilCoolingDXMultiSpeed::addStage(CoilCoolingDXMultiSpeedStageData& stage) { +bool CoilCoolingDXMultiSpeed::addStage(const CoilCoolingDXMultiSpeedStageData& stage) { return getImpl()->addStage(stage); } +bool CoilCoolingDXMultiSpeed::addStage(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index) { + return getImpl()->addStage(stage, index); +} + +bool CoilCoolingDXMultiSpeed::setStageIndex(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index) { + return getImpl()->setStageIndex(stage, index); +} + +bool CoilCoolingDXMultiSpeed::setStages(const std::vector& stages) { + return getImpl()->setStages(stages); +} + +void CoilCoolingDXMultiSpeed::removeAllStages() { + getImpl()->removeAllStages(); +} + +bool CoilCoolingDXMultiSpeed::removeStage(const CoilCoolingDXMultiSpeedStageData& stage) { + return getImpl()->removeStage(stage); +} + +bool CoilCoolingDXMultiSpeed::removeStage(unsigned index) { + return getImpl()->removeStage(index); +} + AirflowNetworkEquivalentDuct CoilCoolingDXMultiSpeed::getAirflowNetworkEquivalentDuct(double length, double diameter) { return getImpl()->getAirflowNetworkEquivalentDuct(length, diameter); diff --git a/src/model/CoilCoolingDXMultiSpeed.hpp b/src/model/CoilCoolingDXMultiSpeed.hpp index 2c81e46cedc..6ffa426941f 100644 --- a/src/model/CoilCoolingDXMultiSpeed.hpp +++ b/src/model/CoilCoolingDXMultiSpeed.hpp @@ -132,8 +132,54 @@ class MODEL_API CoilCoolingDXMultiSpeed : public StraightComponent { /** Return the performance data for each stage. **/ std::vector stages() const; - /** Add a new stage after all of the existing stages **/ - void addStage(CoilCoolingDXMultiSpeedStageData& stage); + unsigned numberOfStages() const; + + /* + * Get the index of a given CoilCoolingDXMultiSpeedStageData (1-indexed) + */ + boost::optional stageIndex(const CoilCoolingDXMultiSpeedStageData& stage) const; + + /* + * Add a new stage after all of the existing stages. + */ + bool addStage(const CoilCoolingDXMultiSpeedStageData& stage); + + /* + * Add a new CoilCoolingDXMultiSpeedStageData to the list which a given index (1 to x). + * Internally calls addStage then setStageIndex, see remarks there + */ + bool addStage(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index); + + /* + * You can shuffle the priority of a given CoilCoolingDXMultiSpeedStageData after having added it + * If index is below 1, it's reset to 1. + * If index is greater than the number of stages, will reset to last + */ + bool setStageIndex(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index); + + /* + * Set all stages using a list of CoilCoolingDXMultiSpeedStageDatas + * Internally calls addStage, and will return the global status, but will continue trying if there are problems + * (eg: if you make a vector larger than the number of accepted stages, or a vector that has a stage from another model, the valid stages will be + * added indeed, but it'll eventually return false) + */ + bool setStages(const std::vector& stages); + + /* + * Removes all CoilCoolingDXMultiSpeedStageDatas in this object + */ + void removeAllStages(); + + /* + * Remove the given CoilCoolingDXMultiSpeedStageData from this object's stages + */ + bool removeStage(const CoilCoolingDXMultiSpeedStageData& stage); + + /* + * Remove the CoilCoolingDXMultiSpeedStageData at the given index (1-indexed) + */ + bool removeStage(unsigned index); + /** Creates a new equivalent duct object if an object is not already attached. */ AirflowNetworkEquivalentDuct getAirflowNetworkEquivalentDuct(double length, double diameter); diff --git a/src/model/CoilCoolingDXMultiSpeedStageData.cpp b/src/model/CoilCoolingDXMultiSpeedStageData.cpp index 8ea3f323d0c..139f195c721 100644 --- a/src/model/CoilCoolingDXMultiSpeedStageData.cpp +++ b/src/model/CoilCoolingDXMultiSpeedStageData.cpp @@ -37,6 +37,7 @@ #include "../model/CurveBiquadratic_Impl.hpp" #include "../model/CurveQuadratic.hpp" #include "../model/CurveQuadratic_Impl.hpp" +#include "../model/CoilCoolingDXMultiSpeed.hpp" #include "../model/CoilCoolingDXMultiSpeed_Impl.hpp" #include #include @@ -416,39 +417,39 @@ namespace detail { return t_clone; } - boost::optional> CoilCoolingDXMultiSpeedStageData_Impl::stageIndexAndParentCoil() const { + std::vector CoilCoolingDXMultiSpeedStageData_Impl::remove() { + if (auto _coil = parentCoil()) { + _coil->removeStage(getObject()); + } + return ParentObject_Impl::remove(); + } - boost::optional> result; - // This coil performance object can only be found in a CoilCoolingDXMultiSpeed - // Check all CoilCoolingDXMultiSpeeds in the model, seeing if this is inside of one of them. - boost::optional stageIndex; - boost::optional parentCoil; - auto coilCoolingDXMultiSpeeds = this->model().getConcreteModelObjects(); - for (const auto & coilInModel : coilCoolingDXMultiSpeeds) { - // Check the coil performance objects in this coil to see if one of them is this object - std::vector perfStages = coilInModel.stages(); - int i = 1; - for (auto perfStage : perfStages) { - if (perfStage.handle() == this->handle()) { - stageIndex = i; - parentCoil = coilInModel; - break; - } - i++; - } + boost::optional CoilCoolingDXMultiSpeedStageData_Impl::parentCoil() const { + auto coils = getObject().getModelObjectSources(CoilCoolingDXMultiSpeed::iddObjectType()); + auto count = coils.size(); + if (count == 1) { + return coils[0]; + } else if (count > 1) { + LOG(Error, briefDescription() << " is referenced by more than one CoilCoolingDXMultiSpeed, returning the first"); + return coils[0]; } + return boost::none; + } + + boost::optional> CoilCoolingDXMultiSpeedStageData_Impl::stageIndexAndParentCoil() const { - // Warn if this coil performance object was not found inside a coil - if (!parentCoil) { + boost::optional> result; + + if (auto _coil = parentCoil()) { + result = std::make_tuple(_coil->stageIndex(getObject()).get(), _coil.get()); + } else { LOG(Warn, name().get() + " was not found inside a CoilCoolingDXMultiSpeed in the model, cannot retrieve the autosized value."); - return result; } - return std::make_tuple(stageIndex.get(), parentCoil.get()); + return result; } - boost::optional CoilCoolingDXMultiSpeedStageData_Impl::autosizedGrossRatedTotalCoolingCapacity() const { auto indexAndNameOpt = stageIndexAndParentCoil(); boost::optional result; @@ -906,6 +907,10 @@ CoilCoolingDXMultiSpeedStageData::CoilCoolingDXMultiSpeedStageData(std::shared_p return getImpl()->applySizingValues(); } + boost::optional CoilCoolingDXMultiSpeedStageData::parentCoil() const { + return getImpl()->parentCoil(); + } + boost::optional> CoilCoolingDXMultiSpeedStageData::stageIndexAndParentCoil() const { return getImpl()->stageIndexAndParentCoil(); } diff --git a/src/model/CoilCoolingDXMultiSpeedStageData.hpp b/src/model/CoilCoolingDXMultiSpeedStageData.hpp index b109594d605..fc594a375ca 100644 --- a/src/model/CoilCoolingDXMultiSpeedStageData.hpp +++ b/src/model/CoilCoolingDXMultiSpeedStageData.hpp @@ -176,6 +176,9 @@ class MODEL_API CoilCoolingDXMultiSpeedStageData : public ParentObject { /** @name Other */ //@{ + // Returns the CoilCoolingDXMultiSpeed that references it if any + boost::optional parentCoil() const; + boost::optional autosizedGrossRatedTotalCoolingCapacity() const ; boost::optional autosizedGrossRatedSensibleHeatRatio() const ; diff --git a/src/model/CoilCoolingDXMultiSpeedStageData_Impl.hpp b/src/model/CoilCoolingDXMultiSpeedStageData_Impl.hpp index ddd962cd83b..db4b1c6ff5f 100644 --- a/src/model/CoilCoolingDXMultiSpeedStageData_Impl.hpp +++ b/src/model/CoilCoolingDXMultiSpeedStageData_Impl.hpp @@ -37,6 +37,7 @@ namespace openstudio { namespace model { class Curve; +class CoilCoolingDXMultiSpeed; namespace detail { @@ -68,6 +69,13 @@ namespace detail { virtual IddObjectType iddObjectType() const override; + virtual std::vector children() const override; + + virtual ModelObject clone(Model model) const override; + + // If this object is used by any CoilCoolingDXMultiSpeed, remove the corresponding extensible group to avoid having 'blanks' + virtual std::vector remove() override; + //@} /** @name Getters */ //@{ @@ -120,20 +128,6 @@ namespace detail { bool isRatedEvaporativeCondenserPumpPowerConsumptionAutosized() const; - boost::optional autosizedGrossRatedTotalCoolingCapacity() const ; - - boost::optional autosizedGrossRatedSensibleHeatRatio() const ; - - boost::optional autosizedRatedAirFlowRate() const ; - - boost::optional autosizedEvaporativeCondenserAirFlowRate() const ; - - boost::optional autosizedRatedEvaporativeCondenserPumpPowerConsumption() const ; - - void autosize(); - - void applySizingValues(); - //@} /** @name Setters */ //@{ @@ -190,13 +184,28 @@ namespace detail { /** @name Other */ //@{ - virtual std::vector children() const override; - virtual ModelObject clone(Model model) const override; + boost::optional autosizedGrossRatedTotalCoolingCapacity() const ; + + boost::optional autosizedGrossRatedSensibleHeatRatio() const ; + + boost::optional autosizedRatedAirFlowRate() const ; + + boost::optional autosizedEvaporativeCondenserAirFlowRate() const ; + + boost::optional autosizedRatedEvaporativeCondenserPumpPowerConsumption() const ; + + void autosize(); + + void applySizingValues(); + + // Returns the CoilCoolingDXMultiSpeed that references it if any + boost::optional parentCoil() const; // Used to determine the index of this performance data in the // list of stages in the parent object. boost::optional> stageIndexAndParentCoil() const; + //@} protected: private: diff --git a/src/model/CoilCoolingDXMultiSpeed_Impl.hpp b/src/model/CoilCoolingDXMultiSpeed_Impl.hpp index 5625a059b0b..23212e3d17f 100644 --- a/src/model/CoilCoolingDXMultiSpeed_Impl.hpp +++ b/src/model/CoilCoolingDXMultiSpeed_Impl.hpp @@ -147,9 +147,19 @@ namespace detail { /** @name Other */ //@{ + // Extensible: Stages std::vector stages() const; - - void addStage(CoilCoolingDXMultiSpeedStageData& stage); + unsigned numberOfStages() const; + boost::optional stageIndex(const CoilCoolingDXMultiSpeedStageData& stage) const; + + // Note: a CoilCoolingDXMultiSpeedStageData can be used only by one CoilCoolingDXMultiSpeed + bool addStage(const CoilCoolingDXMultiSpeedStageData& stage); + bool addStage(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index); + bool setStageIndex(const CoilCoolingDXMultiSpeedStageData& stage, unsigned index); + bool setStages(const std::vector& stages); + void removeAllStages(); + bool removeStage(const CoilCoolingDXMultiSpeedStageData& stage); + bool removeStage(unsigned index); AirflowNetworkEquivalentDuct getAirflowNetworkEquivalentDuct(double length, double diameter); diff --git a/src/model/FanSystemModel.cpp b/src/model/FanSystemModel.cpp index f1e52d96de3..73a0c84f16b 100644 --- a/src/model/FanSystemModel.cpp +++ b/src/model/FanSystemModel.cpp @@ -676,9 +676,8 @@ namespace detail { boost::optional FanSystemModel_Impl::speedIndex(const FanSystemModelSpeed& t_speed) const { boost::optional result; - // Find with custom predicate, checking handle equality between the toSurface and the fromSurface pairs - // We do it with extensibleGroups() (rather than viewFactors()) and getString to avoid overhead - // of manipulating actual model objects (getTarget, then create a ViewFactor wrapper, get handle convert to string...) and speed up the routine + // We do it with extensibleGroups() (rather than speeds()) and getString to avoid overhead + // of manipulating actual model objects and speed up the routine auto egs = castVector(extensibleGroups()); auto flowFraction = toString(t_speed.flowFraction()); auto it = std::find_if(egs.begin(), egs.end(), diff --git a/src/model/test/CoilCoolingDXMultiSpeedStageData_GTest.cpp b/src/model/test/CoilCoolingDXMultiSpeedStageData_GTest.cpp index 1275f984ecb..08c455e7fc0 100644 --- a/src/model/test/CoilCoolingDXMultiSpeedStageData_GTest.cpp +++ b/src/model/test/CoilCoolingDXMultiSpeedStageData_GTest.cpp @@ -37,3 +37,73 @@ using namespace openstudio; using namespace openstudio::model; +TEST_F(ModelFixture, CoilCoolingDXMultiSpeedStageData_CoilCoolingDXMultiSpeedStageData) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + ASSERT_EXIT( + { + // create a model to use + Model model; + + // create a coil cooling dx curve fit speed object to use + CoilCoolingDXMultiSpeedStageData speed(model); + + exit(0); + }, + ::testing::ExitedWithCode(0), + "" + ); +} + +TEST_F(ModelFixture, CoilCoolingDXMultiSpeedStageData_Unicity) { + Model model; + + CoilCoolingDXMultiSpeedStageData stage(model); + EXPECT_FALSE(stage.parentCoil()); + + CoilCoolingDXMultiSpeed dx1(model); + EXPECT_TRUE(dx1.addStage(stage)); + ASSERT_TRUE(stage.parentCoil()); + EXPECT_EQ(dx1, stage.parentCoil().get()); + + // StageData is already used, so refuse + CoilCoolingDXMultiSpeed dx2(model); + EXPECT_FALSE(dx2.addStage(stage)); + EXPECT_EQ(1u, model.getConcreteModelObjects().size()); + + EXPECT_EQ(1u, dx1.numExtensibleGroups()); + EXPECT_EQ(1u, dx1.numberOfStages()); + EXPECT_EQ(1u, dx1.stages().size()); + EXPECT_EQ(0u, dx2.numExtensibleGroups()); + EXPECT_EQ(0u, dx2.numberOfStages()); + EXPECT_EQ(0u, dx2.stages().size()); + +} + +TEST_F(ModelFixture, CoilCoolingDXMultiSpeedStageData_remove) { + // If we remove a stage, we would like any CoilCoolingDXMultiSpeed that use it to have their extensible groups adjusted + Model model; + + CoilCoolingDXMultiSpeedStageData stage1(model); + CoilCoolingDXMultiSpeedStageData stage2(model); + + CoilCoolingDXMultiSpeed dx1(model); + EXPECT_TRUE(dx1.addStage(stage1)); + EXPECT_TRUE(dx1.addStage(stage2)); + + EXPECT_EQ(2u, dx1.numExtensibleGroups()); + EXPECT_EQ(2u, dx1.numberOfStages()); + EXPECT_EQ(2u, dx1.stages().size()); + + EXPECT_EQ(2u, model.getConcreteModelObjects().size()); + + stage1.remove(); + + EXPECT_EQ(1u, model.getConcreteModelObjects().size()); + + EXPECT_EQ(1u, dx1.numExtensibleGroups()); + EXPECT_EQ(1u, dx1.numberOfStages()); + EXPECT_EQ(1u, dx1.stages().size()); + + EXPECT_EQ(stage2, dx1.stages()[0]); +} diff --git a/src/model/test/CoilCoolingDXMultiSpeed_GTest.cpp b/src/model/test/CoilCoolingDXMultiSpeed_GTest.cpp index 1ef5aa790a9..488c4f9f3b2 100644 --- a/src/model/test/CoilCoolingDXMultiSpeed_GTest.cpp +++ b/src/model/test/CoilCoolingDXMultiSpeed_GTest.cpp @@ -124,7 +124,7 @@ TEST_F(ModelFixture, CoilCoolingDXMultiSpeed_DefaultConstructors) ::testing::ExitedWithCode(0), "" ); } -TEST_F(ModelFixture, CoilCoolingDXMultiSpeed_Stages) +TEST_F(ModelFixture, CoilCoolingDXMultiSpeed_Stages_API) { Model m; CoilCoolingDXMultiSpeed coil(m); @@ -144,3 +144,127 @@ TEST_F(ModelFixture, CoilCoolingDXMultiSpeed_Stages) EXPECT_TRUE(coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(-5)); EXPECT_EQ(-5, coil.minimumOutdoorDryBulbTemperatureforCompressorOperation()); } +TEST_F(ModelFixture, CoilCoolingDXMultiSpeed_Stages) +{ + Model model; + CoilCoolingDXMultiSpeed dx(model); + + std::vector stages; + for (unsigned i = 1; i <= 4; ++i) { + CoilCoolingDXMultiSpeedStageData stage(model); + stage.setName("Stage " + std::to_string(i)); + stages.push_back(stage); + EXPECT_TRUE(dx.addStage(stage)); + EXPECT_EQ(i, dx.numberOfStages()); + EXPECT_EQ(stages, dx.stages()); + } + + // Can't add more than 4 Stages; + CoilCoolingDXMultiSpeedStageData anotherStage(model); + EXPECT_FALSE(dx.addStage(anotherStage)); + EXPECT_EQ(4, dx.numberOfStages()); + EXPECT_EQ(stages, dx.stages()); + + // Can't remove a stage that's not in there... + EXPECT_FALSE(dx.removeStage(anotherStage)); + EXPECT_EQ(4, dx.numberOfStages()); + EXPECT_EQ(stages, dx.stages()); + + { + int stageIndex = 3; + std::vector thisStages = dx.stages(); + const auto& stageAtIndex = thisStages[stageIndex - 1]; + EXPECT_TRUE(std::find(thisStages.begin(), thisStages.end(), stageAtIndex) != thisStages.end()); + auto optIndex = dx.stageIndex(stageAtIndex); + ASSERT_TRUE(optIndex); + EXPECT_EQ(stageIndex, optIndex.get()); + EXPECT_TRUE(dx.removeStage(stageIndex)); + EXPECT_EQ(3, dx.numberOfStages()); + thisStages = dx.stages(); + EXPECT_FALSE(std::find(thisStages.begin(), thisStages.end(), stageAtIndex) != thisStages.end()); + // Do the same on our vector, so we're up to date... + stages.erase(stages.begin() + stageIndex - 1); + EXPECT_EQ(stages, dx.stages()); + } + + { + int stageIndex = 2; + std::vector thisStages = dx.stages(); + const auto& stageAtIndex = thisStages[stageIndex - 1]; + EXPECT_TRUE(std::find(thisStages.begin(), thisStages.end(), stageAtIndex) != thisStages.end()); + auto optIndex = dx.stageIndex(stageAtIndex); + ASSERT_TRUE(optIndex); + EXPECT_EQ(stageIndex, optIndex.get()); + EXPECT_TRUE(dx.removeStage(stageAtIndex)); + EXPECT_EQ(2, dx.numberOfStages()); + thisStages = dx.stages(); + EXPECT_FALSE(std::find(thisStages.begin(), thisStages.end(), stageAtIndex) != thisStages.end()); + // Do the same on our vector, so we're up to date... + stages.erase(std::find(stages.begin(), stages.end(), stageAtIndex)); + EXPECT_EQ(stages, dx.stages()); + } + + dx.removeAllStages(); + EXPECT_EQ(0, dx.numberOfStages()); + + EXPECT_TRUE(dx.setStages(stages)); + EXPECT_EQ(2, dx.numberOfStages()); + EXPECT_EQ(stages, dx.stages()); + + for (unsigned i = 5; i <= 7; ++i) { + CoilCoolingDXMultiSpeedStageData stage(model); + dx.setName("Stage " + std::to_string(i)); + stages.push_back(stage); + } + EXPECT_EQ(5u, stages.size()); + dx.removeAllStages(); + EXPECT_TRUE(dx.addStage(anotherStage)); + + // This should clear, then assign the first 4, but then return false since the 5th failed + EXPECT_FALSE(dx.setStages(stages)); + EXPECT_EQ(4, dx.numberOfStages()); + { + std::vector thisStages = dx.stages(); + for (unsigned i = 0; i < 4; ++i) { + EXPECT_EQ(stages[i], thisStages[i]); + } + } + stages.pop_back(); + EXPECT_EQ(4u, stages.size()); + + { + const auto& stageAtEnd = stages.back(); + auto optIndex = dx.stageIndex(stageAtEnd); + ASSERT_TRUE(optIndex); + EXPECT_EQ(dx.numberOfStages(), optIndex.get()); + + EXPECT_TRUE(dx.setStageIndex(stageAtEnd, 2)); + std::vector thisStages = dx.stages(); + optIndex = dx.stageIndex(stageAtEnd); + ASSERT_TRUE(optIndex); + EXPECT_EQ(2, optIndex.get()); + EXPECT_EQ(4, dx.numberOfStages()); + for (unsigned i = 1; i <= dx.numberOfStages(); ++i) { + if (i < optIndex.get()) { + EXPECT_EQ(stages[i-1], dx.stages()[i-1]); + } else if (i > optIndex.get()) { + EXPECT_EQ(stages[i-2], dx.stages()[i-1]); + } + } + } + + + + dx.removeAllStages(); + EXPECT_EQ(0u, dx.numExtensibleGroups()); + EXPECT_EQ(0u, dx.numberOfStages()); + EXPECT_EQ(0u, dx.stages().size()); + + // Test that added a stage from another model will fail but not add a blank extensible group + Model model2; + CoilCoolingDXMultiSpeedStageData stageFromAnotherModel(model2); + EXPECT_FALSE(dx.addStage(stageFromAnotherModel)); + EXPECT_EQ(0u, dx.numExtensibleGroups()); + EXPECT_EQ(0u, dx.numberOfStages()); + EXPECT_EQ(0u, dx.stages().size()); +} From df1d111815e176b7c3fcef75041096fb5529faec Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 15 Jun 2020 15:25:30 +0200 Subject: [PATCH 2/2] #3990 - SWIG for CoilCoolingDXMultiSpeedStageData::parentCoil --- src/model/ModelHVAC.i | 6 ++++++ src/model/ModelStraightComponent.i | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/model/ModelHVAC.i b/src/model/ModelHVAC.i index 7978943c694..15f9ad1f3e5 100644 --- a/src/model/ModelHVAC.i +++ b/src/model/ModelHVAC.i @@ -55,6 +55,9 @@ // WaterUseConnections is defined in ModelStraightComponent.i (which depends on this file) %ignore openstudio::model::WaterUseEquipment::waterUseConnections; + // Defined in ModelStraightComponent.i + %ignore openstudio::model::CoilCoolingDXMultiSpeedStageData::parentCoil; + #endif namespace openstudio { @@ -70,6 +73,8 @@ namespace model { %feature("valuewrapper") PlantEquipmentOperationHeatingLoad; %feature("valuewrapper") PlantEquipmentOperationCoolingLoad; %feature("valuewrapper") WaterUseConnections; +%feature("valuewrapper") CoilCoolingDXMultiSpeed; + class AirflowNetworkDistributionNode; class AirflowNetworkZone; class AirflowNetworkEquivalentDuct; @@ -79,6 +84,7 @@ class AirflowNetworkZoneExhaustFan; class PlantEquipmentOperationHeatingLoad; class PlantEquipmentOperationCoolingLoad; class WaterUseConnections; +class CoilCoolingDXMultiSpeed; } } diff --git a/src/model/ModelStraightComponent.i b/src/model/ModelStraightComponent.i index d925ac85b8c..11c5c7062a6 100644 --- a/src/model/ModelStraightComponent.i +++ b/src/model/ModelStraightComponent.i @@ -267,6 +267,10 @@ SWIG_MODELOBJECT(WaterUseConnections,1); OptionalWaterUseConnections waterUseConnections(const openstudio::model::WaterUseEquipment& weq){ return weq.waterUseConnections(); } + + OptionalCoilCoolingDXMultiSpeed parentCoil(const openstudio::model::CoilCoolingDXMultiSpeedStageData& stage){ + return stage.parentCoil(); + } } } } @@ -286,6 +290,13 @@ SWIG_MODELOBJECT(WaterUseConnections,1); } } + public partial class CoilCoolingDXMultiSpeedStageData : ParentObject + { + public OptionalCoilCoolingDXMultiSpeed parentCoil() { + return OpenStudio.OpenStudioModelStraightComponent.parentCoil(this); + } + } + %} #endif #endif