Skip to content

Commit fd2a28f

Browse files
committed
Space IV-SpaceHVACZoneReturnMixer 2
1 parent 57a62ce commit fd2a28f

File tree

4 files changed

+125
-51
lines changed

4 files changed

+125
-51
lines changed

src/EnergyPlus/DataZoneEquipment.cc

Lines changed: 101 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ void GetZoneEquipmentData(EnergyPlusData &state)
587587
continue;
588588
}
589589

590-
processZoneReturnMixerInput(state, CurrentModuleObject, zoneNum, objectSchemaProps, objectFields, thisZretMixer);
590+
processZoneReturnMixerInput(state, CurrentModuleObject, zoneNum, objectSchemaProps, objectFields, zeqRetNum);
591591
}
592592
} // end loop over zone return mixers
593593

@@ -1326,20 +1326,20 @@ void processZoneEquipMixerInput(EnergyPlusData &state,
13261326
static constexpr std::string_view RoutineName("processZoneEquipMixerInput: "); // include trailing blank space
13271327
auto &ip = state.dataInputProcessing->inputProcessor;
13281328
bool objectIsParent = true;
1329-
thisZeqMixer.zoneEquipInletNodeNum = GetOnlySingleNode(state,
1330-
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_equipment_inlet_node_name"),
1331-
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
1332-
thisZeqMixer.spaceEquipType,
1333-
thisZeqMixer.Name,
1334-
DataLoopNode::NodeFluidType::Air,
1335-
DataLoopNode::ConnectionType::Outlet,
1336-
NodeInputManager::CompFluidStream::Primary,
1337-
objectIsParent);
1329+
thisZeqMixer.outletNodeNum = GetOnlySingleNode(state,
1330+
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_equipment_inlet_node_name"),
1331+
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
1332+
thisZeqMixer.spaceEquipType,
1333+
thisZeqMixer.Name,
1334+
DataLoopNode::NodeFluidType::Air,
1335+
DataLoopNode::ConnectionType::Outlet,
1336+
NodeInputManager::CompFluidStream::Primary,
1337+
objectIsParent);
13381338
// Check zone exhaust nodes
13391339
bool found = false;
13401340
auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
13411341
for (int exhNodeNum : thisZoneEquipConfig.ExhaustNode) {
1342-
if (thisZeqMixer.zoneEquipInletNodeNum == exhNodeNum) {
1342+
if (thisZeqMixer.outletNodeNum == exhNodeNum) {
13431343
found = true;
13441344
break;
13451345
}
@@ -1348,7 +1348,7 @@ void processZoneEquipMixerInput(EnergyPlusData &state,
13481348
ShowSevereError(state, format("{}{}={}", RoutineName, zeqMixerModuleObject, thisZeqMixer.Name));
13491349
ShowContinueError(state,
13501350
format("Zone Equipment Inlet Node Name={} is not an exhaust node for ZoneHVAC:EquipmentConnections={}.",
1351-
state.dataLoopNodes->NodeID(thisZeqMixer.zoneEquipInletNodeNum),
1351+
state.dataLoopNodes->NodeID(thisZeqMixer.outletNodeNum),
13521352
thisZoneEquipConfig.ZoneName));
13531353
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound = true;
13541354
}
@@ -1410,35 +1410,45 @@ void processZoneReturnMixerInput(EnergyPlusData &state,
14101410
int const zoneNum,
14111411
InputProcessor::json const objectSchemaProps,
14121412
InputProcessor::json const objectFields,
1413-
DataZoneEquipment::ZoneReturnMixer &thisZretMixer)
1413+
int mixerIndex)
14141414

14151415
{
14161416
static constexpr std::string_view RoutineName("processZoneReturnMixerInput: "); // include trailing blank space
14171417
auto &ip = state.dataInputProcessing->inputProcessor;
14181418
bool objectIsParent = true;
1419-
thisZretMixer.zoneReturnNodeNum = GetOnlySingleNode(state,
1420-
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_return_air_node_name"),
1421-
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
1422-
thisZretMixer.spaceEquipType,
1423-
thisZretMixer.Name,
1424-
DataLoopNode::NodeFluidType::Air,
1425-
DataLoopNode::ConnectionType::Outlet,
1426-
NodeInputManager::CompFluidStream::Primary,
1427-
objectIsParent);
1419+
auto &thisZretMixer = state.dataZoneEquip->zoneReturnMixer[mixerIndex];
1420+
thisZretMixer.outletNodeNum = GetOnlySingleNode(state,
1421+
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_return_air_node_name"),
1422+
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
1423+
thisZretMixer.spaceEquipType,
1424+
thisZretMixer.Name,
1425+
DataLoopNode::NodeFluidType::Air,
1426+
DataLoopNode::ConnectionType::Outlet,
1427+
NodeInputManager::CompFluidStream::Primary,
1428+
objectIsParent);
14281429
// Check zone return nodes
14291430
bool found = false;
14301431
auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
1432+
thisZoneEquipConfig.returnNodeSpaceMixerIndex.allocate(thisZoneEquipConfig.NumReturnNodes);
1433+
for (int &mixIndex : thisZoneEquipConfig.returnNodeSpaceMixerIndex) {
1434+
mixIndex = -1;
1435+
}
1436+
1437+
int nodeCounter = 0;
14311438
for (int retNodeNum : thisZoneEquipConfig.ReturnNode) {
1432-
if (thisZretMixer.zoneReturnNodeNum == retNodeNum) {
1439+
++nodeCounter;
1440+
if (thisZretMixer.outletNodeNum == retNodeNum) {
14331441
found = true;
1442+
// Zone return node is fed by a space return mixer
1443+
thisZoneEquipConfig.returnNodeSpaceMixerIndex(nodeCounter) = mixerIndex;
14341444
break;
14351445
}
14361446
}
14371447
if (!found) {
14381448
ShowSevereError(state, format("{}{}={}", RoutineName, zeqMixerModuleObject, thisZretMixer.Name));
14391449
ShowContinueError(state,
14401450
format("Zone Equipment Return Air Node Name={} is not a return air node for ZoneHVAC:EquipmentConnections={}.",
1441-
state.dataLoopNodes->NodeID(thisZretMixer.zoneReturnNodeNum),
1451+
state.dataLoopNodes->NodeID(thisZretMixer.outletNodeNum),
14421452
thisZoneEquipConfig.ZoneName));
14431453
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound = true;
14441454
}
@@ -1923,17 +1933,17 @@ void ZoneEquipmentSplitterMixer::size(EnergyPlusData &state)
19231933
}
19241934
}
19251935

1926-
void ZoneEquipmentMixer::setOutletConditions(EnergyPlusData &state)
1936+
void ZoneMixer::setOutletConditions(EnergyPlusData &state)
19271937
{
1928-
if (this->zoneEquipInletNodeNum == 0) return;
1938+
if (this->outletNodeNum == 0) return;
19291939

19301940
Real64 sumEnthalpy = 0.0;
19311941
Real64 sumHumRat = 0.0;
19321942
Real64 sumCO2 = 0.0;
19331943
Real64 sumGenContam = 0.0;
19341944
Real64 sumPressure = 0.0;
19351945
Real64 sumFractions = 0.0;
1936-
auto &equipInletNode = state.dataLoopNodes->Node(this->zoneEquipInletNodeNum);
1946+
auto &outletNode = state.dataLoopNodes->Node(this->outletNodeNum);
19371947
for (auto &mixerSpace : this->spaces) {
19381948
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
19391949
sumEnthalpy += spaceOutletNode.Enthalpy * mixerSpace.fraction;
@@ -1947,25 +1957,47 @@ void ZoneEquipmentMixer::setOutletConditions(EnergyPlusData &state)
19471957
sumPressure += spaceOutletNode.Press * mixerSpace.fraction;
19481958
sumFractions += mixerSpace.fraction;
19491959
}
1950-
equipInletNode.Enthalpy = sumEnthalpy / sumFractions;
1951-
equipInletNode.HumRat = sumHumRat / sumFractions;
1952-
if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1953-
equipInletNode.CO2 = sumCO2 / sumFractions;
1954-
}
1955-
if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1956-
equipInletNode.GenContam = sumGenContam / sumFractions;
1957-
}
1958-
equipInletNode.Press = sumPressure / sumFractions;
19591960

1960-
// Use Enthalpy and humidity ratio to get outlet temperature from psych chart
1961-
equipInletNode.Temp = Psychrometrics::PsyTdbFnHW(equipInletNode.Enthalpy, equipInletNode.HumRat);
1961+
// For SpaceHVAC:ZoneReturnMixer, the fractions are dynamic and could be zero if there is no flow
1962+
if (sumFractions > 0) {
1963+
outletNode.Enthalpy = sumEnthalpy / sumFractions;
1964+
outletNode.HumRat = sumHumRat / sumFractions;
1965+
if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1966+
outletNode.CO2 = sumCO2 / sumFractions;
1967+
}
1968+
if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1969+
outletNode.GenContam = sumGenContam / sumFractions;
1970+
}
1971+
outletNode.Press = sumPressure / sumFractions;
1972+
1973+
// Use Enthalpy and humidity ratio to get outlet temperature from psych chart
1974+
outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
1975+
}
19621976
}
19631977

