-
Notifications
You must be signed in to change notification settings - Fork 396
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Correction of Inconsistent Flow Rates from Swimming Pools #10303
Changes from all commits
1ffa003
5712f10
9eb69e3
be27610
efd8ffd
1834f50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -105,29 +105,23 @@ namespace EnergyPlus::SwimmingPool { | |
// 4. Smith, C., R. Jones, and G. Lof (1993). Energy Requirements and Potential Savings for Heated | ||
// Indoor Swimming Pools. ASHRAE Transactions 99(2), p.864-874. | ||
|
||
void SimSwimmingPool(EnergyPlusData &state, bool FirstHVACIteration) | ||
SwimmingPoolData *SwimmingPoolData::factory(EnergyPlusData &state, std::string const &objectName) | ||
{ | ||
// Process the input data if it hasn't been done already | ||
if (state.dataSwimmingPools->getSwimmingPoolInput) { | ||
GetSwimmingPool(state); | ||
state.dataSwimmingPools->getSwimmingPoolInput = false; | ||
} | ||
|
||
// System wide (for all pools) inits | ||
state.dataHeatBalFanSys->SumConvPool = 0.0; | ||
state.dataHeatBalFanSys->SumLatentPool = 0.0; | ||
|
||
PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0); | ||
Real64 CurLoad = 0.0; | ||
bool RunFlag = true; | ||
|
||
for (auto &thisPool : state.dataSwimmingPools->Pool) { | ||
thisPool.simulate(state, A, FirstHVACIteration, CurLoad, RunFlag); | ||
// Now look for this particular swimming pool in the list | ||
for (auto &pool : state.dataSwimmingPools->Pool) { | ||
if (pool.Name == objectName) { | ||
return &pool; | ||
} | ||
} | ||
|
||
if (state.dataSwimmingPools->NumSwimmingPools > 0) HeatBalanceSurfaceManager::CalcHeatBalanceInsideSurf(state); | ||
|
||
ReportSwimmingPool(state); | ||
// If we didn't find it, fatal | ||
ShowFatalError(state, | ||
format("LocalSwimmingPoolFactory: Error getting inputs or index for swimming pool named: {}", objectName)); // LCOV_EXCL_LINE | ||
// Shut up the compiler | ||
return nullptr; // LCOV_EXCL_LINE | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, looks like a typical plant factory. I like that it returns a SwimmingPoolData* instead of a PlantComponent*. That makes things easier. |
||
} | ||
|
||
void SwimmingPoolData::simulate(EnergyPlusData &state, | ||
|
@@ -136,11 +130,21 @@ void SwimmingPoolData::simulate(EnergyPlusData &state, | |
[[maybe_unused]] Real64 &CurLoad, | ||
[[maybe_unused]] bool RunFlag) | ||
{ | ||
state.dataHeatBalFanSys->SumConvPool(this->ZonePtr) = 0.0; | ||
state.dataHeatBalFanSys->SumLatentPool(this->ZonePtr) = 0.0; | ||
|
||
CurLoad = 0.0; | ||
RunFlag = true; | ||
|
||
this->initialize(state, FirstHVACIteration); | ||
|
||
this->calculate(state); | ||
|
||
this->update(state); | ||
|
||
if (state.dataSwimmingPools->NumSwimmingPools > 0) HeatBalanceSurfaceManager::CalcHeatBalanceInsideSurf(state); | ||
|
||
this->report(state); | ||
} | ||
|
||
void GetSwimmingPool(EnergyPlusData &state) | ||
|
@@ -1011,13 +1015,61 @@ void SwimmingPoolData::update(EnergyPlusData &state) | |
Real64 WaterMassFlow = state.dataLoopNodes->Node(this->WaterInletNode).MassFlowRate; // water mass flow rate | ||
if (WaterMassFlow > 0.0) state.dataLoopNodes->Node(this->WaterOutletNode).Temp = this->PoolWaterTemp; | ||
} | ||
|
||
void SwimmingPoolData::oneTimeInit_new([[maybe_unused]] EnergyPlusData &state) | ||
{ | ||
} | ||
|
||
void SwimmingPoolData::oneTimeInit([[maybe_unused]] EnergyPlusData &state) | ||
{ | ||
} | ||
|
||
void SwimmingPoolData::report(EnergyPlusData &state) | ||
{ | ||
// SUBROUTINE INFORMATION: | ||
// AUTHOR Rick Strand, Ho-Sung Kim | ||
// DATE WRITTEN October 2014 | ||
|
||
// PURPOSE OF THIS SUBROUTINE: | ||
// This subroutine simply produces output for the swimming pool model. | ||
|
||
// SUBROUTINE PARAMETER DEFINITIONS: | ||
static constexpr std::string_view RoutineName("SwimmingPoolData::report"); | ||
Real64 constexpr MinDensity = 1.0; // to avoid a divide by zero | ||
|
||
int SurfNum = this->SurfacePtr; // surface number index | ||
|
||
// First transfer the surface inside temperature data to the current pool water temperature | ||
this->PoolWaterTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum); | ||
|
||
// Next calculate the amount of heating done by the plant loop | ||
Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, "WATER", this->PoolWaterTemp, this->GlycolIndex, | ||
RoutineName); // specific heat of water | ||
this->HeatPower = this->WaterMassFlowRate * Cp * (this->WaterInletTemp - this->PoolWaterTemp); | ||
|
||
// Now the power consumption of miscellaneous equipment | ||
Real64 Density = FluidProperties::GetDensityGlycol(state, "WATER", this->PoolWaterTemp, this->GlycolIndex, | ||
RoutineName); // density of water | ||
if (Density > MinDensity) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. I'm not sure GetDensityGlycol will ever give back zero density, but this check is fine I guess. |
||
this->MiscEquipPower = this->MiscPowerFactor * this->WaterMassFlowRate / Density; | ||
} else { | ||
this->MiscEquipPower = 0.0; | ||
} | ||
|
||
// Also the radiant exchange converted to convection by the pool cover | ||
this->RadConvertToConvectRep = this->RadConvertToConvect * state.dataSurface->Surface(SurfNum).Area; | ||
|
||
// Finally calculate the summed up report variables | ||
Real64 thisTimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec; | ||
this->MiscEquipEnergy = this->MiscEquipPower * thisTimeStepSysSec; | ||
this->HeatEnergy = this->HeatPower * thisTimeStepSysSec; | ||
this->MakeUpWaterMass = this->MakeUpWaterMassFlowRate * thisTimeStepSysSec; | ||
this->EvapEnergyLoss = this->EvapHeatLossRate * thisTimeStepSysSec; | ||
|
||
this->MakeUpWaterVolFlowRate = MakeUpWaterVolFlowFunct(this->MakeUpWaterMassFlowRate, Density); | ||
this->MakeUpWaterVol = MakeUpWaterVolFunct(this->MakeUpWaterMass, Density); | ||
} | ||
|
||
void UpdatePoolSourceValAvg(EnergyPlusData &state, bool &SwimmingPoolOn) // .TRUE. if the swimming pool "runs" this zone time step | ||
{ | ||
// SUBROUTINE INFORMATION: | ||
|
@@ -1090,68 +1142,6 @@ void UpdatePoolSourceValAvg(EnergyPlusData &state, bool &SwimmingPoolOn) // .TRU | |
} | ||
} | ||
|
||
void ReportSwimmingPool(EnergyPlusData &state) | ||
{ | ||
// SUBROUTINE INFORMATION: | ||
// AUTHOR Rick Strand, Ho-Sung Kim | ||
// DATE WRITTEN October 2014 | ||
|
||
// PURPOSE OF THIS SUBROUTINE: | ||
// This subroutine simply produces output for the swimming pool model. | ||
|
||
// SUBROUTINE PARAMETER DEFINITIONS: | ||
static constexpr std::string_view RoutineName("ReportSwimmingPool"); | ||
Real64 constexpr MinDensity = 1.0; // to avoid a divide by zero | ||
|
||
for (int PoolNum = 1; PoolNum <= state.dataSwimmingPools->NumSwimmingPools; ++PoolNum) { | ||
|
||
int SurfNum = state.dataSwimmingPools->Pool(PoolNum).SurfacePtr; // surface number index | ||
|
||
// First transfer the surface inside temperature data to the current pool water temperature | ||
state.dataSwimmingPools->Pool(PoolNum).PoolWaterTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum); | ||
|
||
// Next calculate the amount of heating done by the plant loop | ||
Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, | ||
"WATER", | ||
state.dataSwimmingPools->Pool(PoolNum).PoolWaterTemp, | ||
state.dataSwimmingPools->Pool(PoolNum).GlycolIndex, | ||
RoutineName); // specific heat of water | ||
state.dataSwimmingPools->Pool(PoolNum).HeatPower = | ||
state.dataSwimmingPools->Pool(PoolNum).WaterMassFlowRate * Cp * | ||
(state.dataSwimmingPools->Pool(PoolNum).WaterInletTemp - state.dataSwimmingPools->Pool(PoolNum).PoolWaterTemp); | ||
|
||
// Now the power consumption of miscellaneous equipment | ||
Real64 Density = FluidProperties::GetDensityGlycol(state, | ||
"WATER", | ||
state.dataSwimmingPools->Pool(PoolNum).PoolWaterTemp, | ||
state.dataSwimmingPools->Pool(PoolNum).GlycolIndex, | ||
RoutineName); // density of water | ||
if (Density > MinDensity) { | ||
state.dataSwimmingPools->Pool(PoolNum).MiscEquipPower = | ||
state.dataSwimmingPools->Pool(PoolNum).MiscPowerFactor * state.dataSwimmingPools->Pool(PoolNum).WaterMassFlowRate / Density; | ||
} else { | ||
state.dataSwimmingPools->Pool(PoolNum).MiscEquipPower = 0.0; | ||
} | ||
|
||
// Also the radiant exchange converted to convection by the pool cover | ||
state.dataSwimmingPools->Pool(PoolNum).RadConvertToConvectRep = | ||
state.dataSwimmingPools->Pool(PoolNum).RadConvertToConvect * state.dataSurface->Surface(SurfNum).Area; | ||
|
||
// Finally calculate the summed up report variables | ||
state.dataSwimmingPools->Pool(PoolNum).MiscEquipEnergy = | ||
state.dataSwimmingPools->Pool(PoolNum).MiscEquipPower * state.dataHVACGlobal->TimeStepSysSec; | ||
state.dataSwimmingPools->Pool(PoolNum).HeatEnergy = state.dataSwimmingPools->Pool(PoolNum).HeatPower * state.dataHVACGlobal->TimeStepSysSec; | ||
state.dataSwimmingPools->Pool(PoolNum).MakeUpWaterMass = | ||
state.dataSwimmingPools->Pool(PoolNum).MakeUpWaterMassFlowRate * state.dataHVACGlobal->TimeStepSysSec; | ||
state.dataSwimmingPools->Pool(PoolNum).EvapEnergyLoss = | ||
state.dataSwimmingPools->Pool(PoolNum).EvapHeatLossRate * state.dataHVACGlobal->TimeStepSysSec; | ||
|
||
state.dataSwimmingPools->Pool(PoolNum).MakeUpWaterVolFlowRate = | ||
MakeUpWaterVolFlowFunct(state.dataSwimmingPools->Pool(PoolNum).MakeUpWaterMassFlowRate, Density); | ||
state.dataSwimmingPools->Pool(PoolNum).MakeUpWaterVol = MakeUpWaterVolFunct(state.dataSwimmingPools->Pool(PoolNum).MakeUpWaterMass, Density); | ||
} | ||
} | ||
|
||
Real64 MakeUpWaterVolFlowFunct(Real64 MakeUpWaterMassFlowRate, Real64 Density) | ||
{ | ||
return MakeUpWaterMassFlowRate / Density; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,6 +56,7 @@ | |
#include <EnergyPlus/Data/EnergyPlusData.hh> | ||
#include <EnergyPlus/DataEnvironment.hh> | ||
#include <EnergyPlus/DataHeatBalFanSys.hh> | ||
#include <EnergyPlus/DataHeatBalSurface.hh> | ||
#include <EnergyPlus/DataSizing.hh> | ||
#include <EnergyPlus/DataSurfaces.hh> | ||
#include <EnergyPlus/Plant/DataPlant.hh> | ||
|
@@ -460,3 +461,83 @@ TEST_F(EnergyPlusFixture, SwimmingPool_MultiplePoolUpdatePoolSourceValAvgTest) | |
EXPECT_NEAR(HBFanData->PoolHeatTransCoefs(1), Pool1Data.HeatTransCoefsAvg, closeEnough); | ||
EXPECT_NEAR(HBFanData->PoolHeatTransCoefs(2), Pool2Data.HeatTransCoefsAvg, closeEnough); | ||
} | ||
|
||
TEST_F(EnergyPlusFixture, SwimmingPool_factoryTest) | ||
{ | ||
// Test of new factory routine as part of the move to make swimming pools a plant loop object called | ||
// from the plant loop and not zone equipment. | ||
state->dataSwimmingPools->getSwimmingPoolInput = false; | ||
state->dataSwimmingPools->NumSwimmingPools = 4; | ||
state->dataSwimmingPools->Pool.allocate(state->dataSwimmingPools->NumSwimmingPools); | ||
|
||
auto &poolData = state->dataSwimmingPools->Pool; | ||
poolData(1).Name = "Schwimmbad Nummer Eins"; | ||
poolData(2).Name = "Schwimmbad Nummer Zwei"; | ||
poolData(3).Name = "Schwimmbad Nummer Drei"; | ||
poolData(4).Name = "Schwimmbad Nummer Vier"; | ||
|
||
// Test 1: First Pool | ||
SwimmingPoolData *factoryResult = SwimmingPoolData::factory(*state, poolData(1).Name); | ||
EXPECT_NE(factoryResult, nullptr); | ||
|
||
// Test 2: First Pool | ||
factoryResult = SwimmingPoolData::factory(*state, poolData(2).Name); | ||
EXPECT_NE(factoryResult, nullptr); | ||
|
||
// Test 3: First Pool | ||
factoryResult = SwimmingPoolData::factory(*state, poolData(3).Name); | ||
EXPECT_NE(factoryResult, nullptr); | ||
|
||
// Test 4: First Pool | ||
factoryResult = SwimmingPoolData::factory(*state, poolData(4).Name); | ||
EXPECT_NE(factoryResult, nullptr); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Auf allen steht „First Pool“, aber ansonsten sieht der test gut aus. |
||
|
||
TEST_F(EnergyPlusFixture, SwimmingPool_reportTest) | ||
{ | ||
// Test of modified report routine as part of the move to make swimming pools a plant loop object called | ||
// from the plant loop and not zone equipment. Report routine gets called for each pool separately rather | ||
// than reporting for everything all at once. | ||
Real64 constexpr closeEnough = 0.000001; | ||
SwimmingPoolData myPool; | ||
|
||
// Test Data | ||
myPool.Name = "This Pool"; | ||
myPool.SurfacePtr = 1; | ||
state->dataHeatBalSurf->SurfInsideTempHist.allocate(1); | ||
state->dataHeatBalSurf->SurfInsideTempHist(1).dimension(1, 0); | ||
state->dataHeatBalSurf->SurfInsideTempHist(1)(1) = 10.0; | ||
myPool.WaterMassFlowRate = 0.001; | ||
myPool.WaterInletTemp = 40.0; | ||
myPool.MiscPowerFactor = 1000.0; | ||
myPool.RadConvertToConvect = 0.5; | ||
state->dataSurface->Surface.allocate(1); | ||
state->dataSurface->Surface(1).Area = 5.0; | ||
state->dataHVACGlobal->TimeStepSysSec = 60.0; | ||
myPool.MiscEquipPower = 0.12; | ||
myPool.MakeUpWaterMassFlowRate = 0.1; | ||
myPool.EvapHeatLossRate = 0.016; | ||
|
||
// Test of .report routine | ||
myPool.report(*state); | ||
Real64 expectedHeatPower = 125.73; | ||
Real64 expectedMiscEquipPower = 0.0010003; | ||
Real64 expectedRadConvertToConvectRep = 2.5; | ||
Real64 expectedMiscEquipEnergy = 0.060018; | ||
Real64 expectedHeatEnergy = 7543.8; | ||
Real64 expectedMakeUpWaterMass = 6.0; | ||
Real64 expectedEvapEnergyLoss = 0.96; | ||
Real64 expectedMakeUpWaterVolFlowRate = 0.0001003; | ||
Real64 expectedMakeUpWaterVol = 0.0060018; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these based on a specific controlled calculation? |
||
|
||
EXPECT_NEAR(state->dataHeatBalSurf->SurfInsideTempHist(1)(1), myPool.PoolWaterTemp, closeEnough); | ||
EXPECT_NEAR(expectedHeatPower, myPool.HeatPower, closeEnough); | ||
EXPECT_NEAR(expectedMiscEquipPower, myPool.MiscEquipPower, closeEnough); | ||
EXPECT_NEAR(expectedRadConvertToConvectRep, myPool.RadConvertToConvectRep, closeEnough); | ||
EXPECT_NEAR(expectedMiscEquipEnergy, myPool.MiscEquipEnergy, closeEnough); | ||
EXPECT_NEAR(expectedHeatEnergy, myPool.HeatEnergy, closeEnough); | ||
EXPECT_NEAR(expectedMakeUpWaterMass, myPool.MakeUpWaterMass, closeEnough); | ||
EXPECT_NEAR(expectedEvapEnergyLoss, myPool.EvapEnergyLoss, closeEnough); | ||
EXPECT_NEAR(expectedMakeUpWaterVolFlowRate, myPool.MakeUpWaterVolFlowRate, closeEnough); | ||
EXPECT_NEAR(expectedMakeUpWaterVol, myPool.MakeUpWaterVol, closeEnough); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks exactly right. I think there should be one more spot in PlantManager where we need to switch on equipment type. Down where we assign the flow request priority. If that's the only change needed here, I can add that while I'm doing final testing.