Skip to content

Commit

Permalink
Space IV-SpaceHVACZoneReturnMixer 2
Browse files Browse the repository at this point in the history
  • Loading branch information
mjwitte committed Aug 12, 2024
1 parent 57a62ce commit fd2a28f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 51 deletions.
141 changes: 101 additions & 40 deletions src/EnergyPlus/DataZoneEquipment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ void GetZoneEquipmentData(EnergyPlusData &state)
continue;
}

processZoneReturnMixerInput(state, CurrentModuleObject, zoneNum, objectSchemaProps, objectFields, thisZretMixer);
processZoneReturnMixerInput(state, CurrentModuleObject, zoneNum, objectSchemaProps, objectFields, zeqRetNum);
}
} // end loop over zone return mixers

Expand Down Expand Up @@ -1326,20 +1326,20 @@ void processZoneEquipMixerInput(EnergyPlusData &state,
static constexpr std::string_view RoutineName("processZoneEquipMixerInput: "); // include trailing blank space
auto &ip = state.dataInputProcessing->inputProcessor;
bool objectIsParent = true;
thisZeqMixer.zoneEquipInletNodeNum = GetOnlySingleNode(state,
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_equipment_inlet_node_name"),
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
thisZeqMixer.spaceEquipType,
thisZeqMixer.Name,
DataLoopNode::NodeFluidType::Air,
DataLoopNode::ConnectionType::Outlet,
NodeInputManager::CompFluidStream::Primary,
objectIsParent);
thisZeqMixer.outletNodeNum = GetOnlySingleNode(state,
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_equipment_inlet_node_name"),
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
thisZeqMixer.spaceEquipType,
thisZeqMixer.Name,
DataLoopNode::NodeFluidType::Air,
DataLoopNode::ConnectionType::Outlet,
NodeInputManager::CompFluidStream::Primary,
objectIsParent);
// Check zone exhaust nodes
bool found = false;
auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
for (int exhNodeNum : thisZoneEquipConfig.ExhaustNode) {
if (thisZeqMixer.zoneEquipInletNodeNum == exhNodeNum) {
if (thisZeqMixer.outletNodeNum == exhNodeNum) {
found = true;
break;
}
Expand All @@ -1348,7 +1348,7 @@ void processZoneEquipMixerInput(EnergyPlusData &state,
ShowSevereError(state, format("{}{}={}", RoutineName, zeqMixerModuleObject, thisZeqMixer.Name));
ShowContinueError(state,
format("Zone Equipment Inlet Node Name={} is not an exhaust node for ZoneHVAC:EquipmentConnections={}.",
state.dataLoopNodes->NodeID(thisZeqMixer.zoneEquipInletNodeNum),
state.dataLoopNodes->NodeID(thisZeqMixer.outletNodeNum),
thisZoneEquipConfig.ZoneName));
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound = true;
}
Expand Down Expand Up @@ -1410,35 +1410,45 @@ void processZoneReturnMixerInput(EnergyPlusData &state,
int const zoneNum,
InputProcessor::json const objectSchemaProps,
InputProcessor::json const objectFields,
DataZoneEquipment::ZoneReturnMixer &thisZretMixer)
int mixerIndex)

{
static constexpr std::string_view RoutineName("processZoneReturnMixerInput: "); // include trailing blank space
auto &ip = state.dataInputProcessing->inputProcessor;
bool objectIsParent = true;
thisZretMixer.zoneReturnNodeNum = GetOnlySingleNode(state,
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_return_air_node_name"),
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
thisZretMixer.spaceEquipType,
thisZretMixer.Name,
DataLoopNode::NodeFluidType::Air,
DataLoopNode::ConnectionType::Outlet,
NodeInputManager::CompFluidStream::Primary,
objectIsParent);
auto &thisZretMixer = state.dataZoneEquip->zoneReturnMixer[mixerIndex];
thisZretMixer.outletNodeNum = GetOnlySingleNode(state,
ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_return_air_node_name"),
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound,
thisZretMixer.spaceEquipType,
thisZretMixer.Name,
DataLoopNode::NodeFluidType::Air,
DataLoopNode::ConnectionType::Outlet,
NodeInputManager::CompFluidStream::Primary,
objectIsParent);
// Check zone return nodes
bool found = false;
auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
thisZoneEquipConfig.returnNodeSpaceMixerIndex.allocate(thisZoneEquipConfig.NumReturnNodes);
for (int &mixIndex : thisZoneEquipConfig.returnNodeSpaceMixerIndex) {
mixIndex = -1;
}

int nodeCounter = 0;
for (int retNodeNum : thisZoneEquipConfig.ReturnNode) {
if (thisZretMixer.zoneReturnNodeNum == retNodeNum) {
++nodeCounter;
if (thisZretMixer.outletNodeNum == retNodeNum) {
found = true;
// Zone return node is fed by a space return mixer
thisZoneEquipConfig.returnNodeSpaceMixerIndex(nodeCounter) = mixerIndex;
break;
}
}
if (!found) {
ShowSevereError(state, format("{}{}={}", RoutineName, zeqMixerModuleObject, thisZretMixer.Name));
ShowContinueError(state,
format("Zone Equipment Return Air Node Name={} is not a return air node for ZoneHVAC:EquipmentConnections={}.",
state.dataLoopNodes->NodeID(thisZretMixer.zoneReturnNodeNum),
state.dataLoopNodes->NodeID(thisZretMixer.outletNodeNum),
thisZoneEquipConfig.ZoneName));
state.dataZoneEquip->GetZoneEquipmentDataErrorsFound = true;
}
Expand Down Expand Up @@ -1923,17 +1933,17 @@ void ZoneEquipmentSplitterMixer::size(EnergyPlusData &state)
}
}

void ZoneEquipmentMixer::setOutletConditions(EnergyPlusData &state)
void ZoneMixer::setOutletConditions(EnergyPlusData &state)
{
if (this->zoneEquipInletNodeNum == 0) return;
if (this->outletNodeNum == 0) return;

Real64 sumEnthalpy = 0.0;
Real64 sumHumRat = 0.0;
Real64 sumCO2 = 0.0;
Real64 sumGenContam = 0.0;
Real64 sumPressure = 0.0;
Real64 sumFractions = 0.0;
auto &equipInletNode = state.dataLoopNodes->Node(this->zoneEquipInletNodeNum);
auto &outletNode = state.dataLoopNodes->Node(this->outletNodeNum);
for (auto &mixerSpace : this->spaces) {
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
sumEnthalpy += spaceOutletNode.Enthalpy * mixerSpace.fraction;
Expand All @@ -1947,25 +1957,47 @@ void ZoneEquipmentMixer::setOutletConditions(EnergyPlusData &state)
sumPressure += spaceOutletNode.Press * mixerSpace.fraction;
sumFractions += mixerSpace.fraction;
}
equipInletNode.Enthalpy = sumEnthalpy / sumFractions;
equipInletNode.HumRat = sumHumRat / sumFractions;
if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
equipInletNode.CO2 = sumCO2 / sumFractions;
}
if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
equipInletNode.GenContam = sumGenContam / sumFractions;
}
equipInletNode.Press = sumPressure / sumFractions;

// Use Enthalpy and humidity ratio to get outlet temperature from psych chart
equipInletNode.Temp = Psychrometrics::PsyTdbFnHW(equipInletNode.Enthalpy, equipInletNode.HumRat);
// For SpaceHVAC:ZoneReturnMixer, the fractions are dynamic and could be zero if there is no flow
if (sumFractions > 0) {
outletNode.Enthalpy = sumEnthalpy / sumFractions;
outletNode.HumRat = sumHumRat / sumFractions;
if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
outletNode.CO2 = sumCO2 / sumFractions;
}
if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
outletNode.GenContam = sumGenContam / sumFractions;
}
outletNode.Press = sumPressure / sumFractions;