1978+
void ZoneReturnMixer::setInletConditions(EnergyPlusData &state)
1979+
{
1980+
for (auto &mixerSpace : this->spaces) {
1981+
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
1982+
int spaceZoneNodeNum = state.dataZoneEquip->spaceEquipConfig(mixerSpace.spaceIndex).ZoneNode;
1983+
auto &spaceNode = state.dataLoopNodes->Node(spaceZoneNodeNum);
1984+
spaceOutletNode.Temp = spaceNode.Temp;
1985+
spaceOutletNode.HumRat = spaceNode.HumRat;
1986+
spaceOutletNode.Enthalpy = spaceNode.Enthalpy;
1987+
spaceOutletNode.Press = spaceNode.Press;
1988+
if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1989+
spaceOutletNode.CO2 = spaceNode.CO2;
1990+
}
1991+
if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1992+
spaceOutletNode.GenContam = spaceNode.GenContam;
1993+
}
1994+
}
1995+
}
19641996
void ZoneEquipmentMixer::setInletFlows(EnergyPlusData &state)
19651997
{
1966-
if (this->zoneEquipInletNodeNum == 0) return;
1998+
if (this->outletNodeNum == 0) return;
19671999

1968-
auto &equipInletNode = state.dataLoopNodes->Node(this->zoneEquipInletNodeNum);
2000+
auto &equipInletNode = state.dataLoopNodes->Node(this->outletNodeNum);
19692001
for (auto &mixerSpace : this->spaces) {
19702002
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
19712003
spaceOutletNode.MassFlowRate = equipInletNode.MassFlowRate * mixerSpace.fraction;
@@ -1974,6 +2006,35 @@ void ZoneEquipmentMixer::setInletFlows(EnergyPlusData &state)
19742006
}
19752007
}
19762008

