Skip to content

Commit

Permalink
Merge pull request #3985 from NREL/3926_ControllerMV_MultipleLoops
Browse files Browse the repository at this point in the history
Fix 3926 & 3984 - Avoid crash when orphaned ControllerOutdoorAir, and map the DSOA to all ControllerMechanicalVentilation when multiple AirLoopHVACs
  • Loading branch information
tijcolem authored Jun 4, 2020
2 parents 57eeafd + d8c85ea commit a03e286
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 64 deletions.
1 change: 1 addition & 0 deletions src/energyplus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ set(${target_name}_test_src
Test/CoilCoolingDXTwoStageWithHumidityControlMode_GTest.cpp
Test/CoilCoolingDXVariableSpeed_GTest.cpp
Test/Construction_GTest.cpp
Test/ControllerOutdoorAir_GTest.cpp
Test/DaylightingControl_GTest.cpp
Test/DaylightingDeviceShelf_GTest.cpp
Test/DesignSpecificationOutdoorAir_GTest.cpp
Expand Down
7 changes: 5 additions & 2 deletions src/energyplus/ForwardTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3576,7 +3576,10 @@ std::vector<IddObjectType> ForwardTranslator::iddObjectsToTranslateInitializer()

result.push_back(IddObjectType::OS_AirLoopHVAC);
result.push_back(IddObjectType::OS_AirLoopHVAC_ControllerList);
result.push_back(IddObjectType::OS_AirLoopHVAC_OutdoorAirSystem);

// Translated by AirLoopHVAC (and AirLoopHVAC:DedicatedOutdoorAirSystem but not wrapped)
// result.push_back(IddObjectType::OS_AirLoopHVAC_OutdoorAirSystem)

result.push_back(IddObjectType::OS_AirLoopHVAC_UnitaryHeatCool_VAVChangeoverBypass);
result.push_back(IddObjectType::OS_AirLoopHVAC_UnitaryCoolOnly);
result.push_back(IddObjectType::OS_AirLoopHVAC_ZoneMixer);
Expand All @@ -3600,7 +3603,7 @@ std::vector<IddObjectType> ForwardTranslator::iddObjectsToTranslateInitializer()
result.push_back(IddObjectType::OS_Connection);
result.push_back(IddObjectType::OS_Connector_Mixer);
result.push_back(IddObjectType::OS_Connector_Splitter);
result.push_back(IddObjectType::OS_Controller_OutdoorAir);
// result.push_back(IddObjectType::OS_Controller_OutdoorAir); // Will be translated by the AirLoopHVACOutdoorAirSystem
result.push_back(IddObjectType::OS_CoolingTower_SingleSpeed);
result.push_back(IddObjectType::OS_Curve_Bicubic);
result.push_back(IddObjectType::OS_Curve_Biquadratic);
Expand Down
91 changes: 33 additions & 58 deletions src/energyplus/ForwardTranslator/ForwardTranslateSizingZone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,20 @@ boost::optional<IdfObject> ForwardTranslator::translateSizingZone( SizingZone &
boost::optional<std::string> s;
boost::optional<double> value;

IdfObject idfObject(IddObjectType::Sizing_Zone);

m_idfObjects.push_back(idfObject);

// ZoneorZoneListName

model::ThermalZone thermalZone = modelObject.thermalZone();

boost::optional<IdfObject> _thermalZone = translateAndMapModelObject(thermalZone);

boost::optional<std::string> name;

if( _thermalZone )
{
name = _thermalZone->name();
if (!_thermalZone) {
// This shouldn't even happen, but in any case, there's no point translating a Sizing:Zone for no Zone...
return boost::none;
}

if( name )
IdfObject idfObject(IddObjectType::Sizing_Zone);
m_idfObjects.push_back(idfObject);

std::string name = _thermalZone->nameString();
{
idfObject.setString(Sizing_ZoneFields::ZoneorZoneListName,name.get());
idfObject.setString(Sizing_ZoneFields::ZoneorZoneListName, name);
}

// ZoneCoolingDesignSupplyAirTemperatureInputMethod
Expand Down Expand Up @@ -261,11 +255,8 @@ boost::optional<IdfObject> ForwardTranslator::translateSizingZone( SizingZone &
{
IdfObject dSZAD(IddObjectType::DesignSpecification_ZoneAirDistribution);

if( name )
{
dSZADName = name.get() + " Design Spec Zone Air Dist";
dSZAD.setName(dSZADName);
}
dSZADName = name + " Design Spec Zone Air Dist";
dSZAD.setName(dSZADName);

// register the DSZAD
m_idfObjects.push_back(dSZAD);
Expand Down Expand Up @@ -298,50 +289,34 @@ boost::optional<IdfObject> ForwardTranslator::translateSizingZone( SizingZone &
// Add ThermalZone and associated design objects to ControllerMechanicalVentilation.
// This would be done in forwardTranslateControllerMechanicalVentilation except doing it here maintains proper order of the idf file.

boost::optional<model::ControllerMechanicalVentilation> controllerMechanicalVentilation;
boost::optional<IdfObject> _controllerMechanicalVentilation;

if( boost::optional<model::AirLoopHVAC> airLoopHVAC = thermalZone.airLoopHVAC() )
{
if( boost::optional<model::AirLoopHVACOutdoorAirSystem> oaSystem = airLoopHVAC->airLoopHVACOutdoorAirSystem() )
{
// Now that Multiple AirLoopHVACs serving the same zone are possible, need to loop on all
for (const auto& airLoopHVAC : thermalZone.airLoopHVACs()) {
if (boost::optional<model::AirLoopHVACOutdoorAirSystem> oaSystem = airLoopHVAC.airLoopHVACOutdoorAirSystem()) {
model::ControllerOutdoorAir controllerOutdoorAir = oaSystem->getControllerOutdoorAir();
model::ControllerMechanicalVentilation controllerMechanicalVentilation = controllerOutdoorAir.controllerMechanicalVentilation();
if (boost::optional<IdfObject> _controllerMechanicalVentilation = translateAndMapModelObject(controllerMechanicalVentilation)) {
IdfExtensibleGroup eg = _controllerMechanicalVentilation->pushExtensibleGroup();

// Thermal Zone Name
eg.setString(Controller_MechanicalVentilationExtensibleFields::ZoneorZoneListName, name);

// DesignSpecificationOutdoorAir
std::vector<model::Space> spaces = thermalZone.spaces();

if (spaces.size() > 0) {
if (boost::optional<model::DesignSpecificationOutdoorAir> designOASpec = spaces.front().designSpecificationOutdoorAir()) {
if (boost::optional<IdfObject> _designOASpec = translateAndMapModelObject(designOASpec.get()) ) {
eg.setString(Controller_MechanicalVentilationExtensibleFields::DesignSpecificationOutdoorAirObjectName,_designOASpec->name().get());
}
}
}

controllerMechanicalVentilation = controllerOutdoorAir.controllerMechanicalVentilation();
}
}

if( controllerMechanicalVentilation )
{
_controllerMechanicalVentilation = translateAndMapModelObject(controllerMechanicalVentilation.get());
}

if( _controllerMechanicalVentilation && _thermalZone )
{
IdfExtensibleGroup eg = _controllerMechanicalVentilation->pushExtensibleGroup();

// Thermal Zone Name
eg.setString(Controller_MechanicalVentilationExtensibleFields::ZoneorZoneListName,_thermalZone->name().get());

// DesignSpecificationOutdoorAir
std::vector<model::Space> spaces = thermalZone.spaces();

if( spaces.size() > 0 )
{
if( boost::optional<model::DesignSpecificationOutdoorAir> designOASpec = spaces.front().designSpecificationOutdoorAir() )
{
if( boost::optional<IdfObject> _designOASpec = translateAndMapModelObject(designOASpec.get()) )
{
eg.setString(Controller_MechanicalVentilationExtensibleFields::DesignSpecificationOutdoorAirObjectName,_designOASpec->name().get());
// DesignSpecificationZoneAirDistributionObjectName
if (isDSZADTranslated) {
eg.setString(Controller_MechanicalVentilationExtensibleFields::DesignSpecificationZoneAirDistributionObjectName, dSZADName);
}
}
}

// DesignSpecificationZoneAirDistributionObjectName
if( _thermalZone && isDSZADTranslated )
{
eg.setString(Controller_MechanicalVentilationExtensibleFields::DesignSpecificationZoneAirDistributionObjectName, dSZADName);
}
}

if( modelObject.accountforDedicatedOutdoorAirSystem() ) {
Expand Down
70 changes: 66 additions & 4 deletions src/energyplus/Test/AirLoopHVAC_GTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,36 @@
#include "../ForwardTranslator.hpp"

#include "../../model/AirLoopHVAC.hpp"
#include "../../model/AirLoopHVAC_Impl.hpp"

#include "../../model/Model.hpp"
#include "../../model/AvailabilityManagerAssignmentList.hpp"
#include "../../model/AvailabilityManagerAssignmentList_Impl.hpp"
#include "../../model/AvailabilityManagerScheduledOn.hpp"
#include "../../model/Node.hpp"
#include "../../model/Schedule.hpp"
#include "../../model/ScheduleCompact.hpp"
#include "../../model/FanVariableVolume.hpp"
#include "../../model/FanConstantVolume.hpp"
#include "../../model/AirTerminalSingleDuctSeriesPIUReheat.hpp"
#include "../../model/AirTerminalSingleDuctConstantVolumeNoReheat.hpp"
#include "../../model/CoilHeatingElectric.hpp"

#include "../../model/DesignSpecificationOutdoorAir.hpp"
#include "../../model/Space.hpp"
#include "../../model/ThermalZone.hpp"
#include "../../utilities/geometry/Point3d.hpp"

#include "../../model/AirLoopHVAC_Impl.hpp"
#include "../../model/ThermalZone_Impl.hpp"
#include "../../model/AirLoopHVACOutdoorAirSystem.hpp"
#include "../../model/ControllerOutdoorAir.hpp"
#include "../../model/ControllerMechanicalVentilation.hpp"
#include "../../model/DesignDay.hpp"
#include "../../utilities/geometry/Point3d.hpp"

#include <utilities/idd/AirLoopHVAC_FieldEnums.hxx>
#include <utilities/idd/AvailabilityManagerAssignmentList_FieldEnums.hxx>
#include <utilities/idd/Fan_ConstantVolume_FieldEnums.hxx>
#include <utilities/idd/Fan_VariableVolume_FieldEnums.hxx>
#include <utilities/idd/AirTerminal_SingleDuct_SeriesPIU_Reheat_FieldEnums.hxx>
#include <utilities/idd/Controller_MechanicalVentilation_FieldEnums.hxx>

#include <utilities/idd/IddEnums.hxx>
#include "../../utilities/idf/IdfExtensibleGroup.hpp"
Expand Down Expand Up @@ -290,4 +296,60 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_AirLoopHVAC_AvailabilityManagers_Nig

}

TEST_F(EnergyPlusFixture, ForwardTranslator_AirLoopHVAC_MultiLoop_ControllerMV) {
// Test for #3926

Model m;

ThermalZone z(m);
Space s(m);
DesignSpecificationOutdoorAir dsoa(m);
EXPECT_TRUE(dsoa.setOutdoorAirMethod("Sum"));
EXPECT_TRUE(dsoa.setOutdoorAirFlowAirChangesperHour(0.5));
EXPECT_TRUE(s.setThermalZone(z));
EXPECT_TRUE(s.setDesignSpecificationOutdoorAir(dsoa));

// Absolutely need a sizing period for the Sizing:Zone to be translated
DesignDay d(m);

Schedule alwaysOn = m.alwaysOnDiscreteSchedule();

AirLoopHVAC a1(m);
ControllerOutdoorAir controller_oa1(m);
AirLoopHVACOutdoorAirSystem oa_sys1(m, controller_oa1);
Node supplyInletNode1 = a1.supplyInletNode();
EXPECT_TRUE(oa_sys1.addToNode(supplyInletNode1));
AirTerminalSingleDuctConstantVolumeNoReheat atu1(m, alwaysOn);
EXPECT_TRUE(a1.multiAddBranchForZone(z, atu1));

AirLoopHVAC a2(m);
ControllerOutdoorAir controller_oa2(m);
AirLoopHVACOutdoorAirSystem oa_sys2(m, controller_oa2);
Node supplyInletNode2 = a2.supplyInletNode();
EXPECT_TRUE(oa_sys2.addToNode(supplyInletNode2));
AirTerminalSingleDuctConstantVolumeNoReheat atu2(m, alwaysOn);
EXPECT_TRUE(a2.multiAddBranchForZone(z, atu2));

EXPECT_EQ(2u, z.airLoopHVACs().size());

ForwardTranslator ft;
Workspace w = ft.translateModel(m);

WorkspaceObjectVector idf_controller_mvs(w.getObjectsByType(IddObjectType::Controller_MechanicalVentilation));
ASSERT_EQ(2u, idf_controller_mvs.size());

int i = 1;
for (const auto& idf_controller_mv: idf_controller_mvs) {
// This construct is weird but I want to showcase that it works fine for the first ControllerMV but not the second one
// so I don't want to use ASSERT_EQ
if (idf_controller_mv.numExtensibleGroups() == 1u) {
IdfExtensibleGroup w_eg = idf_controller_mv.extensibleGroups()[0];
EXPECT_EQ(z.nameString(), w_eg.getString(Controller_MechanicalVentilationExtensibleFields::ZoneorZoneListName).get());
EXPECT_EQ(dsoa.nameString(), w_eg.getString(Controller_MechanicalVentilationExtensibleFields::DesignSpecificationOutdoorAirObjectName).get());
} else {
EXPECT_EQ(1u, idf_controller_mv.numExtensibleGroups()) << "Failed for " << idf_controller_mv.nameString() << "(i = " << i << ")";
}
++i;
}

}
96 changes: 96 additions & 0 deletions src/energyplus/Test/ControllerOutdoorAir_GTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/***********************************************************************************************************************
* OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission from the respective party.
*
* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works
* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior
* written permission from Alliance for Sustainable Energy, LLC.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED
* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***********************************************************************************************************************/

#include <gtest/gtest.h>
#include "EnergyPlusFixture.hpp"

#include "../ForwardTranslator.hpp"

#include "../../model/ControllerOutdoorAir.hpp"

#include "../../model/Model.hpp"
#include "../../model/AirLoopHVAC.hpp"
#include "../../model/AirLoopHVACOutdoorAirSystem.hpp"
#include "../../model/Node.hpp"
#include "../../model/ControllerMechanicalVentilation.hpp"

#include <utilities/idd/Controller_OutdoorAir_FieldEnums.hxx>
#include <utilities/idd/IddEnums.hxx>
#include "../../utilities/idf/IdfExtensibleGroup.hpp"

#include "../../utilities/core/Logger.hpp"

using namespace openstudio::energyplus;
using namespace openstudio::model;
using namespace openstudio;


TEST_F(EnergyPlusFixture, ForwardTranslator_ControllerOutdoorAir) {
// Test for #3984
Model m;
ForwardTranslator ft;

ControllerOutdoorAir controller_oa(m);

// Not used: not translated
{
ASSERT_NO_THROW(ft.translateModel(m));
Workspace w = ft.translateModel(m);

WorkspaceObjectVector idf_controller_oas(w.getObjectsByType(IddObjectType::Controller_OutdoorAir));
EXPECT_EQ(0u, idf_controller_oas.size());
EXPECT_EQ(0u, w.getObjectsByType(IddObjectType::AirLoopHVAC).size());
EXPECT_EQ(0u, w.getObjectsByType(IddObjectType::AirLoopHVAC_OutdoorAirSystem).size());
}

AirLoopHVAC a(m);
AirLoopHVACOutdoorAirSystem oa_sys(m, controller_oa);

// Not used since the OutdoorAirSystem isn't used itself: not translated
{
Workspace w = ft.translateModel(m);

WorkspaceObjectVector idf_controller_oas(w.getObjectsByType(IddObjectType::Controller_OutdoorAir));
EXPECT_EQ(0u, idf_controller_oas.size());
EXPECT_EQ(1u, w.getObjectsByType(IddObjectType::AirLoopHVAC).size());
EXPECT_EQ(0u, w.getObjectsByType(IddObjectType::AirLoopHVAC_OutdoorAirSystem).size());
}

Node supplyOutlet = a.supplyOutletNode();
EXPECT_TRUE(oa_sys.addToNode(supplyOutlet));
// Not used: should be translated
{
Workspace w = ft.translateModel(m);

WorkspaceObjectVector idf_controller_oas(w.getObjectsByType(IddObjectType::Controller_OutdoorAir));
EXPECT_EQ(1u, idf_controller_oas.size());
EXPECT_EQ(1u, w.getObjectsByType(IddObjectType::AirLoopHVAC).size());
EXPECT_EQ(1u, w.getObjectsByType(IddObjectType::AirLoopHVAC_OutdoorAirSystem).size());
}
}

0 comments on commit a03e286

Please sign in to comment.