// Use Enthalpy and humidity ratio to get outlet temperature from psych chart
outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
}
}

void ZoneReturnMixer::setInletConditions(EnergyPlusData &state)
{
for (auto &mixerSpace : this->spaces) {
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
int spaceZoneNodeNum = state.dataZoneEquip->spaceEquipConfig(mixerSpace.spaceIndex).ZoneNode;
auto &spaceNode = state.dataLoopNodes->Node(spaceZoneNodeNum);
spaceOutletNode.Temp = spaceNode.Temp;
spaceOutletNode.HumRat = spaceNode.HumRat;
spaceOutletNode.Enthalpy = spaceNode.Enthalpy;
spaceOutletNode.Press = spaceNode.Press;
if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
spaceOutletNode.CO2 = spaceNode.CO2;
}
if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
spaceOutletNode.GenContam = spaceNode.GenContam;
}
}
}
void ZoneEquipmentMixer::setInletFlows(EnergyPlusData &state)
{
if (this->zoneEquipInletNodeNum == 0) return;
if (this->outletNodeNum == 0) return;

auto &equipInletNode = state.dataLoopNodes->Node(this->zoneEquipInletNodeNum);
auto &equipInletNode = state.dataLoopNodes->Node(this->outletNodeNum);
for (auto &mixerSpace : this->spaces) {
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
spaceOutletNode.MassFlowRate = equipInletNode.MassFlowRate * mixerSpace.fraction;
Expand All @@ -1974,6 +2006,35 @@ void ZoneEquipmentMixer::setInletFlows(EnergyPlusData &state)
}
}

