Skip to content

Commit

Permalink
Merge pull request #4949 from NREL/boiler-hot-water
Browse files Browse the repository at this point in the history
BoilerHotWater: new Off Cycle Parasitic Fuel Load field
  • Loading branch information
jmarrec authored Sep 13, 2023
2 parents d0e00ef + 6f98eea commit 0d36b05
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 12 deletions.
19 changes: 13 additions & 6 deletions resources/model/OpenStudio.idd
Original file line number Diff line number Diff line change
Expand Up @@ -13633,7 +13633,7 @@ OS:Boiler:HotWater,
\memo curves are generated by fitting catalog data to polynomial equations.
\memo A constant efficiency boiler may be modeled by leaving the normalized
\memo boiler efficiency curve name input blank.
\min-fields 13
\min-fields 19
A1, \field Handle
\type handle
\required-field
Expand Down Expand Up @@ -13727,22 +13727,29 @@ OS:Boiler:HotWater,
\key LeavingSetpointModulated
\key NotModulated
\default NotModulated
N8, \field Parasitic Electric Load
N8, \field On Cycle Parasitic Electric Load
\type real
\units W
\ip-units W
\ip-units W
\minimum 0.0
\default 0.0
N9, \field Sizing Factor
N9, \field Off Cycle Parasitic Fuel Load
\type real
\units W
\ip-units W
\minimum 0.0
\note parasitic fuel load when the boiler is not operating (i.e., standing pilot)
\required-field
N10,\field Sizing Factor
\note Multiplies the autosized capacity and flow rates
\type real
\minimum> 0.0
\default 1.0
A9 ; \field End-Use Subcategory
A9; \field End-Use Subcategory
\note Any text may be used here to categorize the end-uses in the ABUPS End Uses by Subcategory table.
\type alpha
\retaincase
\default General
\required-field

OS:Boiler:Steam,
\memo This boiler model is an adaptation of the empirical model from the Building
Expand Down
1 change: 1 addition & 0 deletions src/energyplus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ set(${target_name}_test_src
Test/AvailabilityManagerLowTemperatureTurnOff_GTest.cpp
Test/AvailabilityManagerNightCycle_GTest.cpp
Test/AvailabilityManagerHybridVentilation_GTest.cpp
Test/BoilerHotWater_GTest.cpp
Test/Building_GTest.cpp
Test/ChillerElectricASHRAE205_GTest.cpp
Test/CentralHeatPumpSystem_GTest.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ namespace energyplus {
idfObject.setString(Boiler_HotWaterFields::EndUseSubcategory, s.get());
}

// OffCycleParasiticFuelLoad

if ((value = modelObject.offCycleParasiticFuelLoad())) {
idfObject.setDouble(Boiler_HotWaterFields::OffCycleParasiticFuelLoad, value.get());
}

return boost::optional<IdfObject>(idfObject);
}

Expand Down
82 changes: 82 additions & 0 deletions src/energyplus/Test/BoilerHotWater_GTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/***********************************************************************************************************************
* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
* See also https://openstudio.net/license
***********************************************************************************************************************/

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

#include "../ForwardTranslator.hpp"

#include "../../model/Model.hpp"
#include "../../model/BoilerHotWater.hpp"
#include "../../model/Node.hpp"
#include "../../model/PlantLoop.hpp"
#include "../../model/CurveQuadratic.hpp"

#include <utilities/idd/Boiler_HotWater_FieldEnums.hxx>

#include <utilities/idd/IddEnums.hxx>
#include "../../utilities/idf/IdfObject.hpp"
#include "../../utilities/idf/IdfObject_Impl.hpp"

#include "../../utilities/idf/WorkspaceObject.hpp"
#include "../../utilities/idf/WorkspaceObject_Impl.hpp"

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