2009+
void ZoneReturnMixer::setInletFlows(EnergyPlusData &state)
2010+
{
2011+
if (this->outletNodeNum == 0) return;
2012+
auto &outletNode = state.dataLoopNodes->Node(this->outletNodeNum);
2013+
2014+
Real64 sumMixerInletMassFlow = 0;
2015+
for (auto const &mixerSpace : this->spaces) {
2016+
// calc return flows for spaces feeding this mixer
2017+
auto &spaceEquipConfig = state.dataZoneEquip->spaceEquipConfig(mixerSpace.spaceIndex);
2018+
Real64 outletMassFlowRate = outletNode.MassFlowRate; // calcReturnFlows might adjust this parameter value, so make a copy here
2019+
Real64 spaceReturnFlow = 0.0;
2020+
spaceEquipConfig.calcReturnFlows(state, outletMassFlowRate, spaceReturnFlow);
2021+
sumMixerInletMassFlow += spaceReturnFlow;
2022+
}
2023+
2024+
for (auto &mixerSpace : this->spaces) {
2025+
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
2026+
// For return mixer, fraction is calculated every time step, not a user input
2027+
if (sumMixerInletMassFlow > 0.0) {
2028+
mixerSpace.fraction = spaceOutletNode.MassFlowRate / sumMixerInletMassFlow;
2029+
} else {
2030+
mixerSpace.fraction = 0.0;
2031+
}
2032+
spaceOutletNode.MassFlowRate = outletNode.MassFlowRate * mixerSpace.fraction;
2033+
spaceOutletNode.MassFlowRateMaxAvail = outletNode.MassFlowRateMaxAvail * mixerSpace.fraction;
2034+
spaceOutletNode.MassFlowRateMinAvail = outletNode.MassFlowRateMinAvail * mixerSpace.fraction;
2035+
}
2036+
}
2037+
19772038
void ZoneEquipmentSplitter::adjustLoads(EnergyPlusData &state, int zoneNum, int equipTypeNum)
19782039
{
19792040
auto &thisZoneEnergyDemand = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum);