void ZoneReturnMixer::setInletFlows(EnergyPlusData &state)
{
if (this->outletNodeNum == 0) return;
auto &outletNode = state.dataLoopNodes->Node(this->outletNodeNum);

Real64 sumMixerInletMassFlow = 0;
for (auto const &mixerSpace : this->spaces) {
// calc return flows for spaces feeding this mixer
auto &spaceEquipConfig = state.dataZoneEquip->spaceEquipConfig(mixerSpace.spaceIndex);
Real64 outletMassFlowRate = outletNode.MassFlowRate; // calcReturnFlows might adjust this parameter value, so make a copy here
Real64 spaceReturnFlow = 0.0;
spaceEquipConfig.calcReturnFlows(state, outletMassFlowRate, spaceReturnFlow);
sumMixerInletMassFlow += spaceReturnFlow;
}

for (auto &mixerSpace : this->spaces) {
auto &spaceOutletNode = state.dataLoopNodes->Node(mixerSpace.spaceNodeNum);
// For return mixer, fraction is calculated every time step, not a user input
if (sumMixerInletMassFlow > 0.0) {
mixerSpace.fraction = spaceOutletNode.MassFlowRate / sumMixerInletMassFlow;
} else {
mixerSpace.fraction = 0.0;
}
spaceOutletNode.MassFlowRate = outletNode.MassFlowRate * mixerSpace.fraction;
spaceOutletNode.MassFlowRateMaxAvail = outletNode.MassFlowRateMaxAvail * mixerSpace.fraction;
spaceOutletNode.MassFlowRateMinAvail = outletNode.MassFlowRateMinAvail * mixerSpace.fraction;
}
}

