Skip to content

Commit

Permalink
Merge pull request #5122 from NREL/5121_ExtensibleGroups_Issues_when_…
Browse files Browse the repository at this point in the history
…name_is_same

#5121 - Extensible Groups problems in ModelObject/WorkspaceObject
  • Loading branch information
kbenne committed Apr 9, 2024
2 parents e1005ff + 0996673 commit b809318
Show file tree
Hide file tree
Showing 13 changed files with 360 additions and 52 deletions.
228 changes: 226 additions & 2 deletions src/model/test/ZoneHVACEquipmentList_GTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,28 @@
* See also https://openstudio.net/license
***********************************************************************************************************************/

#include <algorithm>

#include <gtest/gtest.h>

#include "ModelFixture.hpp"

#include "../ZoneHVACEquipmentList.hpp"
#include "../ZoneHVACEquipmentList_Impl.hpp"

#include "../ThermalZone.hpp"
#include "../ZoneHVACBaseboardConvectiveElectric.hpp"
#include "../ScheduleConstant.hpp"
#include "../Schedule.hpp"
#include "../ScheduleTypeLimits.hpp"

#include "../../utilities/idd/IddObject.hpp"
#include "../../utilities/idf/IdfObject.hpp"
#include "../../utilities/idf/IdfExtensibleGroup.hpp"

#include <utilities/idd/OS_ZoneHVAC_EquipmentList_FieldEnums.hxx>
#include <utilities/idd/IddEnums.hxx>

#include <algorithm>

using namespace openstudio;
using namespace openstudio::model;

Expand Down Expand Up @@ -143,3 +154,216 @@ TEST_F(ModelFixture, ZoneHVACEquipmentList_ScheduleTypeLimits) {
ASSERT_TRUE(_sch_lim);
EXPECT_EQ("Dimensionless", _sch_lim->unitType());
}

TEST_F(ModelFixture, ZoneHVACEquipmentList_RemoveEquipment_ModelObject_Is_First) {

Model m;
ThermalZone z(m);

auto eqlists = m.getConcreteModelObjects<ZoneHVACEquipmentList>();
EXPECT_EQ(1, eqlists.size());
auto& eqlist = eqlists.front();

ZoneHVACBaseboardConvectiveElectric bb_delete(m);

ScheduleConstant bb_sch(m);

ZoneHVACBaseboardConvectiveElectric bb(m);

bb.setName("Baseboard");
bb_sch.setName(bb.nameString());
EXPECT_TRUE(bb.setAvailabilitySchedule(bb_sch));
EXPECT_EQ(bb.nameString(), bb_sch.nameString());

EXPECT_TRUE(bb_delete.addToThermalZone(z));
EXPECT_TRUE(bb.addToThermalZone(z));

{
EXPECT_EQ(2, eqlist.numExtensibleGroups());
auto idf_egs = eqlist.extensibleGroups();
for (const auto& idf_eg : idf_egs) {
// Using getField so we don't resolve object name, but get the handle string
const auto val_ = idf_eg.getField(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment);
const auto uid = openstudio::toUUID(*val_);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}
}

{
auto objects = m.getObjectsByName(bb.nameString());
EXPECT_EQ(2, objects.size());
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), objects.front().iddObject().type());
}

EXPECT_NO_THROW(bb_delete.remove());

{
EXPECT_EQ(1, eqlist.numExtensibleGroups());
auto idf_egs = eqlist.extensibleGroups();
for (const auto& idf_eg : idf_egs) {
// Using getField so we don't resolve object name, but get the handle string
const auto val_ = idf_eg.getField(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment);
const auto uid = openstudio::toUUID(*val_);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}
}
}