src/EnergyPlus/DataZoneEquipment.hh

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,9 @@ namespace DataZoneEquipment {
326326
Array1D_int ReturnNodePlenumNum; // number of the return plenum attached to this return node (zero if none)
327327
Array1D_int ReturnFlowBasisNode; // return air flow basis nodes
328328
Array1D_int ReturnNodeExhaustNodeNum; // Exhaust node number flow to a corrsponding return node due to light heat gain
329-
// Array1D_int SharedExhaustNode; // Exhaust node number shared by return nodes 0 No exhaust; 1 No share; > 1 shared; -1 use the
330-
// exhaust node value
331329
Array1D<LightReturnExhaustConfig>
332330
SharedExhaustNode; // Exhaust node number shared by return nodes 0 No exhaust; 1 No share; > 1 shared; -1 use the exhaust node value
331+
Array1D_int returnNodeSpaceMixerIndex; // index to SpaceHVAC:ZoneReturnMixer that feeds this return node (-1 if there is none)
333332

334333
bool ZonalSystemOnly; // TRUE if served by a zonal system (only)
335334
bool IsControlled; // True when this is a controlled zone.
@@ -482,22 +481,27 @@ namespace DataZoneEquipment {
482481
void adjustLoads(EnergyPlusData &state, int zoneNum, int equipTypeNum);
483482
};
484483

485-
struct ZoneEquipmentMixer : ZoneEquipmentSplitterMixer
484+
struct ZoneMixer : ZoneEquipmentSplitterMixer
486485
{
487-
int zoneEquipInletNodeNum = 0;
486+
int outletNodeNum = 0;
488487

489488
void setOutletConditions(EnergyPlusData &state);
489+
};
490+
491+
struct ZoneEquipmentMixer : ZoneMixer
492+
{
493+
// int zoneEquipInletNodeNum = 0;
490494

491495
void setInletFlows(EnergyPlusData &state);
492496
};
493497

494-
struct ZoneReturnMixer : ZoneEquipmentSplitterMixer
498+
struct ZoneReturnMixer : ZoneMixer
495499
{
496-
int zoneReturnNodeNum = 0;
500+
// int zoneReturnNodeNum = 0;
497501

498-
// void setOutletConditions(EnergyPlusData &state);
502+
void setInletConditions(EnergyPlusData &state);
499503

500-
// void setInletFlows(EnergyPlusData &state);
504+
void setInletFlows(EnergyPlusData &state);
501505
};
502506
struct ControlList
503507
{
@@ -585,7 +589,7 @@ namespace DataZoneEquipment {
585589
int const zoneNum,
586590
InputProcessor::json const objectSchemaProps,
587591
InputProcessor::json const objectFields,
588-
DataZoneEquipment::ZoneReturnMixer &thisZretMixer);
592+
int mixerIndex);
589593

590594
bool CheckZoneEquipmentList(EnergyPlusData &state,
591595
std::string_view ComponentType, // Type of component

src/EnergyPlus/ZoneEquipmentManager.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5170,6 +5170,15 @@ void CalcZoneLeavingConditions(EnergyPlusData &state, bool const FirstHVACIterat
51705170
Real64 TempZoneAir; // Zone air temperature [C]
51715171
Real64 SumRetAirLatentGainRate;
51725172

5173+
// If SpaceHVAC is active calculate SpaceHVAC:ReturnMixer inlet and outlet conditions before setting zone return conditions
5174+
if (state.dataHeatBal->doSpaceHeatBalanceSimulation && !state.dataGlobal->DoingSizing) {
5175+
for (auto &thisSpaceHVACMixer : state.dataZoneEquip->zoneReturnMixer) {
5176+
thisSpaceHVACMixer.setInletFlows(state);
5177+
thisSpaceHVACMixer.setInletConditions(state);
5178+
thisSpaceHVACMixer.setOutletConditions(state);
5179+
}
5180+
}
5181+
51735182
for (int ZoneNum = 1; ZoneNum <= state.dataGlobal->NumOfZones; ++ZoneNum) {
51745183
if (!state.dataZoneEquip->ZoneEquipConfig(ZoneNum).IsControlled) continue;
51755184
// A return air system may not exist for certain systems; Therefore when no return node exists

tst/EnergyPlus/unit/ZoneEquipmentManager.unit.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5217,8 +5217,8 @@ TEST_F(EnergyPlusFixture, SpaceHVACMixerTest)
52175217
mixSpace2.spaceNodeNum = 12;
52185218
mixSpace3.spaceNodeNum = 13;
52195219
state->dataLoopNodes->Node.allocate(13);
5220-
thisMixer.zoneEquipInletNodeNum = 1;
5221-
auto &equipInletNode = state->dataLoopNodes->Node(thisMixer.zoneEquipInletNodeNum);
5220+
thisMixer.outletNodeNum = 1;
5221+
auto &equipInletNode = state->dataLoopNodes->Node(thisMixer.outletNodeNum);
52225222
auto &mixSpace1Node = state->dataLoopNodes->Node(mixSpace1.spaceNodeNum);
52235223
auto &mixSpace2Node = state->dataLoopNodes->Node(mixSpace2.spaceNodeNum);
52245224
auto &mixSpace3Node = state->dataLoopNodes->Node(mixSpace3.spaceNodeNum);

0 commit comments

Comments
 (0)