-
Notifications
You must be signed in to change notification settings - Fork 448
Air leakage in parallel fan-powered terminal units #11038
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
base: develop
Are you sure you want to change the base?
Conversation
|
|
@lymereJ @Myoldmopar it has been 31 days since this pull request was last updated. |
@lymereJ @Myoldmopar it has been 34 days since this pull request was last updated. |
@lymereJ @Myoldmopar it has been 30 days since this pull request was last updated. |
@lymereJ @Myoldmopar it has been 28 days since this pull request was last updated. |
@lymereJ Is this targeted for 25.2? |
@mjwitte - Ideally, yes, but it's not quite ready. I'm hoping to be able to get back to it sometime next week. |
d7d35c6
to
4254b29
Compare
|
|
|
|
|
|
|
|
|
|
|
|
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.
Code walk-through:
|
||
// Get damper leakage inputs | ||
if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") { | ||
std::string damperLeakageFractionCurveName = | ||
ip->getAlphaFieldValue(fields, objectSchemaProps, "backdraft_damper_leakage_fraction_curve_name"); | ||
thisPIU.leakFracCurve = Curve::GetCurveIndex(state, damperLeakageFractionCurveName); | ||
|
||
if (!damperLeakageFractionCurveName.empty() && thisPIU.leakFracCurve == 0) { | ||
ShowSevereError(state, | ||
format("The air leakage fraction curve for the {} {} is missing. No air leakage will be modeled.", | ||
cCurrentModuleObject, | ||
thisPIU.Name)); | ||
} else if (thisPIU.leakFracCurve > 0) { | ||
// Find the secondary zone or plenum index | ||
// The secondary air inlet node should either be a zone exhaust air node... | ||
for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) { | ||
for (int exhaustNum = 1; exhaustNum <= state.dataZoneEquip->ZoneEquipConfig(zoneNum).NumExhaustNodes; ++exhaustNum) { | ||
if (thisPIU.SecAirInNode == state.dataZoneEquip->ZoneEquipConfig(zoneNum).ExhaustNode(exhaustNum)) { | ||
state.dataHeatBal->Zone(zoneNum).isSourceForParallelPIU = true; | ||
break; | ||
} | ||
} | ||
} | ||
// ... or an induced air node of a return plenum | ||
for (int zoneNum = 1; zoneNum <= state.dataZonePlenum->NumZoneSupplyPlenums; ++zoneNum) { | ||
for (int nodeNum = 1; nodeNum <= state.dataZonePlenum->ZoneRetPlenCond(zoneNum).NumInducedNodes; ++nodeNum) { | ||
if (thisPIU.SecAirInNode == state.dataZonePlenum->ZoneRetPlenCond(zoneNum).InducedNode(nodeNum)) { | ||
state.dataHeatBal->Zone(zoneNum).isSourceForParallelPIU = true; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} |
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.
New input validation for the leakage curve.
if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") { | ||
SetupOutputVariable(state, | ||
"Zone Air Terminal Backdraft Damper Leakage Mass Flow Rate", | ||
Constant::Units::kg_s, | ||
thisPIU.leakFlow, | ||
OutputProcessor::TimeStepType::System, | ||
OutputProcessor::StoreType::Average, | ||
state.dataPowerInductionUnits->PIU(PIURpt).Name); | ||
} |
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.
New input is only valid for parallel PIUs.
// PIU leakage calculations | ||
if (thisPIU.leakFracCurve > 0 && state.dataHVACGlobal->TurnFansOn == false) { | ||
// Determine leakage fraction as a function of the primary airflow fraction | ||
const Real64 airflowFrac = thisPIU.PriAirMassFlow / thisPIU.MaxPriAirMassFlow; | ||
thisPIU.leakFrac = min(1.0, Curve::CurveValue(state, thisPIU.leakFracCurve, airflowFrac)); | ||
// Determine leakage rate that won't make it to the zone served by the terminal | ||
thisPIU.leakFlow = thisPIU.leakFrac * thisPIU.PriAirMassFlow; | ||
} |
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.
We're in dead-band operation here so we only determine the leakage based on the minimum primary air flow rate.
// Determine leakage rate that won't make it to the zone served by the terminal | ||
thisPIU.leakFlow = thisPIU.leakFrac * thisPIU.PriAirMassFlow; | ||
// Increase the primary flow rate to meet the cooling load with leakage | ||
thisPIU.PriAirMassFlow *= 1 / (1 - thisPIU.leakFrac); //(1 + thisPIU.leakFrac); |
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.
Same as above except that we're increasing the primary air flow rate to meet the load and when taking leaks into account.
} | ||
// Make sure that the primary airflow doesn't exceed the maximum when leakage is modeled | ||
// When the primary airflow is limited to the maximum, the load won't likely be met | ||
thisPIU.PriAirMassFlow = min(thisPIU.PriAirMassFlow, PriAirMassFlowMax); |
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.
Primary flow rate cannot be larger than the maximum value.
if (state.dataHeatBal->Zone(zoneNum).isSourceForParallelPIU) { | ||
for (int iExhNode = 1; iExhNode <= state.dataZoneEquip->ZoneEquipConfig(zoneNum).NumExhaustNodes; ++iExhNode) { | ||
int piuNum = | ||
PoweredInductionUnits::getParallelPIUNumFromSecNodeNum(state, state.dataZoneEquip->ZoneEquipConfig(zoneNum).ExhaustNode(iExhNode)); | ||
if (piuNum > 0) { | ||
auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum); | ||
MoistureMassFlowRate += (thisPIU.leakFlow * state.dataLoopNodes->Node(thisPIU.PriAirInNode).HumRat) / ZoneMult; | ||
ZoneMassFlowRate += thisPIU.leakFlow / ZoneMult; | ||
} | ||
} | ||
} | ||
|
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.
Impact of leaks on the zone conditions. Here, humidity, below temperature and sensible load.
state.dataDefineEquipment->AirDistUnit(ADUNum).MassFlowRateDnStrLk + | ||
state.dataDefineEquipment->AirDistUnit(ADUNum) | ||
.massFlowRateParallelPIULk; // Even though the PIU leakage flow rate doesn't necessarily go through the plenum, the same amount | ||
// ends-up being returned |
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.
Account for PIU leaks separately from SDLM downstream leaks.
if ((airDistUnit.UpStreamLeak || airDistUnit.DownStreamLeak || airDistUnit.parallelPIUTerminalLeakFrac > 0.0) && | ||
MassFlowRateMaxAvail > 0.0) { | ||
airDistUnit.MassFlowRateTU = state.dataLoopNodes->Node(InNodeNum).MassFlowRate; | ||
airDistUnit.MassFlowRateZSup = airDistUnit.MassFlowRateTU * (1.0 - airDistUnit.DownStreamLeakFrac); | ||
airDistUnit.MassFlowRateZSup = | ||
max(airDistUnit.MassFlowRateTU * (1.0 - airDistUnit.DownStreamLeakFrac - airDistUnit.parallelPIUTerminalLeakFrac), 0.0); | ||
airDistUnit.MassFlowRateDnStrLk = airDistUnit.MassFlowRateTU * airDistUnit.DownStreamLeakFrac; | ||
airDistUnit.massFlowRateParallelPIULk = airDistUnit.MassFlowRateTU * airDistUnit.parallelPIUTerminalLeakFrac; | ||
airDistUnit.MassFlowRateSup = airDistUnit.MassFlowRateTU + airDistUnit.MassFlowRateUpStrLk; | ||
state.dataLoopNodes->Node(InNodeNum).MassFlowRate = airDistUnit.MassFlowRateSup; | ||
state.dataLoopNodes->Node(OutNodeNum).MassFlowRate = airDistUnit.MassFlowRateZSup; | ||
state.dataLoopNodes->Node(OutNodeNum).MassFlowRateMaxAvail = | ||
max(0.0, MassFlowRateMaxAvail - airDistUnit.MassFlowRateDnStrLk - airDistUnit.MassFlowRateUpStrLk); | ||
max(0.0, | ||
MassFlowRateMaxAvail - airDistUnit.MassFlowRateDnStrLk - airDistUnit.MassFlowRateUpStrLk - | ||
airDistUnit.massFlowRateParallelPIULk); | ||
state.dataLoopNodes->Node(OutNodeNum).MassFlowRateMinAvail = | ||
max(0.0, MassFlowRateMinAvail - airDistUnit.MassFlowRateDnStrLk - airDistUnit.MassFlowRateUpStrLk); | ||
max(0.0, | ||
MassFlowRateMinAvail - airDistUnit.MassFlowRateDnStrLk - airDistUnit.MassFlowRateUpStrLk - | ||
airDistUnit.massFlowRateParallelPIULk); |
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.
Leaks are "removed" from terminal outlet here.
// Nineth test - Cooling load TurnFansOn is false, yes primary flow - expecting secondary flow, leakage | ||
state->dataLoopNodes->Node(PriNodeNum).MassFlowRate = state->dataPowerInductionUnits->PIU(SysNum).MaxPriAirMassFlow; | ||
state->dataLoopNodes->Node(PriNodeNum).MassFlowRateMaxAvail = state->dataPowerInductionUnits->PIU(SysNum).MaxPriAirMassFlow; | ||
state->dataLoopNodes->Node(PriNodeNum).MassFlowRateMinAvail = state->dataPowerInductionUnits->PIU(SysNum).MinPriAirMassFlow; | ||
state->dataZoneEnergyDemand->ZoneSysEnergyDemand(1).RemainingOutputRequired = -2000.0; // Cooling load | ||
state->dataZoneEnergyDemand->ZoneSysEnergyDemand(1).RemainingOutputReqToHeatSP = -2000.0; | ||
state->dataZoneEnergyDemand->CurDeadBandOrSetback(1) = false; | ||
state->dataHVACGlobal->TurnFansOn = false; | ||
state->dataPowerInductionUnits->PIU(SysNum).leakFracCurve = Curve::GetCurveIndex(*state, "CONSTANT_LEAKAGE"); | ||
PoweredInductionUnits::CalcParallelPIU(*state, SysNum, ZoneNum, ZoneNodeNum, FirstHVACIteration); | ||
|
||
Real64 SysOutputProvided = 0.0; | ||
Real64 NonAirSysOutput = 0.0; | ||
Real64 LatOutputProvided = 0.0; | ||
int AirDistUnitNum = 1; | ||
state->dataPowerInductionUnits->GetPIUInputFlag = false; | ||
ZoneAirLoopEquipmentManager::SimZoneAirLoopEquipment(*state, | ||
AirDistUnitNum, | ||
SysOutputProvided, | ||
NonAirSysOutput, | ||
LatOutputProvided, | ||
FirstHVACIteration, | ||
state->dataPowerInductionUnits->PIU(SysNum).CtrlZoneNum); | ||
EXPECT_TRUE(state->dataDefineEquipment->AirDistUnit(1).MassFlowRateTU > state->dataDefineEquipment->AirDistUnit(1).MassFlowRateZSup); | ||
|
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.
Piggy backing on existing test to test leak flow calculations.
|
||
// Check that parallel PIU leakage is accounted for | ||
state->dataHeatBal->Zone(1).isSourceForParallelPIU = true; | ||
state->dataPowerInductionUnits->GetPIUInputFlag = false; | ||
state->dataPowerInductionUnits->NumPIUs = 1; | ||
state->dataPowerInductionUnits->PIU.allocate(1); | ||
state->dataPowerInductionUnits->PIU(1).SecAirInNode = 3; // exhaust node | ||
state->dataPowerInductionUnits->PIU(1).PriAirInNode = 5; // primary air node | ||
state->dataPowerInductionUnits->PIU(1).leakFlow = 0.1; | ||
state->dataLoopNodes->Node(5).HumRat = 0.008; | ||
state->dataLoopNodes->Node(5).Temp = 12.8; | ||
thisZoneHB.calcZoneOrSpaceSums(*state, true, ZoneNum); | ||
EXPECT_NEAR(402.67958, thisZoneHB.SumSysMCp, 0.0001); | ||
EXPECT_NEAR(7328.768356, thisZoneHB.SumSysMCpT, 0.0001); |
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.
Same but for impacts of leaks to the zone conditions.
|
|
Pull request overview
Add new inputs/outputs to model air leakage in parallel fan-powered terminal units.
Pull Request Author
Reviewer