TEST_F(ModelFixture, ZoneHVACEquipmentList_RemoveEquipment_Schedule_Is_First) {

Model m;
ThermalZone z(m);

auto eqlists = m.getConcreteModelObjects<ZoneHVACEquipmentList>();
EXPECT_EQ(1, eqlists.size());
auto& eqlist = eqlists.front();

ZoneHVACBaseboardConvectiveElectric bb_delete(m);

ZoneHVACBaseboardConvectiveElectric bb(m);

ScheduleConstant bb_sch(m);

bb.setName("Baseboard");
bb_sch.setName(bb.nameString());
EXPECT_TRUE(bb.setAvailabilitySchedule(bb_sch));
EXPECT_EQ(bb.nameString(), bb_sch.nameString());

EXPECT_TRUE(bb_delete.addToThermalZone(z));
EXPECT_TRUE(bb.addToThermalZone(z));

{
EXPECT_EQ(2, eqlist.numExtensibleGroups());
auto idf_egs = eqlist.extensibleGroups();
for (const auto& idf_eg : idf_egs) {
// Using getField so we don't resolve object name, but get the handle string
const auto val_ = idf_eg.getField(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment);
const auto uid = openstudio::toUUID(*val_);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}
}

{
auto objects = m.getObjectsByName(bb.nameString());
EXPECT_EQ(2, objects.size());
EXPECT_EQ(IddObjectType(IddObjectType::OS_Schedule_Constant), objects.front().iddObject().type());
}

EXPECT_NO_THROW(bb_delete.remove());

{
EXPECT_EQ(1, eqlist.numExtensibleGroups());
auto idf_egs = eqlist.extensibleGroups();
for (const auto& idf_eg : idf_egs) {
// Using getField so we don't resolve object name, but get the handle string
const auto val_ = idf_eg.getField(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment);
const auto uid = openstudio::toUUID(*val_);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}
}
}

TEST_F(ModelFixture, ZoneHVACEquipmentList_RemoveEquipment_Schedule_Is_First_OnlyPop) {

Model m;
ThermalZone z(m);

auto eqlists = m.getConcreteModelObjects<ZoneHVACEquipmentList>();
EXPECT_EQ(1, eqlists.size());
auto& eqlist = eqlists.front();

ZoneHVACBaseboardConvectiveElectric bb_delete(m);

ZoneHVACBaseboardConvectiveElectric bb(m);

ScheduleConstant bb_sch(m);

bb.setName("Baseboard");
bb_sch.setName(bb.nameString());
EXPECT_TRUE(bb.setAvailabilitySchedule(bb_sch));
EXPECT_EQ(bb.nameString(), bb_sch.nameString());

EXPECT_TRUE(bb_delete.addToThermalZone(z));
EXPECT_TRUE(bb.addToThermalZone(z));

EXPECT_EQ(2, eqlist.numExtensibleGroups());
// I deliberately use idfObject.extensibleGroups so the handles aren't resolved to object name
auto idf_egs = eqlist.idfObject().extensibleGroups();

for (const auto& idf_eg : idf_egs) {
const std::string val = idf_eg.getString(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment).get();
const auto uid = openstudio::toUUID(val);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}

eqlist.eraseExtensibleGroup(0);

EXPECT_EQ(1, eqlist.numExtensibleGroups());
idf_egs = eqlist.idfObject().extensibleGroups();
for (const auto& idf_eg : idf_egs) {
const std::string val = idf_eg.getString(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment).get();
const auto uid = openstudio::toUUID(val);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}
}

TEST_F(ModelFixture, ZoneHVACEquipmentList_RemoveEquipment_Schedule_OnlyPop_Works_With_IdfObject) {

Model m;
ThermalZone z(m);

auto eqlists = m.getConcreteModelObjects<ZoneHVACEquipmentList>();
EXPECT_EQ(1, eqlists.size());
auto& eqlist = eqlists.front();

ZoneHVACBaseboardConvectiveElectric bb_delete(m);

ZoneHVACBaseboardConvectiveElectric bb(m);

ScheduleConstant bb_sch(m);

bb.setName("Baseboard");
bb_sch.setName(bb.nameString());
EXPECT_TRUE(bb.setAvailabilitySchedule(bb_sch));
EXPECT_EQ(bb.nameString(), bb_sch.nameString());

EXPECT_TRUE(bb_delete.addToThermalZone(z));
EXPECT_TRUE(bb.addToThermalZone(z));

EXPECT_EQ(2, eqlist.numExtensibleGroups());
// I deliberately use idfObject.extensibleGroups so the handles aren't resolved to object name
auto idf_egs = eqlist.idfObject().extensibleGroups();

for (const auto& idf_eg : idf_egs) {
const std::string val = idf_eg.getString(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment).get();
const auto uid = openstudio::toUUID(val);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}

auto i = eqlist.idfObject();
i.eraseExtensibleGroup(0);

EXPECT_EQ(1, i.numExtensibleGroups());
idf_egs = i.extensibleGroups();
for (const auto& idf_eg : idf_egs) {
const std::string val = idf_eg.getString(OS_ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipment).get();
const auto uid = openstudio::toUUID(val);
auto obj_ = m.getObject(uid);
ASSERT_TRUE(obj_);
EXPECT_EQ(IddObjectType(IddObjectType::OS_ZoneHVAC_Baseboard_Convective_Electric), obj_->iddObject().type());
}
}
22 changes: 20 additions & 2 deletions src/utilities/idf/IdfExtensibleGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ StringVector IdfExtensibleGroup::fields(bool returnDefault) const {
return result;
}