TEST_F(EnergyPlusFixture, ForwardTranslator_BoilerHotWater) {
Model m;

PlantLoop plant_loop(m);

BoilerHotWater bhw(m);

EXPECT_TRUE(bhw.setFuelType("Propane"));
EXPECT_TRUE(bhw.setNominalCapacity(1.0));
EXPECT_TRUE(bhw.setNominalThermalEfficiency(0.9));
EXPECT_TRUE(bhw.setEfficiencyCurveTemperatureEvaluationVariable("EnteringBoiler"));
CurveQuadratic curve1(m);
EXPECT_TRUE(bhw.setNormalizedBoilerEfficiencyCurve(curve1));
EXPECT_TRUE(bhw.setDesignWaterFlowRate(3.0));
EXPECT_TRUE(bhw.setMinimumPartLoadRatio(4.0));
EXPECT_TRUE(bhw.setMaximumPartLoadRatio(5.0));
EXPECT_TRUE(bhw.setOptimumPartLoadRatio(6.0));
EXPECT_TRUE(bhw.setWaterOutletUpperTemperatureLimit(7.0));
EXPECT_TRUE(bhw.setBoilerFlowMode("LeavingSetpointModulated"));
EXPECT_TRUE(bhw.setParasiticElectricLoad(8.0));
EXPECT_TRUE(bhw.setSizingFactor(9.0));
EXPECT_TRUE(bhw.setEndUseSubcategory("Test"));
EXPECT_TRUE(bhw.setOffCycleParasiticFuelLoad(10.0));

EXPECT_TRUE(plant_loop.addSupplyBranchForComponent(bhw));

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

EXPECT_EQ(1u, w.getObjectsByType(IddObjectType::Boiler_HotWater).size());
EXPECT_EQ(1u, w.getObjectsByType(IddObjectType::Curve_Quadratic).size());

IdfObject idf_bhw = w.getObjectsByType(IddObjectType::Boiler_HotWater)[0];

EXPECT_EQ(bhw.nameString(), idf_bhw.getString(Boiler_HotWaterFields::Name, false).get());
EXPECT_EQ("Propane", idf_bhw.getString(Boiler_HotWaterFields::FuelType, false).get());
EXPECT_EQ(1.0, idf_bhw.getDouble(Boiler_HotWaterFields::NominalCapacity, false).get());
EXPECT_EQ(0.9, idf_bhw.getDouble(Boiler_HotWaterFields::NominalThermalEfficiency, false).get());
EXPECT_EQ("EnteringBoiler", idf_bhw.getString(Boiler_HotWaterFields::EfficiencyCurveTemperatureEvaluationVariable, false).get());
EXPECT_EQ(curve1.nameString(), idf_bhw.getString(Boiler_HotWaterFields::NormalizedBoilerEfficiencyCurveName, false).get());
EXPECT_EQ(3.0, idf_bhw.getDouble(Boiler_HotWaterFields::DesignWaterFlowRate, false).get());
EXPECT_EQ(4.0, idf_bhw.getDouble(Boiler_HotWaterFields::MinimumPartLoadRatio, false).get());
EXPECT_EQ(5.0, idf_bhw.getDouble(Boiler_HotWaterFields::MaximumPartLoadRatio, false).get());
EXPECT_EQ(6.0, idf_bhw.getDouble(Boiler_HotWaterFields::OptimumPartLoadRatio, false).get());
EXPECT_EQ(bhw.inletModelObject().get().nameString(), idf_bhw.getString(Boiler_HotWaterFields::BoilerWaterInletNodeName, false).get());
EXPECT_EQ(bhw.outletModelObject().get().nameString(), idf_bhw.getString(Boiler_HotWaterFields::BoilerWaterOutletNodeName, false).get());
EXPECT_EQ(7.0, idf_bhw.getDouble(Boiler_HotWaterFields::WaterOutletUpperTemperatureLimit, false).get());
EXPECT_EQ("LeavingSetpointModulated", idf_bhw.getString(Boiler_HotWaterFields::BoilerFlowMode, false).get());
EXPECT_EQ(8.0, idf_bhw.getDouble(Boiler_HotWaterFields::OnCycleParasiticElectricLoad, false).get());
EXPECT_EQ(9.0, idf_bhw.getDouble(Boiler_HotWaterFields::SizingFactor, false).get());
EXPECT_EQ("Test", idf_bhw.getString(Boiler_HotWaterFields::EndUseSubcategory, false).get());
EXPECT_EQ(10.0, idf_bhw.getDouble(Boiler_HotWaterFields::OffCycleParasiticFuelLoad, false).get());
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,3 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilCoolingWaterToAirHeatPumpVariabl
->nameString());
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ TEST_F(EnergyPlusFixture, WindowPropertyFrameAndDivider) {
EXPECT_TRUE(subSurface2.setWindowPropertyFrameAndDivider(frameAndDivider));
ASSERT_TRUE(subSurface2.windowPropertyFrameAndDivider());


ForwardTranslator forwardTranslator;
auto workspace = forwardTranslator.translateModel(model);

Expand Down
29 changes: 25 additions & 4 deletions src/model/BoilerHotWater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ namespace model {
}

boost::optional<double> BoilerHotWater_Impl::parasiticElectricLoad() const {
return getDouble(OS_Boiler_HotWaterFields::ParasiticElectricLoad, true);
return getDouble(OS_Boiler_HotWaterFields::OnCycleParasiticElectricLoad, true);
}

double BoilerHotWater_Impl::sizingFactor() const {
Expand All @@ -203,6 +203,12 @@ namespace model {
return isEmpty(OS_Boiler_HotWaterFields::SizingFactor);
}

double BoilerHotWater_Impl::offCycleParasiticFuelLoad() const {
boost::optional<double> value = getDouble(OS_Boiler_HotWaterFields::OffCycleParasiticFuelLoad, true);
OS_ASSERT(value);
return value.get();
}

bool BoilerHotWater_Impl::setFuelType(const std::string& fuelType) {
bool result = setString(OS_Boiler_HotWaterFields::FuelType, fuelType);
return result;
Expand Down Expand Up @@ -344,15 +350,15 @@ namespace model {
bool BoilerHotWater_Impl::setParasiticElectricLoad(boost::optional<double> parasiticElectricLoad) {
bool result = false;
if (parasiticElectricLoad) {
result = setDouble(OS_Boiler_HotWaterFields::ParasiticElectricLoad, parasiticElectricLoad.get());
result = setDouble(OS_Boiler_HotWaterFields::OnCycleParasiticElectricLoad, parasiticElectricLoad.get());
} else {
result = setString(OS_Boiler_HotWaterFields::ParasiticElectricLoad, "");
result = setString(OS_Boiler_HotWaterFields::OnCycleParasiticElectricLoad, "");
}
return result;
}

void BoilerHotWater_Impl::resetParasiticElectricLoad() {
bool result = setString(OS_Boiler_HotWaterFields::ParasiticElectricLoad, "");
bool result = setString(OS_Boiler_HotWaterFields::OnCycleParasiticElectricLoad, "");
OS_ASSERT(result);
}

Expand All @@ -366,6 +372,11 @@ namespace model {
OS_ASSERT(result);
}

bool BoilerHotWater_Impl::setOffCycleParasiticFuelLoad(double offCycleParasiticFuelLoad) {
bool result = setDouble(OS_Boiler_HotWaterFields::OffCycleParasiticFuelLoad, offCycleParasiticFuelLoad);
return result;
}

bool BoilerHotWater_Impl::addToNode(Node& node) {
if (boost::optional<PlantLoop> plant = node.plantLoop()) {
if (plant->supplyComponent(node.handle())) {
Expand Down Expand Up @@ -472,6 +483,8 @@ namespace model {
setSizingFactor(1.0);

setEndUseSubcategory("General");

setOffCycleParasiticFuelLoad(0.0);
}

IddObjectType BoilerHotWater::iddObjectType() {
Expand Down Expand Up @@ -576,6 +589,10 @@ namespace model {
return getImpl<detail::BoilerHotWater_Impl>()->isSizingFactorDefaulted();
}

double BoilerHotWater::offCycleParasiticFuelLoad() const {
return getImpl<detail::BoilerHotWater_Impl>()->offCycleParasiticFuelLoad();
}

bool BoilerHotWater::setFuelType(const std::string& fuelType) {
return getImpl<detail::BoilerHotWater_Impl>()->setFuelType(fuelType);
}
Expand Down Expand Up @@ -688,6 +705,10 @@ namespace model {
return getImpl<detail::BoilerHotWater_Impl>()->setEndUseSubcategory(endUseSubcategory);
}

bool BoilerHotWater::setOffCycleParasiticFuelLoad(double offCycleParasiticFuelLoad) {
return getImpl<detail::BoilerHotWater_Impl>()->setOffCycleParasiticFuelLoad(offCycleParasiticFuelLoad);
}

/// @cond
BoilerHotWater::BoilerHotWater(std::shared_ptr<detail::BoilerHotWater_Impl> impl) : StraightComponent(std::move(impl)) {}
/// @endcond
Expand Down
4 changes: 4 additions & 0 deletions src/model/BoilerHotWater.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ namespace model {

std::string endUseSubcategory() const;

double offCycleParasiticFuelLoad() const;

//@}
/** @name Setters */
//@{
Expand Down Expand Up @@ -157,6 +159,8 @@ namespace model {

bool setEndUseSubcategory(const std::string& endUseSubcategory);

bool setOffCycleParasiticFuelLoad(double offCycleParasiticFuelLoad);

//@}
protected:
/// @cond
Expand Down
4 changes: 4 additions & 0 deletions src/model/BoilerHotWater_Impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ namespace model {

std::string endUseSubcategory() const;

double offCycleParasiticFuelLoad() const;

//@}
/** @name Setters */
//@{
Expand Down Expand Up @@ -168,6 +170,8 @@ namespace model {

bool setEndUseSubcategory(const std::string& endUseSubcategory);

bool setOffCycleParasiticFuelLoad(double offCycleParasiticFuelLoad);

//@}
private:
REGISTER_LOGGER("openstudio.model.BoilerHotWater");
Expand Down
14 changes: 14 additions & 0 deletions src/model/test/BoilerHotWater_GTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ TEST_F(ModelFixture, BoilerHotWater_BoilerHotWater) {
exit(0);
},
::testing::ExitedWithCode(0), "");

Model m;
BoilerHotWater boiler(m);

ASSERT_TRUE(boiler.parasiticElectricLoad());
EXPECT_EQ(0.0, boiler.parasiticElectricLoad().get());
EXPECT_EQ(0.0, boiler.offCycleParasiticFuelLoad());

EXPECT_TRUE(boiler.setParasiticElectricLoad(0.5));
ASSERT_TRUE(boiler.parasiticElectricLoad());
EXPECT_EQ(0.5, boiler.parasiticElectricLoad().get());

EXPECT_TRUE(boiler.setOffCycleParasiticFuelLoad(0.8));
EXPECT_EQ(0.8, boiler.offCycleParasiticFuelLoad());
}

TEST_F(ModelFixture, BoilerHotWater_connections) {
Expand Down
23 changes: 23 additions & 0 deletions src/osversion/VersionTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8287,6 +8287,29 @@ namespace osversion {
m_refactored.push_back(RefactoredObjectData(object, newObject));
ss << newObject;

} else if (iddname == "OS:Boiler:HotWater") {

// 1 Field has been added from 3.6.1 to 3.7.0:
// -------------------------------------------
// * Off Cycle Parasitic Fuel Load * 16
auto iddObject = idd_3_7_0.getObject(iddname);
IdfObject newObject(iddObject.get());

for (size_t i = 0; i < object.numFields(); ++i) {
if ((value = object.getString(i))) {
if (i < 16) {
newObject.setString(i, value.get());
} else {
newObject.setString(i + 1, value.get());
}
}
}

newObject.setDouble(16, 0.0);

m_refactored.push_back(RefactoredObjectData(object, newObject));
ss << newObject;

// No-op
} else {
ss << object;
Expand Down
34 changes: 34 additions & 0 deletions src/osversion/test/3_7_0/test_vt_BoilerHotWater.osm
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

OS:Version,
{da82353f-7f06-44d3-9003-9db5586aec1f}, !- Handle
3.6.1; !- Version Identifier

OS:Boiler:HotWater,
{4b65bc89-fbb7-423b-8b56-bfe9ec5ebd7f}, !- Handle
Boiler Hot Water 1, !- Name
Propane, !- Fuel Type
1, !- Nominal Capacity {W}
0.9, !- Nominal Thermal Efficiency
EnteringBoiler, !- Efficiency Curve Temperature Evaluation Variable
{9f578c74-3ee0-4edc-9521-b8ae5fb65fe7}, !- Normalized Boiler Efficiency Curve Name
3, !- Design Water Flow Rate {m3/s}
4, !- Minimum Part Load Ratio
5, !- Maximum Part Load Ratio
6, !- Optimum Part Load Ratio
, !- Boiler Water Inlet Node Name
, !- Boiler Water Outlet Node Name
7, !- Water Outlet Upper Temperature Limit {C}
LeavingSetpointModulated, !- Boiler Flow Mode
8, !- Parasitic Electric Load {W}
9, !- Sizing Factor
Test; !- End-Use Subcategory

OS:Curve:Quadratic,
{9f578c74-3ee0-4edc-9521-b8ae5fb65fe7}, !- Handle
Curve Quadratic 1, !- Name
0, !- Coefficient1 Constant
0, !- Coefficient2 x
1, !- Coefficient3 x**2
0, !- Minimum Value of x
1; !- Maximum Value of x

24 changes: 24 additions & 0 deletions src/osversion/test/3_7_0/test_vt_BoilerHotWater.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#require '/usr/local/openstudio-3.6.1/Ruby/openstudio'

include OpenStudio::Model

m = Model.new

boiler = BoilerHotWater.new(m)
boiler.setFuelType("Propane")
boiler.setNominalCapacity(1.0)
boiler.setNominalThermalEfficiency(0.9)
boiler.setEfficiencyCurveTemperatureEvaluationVariable("EnteringBoiler")
curve = CurveQuadratic.new(m)
boiler.setNormalizedBoilerEfficiencyCurve(curve)
boiler.setDesignWaterFlowRate(3.0)
boiler.setMinimumPartLoadRatio(4.0)
boiler.setMaximumPartLoadRatio(5.0)
boiler.setOptimumPartLoadRatio(6.0)
boiler.setWaterOutletUpperTemperatureLimit(7.0)
boiler.setBoilerFlowMode("LeavingSetpointModulated")
boiler.setParasiticElectricLoad(8.0)
boiler.setSizingFactor(9.0)
boiler.setEndUseSubcategory("Test")

m.save('test_vt_BoilerHotWater.osm', true)
Loading

0 comments on commit 0d36b05

Please sign in to comment.