Fix #4673 - Allow setting ZoneMixing objects at Space level#4700
Fix #4673 - Allow setting ZoneMixing objects at Space level#4700
Conversation
…ranslation is true: combineSpaces must hardSize then apply to combine space
7dd3aa7 to
1961b6b
Compare
…ng to a zone, two spaces in two zone, or two spaces in the same zone
2808696 to
a4fd9e6
Compare
| for (ZoneMixing& zm : space.supplyZoneMixing()) { | ||
| zm.hardSize(); | ||
| zm.setParent(newSpace); | ||
| } | ||
|
|
||
| for (ZoneMixing& zm : space.exhaustZoneMixing()) { | ||
| // We only need to hardSize when it's the RECEIVING zone | ||
| zm.setSourceSpace(newSpace); | ||
| } |
There was a problem hiding this comment.
ThermalZone::combineSpaces
| bool ZoneMixing_Impl::hardSize() { | ||
| boost::optional<Space> space = this->space(); | ||
| if (!space) { | ||
| return false; | ||
| } | ||
|
|
||
| // source zone cannot be the same as this zone | ||
| if (zone.handle() != this->zone().handle()) { | ||
| result = setPointer(OS_ZoneMixingFields::SourceZoneName, zone.handle()); | ||
| if (this->designFlowRate()) { | ||
| return true; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| void ZoneMixing_Impl::resetSourceZone() { | ||
| bool result = setString(OS_ZoneMixingFields::SourceZoneName, ""); | ||
| OS_ASSERT(result); | ||
| if (boost::optional<double> flowRateperFloorArea = this->flowRateperFloorArea()) { | ||
| return this->setDesignFlowRate(*flowRateperFloorArea * space->floorArea()); | ||
| } | ||
|
|
||
| if (boost::optional<double> flowRateperPerson = this->flowRateperPerson()) { | ||
| return this->setDesignFlowRate(*flowRateperPerson * space->numberOfPeople()); | ||
| } | ||
|
|
||
| if (boost::optional<double> airChangesperHour = this->airChangesperHour()) { | ||
| return this->setDesignFlowRate(*airChangesperHour * space->volume() / 3600.0); | ||
| } | ||
|
|
||
| return false; |
| bool ZoneMixing_Impl::setSourceZone(const ThermalZone& zone) { | ||
|
|
||
| auto receiving = this->zoneOrSpace(); | ||
| // source zone cannot be the same as this zone | ||
| if (zone.handle() == receiving.handle()) { | ||
| LOG(Warn, "For " << briefDescription() << ", Source Zone cannot be the same as the Receiving Zone '" << zone.nameString() << "'."); | ||
| return false; | ||
| } | ||
| if (receiving.iddObjectType() != IddObjectType::OS_ThermalZone) { | ||
| LOG(Warn, "For " << briefDescription() << ", Receiving is a Space, so you cannot set Source as a Zone."); | ||
| return false; | ||
| } | ||
|
|
||
| return setPointer(OS_ZoneMixingFields::SourceZoneorSpaceName, zone.handle()); | ||
| } | ||
|
|
||
| bool ZoneMixing_Impl::setSourceSpace(const Space& space) { | ||
| auto receiving = this->zoneOrSpace(); | ||
| // source zone cannot be the same as this zone | ||
| if (space.handle() == receiving.handle()) { | ||
| LOG(Warn, "For " << briefDescription() << ", Source Space cannot be the same as the Receiving Space '" << space.nameString() << "'."); | ||
| return false; | ||
| } | ||
| if (receiving.iddObjectType() != IddObjectType::OS_Space) { | ||
| LOG(Warn, "For " << briefDescription() << ", Receiving is a Zone, so you cannot set Source as a Space."); | ||
| return false; | ||
| } | ||
|
|
||
| return setPointer(OS_ZoneMixingFields::SourceZoneorSpaceName, space.handle()); | ||
| } |
There was a problem hiding this comment.
Protect users from themselves
| boost::optional<ThermalZone> zone() const; | ||
| boost::optional<Space> space() const; | ||
| ModelObject zoneOrSpace() const; |
There was a problem hiding this comment.
this is the API break
| OS_DEPRECATED boost::optional<double> flowRateperZoneFloorArea() const; | ||
| boost::optional<double> flowRateperFloorArea() const; |
There was a problem hiding this comment.
Lots of deprecations
| TEST_F(ModelFixture, ZoneMixing_CantMixSpacesAndZones) { | ||
|
|
||
| Model m; | ||
|
|
||
| constexpr double width = 10.0; | ||
| constexpr double height = 3.6; // It's convenient for ACH, since 3600 s/hr | ||
| // constexpr double spaceFloorArea = width * width; | ||
| // constexpr double spaceVolume = spaceFloorArea * height; | ||
| // constexpr double oneWallArea = width * height; | ||
|
|
||
| // y (=North) | ||
| // ▲ | ||
| // │ building height = 3m | ||
| // 10├────────┼────────┼────────┤ | ||
| // │ │ │ │ | ||
| // │ Zone 1 │ Zone 1 │ Zone 2 │ | ||
| // │ Space 1│ Space 2│ Space 3│ | ||
| // │ │ │ │ | ||
| // └────────┴────────┴────────┴─────► x | ||
| // 0 10 20 30 | ||
|
|
||
| // Counterclockwise points | ||
| std::vector<Point3d> floorPointsSpace1{{0.0, 0.0, 0.0}, {0.0, width, 0.0}, {width, width, 0.0}, {width, 0.0, 0.0}}; | ||
|
|
||
| auto space1 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| auto space2 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space2.setXOrigin(width); | ||
| auto space3 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space3.setXOrigin(width * 2); | ||
|
|
||
| ThermalZone z1(m); | ||
| space1.setThermalZone(z1); | ||
| space2.setThermalZone(z1); | ||
|
|
||
| ThermalZone z2(m); | ||
| space3.setThermalZone(z2); | ||
|
|
||
| { | ||
| ZoneMixing zm(space1); | ||
| // Can't mix Spaces and Zones | ||
| EXPECT_FALSE(zm.setSourceZone(z1)); | ||
| EXPECT_FALSE(zm.sourceZoneOrSpace()); | ||
| EXPECT_FALSE(zm.sourceZone()); | ||
| EXPECT_FALSE(zm.sourceSpace()); | ||
|
|
||
| EXPECT_TRUE(zm.setSourceSpace(space3)); | ||
| ASSERT_TRUE(zm.sourceZoneOrSpace()); | ||
| EXPECT_EQ(space3, zm.sourceZoneOrSpace().get()); | ||
| ASSERT_TRUE(zm.sourceSpace()); | ||
| EXPECT_EQ(space3, zm.sourceSpace().get()); | ||
| EXPECT_FALSE(zm.sourceZone()); | ||
| } | ||
|
|
||
| { | ||
| ZoneMixing zm(z1); | ||
| // Can't mix Spaces and Zones | ||
| EXPECT_FALSE(zm.setSourceSpace(space1)); | ||
| EXPECT_FALSE(zm.sourceZoneOrSpace()); | ||
| EXPECT_FALSE(zm.sourceZone()); | ||
| EXPECT_FALSE(zm.sourceSpace()); | ||
|
|
||
| EXPECT_TRUE(zm.setSourceZone(z2)); | ||
| ASSERT_TRUE(zm.sourceZoneOrSpace()); | ||
| EXPECT_EQ(z2, zm.sourceZoneOrSpace().get()); | ||
| ASSERT_TRUE(zm.sourceZone()); | ||
| EXPECT_EQ(z2, zm.sourceZone().get()); | ||
| EXPECT_FALSE(zm.sourceSpace()); | ||
| } | ||
| } |
There was a problem hiding this comment.
Test protecting users
| TEST_F(ModelFixture, ZoneMixing_CombineSpacesDifferentZones) { | ||
|
|
||
| Model m; | ||
|
|
||
| constexpr double width = 10.0; | ||
| constexpr double height = 3.6; // It's convenient for ACH, since 3600 s/hr | ||
| constexpr double spaceFloorArea = width * width; | ||
| constexpr double spaceVolume = spaceFloorArea * height; | ||
| constexpr double numPeoplePerSpace = 10.0; | ||
|
|
||
| // y (=North) | ||
| // ▲ | ||
| // │ building height = 3m | ||
| // 10├────────┼────────┼────────┼────────┤ | ||
| // │ │ │ │ │ | ||
| // │ Zone 1 │ Zone 1 │ Zone 2 │ Zone 2 │ | ||
| // │ Space 1│ Space 2│ Space 3│ Space 4│ | ||
| // │ │ │ │ │ | ||
| // └────────┴────────┴────────┴────────┴──► x | ||
| // 0 10 20 30 40 | ||
|
|
||
| // Counterclockwise points | ||
| std::vector<Point3d> floorPointsSpace1{{0.0, 0.0, 0.0}, {0.0, width, 0.0}, {width, width, 0.0}, {width, 0.0, 0.0}}; | ||
|
|
||
| auto space1 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| auto space2 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space2.setXOrigin(width); | ||
| auto space3 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space3.setXOrigin(width * 2); | ||
| auto space4 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space4.setXOrigin(width * 3); | ||
|
|
||
| ThermalZone z1(m); | ||
| space1.setThermalZone(z1); | ||
| space2.setThermalZone(z1); | ||
|
|
||
| ThermalZone z2(m); | ||
| space3.setThermalZone(z2); | ||
| space4.setThermalZone(z2); | ||
|
|
||
| // Add 10 People in each space | ||
| SpaceType buildingSpaceType(m); | ||
| buildingSpaceType.setName("buildingSpaceType"); | ||
| PeopleDefinition pd(m); | ||
| pd.setNumberofPeople(numPeoplePerSpace); | ||
| People p(pd); | ||
| EXPECT_TRUE(p.setSpaceType(buildingSpaceType)); | ||
| auto building = m.getUniqueModelObject<Building>(); | ||
| EXPECT_TRUE(building.setSpaceType(buildingSpaceType)); | ||
|
|
||
| ZoneMixing zmAbs(space3); | ||
| EXPECT_TRUE(zmAbs.setSourceSpace(space2)); | ||
| constexpr double m3sAbs = 0.6; | ||
| EXPECT_TRUE(zmAbs.setDesignFlowRate(m3sAbs)); | ||
|
|
||
| ZoneMixing zmFloor(space2); | ||
| EXPECT_TRUE(zmFloor.setSourceSpace(space4)); | ||
| constexpr double m3sPerFlow = 0.001; | ||
| constexpr double equivm3s_Floor = m3sPerFlow * spaceFloorArea; | ||
| EXPECT_TRUE(zmFloor.setFlowRateperFloorArea(m3sPerFlow)); | ||
|
|
||
| ZoneMixing zmPerson(space4); | ||
| EXPECT_TRUE(zmPerson.setSourceSpace(space1)); | ||
| constexpr double m3sPerPerson = 0.03; | ||
| constexpr double equivm3s_Person = m3sPerPerson * numPeoplePerSpace; | ||
| EXPECT_TRUE(zmPerson.setFlowRateperPerson(m3sPerPerson)); | ||
|
|
||
| ZoneMixing zmACH(space1); | ||
| EXPECT_TRUE(zmACH.setSourceSpace(space3)); | ||
| constexpr double ach = 0.5; | ||
| constexpr double equivm3s_ACH = ach * spaceVolume / 3600.0; | ||
| EXPECT_TRUE(zmACH.setAirChangesperHour(ach)); | ||
|
|
||
| // | Zone Mixing | Receiving | Source | Flow/Space | Per Floor Area | Per Person | ACH ) || Calculated Total | | ||
| // | Name | Space | Space | (m3/s) | (m3/s-m2) | (m3/s.p) | (1/hr) || For 1 Space (m3/s) | | ||
| // |------------------|-----------|---------|------------|----------------|------------|----------||--------------------| | ||
| // | zmAbs | space3 | space2 | 0.6 | | | || 0.6 | | ||
| // | zmFloor | space2 | space4 | | 0.001 | | || 0.1 | | ||
| // | zmPerson | space4 | space1 | | | 0.03 | || 0.3 | | ||
| // | zmACH | space1 | space3 | | | | 0.5 || 0.05 | | ||
|
|
||
| EXPECT_EQ(0.6, m3sAbs); | ||
| EXPECT_EQ(0.1, equivm3s_Floor); | ||
| EXPECT_EQ(0.3, equivm3s_Person); | ||
| EXPECT_EQ(0.05, equivm3s_ACH); | ||
|
|
||
| // Combine Zone 1's space | ||
| // Only receiving spaces trigger a hardsize, so zmFloor and zmACH should be hardsized | ||
| auto space12_ = z1.combineSpaces(); | ||
| ASSERT_TRUE(space12_); | ||
|
|
||
| EXPECT_EQ(3, m.getConcreteModelObjects<Space>().size()); | ||
|
|
||
| ASSERT_TRUE(zmAbs.designFlowRate()); | ||
| EXPECT_EQ(m3sAbs, zmAbs.designFlowRate().get()); | ||
| EXPECT_FALSE(zmAbs.flowRateperFloorArea()); | ||
| EXPECT_FALSE(zmAbs.flowRateperPerson()); | ||
| EXPECT_FALSE(zmAbs.airChangesperHour()); | ||
| ASSERT_TRUE(zmAbs.space()); | ||
| EXPECT_EQ(space3, zmAbs.space().get()); | ||
| EXPECT_FALSE(zmAbs.zone()); | ||
| ASSERT_TRUE(zmAbs.sourceSpace()); | ||
| EXPECT_EQ(space12_.get(), zmAbs.sourceSpace().get()); | ||
| EXPECT_FALSE(zmAbs.sourceZone()); | ||
|
|
||
| ASSERT_TRUE(zmFloor.designFlowRate()); | ||
| EXPECT_EQ(equivm3s_Floor, zmFloor.designFlowRate().get()); | ||
| EXPECT_FALSE(zmFloor.flowRateperFloorArea()); | ||
| EXPECT_FALSE(zmFloor.flowRateperPerson()); | ||
| EXPECT_FALSE(zmFloor.airChangesperHour()); | ||
| ASSERT_TRUE(zmFloor.space()); | ||
| EXPECT_EQ(space12_.get(), zmFloor.space().get()); | ||
| EXPECT_FALSE(zmFloor.zone()); | ||
| ASSERT_TRUE(zmFloor.sourceSpace()); | ||
| EXPECT_EQ(space4, zmFloor.sourceSpace().get()); | ||
| EXPECT_FALSE(zmFloor.sourceZone()); | ||
|
|
||
| EXPECT_FALSE(zmPerson.designFlowRate()); | ||
| EXPECT_FALSE(zmPerson.flowRateperFloorArea()); | ||
| ASSERT_TRUE(zmPerson.flowRateperPerson()); | ||
| EXPECT_EQ(m3sPerPerson, zmPerson.flowRateperPerson().get()); | ||
| EXPECT_FALSE(zmPerson.airChangesperHour()); | ||
| ASSERT_TRUE(zmPerson.space()); | ||
| EXPECT_EQ(space4, zmPerson.space().get()); | ||
| EXPECT_FALSE(zmPerson.zone()); | ||
| ASSERT_TRUE(zmPerson.sourceSpace()); | ||
| EXPECT_EQ(space12_.get(), zmPerson.sourceSpace().get()); | ||
| EXPECT_FALSE(zmPerson.sourceZone()); | ||
|
|
||
| ASSERT_TRUE(zmACH.designFlowRate()); | ||
| EXPECT_EQ(equivm3s_ACH, zmACH.designFlowRate().get()); | ||
| EXPECT_FALSE(zmACH.flowRateperFloorArea()); | ||
| EXPECT_FALSE(zmACH.flowRateperPerson()); | ||
| EXPECT_FALSE(zmACH.airChangesperHour()); | ||
| ASSERT_TRUE(zmACH.space()); | ||
| EXPECT_EQ(space12_.get(), zmACH.space().get()); | ||
| EXPECT_FALSE(zmACH.zone()); | ||
| ASSERT_TRUE(zmACH.sourceSpace()); | ||
| EXPECT_EQ(space3, zmACH.sourceSpace().get()); | ||
| EXPECT_FALSE(zmACH.sourceZone()); | ||
|
|
||
| auto space34_ = z2.combineSpaces(); | ||
| ASSERT_TRUE(space34_); | ||
|
|
||
| EXPECT_EQ(2, m.getConcreteModelObjects<Space>().size()); | ||
|
|
||
| ASSERT_TRUE(zmAbs.designFlowRate()); | ||
| EXPECT_EQ(m3sAbs, zmAbs.designFlowRate().get()); | ||
| EXPECT_FALSE(zmAbs.flowRateperFloorArea()); | ||
| EXPECT_FALSE(zmAbs.flowRateperPerson()); | ||
| EXPECT_FALSE(zmAbs.airChangesperHour()); | ||
| ASSERT_TRUE(zmAbs.space()); | ||
| EXPECT_EQ(space34_.get(), zmAbs.space().get()); | ||
| EXPECT_FALSE(zmAbs.zone()); | ||
| ASSERT_TRUE(zmAbs.sourceSpace()); | ||
| EXPECT_EQ(space12_.get(), zmAbs.sourceSpace().get()); | ||
| EXPECT_FALSE(zmAbs.sourceZone()); | ||
|
|
||
| ASSERT_TRUE(zmFloor.designFlowRate()); | ||
| EXPECT_EQ(equivm3s_Floor, zmFloor.designFlowRate().get()); | ||
| EXPECT_FALSE(zmFloor.flowRateperFloorArea()); | ||
| EXPECT_FALSE(zmFloor.flowRateperPerson()); | ||
| EXPECT_FALSE(zmFloor.airChangesperHour()); | ||
| ASSERT_TRUE(zmFloor.space()); | ||
| EXPECT_EQ(space12_.get(), zmFloor.space().get()); | ||
| EXPECT_FALSE(zmFloor.zone()); | ||
| ASSERT_TRUE(zmFloor.sourceSpace()); | ||
| EXPECT_EQ(space34_.get(), zmFloor.sourceSpace().get()); | ||
| EXPECT_FALSE(zmFloor.sourceZone()); | ||
|
|
||
| ASSERT_TRUE(zmPerson.designFlowRate()); | ||
| EXPECT_EQ(equivm3s_Person, zmPerson.designFlowRate().get()); | ||
| EXPECT_FALSE(zmPerson.flowRateperFloorArea()); | ||
| EXPECT_FALSE(zmPerson.flowRateperPerson()); | ||
| EXPECT_FALSE(zmPerson.airChangesperHour()); | ||
| ASSERT_TRUE(zmPerson.space()); | ||
| EXPECT_EQ(space34_.get(), zmPerson.space().get()); | ||
| EXPECT_FALSE(zmPerson.zone()); | ||
| ASSERT_TRUE(zmPerson.sourceSpace()); | ||
| EXPECT_EQ(space12_.get(), zmPerson.sourceSpace().get()); | ||
| EXPECT_FALSE(zmPerson.sourceZone()); | ||
|
|
||
| ASSERT_TRUE(zmACH.designFlowRate()); | ||
| EXPECT_EQ(equivm3s_ACH, zmACH.designFlowRate().get()); | ||
| EXPECT_FALSE(zmACH.flowRateperFloorArea()); | ||
| EXPECT_FALSE(zmACH.flowRateperPerson()); | ||
| EXPECT_FALSE(zmACH.airChangesperHour()); | ||
| ASSERT_TRUE(zmACH.space()); | ||
| EXPECT_EQ(space12_.get(), zmACH.space().get()); | ||
| EXPECT_FALSE(zmACH.zone()); | ||
| ASSERT_TRUE(zmACH.sourceSpace()); | ||
| EXPECT_EQ(space34_.get(), zmACH.sourceSpace().get()); | ||
| EXPECT_FALSE(zmACH.sourceZone()); | ||
| } |
There was a problem hiding this comment.
Detail ThermalZone::combineSpaces test, and the order in which things are done. Some of that is tested in FT, but not all. So I think I like having both tests
| ModelObject zoneOrSpace = modelObject.zoneOrSpace(); | ||
| boost::optional<ModelObject> sourceZoneOrSpace = modelObject.sourceZoneOrSpace(); | ||
|
|
||
| auto getParentObjectName = [this](const ModelObject& mo) { | ||
| if (!m_excludeSpaceTranslation) { | ||
| return mo.nameString(); | ||
| } | ||
|
|
||
| if (auto space_ = mo.optionalCast<Space>()) { | ||
| if (auto thermalZone_ = space_->thermalZone()) { | ||
| return thermalZone_->nameString(); | ||
| } else { | ||
| OS_ASSERT(false); // This shouldn't happen, since we removed all orphaned spaces earlier in the FT | ||
| } | ||
| } | ||
|
|
||
| return mo.nameString(); | ||
| }; | ||
|
|
||
| if (!sourceZoneOrSpace) { | ||
| if (m_excludeSpaceTranslation && modelObject.space()) { | ||
| LOG(Warn, modelObject.briefDescription() | ||
| << " doesn't have a Source Zone or Space, it will not be translated. As you were using Space-Level ZoneMixing, and you are not " | ||
| "translating to Spaces, it's possible it was pointing to two spaces inside the same zone"); | ||
| } else { | ||
| LOG(Warn, modelObject.briefDescription() << " doesn't have a Source Zone or Space, it will not be translated."); | ||
| } | ||
| return boost::none; | ||
| } | ||
|
|
||
| if (zoneOrSpace == sourceZoneOrSpace.get()) { | ||
| // This isn't going to happen, because zm.setSourceSpace(newSpace) in ThermalZone::combineSpaces will be rejected | ||
| // Let's play it safe though | ||
| LOG(Warn, modelObject.briefDescription() << " has the same Receiving and Source Zone or Space, it will not be translated."); | ||
| if (!m_excludeSpaceTranslation) { | ||
| // We don't allow this at model time, the only reason we expect this to happen is when m_excludeSpaceTranslation is true, we call | ||
| // combineSpaces, and if the user has a ZoneMixing pointing to two spaces from the same ThermalZone, you end up with matching Receiving and | ||
| // Source Spaces | ||
| OS_ASSERT(false); | ||
| } | ||
| return boost::none; | ||
| } |
There was a problem hiding this comment.
Ft adjust + warnings in case the user had a ZoneMixing pointing to two spaces inside the same zone AND opting out of space translation. Yep, that's the state of all the corner cases we have to test for nowadays...
| TEST_F(EnergyPlusFixture, ForwardTranslatorZoneMixing_DifferentZones) { | ||
|
|
||
| Model m; | ||
|
|
||
| constexpr double width = 10.0; | ||
| constexpr double height = 3.6; // It's convenient for ACH, since 3600 s/hr | ||
| constexpr double spaceFloorArea = width * width; | ||
| constexpr double spaceVolume = spaceFloorArea * height; | ||
| constexpr double numPeoplePerSpace = 10.0; | ||
|
|
||
| // y (=North) | ||
| // ▲ | ||
| // │ building height = 3m | ||
| // 10├────────┼────────┼────────┼────────┤ | ||
| // │ │ │ │ │ | ||
| // │ Zone 1 │ Zone 1 │ Zone 2 │ Zone 2 │ | ||
| // │ Space 1│ Space 2│ Space 3│ Space 4│ | ||
| // │ │ │ │ │ | ||
| // └────────┴────────┴────────┴────────┴──► x | ||
| // 0 10 20 30 40 | ||
|
|
||
| // Counterclockwise points | ||
| std::vector<Point3d> floorPointsSpace1{{0.0, 0.0, 0.0}, {0.0, width, 0.0}, {width, width, 0.0}, {width, 0.0, 0.0}}; | ||
|
|
||
| auto space1 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| auto space2 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space2.setXOrigin(width); | ||
| auto space3 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space3.setXOrigin(width * 2); | ||
| auto space4 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space4.setXOrigin(width * 3); | ||
|
|
||
| ThermalZone z1(m); | ||
| space1.setThermalZone(z1); | ||
| space2.setThermalZone(z1); | ||
|
|
||
| ThermalZone z2(m); | ||
| space3.setThermalZone(z2); | ||
| space4.setThermalZone(z2); | ||
|
|
||
| // Add 10 People in each space | ||
| SpaceType buildingSpaceType(m); | ||
| buildingSpaceType.setName("buildingSpaceType"); | ||
| PeopleDefinition pd(m); | ||
| pd.setNumberofPeople(numPeoplePerSpace); | ||
| People p(pd); | ||
| EXPECT_TRUE(p.setSpaceType(buildingSpaceType)); | ||
| auto building = m.getUniqueModelObject<Building>(); | ||
| EXPECT_TRUE(building.setSpaceType(buildingSpaceType)); | ||
|
|
||
| ZoneMixing zmAbs(space3); | ||
| zmAbs.setName("zmAbs"); | ||
| EXPECT_TRUE(zmAbs.setSourceSpace(space2)); | ||
| constexpr double m3sAbs = 0.6; | ||
| EXPECT_TRUE(zmAbs.setDesignFlowRate(m3sAbs)); | ||
|
|
||
| ZoneMixing zmFloor(space2); | ||
| zmFloor.setName("zmFloor"); | ||
| EXPECT_TRUE(zmFloor.setSourceSpace(space4)); | ||
| constexpr double m3sPerFlow = 0.001; | ||
| constexpr double equivm3s_Floor = m3sPerFlow * spaceFloorArea; | ||
| EXPECT_TRUE(zmFloor.setFlowRateperFloorArea(m3sPerFlow)); | ||
|
|
||
| ZoneMixing zmPerson(space4); | ||
| zmPerson.setName("zmPerson"); | ||
| EXPECT_TRUE(zmPerson.setSourceSpace(space1)); | ||
| constexpr double m3sPerPerson = 0.03; | ||
| constexpr double equivm3s_Person = m3sPerPerson * numPeoplePerSpace; | ||
| EXPECT_TRUE(zmPerson.setFlowRateperPerson(m3sPerPerson)); | ||
|
|
||
| ZoneMixing zmACH(space1); | ||
| zmACH.setName("zmACH"); | ||
| EXPECT_TRUE(zmACH.setSourceSpace(space3)); | ||
| constexpr double ach = 0.5; | ||
| constexpr double equivm3s_ACH = ach * spaceVolume / 3600.0; | ||
| EXPECT_TRUE(zmACH.setAirChangesperHour(ach)); | ||
|
|
||
| ZoneMixing zmZone(z1); | ||
| zmZone.setName("zmZone"); | ||
| EXPECT_TRUE(zmZone.setSourceZone(z2)); | ||
| EXPECT_TRUE(zmZone.setFlowRateperFloorArea(0.008)); | ||
|
|
||
| // | Zone Mixing | Receiving | Source | Flow/Space | Per Floor Area | Per Person | ACH ) || Calculated Total | | ||
| // | Name | Entity | Entity | (m3/s) | (m3/s-m2) | (m3/s.p) | (1/hr) || For 1 Space (m3/s) | | ||
| // |------------------|-----------|---------|------------|----------------|------------|----------||--------------------| | ||
| // | zmAbs | space3 | space2 | 0.6 | | | || 0.6 | | ||
| // | zmFloor | space2 | space4 | | 0.001 | | || 0.1 | | ||
| // | zmPerson | space4 | space1 | | | 0.03 | || 0.3 | | ||
| // | zmACH | space1 | space3 | | | | 0.5 || 0.05 | | ||
| // | zmZone | **z1** | **z2** | | 0.008 | | 0.5 || 0.05 | | ||
|
|
||
| EXPECT_EQ(0.6, m3sAbs); | ||
| EXPECT_EQ(0.1, equivm3s_Floor); | ||
| EXPECT_EQ(0.3, equivm3s_Person); | ||
| EXPECT_EQ(0.05, equivm3s_ACH); | ||
|
|
||
| EXPECT_TRUE(zmAbs.setDeltaTemperature(0.5)); | ||
|
|
||
| ScheduleConstant mixingSch(m); | ||
| mixingSch.setName("mixingSch"); | ||
| EXPECT_TRUE(zmZone.setSchedule(mixingSch)); | ||
|
|
||
| ScheduleConstant deltaTempSch(m); | ||
| deltaTempSch.setName("deltaTempSch"); | ||
| EXPECT_TRUE(zmZone.setDeltaTemperatureSchedule(deltaTempSch)); | ||
|
|
||
| ScheduleConstant minimumReceivingTempSch(m); | ||
| minimumReceivingTempSch.setName("minimumReceivingTempSch"); | ||
| EXPECT_TRUE(zmZone.setMinimumReceivingTemperatureSchedule(minimumReceivingTempSch)); | ||
|
|
||
| ScheduleConstant minimumSourceTempSch(m); | ||
| minimumSourceTempSch.setName("minimumSourceTempSch"); | ||
| EXPECT_TRUE(zmZone.setMinimumSourceTemperatureSchedule(minimumSourceTempSch)); | ||
|
|
||
| ScheduleConstant minimumOutdoorTempSch(m); | ||
| minimumOutdoorTempSch.setName("minimumOutdoorTempSch"); | ||
| EXPECT_TRUE(zmZone.setMinimumOutdoorTemperatureSchedule(minimumOutdoorTempSch)); | ||
|
|
||
| ScheduleConstant maximumReceivingTempSch(m); | ||
| maximumReceivingTempSch.setName("maximumReceivingTempSch"); | ||
| EXPECT_TRUE(zmZone.setMaximumReceivingTemperatureSchedule(maximumReceivingTempSch)); | ||
|
|
||
| ScheduleConstant maximumSourceTempSch(m); | ||
| maximumSourceTempSch.setName("maximumSourceTempSch"); | ||
| EXPECT_TRUE(zmZone.setMaximumSourceTemperatureSchedule(maximumSourceTempSch)); | ||
|
|
||
| ScheduleConstant maximumOutdoorTempSch(m); | ||
| maximumOutdoorTempSch.setName("maximumOutdoorTempSch"); | ||
| EXPECT_TRUE(zmZone.setMaximumOutdoorTemperatureSchedule(maximumOutdoorTempSch)); | ||
|
|
||
| auto alwaysOnContinuousSchedule = m.alwaysOnContinuousSchedule(); | ||
|
|
||
| ForwardTranslator ft; | ||
| { | ||
| ft.setExcludeSpaceTranslation(false); | ||
|
|
||
| Workspace w = ft.translateModel(m); | ||
|
|
||
| auto zones = w.getObjectsByType(IddObjectType::Zone); | ||
| ASSERT_EQ(2, zones.size()); | ||
| auto zone = zones[0]; | ||
| EXPECT_EQ(4, w.getObjectsByType(IddObjectType::Space).size()); | ||
|
|
||
| auto zms = w.getObjectsByType(IddObjectType::ZoneMixing); | ||
| ASSERT_EQ(5, zms.size()); | ||
|
|
||
| for (const auto& zm : zms) { | ||
| auto name = zm.nameString(); | ||
| auto receiving_ = zm.getTarget(ZoneMixingFields::ZoneorSpaceName); | ||
| ASSERT_TRUE(receiving_); | ||
| auto source_ = zm.getTarget(ZoneMixingFields::SourceZoneorSpaceName); | ||
| ASSERT_TRUE(source_); | ||
| auto sch_ = zm.getTarget(ZoneMixingFields::ScheduleName); | ||
| ASSERT_TRUE(sch_); | ||
|
|
||
| if (name == zmZone.nameString()) { | ||
| EXPECT_EQ(receiving_->iddObject().type(), IddObjectType::Zone); | ||
| EXPECT_EQ(source_->iddObject().type(), IddObjectType::Zone); | ||
| EXPECT_EQ(z1.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ(mixingSch.nameString(), sch_->nameString()); | ||
| EXPECT_EQ("Flow/Area", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DesignFlowRate)); | ||
| EXPECT_EQ(0.008, zm.getDouble(ZoneMixingFields::FlowRateperFloorArea).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(z2.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| EXPECT_EQ("deltaTempSch", zm.getString(ZoneMixingFields::DeltaTemperatureScheduleName).get()); | ||
| EXPECT_EQ("minimumReceivingTempSch", zm.getString(ZoneMixingFields::MinimumReceivingTemperatureScheduleName).get()); | ||
| EXPECT_EQ("maximumReceivingTempSch", zm.getString(ZoneMixingFields::MaximumReceivingTemperatureScheduleName).get()); | ||
| EXPECT_EQ("minimumSourceTempSch", zm.getString(ZoneMixingFields::MinimumSourceTemperatureScheduleName).get()); | ||
| EXPECT_EQ("maximumSourceTempSch", zm.getString(ZoneMixingFields::MaximumSourceTemperatureScheduleName).get()); | ||
| EXPECT_EQ("minimumOutdoorTempSch", zm.getString(ZoneMixingFields::MinimumOutdoorTemperatureScheduleName).get()); | ||
| EXPECT_EQ("maximumOutdoorTempSch", zm.getString(ZoneMixingFields::MaximumOutdoorTemperatureScheduleName).get()); | ||
| continue; | ||
| } | ||
|
|
||
| EXPECT_EQ(receiving_->iddObject().type(), IddObjectType::Space); | ||
| EXPECT_EQ(source_->iddObject().type(), IddObjectType::Space); | ||
| EXPECT_EQ(alwaysOnContinuousSchedule.nameString(), sch_->nameString()); | ||
|
|
||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumReceivingTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumReceivingTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumSourceTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumSourceTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumOutdoorTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumOutdoorTemperatureScheduleName)); | ||
| if (name == zmAbs.nameString()) { | ||
| EXPECT_EQ(space3.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Zone", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_EQ(m3sAbs, zm.getDouble(ZoneMixingFields::DesignFlowRate).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(space2.nameString(), source_->nameString()); | ||
| EXPECT_EQ(0.5, zm.getDouble(ZoneMixingFields::DeltaTemperature).get()); | ||
| } else if (name == zmFloor.nameString()) { | ||
| EXPECT_EQ(space2.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Area", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DesignFlowRate)); | ||
| EXPECT_EQ(0.001, zm.getDouble(ZoneMixingFields::FlowRateperFloorArea).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(space4.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| } else if (name == zmPerson.nameString()) { | ||
| EXPECT_EQ(space4.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Person", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DesignFlowRate)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_EQ(0.03, zm.getDouble(ZoneMixingFields::FlowRateperPerson).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(space1.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| } else if (name == zmACH.nameString()) { | ||
| EXPECT_EQ(space1.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("AirChanges/Hour", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DesignFlowRate)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_EQ(0.5, zm.getDouble(ZoneMixingFields::AirChangesperHour).get()); | ||
| EXPECT_EQ(space3.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| { | ||
| ft.setExcludeSpaceTranslation(true); | ||
|
|
||
| Workspace w = ft.translateModel(m); | ||
|
|
||
| auto zones = w.getObjectsByType(IddObjectType::Zone); | ||
| ASSERT_EQ(2, zones.size()); | ||
| auto zone = zones[0]; | ||
| EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Space).size()); | ||
|
|
||
| auto zms = w.getObjectsByType(IddObjectType::ZoneMixing); | ||
| ASSERT_EQ(5, zms.size()); | ||
|
|
||
| for (const auto& zm : zms) { | ||
| auto name = zm.nameString(); | ||
| auto receiving_ = zm.getTarget(ZoneMixingFields::ZoneorSpaceName); | ||
| ASSERT_TRUE(receiving_); | ||
| auto source_ = zm.getTarget(ZoneMixingFields::SourceZoneorSpaceName); | ||
| ASSERT_TRUE(source_); | ||
| auto sch_ = zm.getTarget(ZoneMixingFields::ScheduleName); | ||
| ASSERT_TRUE(sch_); | ||
|
|
||
| EXPECT_EQ(receiving_->iddObject().type(), IddObjectType::Zone); | ||
| EXPECT_EQ(source_->iddObject().type(), IddObjectType::Zone); | ||
|
|
||
| if (name == zmZone.nameString()) { | ||
| EXPECT_EQ(z1.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ(mixingSch.nameString(), sch_->nameString()); | ||
| EXPECT_EQ("Flow/Area", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DesignFlowRate)); | ||
| EXPECT_EQ(0.008, zm.getDouble(ZoneMixingFields::FlowRateperFloorArea).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(z2.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| EXPECT_EQ("deltaTempSch", zm.getString(ZoneMixingFields::DeltaTemperatureScheduleName).get()); | ||
| EXPECT_EQ("minimumReceivingTempSch", zm.getString(ZoneMixingFields::MinimumReceivingTemperatureScheduleName).get()); | ||
| EXPECT_EQ("maximumReceivingTempSch", zm.getString(ZoneMixingFields::MaximumReceivingTemperatureScheduleName).get()); | ||
| EXPECT_EQ("minimumSourceTempSch", zm.getString(ZoneMixingFields::MinimumSourceTemperatureScheduleName).get()); | ||
| EXPECT_EQ("maximumSourceTempSch", zm.getString(ZoneMixingFields::MaximumSourceTemperatureScheduleName).get()); | ||
| EXPECT_EQ("minimumOutdoorTempSch", zm.getString(ZoneMixingFields::MinimumOutdoorTemperatureScheduleName).get()); | ||
| EXPECT_EQ("maximumOutdoorTempSch", zm.getString(ZoneMixingFields::MaximumOutdoorTemperatureScheduleName).get()); | ||
| continue; | ||
| } | ||
|
|
||
| EXPECT_EQ(alwaysOnContinuousSchedule.nameString(), sch_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumReceivingTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumReceivingTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumSourceTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumSourceTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumOutdoorTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumOutdoorTemperatureScheduleName)); | ||
| if (name == zmAbs.nameString()) { | ||
| EXPECT_EQ(z2.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Zone", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_EQ(m3sAbs, zm.getDouble(ZoneMixingFields::DesignFlowRate).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(z1.nameString(), source_->nameString()); | ||
| EXPECT_EQ(0.5, zm.getDouble(ZoneMixingFields::DeltaTemperature).get()); | ||
| } else if (name == zmFloor.nameString()) { | ||
| EXPECT_EQ(z1.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Zone", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_EQ(equivm3s_Floor, zm.getDouble(ZoneMixingFields::DesignFlowRate).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(z2.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| } else if (name == zmPerson.nameString()) { | ||
| EXPECT_EQ(z2.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Zone", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_EQ(equivm3s_Person, zm.getDouble(ZoneMixingFields::DesignFlowRate).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(z1.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| } else if (name == zmACH.nameString()) { | ||
| EXPECT_EQ(z1.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Zone", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_EQ(equivm3s_ACH, zm.getDouble(ZoneMixingFields::DesignFlowRate).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(z2.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
FT Tests when different zone.
- Test all combinations of Spaces one
- Test one zone one
- Do that when excluding spaces and when including them
| TEST_F(EnergyPlusFixture, ForwardTranslatorZoneMixing_SameZone) { | ||
|
|
||
| Model m; | ||
|
|
||
| constexpr double width = 10.0; | ||
| constexpr double height = 3.6; // It's convenient for ACH, since 3600 s/hr | ||
|
|
||
| // y (=North) | ||
| // ▲ | ||
| // │ building height = 3m | ||
| // 10├────────┼────────┼ | ||
| // │ │ │ | ||
| // │ Zone 1 │ Zone 1 │ | ||
| // │ Space 1│ Space 2│ | ||
| // │ │ │ │ │ | ||
| // └────────┴────────┴────────┴────────┴──► x | ||
| // 0 10 20 30 40 | ||
|
|
||
| // Counterclockwise points | ||
| std::vector<Point3d> floorPointsSpace1{{0.0, 0.0, 0.0}, {0.0, width, 0.0}, {width, width, 0.0}, {width, 0.0, 0.0}}; | ||
|
|
||
| auto space1 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| auto space2 = Space::fromFloorPrint(floorPointsSpace1, height, m).get(); | ||
| space2.setXOrigin(width); | ||
|
|
||
| ThermalZone z1(m); | ||
| space1.setThermalZone(z1); | ||
| space2.setThermalZone(z1); | ||
|
|
||
| ZoneMixing zmAbs(space1); | ||
| zmAbs.setName("zmAbs"); | ||
| EXPECT_TRUE(zmAbs.setSourceSpace(space2)); | ||
| constexpr double m3sAbs = 0.6; | ||
| EXPECT_TRUE(zmAbs.setDesignFlowRate(m3sAbs)); | ||
|
|
||
| // | Zone Mixing | Receiving | Source | Flow/Space | Per Floor Area | Per Person | ACH ) || Calculated Total | | ||
| // | Name | Entity | Entity | (m3/s) | (m3/s-m2) | (m3/s.p) | (1/hr) || For 1 Space (m3/s) | | ||
| // |------------------|-----------|---------|------------|----------------|------------|----------||--------------------| | ||
| // | zmAbs | space1 | space2 | 0.6 | | | || 0.6 | | ||
|
|
||
| auto alwaysOnContinuousSchedule = m.alwaysOnContinuousSchedule(); | ||
|
|
||
| ForwardTranslator ft; | ||
| { | ||
| ft.setExcludeSpaceTranslation(false); | ||
|
|
||
| Workspace w = ft.translateModel(m); | ||
|
|
||
| auto zones = w.getObjectsByType(IddObjectType::Zone); | ||
| ASSERT_EQ(1, zones.size()); | ||
| auto zone = zones[0]; | ||
| EXPECT_EQ(2, w.getObjectsByType(IddObjectType::Space).size()); | ||
|
|
||
| auto zms = w.getObjectsByType(IddObjectType::ZoneMixing); | ||
| ASSERT_EQ(1, zms.size()); | ||
| auto& zm = zms.front(); | ||
|
|
||
| auto name = zm.nameString(); | ||
| auto receiving_ = zm.getTarget(ZoneMixingFields::ZoneorSpaceName); | ||
| ASSERT_TRUE(receiving_); | ||
| auto source_ = zm.getTarget(ZoneMixingFields::SourceZoneorSpaceName); | ||
| ASSERT_TRUE(source_); | ||
| auto sch_ = zm.getTarget(ZoneMixingFields::ScheduleName); | ||
| ASSERT_TRUE(sch_); | ||
|
|
||
| EXPECT_EQ(receiving_->iddObject().type(), IddObjectType::Space); | ||
| EXPECT_EQ(source_->iddObject().type(), IddObjectType::Space); | ||
|
|
||
| EXPECT_EQ(name, zmAbs.nameString()); | ||
| EXPECT_EQ(alwaysOnContinuousSchedule.nameString(), sch_->nameString()); | ||
| EXPECT_EQ(space1.nameString(), receiving_->nameString()); | ||
| EXPECT_EQ("Flow/Zone", zm.getString(ZoneMixingFields::DesignFlowRateCalculationMethod).get()); | ||
| EXPECT_EQ(m3sAbs, zm.getDouble(ZoneMixingFields::DesignFlowRate).get()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperFloorArea)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::FlowRateperPerson)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::AirChangesperHour)); | ||
| EXPECT_EQ(space2.nameString(), source_->nameString()); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperature)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::DeltaTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumReceivingTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumReceivingTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumSourceTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumSourceTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MinimumOutdoorTemperatureScheduleName)); | ||
| EXPECT_TRUE(zm.isEmpty(ZoneMixingFields::MaximumOutdoorTemperatureScheduleName)); | ||
| } | ||
|
|
||
| { | ||
| ft.setExcludeSpaceTranslation(true); | ||
|
|
||
| Workspace w = ft.translateModel(m); | ||
|
|
||
| auto zones = w.getObjectsByType(IddObjectType::Zone); | ||
| ASSERT_EQ(1, zones.size()); | ||
| auto zone = zones[0]; | ||
| EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Space).size()); | ||
|
|
||
| auto zms = w.getObjectsByType(IddObjectType::ZoneMixing); | ||
| ASSERT_EQ(0, zms.size()); | ||
|
|
||
| // This is problematic, we actually try to FT that object 4 TIMES | ||
| // EXPECT_EQ(1, ft.warnings().size()); | ||
| EXPECT_GE(ft.warnings().size(), 1); | ||
| std::string expectedWarning = | ||
| "Object of type 'OS:ZoneMixing' and named 'zmAbs' doesn't have a Source Zone or Space, it will not be translated. As you were using " | ||
| "Space-Level ZoneMixing, and you are not translating to Spaces, it's possible it was pointing to two spaces inside the same zone"; | ||
| auto warnings = ft.warnings(); | ||
| EXPECT_NE(warnings.cend(), | ||
| std::find_if(warnings.cbegin(), warnings.cend(), [&expectedWarning](auto& l) { return l.logMessage() == expectedWarning; })); | ||
| } | ||
| } |
There was a problem hiding this comment.
Make a Space Level ZoneMixing that points to two spaces inside the same zone:
- When trnaslating to spaces: it should work fine
- When excluding spaces: it shouldn't translate it and we should get a nice warning.
Note that I noticed we try to translate it 4 times and opened #4704
|
CI Results for a4fd9e6:
|
I haven't tried a comprehensive set of scenarios but it works for my case. Thanks @jmarrec! |
Pull request overview
ZoneMixingis now allowed at space level #4673Pull Request Author
src/model/test)src/energyplus/Test)src/osversion/VersionTranslator.cpp)Labels:
IDDChangeAPIChangePull Request - Ready for CIso that CI builds your PRReview Checklist
This will not be exhaustively relevant to every PR.