void ZoneEquipmentSplitter::adjustLoads(EnergyPlusData &state, int zoneNum, int equipTypeNum)
{
auto &thisZoneEnergyDemand = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum);
Expand Down
22 changes: 13 additions & 9 deletions src/EnergyPlus/DataZoneEquipment.hh
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,9 @@ namespace DataZoneEquipment {
Array1D_int ReturnNodePlenumNum; // number of the return plenum attached to this return node (zero if none)
Array1D_int ReturnFlowBasisNode; // return air flow basis nodes
Array1D_int ReturnNodeExhaustNodeNum; // Exhaust node number flow to a corrsponding return node due to light heat gain
// Array1D_int SharedExhaustNode; // Exhaust node number shared by return nodes 0 No exhaust; 1 No share; > 1 shared; -1 use the
// exhaust node value
Array1D<LightReturnExhaustConfig>
SharedExhaustNode; // Exhaust node number shared by return nodes 0 No exhaust; 1 No share; > 1 shared; -1 use the exhaust node value
Array1D_int returnNodeSpaceMixerIndex; // index to SpaceHVAC:ZoneReturnMixer that feeds this return node (-1 if there is none)

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

struct ZoneEquipmentMixer : ZoneEquipmentSplitterMixer
struct ZoneMixer : ZoneEquipmentSplitterMixer
{
int zoneEquipInletNodeNum = 0;
int outletNodeNum = 0;

void setOutletConditions(EnergyPlusData &state);
};

struct ZoneEquipmentMixer : ZoneMixer
{
// int zoneEquipInletNodeNum = 0;

void setInletFlows(EnergyPlusData &state);
};

struct ZoneReturnMixer : ZoneEquipmentSplitterMixer
struct ZoneReturnMixer : ZoneMixer
{
int zoneReturnNodeNum = 0;
// int zoneReturnNodeNum = 0;

// void setOutletConditions(EnergyPlusData &state);
void setInletConditions(EnergyPlusData &state);

// void setInletFlows(EnergyPlusData &state);
void setInletFlows(EnergyPlusData &state);
};
struct ControlList
{
Expand Down Expand Up @@ -585,7 +589,7 @@ namespace DataZoneEquipment {
int const zoneNum,
InputProcessor::json const objectSchemaProps,
InputProcessor::json const objectFields,
DataZoneEquipment::ZoneReturnMixer &thisZretMixer);
int mixerIndex);

bool CheckZoneEquipmentList(EnergyPlusData &state,
std::string_view ComponentType, // Type of component
Expand Down
9 changes: 9 additions & 0 deletions src/EnergyPlus/ZoneEquipmentManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5170,6 +5170,15 @@ void CalcZoneLeavingConditions(EnergyPlusData &state, bool const FirstHVACIterat
Real64 TempZoneAir; // Zone air temperature [C]
Real64 SumRetAirLatentGainRate;

// If SpaceHVAC is active calculate SpaceHVAC:ReturnMixer inlet and outlet conditions before setting zone return conditions
if (state.dataHeatBal->doSpaceHeatBalanceSimulation && !state.dataGlobal->DoingSizing) {
for (auto &thisSpaceHVACMixer : state.dataZoneEquip->zoneReturnMixer) {
thisSpaceHVACMixer.setInletFlows(state);
thisSpaceHVACMixer.setInletConditions(state);
thisSpaceHVACMixer.setOutletConditions(state);
}
}

for (int ZoneNum = 1; ZoneNum <= state.dataGlobal->NumOfZones; ++ZoneNum) {
if (!state.dataZoneEquip->ZoneEquipConfig(ZoneNum).IsControlled) continue;
// A return air system may not exist for certain systems; Therefore when no return node exists
Expand Down
4 changes: 2 additions & 2 deletions tst/EnergyPlus/unit/ZoneEquipmentManager.unit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5217,8 +5217,8 @@ TEST_F(EnergyPlusFixture, SpaceHVACMixerTest)
mixSpace2.spaceNodeNum = 12;
mixSpace3.spaceNodeNum = 13;
state->dataLoopNodes->Node.allocate(13);
thisMixer.zoneEquipInletNodeNum = 1;
auto &equipInletNode = state->dataLoopNodes->Node(thisMixer.zoneEquipInletNodeNum);
thisMixer.outletNodeNum = 1;
auto &equipInletNode = state->dataLoopNodes->Node(thisMixer.outletNodeNum);
auto &mixSpace1Node = state->dataLoopNodes->Node(mixSpace1.spaceNodeNum);
auto &mixSpace2Node = state->dataLoopNodes->Node(mixSpace2.spaceNodeNum);
auto &mixSpace3Node = state->dataLoopNodes->Node(mixSpace3.spaceNodeNum);
Expand Down

4 comments on commit fd2a28f

@nrel-bot-2b
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpaceSizingHVACPart4 (mjwitte) - x86_64-Linux-Ubuntu-22.04-gcc-11.4: OK (3351 of 3695 tests passed, 5 test warnings)

Messages:\n

  • 344 tests had: Table big diffs.
  • 5 tests had: Table small diffs.

Failures:\n

regression Test Summary

  • Passed: 467
  • Failed: 344

Build Badge Test Badge

@nrel-bot-3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpaceSizingHVACPart4 (mjwitte) - x86_64-MacOS-10.18-clang-15.0.0: OK (3311 of 3654 tests passed, 5 test warnings)

Messages:\n

  • 343 tests had: Table big diffs.
  • 5 tests had: Table small diffs.

Failures:\n

regression Test Summary

  • Passed: 448
  • Failed: 343

Build Badge Test Badge

@nrel-bot-2c
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpaceSizingHVACPart4 (mjwitte) - x86_64-Linux-Ubuntu-22.04-gcc-11.4-IntegrationCoverage-Debug: OK (795 of 795 tests passed, 0 test warnings)

Build Badge Test Badge Coverage Badge

@nrel-bot-2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpaceSizingHVACPart4 (mjwitte) - x86_64-Linux-Ubuntu-22.04-gcc-11.4-UnitTestsCoverage-Debug: OK (2070 of 2070 tests passed, 0 test warnings)

Build Badge Test Badge Coverage Badge

Please sign in to comment.