StringVector IdfExtensibleGroup::fieldsWithHandles(bool returnDefault) const {
StringVector result;
const unsigned n = numFields();
result.reserve(n);
for (unsigned i = 0; i < n; ++i) {
OptionalString str = getField(i, returnDefault);
result.push_back(str.get_value_or(""));
}
return result;
}

StringVector IdfExtensibleGroup::fieldComments(bool returnDefault) const {
StringVector result;
for (unsigned i = 0, n = numFields(); i < n; ++i) {
Expand Down Expand Up @@ -57,6 +68,13 @@ OptionalString IdfExtensibleGroup::getString(unsigned fieldIndex, bool returnDef
return m_impl->getString(mf_toIndex(fieldIndex), returnDefault);
}

boost::optional<std::string> IdfExtensibleGroup::getField(unsigned index, bool returnDefault) const {
if (!isValid(index)) {
return boost::none;
}
return m_impl->getField(mf_toIndex(index), returnDefault);
}

OptionalDouble IdfExtensibleGroup::getDouble(unsigned fieldIndex, bool returnDefault) const {
if (!isValid(fieldIndex)) {
return boost::none;
Expand Down Expand Up @@ -169,7 +187,7 @@ IdfExtensibleGroup IdfExtensibleGroup::pushClone() const {
return {p, 0};
}

StringVector values = fields();
StringVector values = fieldsWithHandles();
OS_ASSERT(values.size() == numFields());
return m_impl->pushExtensibleGroup(values);
}
Expand All @@ -180,7 +198,7 @@ IdfExtensibleGroup IdfExtensibleGroup::insertClone(unsigned groupIndex) const {
return {p, 0};
}

StringVector values = fields();
StringVector values = fieldsWithHandles();
OS_ASSERT(values.size() == numFields());
return m_impl->insertExtensibleGroup(groupIndex, values);
}
Expand Down
11 changes: 9 additions & 2 deletions src/utilities/idf/IdfExtensibleGroup.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ class UTILITIES_API IdfExtensibleGroup
/** @name Getters */
//@{

/** Returns this extensible group's fields. Return value will be empty() if this group is
* empty(). */
/** Returns this extensible group's fields. Return value will be empty() if this group is empty(). */
std::vector<std::string> fields(bool returnDefault = false) const;

/** Returns this extensible group's fields. Return value will be empty() if this group is empty().
* Unlike fields(), in the case it's a WorkspaceObject_Impl, it uses handles as string and not the name of the target object */
std::vector<std::string> fieldsWithHandles(bool returnDefault = false) const;

/** Returns the comments associated with this extensible group's fields. */
std::vector<std::string> fieldComments(bool returnDefault = false) const;

Expand All @@ -54,6 +57,10 @@ class UTILITIES_API IdfExtensibleGroup
* exists (isValid(fieldIndex)). */
boost::optional<std::string> getString(unsigned fieldIndex, bool returnDefault = false) const;

/** Like getString except for reference fields getString will return the name of the referenced object.
* This method, getField, will always return the string value of the field. */
boost::optional<std::string> getField(unsigned index, bool returnDefault = false) const;

/** Returns true if the field is empty. */
bool isEmpty(unsigned fieldIndex) const;

Expand Down

0 comments on commit b809318

Please sign in to comment.