-
Notifications
You must be signed in to change notification settings - Fork 392
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
Allow zone hvac to draw return air #8719
Conversation
@@ -648,6 +648,7 @@ namespace DataHeatBalance { | |||
Real64 FractionReturnAirPlenTempCoeff1; | |||
Real64 FractionReturnAirPlenTempCoeff2; | |||
int ZoneReturnNum; // zone return index (not the node number) for return heat gain | |||
int ZoneExhaustNodeNum; // Exhaust node number |
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.
Add a new variable to store zone exhaust node number in the Lights object
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.
Why a single node number here? And why do the lights care what kind of node this is?
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.
@mjwitte Although the Lights object does not care what kind of node is, this new feature only adds an additional exhaust node to share the return gain. The existing function only adds return heat gain into a single return node. If a user wants to have more return gain share, more Lights object may be listed.
@@ -265,6 +265,7 @@ namespace DataZoneEquipment { | |||
Array1D_bool FixedReturnFlow; // true if return node is fixed and cannot be adjusted in CalcZoneReturnFlows | |||
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 |
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.
Add a new 1-D array to store exhaust node number in ZoneEquipConfig
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.
I don't see why ReturnNodeExhaustNodeNum
is necessary.
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.
@mjwitte Since we allow multiple Lights objects in a single zone, it is possible there are multiple exhaust nodes in a single zone, so that an 1-D array is created. If not necessary, do you have any suggestions to store exhaust node information in ZoneEquipConfig?
ShowContinueError(state, "..FanCoil inlet node name = " + state.dataLoopNodes->NodeID(FanCoil(FanCoilNum).AirInNode)); | ||
ErrorsFound = true; | ||
} | ||
} |
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.
Allow FanCoil to accept an induced node of return plenum as an inlet node with ATMixer at the supply side, in addition to a zone exhaust node.
ShowContinueError(state, | ||
"..Fan coil unit air inlet node name = " + state.dataLoopNodes->NodeID(FanCoil(FanCoilNum).AirInNode)); | ||
ErrorsFound = true; | ||
} |
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.
Allow FanCoil to accept an induced node of return plenum as an inlet node without ATMixer, in addition to a zone exhaust node.
src/EnergyPlus/InternalHeatGains.cc
Outdated
} | ||
} else { | ||
state.dataHeatBal->Lights(Loop).ZoneReturnNum = ZoneReturnNum; | ||
} |
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.
Allow Lights object to accept a NodeList as Return Air Heat Gain, in addition to return node 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.
If the Lights input is Return Air Heat Gain Node or NodeList Name
then it should accept any number of nodes in the list. If there is a reason to limit it to a single return node and/or a single plenum induced air node, then there should be two input nodes for one node each. Why an Exhaust Node here? It should be a plenum induced air node, correct?
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.
@mjwitte I can make a change for Light object to add one more field. In this scenario, I would like to have a sing return node vs. a single exhaust node to share return heat gain. Induced node is not for this application.
src/EnergyPlus/UnitarySystem.cc
Outdated
ShowContinueError(state, | ||
"Air Inlet Node " + loc_AirInNodeName + | ||
" name does not match any controlled zone exhaust node name. Check ZoneHVAC:EquipmentConnections " | ||
"object inputs."); | ||
ShowContinueError(state, "or Induced Air Outlet Node Name specified in AirLoopHVAC:ReturnPlenum object."); |
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.
Provide information to allow an induced node from ZonePlenum
int ActualZoneNum; // Zone number | ||
int ZoneNode; // Node number of controlled zone | ||
int ReturnNode; // Node number of controlled zone's return air | ||
int ReturnNodeExhaustNum; // Asscoaited exhaust node number, corresponding to the return node |
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.
Add an array to store ReturnExhaust node number
@@ -4958,6 +4963,9 @@ void CalcZoneLeavingConditions(EnergyPlusData &state, bool const FirstHVACIterat | |||
} else { | |||
state.dataLoopNodes->Node(ReturnNode).Temp = TempRetAir; | |||
} | |||
if (ReturnNodeExhaustNum > 0 && state.dataLoopNodes->Node(ReturnNodeExhaustNum).MassFlowRate > 0.0 && QRetAir > 0.0) { | |||
state.dataLoopNodes->Node(ReturnNodeExhaustNum).Temp = TempRetAir; | |||
} |
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.
Assign node temperature after adding return heat to both nodes of return and zone exhaust.
|
||
return Nodefound; | ||
} | ||
|
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.
Ensure induced node is used as inlet node of zone equipment
@@ -212,6 +212,7 @@ add_simulation_test(IDF_FILE DOASDualDuctSchool.idf EPW_FILE USA_IL_Chicago-OHar | |||
add_simulation_test(IDF_FILE DOAToFanCoilInlet.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) | |||
add_simulation_test(IDF_FILE DOAToFanCoilSupply.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) | |||
add_simulation_test(IDF_FILE DOAToPTAC.idf EPW_FILE USA_FL_Miami.Intl.AP.722020_TMY3.epw) | |||
add_simulation_test(IDF_FILE DOAToPTAC_DrawReturnAir.idf EPW_FILE USA_FL_Miami.Intl.AP.722020_TMY3.epw) |
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.
Add a new example file for this new feature
In addition to a new example file, five test files are provided for each of 5 zone equipment types: FanCoilInlet_Test.txt |
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.
@lgu1234 This is a good start, but I have some questions below. If you would like to arrange a call next week to discuss, let me know.
Another way to simplify this would be to create some common functions for:
1.int GetControlledZoneNum(EquipType, EquipName)
2. bool validateZoneHVACEquipInlet(CtrlZoneNum, inletNodeNum) which could call:
3. bool isZoneReturnNode(CtrlZoneNum, NodeNum)
4. bool isReturnPlenumInducedNode(NodeNum, &ReturnPlenumNum?)
Perhaps get this working for one equipment type first, then the same code changes can be implemented in the other equipment types.
idd/Energy+.idd.in
Outdated
@@ -21153,13 +21153,14 @@ Lights, | |||
\units 1/K | |||
\minimum 0.0 | |||
\default 0.0 | |||
A7 ; \field Return Air Heat Gain Node Name | |||
A7 ; \field Return Air Heat Gain Node or NodeList 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.
I don't see why the Lights object needs to care what kind of node(s) it is connected to. The function which calculates the the heat gain to the node can simply look at one or more nodes and allocated the heat gain from this lights object to the node(s) on a mass-flow weighted basis.
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.
@mjwitte The current field input allows a single return node. The expansion allows a node list with a single return node and a single exhaust node to share the return heat gain.
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.
@lgu1234 How about 2 node names here?
Return Air Heat Gain Node Name
Exhaust Heat Gain Node 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.
@mjwitte I am OK with your suggestion. Therefore, A7 is kept as is. A8 will be added as Exhaust Heat Gain Node Name. Thanks.
@@ -648,6 +648,7 @@ namespace DataHeatBalance { | |||
Real64 FractionReturnAirPlenTempCoeff1; | |||
Real64 FractionReturnAirPlenTempCoeff2; | |||
int ZoneReturnNum; // zone return index (not the node number) for return heat gain | |||
int ZoneExhaustNodeNum; // Exhaust node number |
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.
Why a single node number here? And why do the lights care what kind of node this is?
@@ -265,6 +265,7 @@ namespace DataZoneEquipment { | |||
Array1D_bool FixedReturnFlow; // true if return node is fixed and cannot be adjusted in CalcZoneReturnFlows | |||
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 |
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.
I don't see why ReturnNodeExhaustNodeNum
is necessary.
src/EnergyPlus/FanCoilUnits.cc
Outdated
if (FanCoil(FanCoilNum).ControlZoneNum > 0) { | ||
for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(FanCoil(FanCoilNum).ControlZoneNum).NumReturnNodes; | ||
++NodeNum) { | ||
InletNodeFound = ZonePlenum::ValidateInducedNode( | ||
state, FanCoil(FanCoilNum).AirInNode, state.dataZoneEquip->ZoneEquipConfig(CtrlZone).ReturnNode(NodeNum)); | ||
if (InletNodeFound) 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.
This block is testing to see of one if a zone return node is connected to the same plenum as the induced air node? Is this necessary?
This would be more efficient if the full list state.dataZoneEquip->ZoneEquipConfig(CtrlZone).ReturnNode
is passed to the function. Then the function can first search for a plenum that matches the induced air node, then run through the return node list checking for a match there.
Also, if this fails because the induced node happens to be connected to a different plenum that does not return from the same zone, then the error message below will be confusing.
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.
@mjwitte If a full list of return node is passed as an argument, return node do loop will occur inside the function. Therefore, do loop will be needed in either outside function or inside function. I can make change. By the way, how can I place a full list of return nodes as a single argument?
src/EnergyPlus/InternalHeatGains.cc
Outdated
} | ||
} else { | ||
state.dataHeatBal->Lights(Loop).ZoneReturnNum = ZoneReturnNum; | ||
} |
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.
If the Lights input is Return Air Heat Gain Node or NodeList Name
then it should accept any number of nodes in the list. If there is a reason to limit it to a single return node and/or a single plenum induced air node, then there should be two input nodes for one node each. Why an Exhaust Node here? It should be a plenum induced air node, correct?
src/EnergyPlus/FanCoilUnits.cc
Outdated
for (CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) { | ||
for (int Num = 1; Num <= state.dataZoneEquip->ZoneEquipList(CtrlZone).NumOfEquipTypes; ++Num) { | ||
if (UtilityRoutines::SameString(FanCoil(FanCoilNum).Name, state.dataZoneEquip->ZoneEquipList(CtrlZone).EquipName(Num)) && | ||
UtilityRoutines::SameString(FanCoil(FanCoilNum).UnitType, | ||
state.dataZoneEquip->ZoneEquipList(CtrlZone).EquipType(Num))) { | ||
FanCoil(FanCoilNum).ControlZoneNum = CtrlZone; | ||
break; | ||
} | ||
} | ||
if (FanCoil(FanCoilNum).ControlZoneNum > 0) 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.
If this search to set FanCoil(FanCoilNum).ControlZoneNum
was done before all of the node checking, then the loops for CtrlZone
could be eliminated.
Also, the inlet node checks are the same for ATMixer_SupplySide or no mixer, so this can be structured to avoid duplicate code.
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.
@mjwitte I debug the code and found FanCoil(FanCoilNum).ControlZoneNum is not assigned. There is a section to get ControlZoneNum without a mixer. Therefore, this section code is needed.
I agree that the inlet node checks are duplicated. I am going to created a new function.
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.
It would be better to always set ControlZoneNum
before all of this connection checking. I don't think that would break anything else. So, this block can be moved higher up.
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.
Yes, it can be done. However, this search is needed with ATMixer_SupplySide and an induced node is used as an inlet node. Therefore, this section is better to stay as is.
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.
My point here is that I see three new loops over CtrlZone
. It seems that one loop would be adequate.
@mjwitte I checked code in UnitarySystem I modified. There is a do loop section:
This section can be consolidated as you suggested: bool isReturnPlenumInducedNode(NodeNum, &ReturnPlenumNum?) I would like to make changes in UnitarySystem first. As long as the change is OK, I can modify other modules. I would like to have a meeting. I am available either Thursday or Friday afternoon this week. |
@mjwitte I upload the code with a new common function for the FanCoil unit for review and comments. If OK, I hope to use this one as a starting point to generate more common functions in other modules. |
src/EnergyPlus/FanCoilUnits.cc
Outdated
InletNodeFound = isReturnPlenumInducedNode(state, | ||
FanCoil(FanCoilNum).AirInNode, | ||
state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumReturnNodes, | ||
state.dataZoneEquip->ZoneEquipConfig(CtrlZone).ReturnNode); |
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.
Looking at this more, isReturnPlenumInducedNode
is not doing much. How about
ZonePlenum::ValidateInducedNode( state, InletNodeNum, state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumReturnNodes, state.dataZoneEquip->ZoneEquipConfig(CtrlZone).ReturnNode)
right here. With the list of return nodes passed in, ValidateInducedNode
can search the plenum induced air nodes first, then look for matching return node to plenum inlet node once the correct plenum is located.
@mjwitte I revised the ZonePlenum::ValidateInducedNode function and uploaded the code. Please take a look to ensure the change is what you expect. |
src/EnergyPlus/FanCoilUnits.cc
Outdated
|
||
FanCoil(FanCoilNum).ControlZoneNum = ControlZoneNum; | ||
} | ||
|
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.
There are two places to find zone number. I consolidate two places as a common function based on search scenario. The other cases may not need to use the zone number.
src/EnergyPlus/ZonePlenum.cc
Outdated
for (InduceNodeCtr = 1; InduceNodeCtr <= state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).NumInducedNodes; ++InduceNodeCtr) { | ||
if (InduceNodeNum == state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).InducedNode(InduceNodeCtr)) { |
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.
If these lines are moved up after line 1389, then looking for the induced node first would be a little bit faster.
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.
Done. Thanks.
@mjwitte There is a warning in x86_64-MacOS-10.15-clang. |
@lgu1234 Yes, but it will be fixed when #8898 merges (deleted here). So, no need to fix it here. |
Oh right, thanks for the reminder. Heading over there to merge now... |
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.
@lgu1234 Ran some tests with the new example file, with bad node names.
Invalid return node failed as expected.
Invalid exhaust node was just a warning - should be severe and stop.
Exhaust node not in the same zone was just a warning - should be severe and stop.
Exhaust node with blank return node - no warning at all. This is ok.
Here are the test files. |
Docs look good except a couple of warnings (see the file diffs page):
Looks like I never answered this question
If multiple lights objects are present in the zone, it should be ok as long as the return node/exhaust node pair are the same node names. The internal heat gain tracking will add all of the gains to that return/exhaust node pair. But if LightsA reference ReturnNode1 and ExhaustNode1 and LightsB references ReturnNode1 and ExhaustNode2, I think the current code for lights will overwrite the exhaust node and only the last one will get the heat gain. This could be a valid case if there is more than one piece of equipment in the zone that is drawing from the return duct. So, this could be a vector which means a vector on each return node, which seems complicated. Perhaps at this point, you should check if a given return node already has Also, I don't see any Engineering Ref changes, but I can't find anyplace in the Engineering Ref that talks about return air heat gain. I thought there was some place in the docs that explained |
If those are the same warnings that have appeared in other branches, I think I have them fixed in one of my branches. If that is a new warning or a new field entirely or anything, then yeah, that should be fixed here. |
@mjwitte I resolved doc warning by replacing `field-supply-air-fan-operating-mode-schedule-name-4' in doc/input-output-reference/src/overview/group-unitary-equipment.tex#L1029 by '{field-unitaryheatcool-supply-air-fan-operating-mode-schedule-name}'. Instead of using number to show difference, I add the object name in the beginning of the field name. |
@mjwitte I uploaded code by addressing partial comments.
DOAToPTAC_DrawReturnAir-ExhNodeNotInZone-Annual.idf Action: Severe and stop DOAToPTAC_LightsZero-DrawReturnAir-BadExhNode-Annual.idf DOAToPTAC_LightsZero-DrawReturnAir-BadReturnNode-Annual.idf Action: All severe DOAToPTAC_LightsZero-DrawReturnAir-NoReturnNode-Annual.idf No action is needed. I am think of a case with multiple return nodes with the same exhaust node. Do you provide an example file with multiple airloops to serve a single zone? |
@lgu1234 For examples with 2 airloops (and 2 returns) for each zone, see House-2FurnaceAC-SequentialLoad.idf (and other House-2FurnaceAC-*.idf). |
@mjwitte I upload the code and document to address the rest of comments. The new feature can handle multiple return nodes and referred to the same exhaust node in multiple Lights objects. The Engineering reference was revised to add how the node temperatures are calculated with 3 scenarios: Return node only, a pair of return and exhaust nodes, multiple return nodes and a single exhaust node. The corresponding unit test for the last scenario was added. Enclosed is a test file for the last scenario. |
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.
@lgu1234 Thanks for addressing comments and adding the engineering ref section.
|
||
\(CpAir\) is the specific heat (J/kg) | ||
|
||
Although a single Lights object allows a return node and an exhasut node, it is possible to have multiple Lights objects with different return nodes and referrred to the same exhaust node. Therefore, the exhaust node will share return heat from different return nodes. The temperature rise at i-th return node is given below: |
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.
Typo: exhasut
This is not the scenario I was asking about. What would this mean physically for a single zonehvac unit to draw from 2 different return ducts? I'm not sure we should allow this combination.
I was asking about the reverse, where 2 different zoneHVAC units draw from the same return duct. At this point, I'm ok with not allowing that just so we can call this complete.
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.
@mjwitte Thanks for finding typos. I corrected 4. I am going to upload the Eng. Ref.
I can remove the scenario for a single zonehvac unit to draw from 2 different return ducts. Please let me know if the scenario is removed or not.
@mjwitte An error message was added if 2 different exhaust nodes draw from the same return node. Enclosed is a test file. |
The new error check is correct when 2 different exhaust nodes are used with the same return node, as in SingleReturnDoubleExhaust.idf But it should not be an error to use the same pair of exhaust node and return node multiples times. The internal gains tracking will sum the heat gains automatically. This file should run without error: DoubleLightsSingleExhaust-SingleReturn.idf.txt |
@mjwitte The code was changed and uploaded. No error is generated when the same return and exhaust nodes are used in two lights object. |
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.
Retested the new error messages and everything is working as expected. Will merge once CI finishes up.
Mac is green after the latest change. Merging. |
Pull request overview
The new feature requests Zone HVAC objects to draw inlet air from return plenum and return duct. It should be pointed out that no return duct losses are calculated in Existing EnergyPlus, except for the AirflowNetwork model (beyond the topic for this new feature). Instead, light heat from return fraction is added to the return node after mixing. Therefore, light heat will be added into the drawn air for the scenario with return duct. There is no modification for the scenario with return plenum.
This new feature is applied to the system configuration only when a zone equipment and an AirLoop serve the same zone. The proposed IO modification will be made in the ZoneHVAC:EquipmentConnections object by adding a new field as Plenum or Return Air Node Name for the last position, so that no transition is needed for this new feature.
The feature request is GitHub Issue #8190 at #8190
Test files are attached here.
Pull Request Author
Add to this list or remove from it as applicable. This is a simple templated set of guidelines.
Reviewer
This will not be exhaustively relevant to every PR.