Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // ObjexxFCL Headers
49 : #include <ObjexxFCL/Array.functions.hh>
50 : #include <ObjexxFCL/Array1D.hh>
51 : // #include <ObjexxFCL/Fmath.hh>
52 :
53 : // EnergyPlus Headers
54 : #include <AirflowNetwork/Solver.hpp>
55 : #include <EnergyPlus/BaseboardElectric.hh>
56 : #include <EnergyPlus/BaseboardRadiator.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataDefineEquip.hh>
59 : #include <EnergyPlus/DataEnvironment.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/DataHeatBalSurface.hh>
62 : #include <EnergyPlus/DataHeatBalance.hh>
63 : #include <EnergyPlus/DataLoopNode.hh>
64 : #include <EnergyPlus/DataMoistureBalance.hh>
65 : #include <EnergyPlus/DataMoistureBalanceEMPD.hh>
66 : #include <EnergyPlus/DataRoomAirModel.hh>
67 : #include <EnergyPlus/DataSurfaceLists.hh>
68 : #include <EnergyPlus/DataSurfaces.hh>
69 : #include <EnergyPlus/DataZoneEquipment.hh>
70 : #include <EnergyPlus/ElectricBaseboardRadiator.hh>
71 : #include <EnergyPlus/FluidProperties.hh>
72 : #include <EnergyPlus/General.hh>
73 : #include <EnergyPlus/GlobalNames.hh>
74 : #include <EnergyPlus/HWBaseboardRadiator.hh>
75 : #include <EnergyPlus/HeatBalFiniteDiffManager.hh>
76 : #include <EnergyPlus/HeatBalanceHAMTManager.hh>
77 : #include <EnergyPlus/HighTempRadiantSystem.hh>
78 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
79 : #include <EnergyPlus/InternalHeatGains.hh>
80 : #include <EnergyPlus/MoistureBalanceEMPDManager.hh>
81 : #include <EnergyPlus/OutputProcessor.hh>
82 : #include <EnergyPlus/Psychrometrics.hh>
83 : #include <EnergyPlus/RefrigeratedCase.hh>
84 : #include <EnergyPlus/RoomAirModelAirflowNetwork.hh>
85 : #include <EnergyPlus/SteamBaseboardRadiator.hh>
86 : #include <EnergyPlus/UtilityRoutines.hh>
87 : #include <EnergyPlus/ZoneAirLoopEquipmentManager.hh>
88 : #include <EnergyPlus/ZoneDehumidifier.hh>
89 : #include <EnergyPlus/ZonePlenum.hh>
90 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
91 :
92 : namespace EnergyPlus {
93 :
94 : namespace RoomAir {
95 :
96 : // MODULE INFORMATION:
97 : // AUTHOR Brent Griffith
98 : // DATE WRITTEN November 2009
99 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
100 :
101 : // PURPOSE OF THIS MODULE:
102 : // contains the RoomAir model portions of RoomAirflowNetwork modeling
103 :
104 : // METHODOLOGY EMPLOYED:
105 : // Interact with Surface HB, internal gain, HVAC system and Airflow Network Domains
106 : // Do heat and moisture balance calculations on roomair nodes.
107 :
108 : // Using/Aliasing
109 : using namespace DataHeatBalSurface;
110 : using namespace DataSurfaces;
111 : using namespace DataHeatBalance;
112 :
113 0 : void SimRoomAirModelAFN(EnergyPlusData &state, int const zoneNum) // index number for the specified zone
114 : {
115 :
116 : // SUBROUTINE INFORMATION:
117 : // AUTHOR Brent Griffith
118 : // DATE WRITTEN January 2004/Aug 2005
119 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
120 :
121 : // PURPOSE OF THIS SUBROUTINE:
122 : // This subroutine manages RoomAirflowNetwork model simulation
123 :
124 : // METHODOLOGY EMPLOYED:
125 : // calls subroutines (LOL)
126 :
127 0 : auto const &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
128 :
129 : // model control volume for each roomAir:node in the zone.
130 0 : for (int roomAirNodeNum = 1; roomAirNodeNum <= afnZoneInfo.NumOfAirNodes; ++roomAirNodeNum) {
131 0 : InitRoomAirModelAFN(state, zoneNum, roomAirNodeNum);
132 0 : CalcRoomAirModelAFN(state, zoneNum, roomAirNodeNum);
133 : }
134 :
135 0 : UpdateRoomAirModelAFN(state, zoneNum);
136 :
137 0 : } // SimRoomAirModelAirflowNetwork
138 :
139 : //****************************************************
140 :
141 0 : void LoadPredictionRoomAirModelAFN(EnergyPlusData &state,
142 : int const zoneNum,
143 : int const roomAirNodeNum) // index number for the specified zone and node
144 : {
145 :
146 : // SUBROUTINE INFORMATION:
147 : // AUTHOR Lixing Gu
148 : // DATE WRITTEN June, 2015
149 :
150 : // PURPOSE OF THIS SUBROUTINE:
151 : // Predict zone loads at a controlled node
152 :
153 0 : InitRoomAirModelAFN(state, zoneNum, roomAirNodeNum);
154 :
155 0 : } // LoadPredictionRoomAirModelAirflowNetwork
156 :
157 : //****************************************************
158 :
159 3 : void InitRoomAirModelAFN(EnergyPlusData &state, int const zoneNum,
160 : int const roomAirNodeNum) // index number for the specified zone
161 : {
162 :
163 : // SUBROUTINE INFORMATION:
164 : // AUTHOR B. Griffith
165 : // DATE WRITTEN November 2009
166 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 release
167 :
168 : // PURPOSE OF THIS SUBROUTINE:
169 : // Perform one-time checking and term calculations
170 :
171 : using InternalHeatGains::SumInternalLatentGainsByTypes;
172 : using Psychrometrics::PsyCpAirFnW;
173 : using Psychrometrics::PsyRhoAirFnPbTdbW;
174 :
175 3 : Array1D_bool NodeFound; // True if a node is found.
176 3 : Array1D_bool EquipFound;
177 3 : Array1D<Real64> SupplyFrac;
178 3 : Array1D<Real64> ReturnFrac;
179 :
180 3 : if (state.dataRoomAirflowNetModel->OneTimeFlag) { // then do one - time setup inits
181 :
182 : // loop over all zones with RoomAirflowNetwork model
183 2 : for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
184 1 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(iZone);
185 1 : if (!afnZoneInfo.IsUsed) {
186 0 : continue;
187 : }
188 1 : int NumSurfs = 0; // NumSurfs isn't used anywhere?
189 2 : for (int spaceNum : state.dataHeatBal->Zone(iZone).spaceIndexes) {
190 1 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
191 1 : NumSurfs += thisSpace.HTSurfaceLast - thisSpace.HTSurfaceFirst + 1;
192 1 : }
193 :
194 3 : for (auto &afnNode : afnZoneInfo.Node) {
195 : // calculate volume of air in node's control volume
196 2 : afnNode.AirVolume = state.dataHeatBal->Zone(iZone).Volume * afnNode.ZoneVolumeFraction;
197 :
198 4 : SetupOutputVariable(state,
199 : "RoomAirflowNetwork Node NonAirSystemResponse",
200 : Constant::Units::W,
201 2 : afnNode.NonAirSystemResponse,
202 : OutputProcessor::TimeStepType::System,
203 : OutputProcessor::StoreType::Average,
204 2 : afnNode.Name);
205 4 : SetupOutputVariable(state,
206 : "RoomAirflowNetwork Node SysDepZoneLoadsLagged",
207 : Constant::Units::W,
208 2 : afnNode.SysDepZoneLoadsLagged,
209 : OutputProcessor::TimeStepType::System,
210 : OutputProcessor::StoreType::Average,
211 2 : afnNode.Name);
212 4 : SetupOutputVariable(state,
213 : "RoomAirflowNetwork Node SumIntSensibleGain",
214 : Constant::Units::W,
215 2 : afnNode.SumIntSensibleGain,
216 : OutputProcessor::TimeStepType::System,
217 : OutputProcessor::StoreType::Average,
218 2 : afnNode.Name);
219 4 : SetupOutputVariable(state,
220 : "RoomAirflowNetwork Node SumIntLatentGain",
221 : Constant::Units::W,
222 2 : afnNode.SumIntLatentGain,
223 : OutputProcessor::TimeStepType::System,
224 : OutputProcessor::StoreType::Average,
225 2 : afnNode.Name);
226 : }
227 : }
228 1 : state.dataRoomAirflowNetModel->OneTimeFlag = false;
229 : }
230 :
231 3 : if (state.dataRoomAirflowNetModel->OneTimeFlagConf) { // then do one - time setup inits
232 2 : if (allocated(state.dataZoneEquip->ZoneEquipConfig) && allocated(state.dataZoneEquip->ZoneEquipList)) {
233 2 : int MaxNodeNum = 0;
234 2 : int MaxEquipNum = 0;
235 2 : bool ErrorsFound = false;
236 4 : for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
237 2 : if (!state.dataHeatBal->Zone(iZone).IsControlled) {
238 0 : continue;
239 : }
240 2 : MaxEquipNum = max(MaxEquipNum, state.dataZoneEquip->ZoneEquipList(iZone).NumOfEquipTypes);
241 2 : MaxNodeNum = max(MaxNodeNum, state.dataZoneEquip->ZoneEquipConfig(iZone).NumInletNodes);
242 : }
243 2 : if (MaxNodeNum > 0) {
244 2 : NodeFound.allocate(MaxNodeNum);
245 2 : NodeFound = false;
246 : }
247 2 : if (MaxEquipNum > 0) {
248 2 : EquipFound.allocate(MaxEquipNum);
249 2 : SupplyFrac.allocate(MaxEquipNum);
250 2 : ReturnFrac.allocate(MaxEquipNum);
251 2 : EquipFound = false;
252 2 : SupplyFrac = 0.0;
253 2 : ReturnFrac = 0.0;
254 : }
255 :
256 : // loop over all zones with RoomAirflowNetwork model
257 4 : for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
258 2 : auto const &zone = state.dataHeatBal->Zone(iZone);
259 2 : if (!zone.IsControlled) {
260 0 : continue;
261 : }
262 :
263 2 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(iZone);
264 2 : if (!afnZoneInfo.IsUsed) {
265 0 : continue;
266 : }
267 2 : afnZoneInfo.ActualZoneID = iZone;
268 2 : SupplyFrac = 0.0;
269 2 : ReturnFrac = 0.0;
270 2 : NodeFound = false;
271 2 : int numAirDistUnits = 0;
272 :
273 2 : auto const &zoneEquipList = state.dataZoneEquip->ZoneEquipList(iZone);
274 2 : auto const &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(iZone);
275 :
276 : // find supply air node number
277 6 : for (auto &afnNode : afnZoneInfo.Node) {
278 8 : for (auto &afnHVAC : afnNode.HVAC) {
279 8 : for (int I = 1; I <= zoneEquipList.NumOfEquipTypes; ++I) { // loop over all equip types
280 4 : if (zoneEquipList.EquipType(I) == DataZoneEquipment::ZoneEquipType::AirDistributionUnit) {
281 2 : if (numAirDistUnits == 0) {
282 : numAirDistUnits =
283 1 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ZoneHVAC:AirDistributionUnit");
284 : }
285 2 : if (state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag) {
286 0 : ZoneAirLoopEquipmentManager::GetZoneAirLoopEquipment(state);
287 0 : state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag = false;
288 : }
289 :
290 4 : for (int AirDistUnitNum = 1; AirDistUnitNum <= numAirDistUnits; ++AirDistUnitNum) {
291 2 : if (zoneEquipList.EquipName(I) == state.dataDefineEquipment->AirDistUnit(AirDistUnitNum).Name) {
292 2 : if (afnHVAC.Name == state.dataDefineEquipment->AirDistUnit(AirDistUnitNum).EquipName(1)) {
293 2 : if (afnHVAC.EquipConfigIndex == 0) {
294 0 : afnHVAC.EquipConfigIndex = I;
295 : }
296 2 : EquipFound(I) = true;
297 2 : SupplyFrac(I) += afnHVAC.SupplyFraction;
298 2 : ReturnFrac(I) += afnHVAC.ReturnFraction;
299 : }
300 : }
301 : }
302 2 : } else if (Util::SameString(zoneEquipList.EquipName(I), afnHVAC.Name)) {
303 2 : if (afnHVAC.EquipConfigIndex == 0) {
304 2 : afnHVAC.EquipConfigIndex = I;
305 : }
306 2 : EquipFound(I) = true;
307 2 : SupplyFrac(I) += afnHVAC.SupplyFraction;
308 2 : ReturnFrac(I) += afnHVAC.ReturnFraction;
309 : }
310 : }
311 4 : for (int iNode = 1; iNode <= state.dataLoopNodes->NumOfNodes; ++iNode) { // loop over all nodes to find supply node ID
312 4 : if (Util::SameString(state.dataLoopNodes->NodeID(iNode), afnHVAC.SupplyNodeName)) {
313 4 : afnHVAC.SupNodeNum = iNode;
314 4 : break;
315 : }
316 : }
317 : // Verify inlet nodes
318 4 : int inletNodeIndex = 0;
319 4 : for (int iNode = 1; iNode <= zoneEquipConfig.NumInletNodes;
320 : ++iNode) { // loop over all supply inlet nodes in a single zone
321 : // !Get node conditions
322 4 : if (zoneEquipConfig.InletNode(iNode) == afnHVAC.SupNodeNum) {
323 4 : NodeFound(iNode) = true;
324 4 : inletNodeIndex = iNode;
325 4 : break;
326 : }
327 : }
328 :
329 4 : if (afnHVAC.SupNodeNum > 0 && afnHVAC.ReturnNodeName.empty()) {
330 : // Find matching return node
331 0 : for (int retNode = 1; retNode <= zoneEquipConfig.NumReturnNodes; ++retNode) {
332 0 : if ((zoneEquipConfig.ReturnNodeInletNum(retNode) == inletNodeIndex) &&
333 0 : (zoneEquipConfig.ReturnNode(retNode) > 0)) {
334 0 : afnHVAC.RetNodeNum = zoneEquipConfig.ReturnNode(retNode); // Zone return node
335 0 : break;
336 : }
337 : }
338 : }
339 :
340 4 : if (afnHVAC.RetNodeNum == 0) {
341 4 : for (int iNode = 1; iNode <= state.dataLoopNodes->NumOfNodes; ++iNode) { // loop over all nodes to find return node ID
342 4 : if (Util::SameString(state.dataLoopNodes->NodeID(iNode), afnHVAC.ReturnNodeName)) {
343 2 : afnHVAC.RetNodeNum = iNode;
344 2 : break;
345 : }
346 : }
347 : }
348 8 : SetupOutputVariable(state,
349 : "RoomAirflowNetwork Node HVAC Supply Fraction",
350 : Constant::Units::None,
351 4 : afnHVAC.SupplyFraction,
352 : OutputProcessor::TimeStepType::System,
353 : OutputProcessor::StoreType::Average,
354 4 : afnHVAC.Name);
355 8 : SetupOutputVariable(state,
356 : "RoomAirflowNetwork Node HVAC Return Fraction",
357 : Constant::Units::None,
358 4 : afnHVAC.ReturnFraction,
359 : OutputProcessor::TimeStepType::System,
360 : OutputProcessor::StoreType::Average,
361 4 : afnHVAC.Name);
362 : }
363 : }
364 : // Count node with.TRUE.
365 2 : int ISum = 0;
366 4 : for (int iNode = 1; iNode <= MaxNodeNum; ++iNode) { // loop over all supply inlet nodes in a single zone
367 2 : if (NodeFound(iNode)) {
368 2 : ++ISum;
369 : }
370 : }
371 : // Provide error messages with incorrect supplu node inputs
372 2 : if (ISum != zoneEquipConfig.NumInletNodes) {
373 0 : if (ISum > zoneEquipConfig.NumInletNodes) {
374 0 : ShowSevereError(
375 : state, "GetRoomAirflowNetworkData: The number of equipment listed in RoomAirflowNetwork:Node:HVACEquipment objects");
376 0 : ShowContinueError(state, format("is greater than the number of zone configuration inlet nodes in {}", zone.Name));
377 0 : ShowContinueError(state, "Please check inputs of both objects.");
378 0 : ErrorsFound = true;
379 : } else {
380 0 : ShowSevereError(
381 : state, "GetRoomAirflowNetworkData: The number of equipment listed in RoomAirflowNetwork:Node:HVACEquipment objects");
382 0 : ShowContinueError(state, format("is less than the number of zone configuration inlet nodes in {}", zone.Name));
383 0 : ShowContinueError(state, "Please check inputs of both objects.");
384 0 : ErrorsFound = true;
385 : }
386 : }
387 :
388 : // Check equipment names to ensure they are used in RoomAirflowNetwork : Node : HVACEquipment objects
389 4 : for (int I = 1; I <= zoneEquipList.NumOfEquipTypes; ++I) { // loop over all equip types
390 2 : if (!EquipFound(I)) {
391 0 : ShowSevereError(state,
392 : "GetRoomAirflowNetworkData: The equipment listed in ZoneEquipList is not found in the lsit of "
393 : "RoomAir:Node:AirflowNetwork:HVACEquipment objects =");
394 0 : ShowContinueError(state, format("{}. Please check inputs of both objects.", zoneEquipList.EquipName(I)));
395 0 : ErrorsFound = true;
396 : }
397 : }
398 :
399 : // Check fraction to ensure sum = 1.0 for every equipment
400 4 : for (int I = 1; I <= zoneEquipList.NumOfEquipTypes; ++I) { // loop over all equip types
401 2 : if (std::abs(SupplyFrac(I) - 1.0) > 0.001) {
402 0 : ShowSevereError(state, "GetRoomAirflowNetworkData: Invalid, zone supply fractions do not sum to 1.0");
403 0 : ShowContinueError(
404 0 : state, format("Entered in {} defined in RoomAir:Node:AirflowNetwork:HVACEquipment", zoneEquipList.EquipName(I)));
405 0 : ShowContinueError(state,
406 : "The Fraction of supply fraction values across all the roomair nodes in a zone needs to sum to 1.0.");
407 0 : ShowContinueError(state, format("The sum of fractions entered = {:.3R}", SupplyFrac(I)));
408 0 : ErrorsFound = true;
409 : }
410 2 : if (std::abs(ReturnFrac(I) - 1.0) > 0.001) {
411 0 : ShowSevereError(state, "GetRoomAirflowNetworkData: Invalid, zone return fractions do not sum to 1.0");
412 0 : ShowContinueError(
413 0 : state, format("Entered in {} defined in RoomAir:Node:AirflowNetwork:HVACEquipment", zoneEquipList.EquipName(I)));
414 0 : ShowContinueError(state,
415 : "The Fraction of return fraction values across all the roomair nodes in a zone needs to sum to 1.0.");
416 0 : ShowContinueError(state, format("The sum of fractions entered = {:.3R}", ReturnFrac(I)));
417 0 : ErrorsFound = true;
418 : }
419 : }
420 : }
421 2 : state.dataRoomAirflowNetModel->OneTimeFlagConf = false;
422 2 : if (allocated(NodeFound)) {
423 2 : NodeFound.deallocate();
424 : }
425 2 : if (ErrorsFound) {
426 0 : ShowFatalError(state, "GetRoomAirflowNetworkData: Errors found getting air model input. Program terminates.");
427 : }
428 : } // if (allocated)
429 : } // if (OneTimeFlagConf)
430 :
431 3 : if (state.dataGlobal->BeginEnvrnFlag && state.dataRoomAirflowNetModel->EnvrnFlag) {
432 2 : for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
433 1 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(iZone);
434 1 : if (!afnZoneInfo.IsUsed) {
435 0 : continue;
436 : }
437 3 : for (auto &afnNode : afnZoneInfo.Node) {
438 2 : afnNode.AirTemp = 23.0;
439 2 : afnNode.AirTempX = {23.0, 23.0, 23.0, 23.0};
440 2 : afnNode.AirTempDSX = {23.0, 23.0, 23.0, 23.0};
441 2 : afnNode.AirTempT1 = 23.0;
442 2 : afnNode.AirTempTX = 23.0;
443 2 : afnNode.AirTempT2 = 23.0;
444 :
445 2 : afnNode.HumRat = 0.0;
446 2 : afnNode.HumRatX = {0.0, 0.0, 0.0, 0.0};
447 2 : afnNode.HumRatDSX = {0.0, 0.0, 0.0, 0.0};
448 2 : afnNode.HumRatT1 = 0.0;
449 2 : afnNode.HumRatTX = 0.0;
450 2 : afnNode.HumRatT2 = 0.0;
451 :
452 2 : afnNode.SysDepZoneLoadsLagged = 0.0;
453 2 : afnNode.SysDepZoneLoadsLaggedOld = 0.0;
454 : }
455 : }
456 1 : state.dataRoomAirflowNetModel->EnvrnFlag = false;
457 : }
458 3 : if (!state.dataGlobal->BeginEnvrnFlag) {
459 0 : state.dataRoomAirflowNetModel->EnvrnFlag = true;
460 : }
461 :
462 : // reuse code in ZoneTempPredictorCorrector for sensible components.
463 3 : CalcNodeSums(state, zoneNum, roomAirNodeNum);
464 :
465 3 : SumNonAirSystemResponseForNode(state, zoneNum, roomAirNodeNum);
466 :
467 : // latent gains.
468 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
469 3 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
470 :
471 3 : if (allocated(afnNode.SurfMask)) {
472 3 : CalcSurfaceMoistureSums(state, zoneNum, roomAirNodeNum, afnNode.SumHmAW, afnNode.SumHmARa, afnNode.SumHmARaW, afnNode.SurfMask);
473 : }
474 :
475 : // prepare AirflowNetwor flow rates and temperatures
476 3 : Real64 SumLinkMCp = 0.0;
477 3 : Real64 SumLinkMCpT = 0.0;
478 3 : Real64 SumLinkM = 0.0;
479 3 : Real64 SumLinkMW = 0.0;
480 :
481 3 : if (afnNode.AFNNodeID > 0) {
482 12 : for (int iLink = 1; iLink <= afnNode.NumOfAirflowLinks; ++iLink) {
483 9 : auto &afnLink = afnNode.Link(iLink);
484 9 : int linkNum = afnLink.AFNSimuID;
485 9 : if (state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[0] == afnNode.AFNNodeID) { // incoming flow
486 7 : int nodeInNum = state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[1];
487 7 : afnLink.TempIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).TZ;
488 7 : afnLink.HumRatIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).WZ;
489 7 : afnLink.MdotIn = state.afn->AirflowNetworkLinkSimu(linkNum).FLOW2;
490 : }
491 9 : if (state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[1] == afnNode.AFNNodeID) { // outgoing flow
492 2 : int nodeInNum = state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[0];
493 2 : afnLink.TempIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).TZ;
494 2 : afnLink.HumRatIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).WZ;
495 2 : afnLink.MdotIn = state.afn->AirflowNetworkLinkSimu(linkNum).FLOW;
496 : }
497 : }
498 :
499 12 : for (int iLink = 1; iLink <= afnNode.NumOfAirflowLinks; ++iLink) {
500 9 : auto &afnLink = afnNode.Link(iLink);
501 9 : Real64 CpAir = PsyCpAirFnW(afnLink.HumRatIn);
502 9 : SumLinkMCp += CpAir * afnLink.MdotIn;
503 9 : SumLinkMCpT += CpAir * afnLink.MdotIn * afnLink.TempIn;
504 9 : SumLinkM += afnLink.MdotIn;
505 9 : SumLinkMW += afnLink.MdotIn * afnLink.HumRatIn;
506 : }
507 : }
508 :
509 3 : afnNode.SumLinkMCp = SumLinkMCp;
510 3 : afnNode.SumLinkMCpT = SumLinkMCpT;
511 3 : afnNode.SumLinkM = SumLinkM;
512 3 : afnNode.SumLinkMW = SumLinkMW;
513 3 : afnNode.SysDepZoneLoadsLagged = afnNode.SysDepZoneLoadsLaggedOld;
514 :
515 3 : afnNode.RhoAir = PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, afnNode.AirTemp, afnNode.HumRat, "InitRoomAirModelAirflowNetwork");
516 :
517 3 : afnNode.CpAir = PsyCpAirFnW(afnNode.HumRat);
518 :
519 3 : } // InitRoomAirModelAirflowNetwork
520 :
521 : //*****************************************************************************************
522 :
523 2 : void CalcRoomAirModelAFN(EnergyPlusData &state, int const zoneNum,
524 : int const roomAirNodeNum) // index number for the specified zone and node
525 : {
526 :
527 : // SUBROUTINE INFORMATION:
528 : // AUTHOR Brent Griffith
529 : // DATE WRITTEN November 2009
530 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
531 :
532 : // PURPOSE OF THIS SUBROUTINE:
533 : // calculate new values for temperature and humidity ratio for room air node
534 :
535 : // METHODOLOGY EMPLOYED:
536 : // take terms(updated in init routine) and use classic air balance equations
537 : // solved for state variables. Store results in structure.
538 :
539 : // Using/Aliasing
540 2 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
541 : using Psychrometrics::PsyHgAirFnWTdb;
542 : using Psychrometrics::PsyRhFnTdbWPb;
543 :
544 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
545 : std::array<Real64, 3> NodeTempX;
546 : std::array<Real64, 3> NodeHumRatX;
547 : Real64 AirTempT1;
548 : Real64 HumRatT1;
549 :
550 2 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
551 2 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
552 :
553 2 : if (state.dataHVACGlobal->UseZoneTimeStepHistory) {
554 2 : NodeTempX[0] = afnNode.AirTempX[0];
555 2 : NodeTempX[1] = afnNode.AirTempX[1];
556 2 : NodeTempX[2] = afnNode.AirTempX[2];
557 :
558 2 : NodeHumRatX[0] = afnNode.HumRatX[0];
559 2 : NodeHumRatX[1] = afnNode.HumRatX[1];
560 2 : NodeHumRatX[2] = afnNode.HumRatX[2];
561 : } else { // use down - stepped history
562 0 : NodeTempX[0] = afnNode.AirTempDSX[0];
563 0 : NodeTempX[1] = afnNode.AirTempDSX[1];
564 0 : NodeTempX[2] = afnNode.AirTempDSX[2];
565 :
566 0 : NodeHumRatX[0] = afnNode.HumRatDSX[0];
567 0 : NodeHumRatX[1] = afnNode.HumRatDSX[1];
568 0 : NodeHumRatX[2] = afnNode.HumRatDSX[2];
569 : }
570 :
571 2 : if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
572 0 : AirTempT1 = afnNode.AirTempT1;
573 0 : HumRatT1 = afnNode.HumRatT1;
574 : }
575 : // solve for node drybulb temperature
576 2 : Real64 TempDepCoef = afnNode.SumHA + afnNode.SumLinkMCp + afnNode.SumSysMCp;
577 2 : Real64 TempIndCoef = afnNode.SumIntSensibleGain + afnNode.SumHATsurf - afnNode.SumHATref + afnNode.SumLinkMCpT + afnNode.SumSysMCpT +
578 2 : afnNode.NonAirSystemResponse + afnNode.SysDepZoneLoadsLagged;
579 2 : Real64 AirCap = afnNode.AirVolume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpSens * afnNode.RhoAir * afnNode.CpAir / TimeStepSysSec;
580 :
581 2 : if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
582 0 : if (TempDepCoef == 0.0) { // B=0
583 0 : afnNode.AirTemp = AirTempT1 + TempIndCoef / AirCap;
584 : } else {
585 0 : afnNode.AirTemp = (AirTempT1 - TempIndCoef / TempDepCoef) * std::exp(min(700.0, -TempDepCoef / AirCap)) + TempIndCoef / TempDepCoef;
586 : }
587 2 : } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::EulerMethod) {
588 0 : afnNode.AirTemp = (AirCap * AirTempT1 + TempIndCoef) / (AirCap + TempDepCoef);
589 : } else {
590 2 : afnNode.AirTemp = (TempIndCoef + AirCap * (3.0 * NodeTempX[0] - (3.0 / 2.0) * NodeTempX[1] + (1.0 / 3.0) * NodeTempX[2])) /
591 2 : ((11.0 / 6.0) * AirCap + TempDepCoef);
592 : }
593 :
594 : // solve for node humidity ratio using 3 algorithms
595 2 : Real64 H2OHtOfVap = PsyHgAirFnWTdb(afnNode.HumRat, afnNode.AirTemp);
596 2 : Real64 A = afnNode.SumLinkM + afnNode.SumHmARa + afnNode.SumSysM;
597 2 : Real64 B = (afnNode.SumIntLatentGain / H2OHtOfVap) + afnNode.SumSysMW + afnNode.SumLinkMW + afnNode.SumHmARaW;
598 2 : Real64 C = afnNode.RhoAir * afnNode.AirVolume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpMoist / TimeStepSysSec;
599 :
600 : // Exact solution
601 2 : if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
602 0 : if (A == 0.0) { // B=0
603 0 : afnNode.HumRat = HumRatT1 + B / C;
604 : } else {
605 0 : afnNode.HumRat = (HumRatT1 - B / A) * std::exp(min(700., -A / C)) + B / A;
606 : }
607 2 : } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::EulerMethod) {
608 0 : afnNode.HumRat = (C * HumRatT1 + B) / (C + A);
609 : } else {
610 2 : afnNode.HumRat = (B + C * (3.0 * NodeHumRatX[0] - (3.0 / 2.0) * NodeHumRatX[1] + (1.0 / 3.0) * NodeHumRatX[2])) / ((11.0 / 6.0) * C + A);
611 : }
612 :
613 2 : afnNode.AirCap = AirCap;
614 2 : afnNode.AirHumRat = C;
615 :
616 2 : afnNode.RelHumidity =
617 2 : PsyRhFnTdbWPb(state, afnNode.AirTemp, afnNode.HumRat, state.dataEnvrn->OutBaroPress, "CalcRoomAirModelAirflowNetwork") * 100.0;
618 :
619 2 : } // CalcRoomAirModelAirflowNetwork
620 :
621 1 : void UpdateRoomAirModelAFN(EnergyPlusData &state, int const zoneNum)
622 : {
623 :
624 : // SUBROUTINE INFORMATION:
625 : // AUTHOR B Griffith
626 : // DATE WRITTEN November 2009
627 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
628 :
629 : // PURPOSE OF THIS SUBROUTINE:
630 : // update variables
631 1 : auto const &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
632 :
633 1 : if (!afnZoneInfo.IsUsed) {
634 0 : return;
635 : }
636 :
637 1 : if (!state.dataGlobal->ZoneSizingCalc) {
638 1 : SumSystemDepResponseForNode(state, zoneNum);
639 : }
640 :
641 : // Update return node conditions
642 2 : for (int I = 1; I <= state.dataZoneEquip->ZoneEquipList(zoneNum).NumOfEquipTypes; ++I) { // loop over all equip types
643 1 : Real64 SumMass = 0.0;
644 1 : Real64 SumMassT = 0.0;
645 1 : Real64 SumMassW = 0.0;
646 1 : int RetNodeNum = 0;
647 3 : for (auto const &afnNode : afnZoneInfo.Node) {
648 4 : for (auto const &afnHVAC : afnNode.HVAC) {
649 2 : if (afnHVAC.EquipConfigIndex == I && afnHVAC.SupNodeNum > 0 && afnHVAC.RetNodeNum > 0) {
650 2 : Real64 NodeMass = state.dataLoopNodes->Node(afnHVAC.SupNodeNum).MassFlowRate * afnHVAC.ReturnFraction;
651 2 : SumMass += NodeMass;
652 2 : SumMassT += NodeMass * afnNode.AirTemp;
653 2 : SumMassW += NodeMass * afnNode.HumRat;
654 2 : RetNodeNum = afnHVAC.RetNodeNum;
655 : }
656 : }
657 : }
658 1 : if (SumMass > 0.0) {
659 1 : state.dataLoopNodes->Node(RetNodeNum).Temp = SumMassT / SumMass;
660 1 : state.dataLoopNodes->Node(RetNodeNum).HumRat = SumMassW / SumMass;
661 : }
662 : }
663 : } // UpdateRoomAirModelAirflowNetwork
664 :
665 3 : void CalcNodeSums(EnergyPlusData &state, int const zoneNum, int const roomAirNodeNum)
666 : {
667 :
668 : // SUBROUTINE INFORMATION:
669 : // AUTHOR B Griffith
670 : // DATE WRITTEN August 2009
671 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
672 : // RE - ENGINEERED na
673 :
674 : // PURPOSE OF THIS SUBROUTINE :
675 : // This subroutine calculates the various sums that go into the zone heat balance
676 : // equation.This replaces the SUMC, SUMHA, and SUMHAT calculations that were
677 : // previously done in various places throughout the program.
678 : // The SumHAT portion of the code is reproduced in RadiantSystemHighTemp and
679 : // RadiantSystemLowTemp and should be updated accordingly.
680 : //
681 : // A reference temperature(Tref) is specified for use with the ceiling diffuser
682 : // convection correlation.A bogus value of Tref = -999.9 defaults to using
683 : // the zone air(i.e.outlet) temperature for the reference temperature.
684 : // If Tref is applied to all surfaces, SumHA = 0, and SumHATref /= 0.
685 : // If Tref is not used at all, SumHATref = 0, and SumHA /= 0.
686 : //
687 :
688 : // USE STATEMENTS:
689 : using InternalHeatGains::SumInternalConvectionGainsByIndices;
690 : using InternalHeatGains::SumInternalLatentGainsByIndices;
691 : using InternalHeatGains::SumReturnAirConvectionGainsByIndices;
692 : using InternalHeatGains::SumReturnAirConvectionGainsByTypes;
693 : using Psychrometrics::PsyCpAirFnW;
694 : using Psychrometrics::PsyRhoAirFnPbTdbW;
695 :
696 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
697 : Real64 HA; // !Hc*Area
698 : Real64 Area; // !Effective surface area
699 : Real64 RefAirTemp; // !Reference air temperature for surface convection calculations
700 : bool Found; //
701 :
702 3 : Real64 SumIntGain = 0.0; // node sum of convective internal gains
703 3 : Real64 SumHA = 0.0; // Zone sum of Hc*Area
704 3 : Real64 SumHATsurf = 0.0; // Zone sum of Hc*Area*Tsurf
705 3 : Real64 SumHATref = 0.0; // Zone sum of Hc*Area*Tref, for ceiling diffuser convection correlation
706 3 : Real64 SumSysMCp = 0.0; // Zone sum of air system MassFlowRate*Cp
707 3 : Real64 SumSysMCpT = 0.0; // Zone sum of air system MassFlowRate*Cp*T
708 3 : Real64 SumSysM = 0.0; // Zone sum of air system MassFlowRate
709 3 : Real64 SumSysMW = 0.0; // Zone sum of air system MassFlowRate*W
710 :
711 3 : auto const &zone = state.dataHeatBal->Zone(zoneNum);
712 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
713 3 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
714 : // Sum all convective internal gains: SumIntGain
715 6 : afnNode.SumIntSensibleGain = SumInternalConvectionGainsByIndices(
716 3 : state, afnNode.NumIntGains, afnNode.intGainsDeviceSpaces, afnNode.IntGainsDeviceIndices, afnNode.IntGainsFractions);
717 :
718 6 : afnNode.SumIntLatentGain = SumInternalLatentGainsByIndices(
719 3 : state, afnNode.NumIntGains, afnNode.intGainsDeviceSpaces, afnNode.IntGainsDeviceIndices, afnNode.IntGainsFractions);
720 : // Add heat to return air if zonal system(no return air) or cycling system(return air frequently very low or zero)
721 3 : if (state.dataHeatBal->Zone(zoneNum).NoHeatToReturnAir) {
722 : // *******************************************
723 0 : SumIntGain = SumReturnAirConvectionGainsByIndices(
724 0 : state, afnNode.NumIntGains, afnNode.intGainsDeviceSpaces, afnNode.IntGainsDeviceIndices, afnNode.IntGainsFractions);
725 0 : afnNode.SumIntSensibleGain += SumIntGain;
726 : }
727 :
728 : // Check to see if this is a controlled zone
729 : // Check to see if this is a plenum zone
730 3 : int zoneRetPlenumNum = 0;
731 3 : for (int iPlenum = 1; iPlenum <= state.dataZonePlenum->NumZoneReturnPlenums; ++iPlenum) {
732 0 : if (state.dataZonePlenum->ZoneRetPlenCond(iPlenum).ActualZoneNum != zoneNum) {
733 0 : continue;
734 : }
735 0 : zoneRetPlenumNum = iPlenum;
736 0 : break;
737 : }
738 3 : bool zoneSupPlenumNum = false;
739 3 : for (int iPlenum = 1; iPlenum <= state.dataZonePlenum->NumZoneSupplyPlenums; ++iPlenum) {
740 0 : if (state.dataZonePlenum->ZoneSupPlenCond(iPlenum).ActualZoneNum != zoneNum) {
741 0 : continue;
742 : }
743 0 : zoneSupPlenumNum = iPlenum;
744 0 : break;
745 : }
746 :
747 : // Plenum and controlled zones have a different set of inlet nodes which must be calculated.
748 3 : auto &zoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(zoneNum);
749 3 : if (zone.IsControlled) {
750 3 : auto &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
751 6 : for (int iNode = 1; iNode <= zoneEquipConfig.NumInletNodes; ++iNode) {
752 : // Get node conditions
753 : // this next block is of interest to irratic system loads... maybe nodes are not accurate at time of call ?
754 : // how can we tell ? predict step must be lagged ? correct step, systems have run.
755 3 : auto const &inletNode = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(iNode));
756 6 : for (auto const &afnHVAC : afnNode.HVAC) {
757 3 : if (afnHVAC.SupNodeNum == zoneEquipConfig.InletNode(iNode)) {
758 3 : Real64 MassFlowRate = inletNode.MassFlowRate * afnHVAC.SupplyFraction;
759 3 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
760 3 : SumSysMCp += MassFlowRate * CpAir;
761 3 : SumSysMCpT += MassFlowRate * CpAir * inletNode.Temp;
762 3 : SumSysM += MassFlowRate;
763 3 : SumSysMW += MassFlowRate * inletNode.HumRat;
764 : }
765 : } // EquipLoop
766 : } // NodeNum
767 0 : } else if (zoneRetPlenumNum != 0) {
768 0 : auto const &zoneRetPlenum = state.dataZonePlenum->ZoneRetPlenCond(zoneRetPlenumNum);
769 0 : for (int iNode = 1; iNode <= zoneRetPlenum.NumInletNodes; ++iNode) {
770 : // Get node conditions
771 0 : auto const &zoneRetPlenumNode = state.dataLoopNodes->Node(zoneRetPlenum.InletNode(iNode));
772 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
773 0 : SumSysMCp += zoneRetPlenumNode.MassFlowRate * CpAir;
774 0 : SumSysMCpT += zoneRetPlenumNode.MassFlowRate * CpAir * zoneRetPlenumNode.Temp;
775 : } // NodeNum
776 : // add in the leaks
777 0 : for (int iADU = 1; iADU <= zoneRetPlenum.NumADUs; ++iADU) {
778 0 : int ADUNum = zoneRetPlenum.ADUIndex(iADU);
779 0 : auto const &adu = state.dataDefineEquipment->AirDistUnit(ADUNum);
780 0 : if (adu.UpStreamLeak) {
781 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
782 0 : SumSysMCp += adu.MassFlowRateUpStrLk * CpAir;
783 0 : SumSysMCpT += adu.MassFlowRateUpStrLk * CpAir * state.dataLoopNodes->Node(adu.InletNodeNum).Temp;
784 : }
785 0 : if (adu.DownStreamLeak) {
786 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
787 0 : SumSysMCp += adu.MassFlowRateDnStrLk * CpAir;
788 0 : SumSysMCpT += adu.MassFlowRateDnStrLk * CpAir * state.dataLoopNodes->Node(adu.OutletNodeNum).Temp;
789 : }
790 : } // ADUListIndex
791 0 : } else if (zoneSupPlenumNum != 0) {
792 : // Get node conditions
793 0 : auto const &zoneSupPlenum = state.dataZonePlenum->ZoneSupPlenCond(zoneSupPlenumNum);
794 0 : auto const &inletNode = state.dataLoopNodes->Node(zoneSupPlenum.InletNode);
795 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
796 0 : SumSysMCp += inletNode.MassFlowRate * CpAir;
797 0 : SumSysMCpT += inletNode.MassFlowRate * CpAir * inletNode.Temp;
798 : }
799 :
800 3 : int ZoneMult = zone.Multiplier * zone.ListMultiplier;
801 :
802 3 : SumSysMCp /= ZoneMult;
803 3 : SumSysMCpT /= ZoneMult;
804 3 : SumSysM /= ZoneMult;
805 3 : SumSysMW /= ZoneMult;
806 :
807 : // Sum all surface convection : SumHA, SumHATsurf, SumHATref(and additional contributions to SumIntGain)
808 : // Modified by Gu to include assigned surfaces only shown in the surface lsit
809 3 : if (!afnNode.HasSurfacesAssigned) {
810 0 : return;
811 : }
812 :
813 3 : int surfCount = 0;
814 6 : for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
815 3 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
816 9 : for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
817 6 : ++surfCount;
818 6 : if (afnZoneInfo.ControlAirNodeID == roomAirNodeNum) {
819 2 : Found = false;
820 5 : for (int Loop = 1; Loop <= afnZoneInfo.NumOfAirNodes; ++Loop) {
821 4 : if (Loop != roomAirNodeNum) {
822 2 : if (afnZoneInfo.Node(Loop).SurfMask(surfCount)) {
823 1 : Found = true;
824 1 : break;
825 : }
826 : }
827 : }
828 2 : if (Found) {
829 1 : continue;
830 : }
831 : } else {
832 4 : if (!afnNode.SurfMask(surfCount)) {
833 2 : continue;
834 : }
835 : }
836 :
837 3 : HA = 0.0;
838 3 : Area = state.dataSurface->Surface(SurfNum).Area; // For windows, this is the glazing area
839 :
840 3 : if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) {
841 :
842 : // Add to the convective internal gains
843 0 : if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
844 : // The shade area covers the area of the glazing plus the area of the dividers.
845 0 : Area += state.dataSurface->SurfWinDividerArea(SurfNum);
846 0 : SumIntGain += state.dataSurface->SurfWinDividerHeatGain(SurfNum);
847 : }
848 :
849 : // Convective heat gain from natural convection in gap between glass and interior shade or blind
850 0 : if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
851 0 : SumIntGain += state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum);
852 : }
853 :
854 : // Convective heat gain from airflow window
855 0 : if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
856 0 : SumIntGain += state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum);
857 0 : if (zone.NoHeatToReturnAir) {
858 0 : SumIntGain += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
859 0 : state.dataSurface->SurfWinHeatGain(SurfNum) += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
860 0 : if (state.dataSurface->SurfWinHeatGain(SurfNum) >= 0.0) {
861 0 : state.dataSurface->SurfWinHeatGainRep(SurfNum) = state.dataSurface->SurfWinHeatGain(SurfNum);
862 0 : state.dataSurface->SurfWinHeatGainRepEnergy(SurfNum) =
863 0 : state.dataSurface->SurfWinHeatGainRep(SurfNum) * state.dataGlobal->TimeStepZone * Constant::rSecsInHour;
864 : } else {
865 0 : state.dataSurface->SurfWinHeatLossRep(SurfNum) = -state.dataSurface->SurfWinHeatGain(SurfNum);
866 0 : state.dataSurface->SurfWinHeatLossRepEnergy(SurfNum) =
867 0 : state.dataSurface->SurfWinHeatLossRep(SurfNum) * state.dataGlobal->TimeStepZone * Constant::rSecsInHour;
868 : }
869 0 : state.dataSurface->SurfWinHeatTransferRepEnergy(SurfNum) =
870 0 : state.dataSurface->SurfWinHeatGain(SurfNum) * state.dataGlobal->TimeStepZone * Constant::rSecsInHour;
871 : }
872 : }
873 :
874 : // Add to the surface convection sums
875 0 : if (state.dataSurface->SurfWinFrameArea(SurfNum) > 0.0) {
876 : // Window frame contribution
877 0 : SumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
878 0 : (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum)) * state.dataSurface->SurfWinFrameTempIn(SurfNum);
879 0 : HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
880 0 : (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum));
881 : }
882 :
883 0 : if (state.dataSurface->SurfWinDividerArea(SurfNum) > 0.0 &&
884 0 : !ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
885 : // Window divider contribution(only from shade or blind for window with divider and interior shade or blind)
886 0 : SumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
887 0 : (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum)) *
888 0 : state.dataSurface->SurfWinDividerTempIn(SurfNum);
889 0 : HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
890 0 : (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum));
891 : }
892 :
893 : } // End of check if window
894 :
895 3 : HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area;
896 3 : SumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(SurfNum);
897 :
898 3 : if (state.dataSurface->SurfTAirRef(SurfNum) == DataSurfaces::RefAirTemp::ZoneMeanAirTemp) {
899 : // The zone air is the reference temperature(which is to be solved for in CorrectZoneAirTemp).
900 3 : RefAirTemp = zoneHB.MAT;
901 3 : SumHA += HA;
902 0 : } else if (state.dataSurface->SurfTAirRef(SurfNum) == DataSurfaces::RefAirTemp::AdjacentAirTemp) {
903 0 : RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(SurfNum);
904 0 : SumHATref += HA * RefAirTemp;
905 0 : } else if (state.dataSurface->SurfTAirRef(SurfNum) == DataSurfaces::RefAirTemp::ZoneSupplyAirTemp) {
906 : // check whether this zone is a controlled zone or not
907 0 : if (!zone.IsControlled) {
908 0 : ShowFatalError(state,
909 0 : format("Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone {}", zone.Name));
910 0 : return;
911 : }
912 : // determine supply air temperature as a weighted average of the inlet temperatures.
913 0 : RefAirTemp = SumSysMCpT / SumSysMCp;
914 0 : SumHATref += HA * RefAirTemp;
915 : } else {
916 0 : RefAirTemp = zoneHB.MAT;
917 0 : SumHA += HA;
918 : }
919 :
920 : } // SurfNum
921 3 : }
922 : // Assemble values
923 3 : afnNode.SumHA = SumHA;
924 3 : afnNode.SumHATsurf = SumHATsurf;
925 3 : afnNode.SumHATref = SumHATref;
926 3 : afnNode.SumSysMCp = SumSysMCp;
927 3 : afnNode.SumSysMCpT = SumSysMCpT;
928 3 : afnNode.SumSysM = SumSysM;
929 3 : afnNode.SumSysMW = SumSysMW;
930 :
931 : } // CalcNodeSums
932 :
933 3 : void CalcSurfaceMoistureSums(EnergyPlusData &state,
934 : int const zoneNum,
935 : int const roomAirNodeNum,
936 : Real64 &SumHmAW,
937 : Real64 &SumHmARa,
938 : Real64 &SumHmARaW,
939 : [[maybe_unused]] Array1D<bool> const &SurfMask)
940 : {
941 :
942 : // SUBROUTINE INFORMATION:
943 : // AUTHOR B Griffith
944 : // derived from P. Biddulph-- HAMT, L. Gu -- EPMD,
945 : // DATE WRITTEN November 2009
946 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
947 :
948 : // PURPOSE OF THIS SUBROUTINE:
949 : // Breakout summation of surface moisture interaction terms
950 :
951 : // Using/Aliasing
952 :
953 : using HeatBalanceHAMTManager::UpdateHeatBalHAMT;
954 : using MoistureBalanceEMPDManager::UpdateMoistureBalanceEMPD;
955 : using Psychrometrics::PsyRhFnTdbRhov;
956 : using Psychrometrics::PsyRhFnTdbRhovLBnd0C;
957 : using Psychrometrics::PsyRhoAirFnPbTdbW;
958 : using Psychrometrics::PsyWFnTdbRhPb;
959 :
960 3 : SumHmAW = 0.0;
961 3 : SumHmARa = 0.0;
962 3 : SumHmARaW = 0.0;
963 :
964 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
965 :
966 3 : int surfCount = 1;
967 6 : for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
968 3 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
969 9 : for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum, ++surfCount) {
970 6 : auto const &surf = state.dataSurface->Surface(SurfNum);
971 6 : if (surf.Class == SurfaceClass::Window) {
972 0 : continue;
973 : }
974 :
975 6 : if (afnZoneInfo.ControlAirNodeID == roomAirNodeNum) {
976 2 : bool Found = false;
977 6 : for (int Loop = 1; Loop <= afnZoneInfo.NumOfAirNodes && !Found; ++Loop) {
978 : // None - assigned surfaces belong to the zone node
979 4 : Found = (Loop != roomAirNodeNum) && afnZoneInfo.Node(Loop).SurfMask(surfCount);
980 : }
981 2 : if (Found) {
982 1 : continue;
983 : }
984 : } else {
985 4 : if (!afnZoneInfo.Node(roomAirNodeNum).SurfMask(surfCount)) {
986 2 : continue;
987 : }
988 : }
989 :
990 3 : auto const &HMassConvInFD = state.dataMstBal->HMassConvInFD(SurfNum);
991 3 : auto &RhoVaporSurfIn = state.dataMstBal->RhoVaporSurfIn(SurfNum);
992 3 : auto &RhoVaporAirIn = state.dataMstBal->RhoVaporAirIn(SurfNum);
993 3 : if (surf.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::HAMT) {
994 0 : UpdateHeatBalHAMT(state, SurfNum);
995 :
996 0 : SumHmAW += HMassConvInFD * surf.Area * (RhoVaporSurfIn - RhoVaporAirIn);
997 :
998 0 : Real64 RhoAirZone = PsyRhoAirFnPbTdbW(
999 : state,
1000 0 : state.dataEnvrn->OutBaroPress,
1001 0 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(surf.Zone).MAT,
1002 0 : PsyRhFnTdbRhov(state,
1003 0 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataSurface->Surface(SurfNum).Zone).MAT,
1004 : RhoVaporAirIn,
1005 : "RhoAirZone"));
1006 :
1007 0 : Real64 Wsurf = PsyWFnTdbRhPb(state,
1008 0 : state.dataHeatBalSurf->SurfTempInTmp(SurfNum),
1009 0 : PsyRhFnTdbRhov(state, state.dataHeatBalSurf->SurfTempInTmp(SurfNum), RhoVaporSurfIn, "Wsurf"),
1010 0 : state.dataEnvrn->OutBaroPress);
1011 :
1012 0 : SumHmARa += HMassConvInFD * surf.Area * RhoAirZone;
1013 0 : SumHmARaW += HMassConvInFD * surf.Area * RhoAirZone * Wsurf;
1014 : }
1015 :
1016 3 : else if (surf.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::EMPD) {
1017 :
1018 3 : UpdateMoistureBalanceEMPD(state, SurfNum);
1019 3 : RhoVaporSurfIn = state.dataMstBalEMPD->RVSurface(SurfNum);
1020 :
1021 3 : SumHmAW += HMassConvInFD * surf.Area * (RhoVaporSurfIn - RhoVaporAirIn);
1022 3 : SumHmARa +=
1023 6 : HMassConvInFD * surf.Area *
1024 6 : PsyRhoAirFnPbTdbW(state,
1025 3 : state.dataEnvrn->OutBaroPress,
1026 3 : state.dataHeatBalSurf->SurfTempInTmp(SurfNum),
1027 3 : PsyWFnTdbRhPb(state,
1028 3 : state.dataHeatBalSurf->SurfTempInTmp(SurfNum),
1029 3 : PsyRhFnTdbRhovLBnd0C(state, state.dataHeatBalSurf->SurfTempInTmp(SurfNum), RhoVaporAirIn),
1030 3 : state.dataEnvrn->OutBaroPress));
1031 3 : SumHmARaW += HMassConvInFD * surf.Area * RhoVaporSurfIn;
1032 : }
1033 : } // for (SurfNum)
1034 3 : } // for (spaceNum)
1035 :
1036 3 : } // CalcSurfaceMoistureSums
1037 :
1038 3 : void SumNonAirSystemResponseForNode(EnergyPlusData &state, int const zoneNum, int const roomAirNodeNum)
1039 : {
1040 :
1041 : // SUBROUTINE INFORMATION:
1042 : // AUTHOR B. Griffith
1043 : // DATE WRITTEN June 2012
1044 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
1045 :
1046 : // PURPOSE OF THIS SUBROUTINE:
1047 : // Sum system response from none air systems
1048 :
1049 : // USE STATEMENTS:
1050 : using BaseboardElectric::SimElectricBaseboard;
1051 : using BaseboardRadiator::SimBaseboard;
1052 : using ElectricBaseboardRadiator::SimElecBaseboard;
1053 : using HighTempRadiantSystem::SimHighTempRadiantSystem;
1054 : using HWBaseboardRadiator::SimHWBaseboard;
1055 : using RefrigeratedCase::SimAirChillerSet;
1056 : using SteamBaseboardRadiator::SimSteamBaseboard;
1057 :
1058 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1059 : Real64 SysOutputProvided;
1060 : Real64 LatOutputProvided;
1061 :
1062 : // TODO
1063 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
1064 3 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
1065 :
1066 3 : afnNode.NonAirSystemResponse = 0.0;
1067 :
1068 3 : if (!allocated(state.dataZoneEquip->ZoneEquipConfig)) {
1069 0 : return;
1070 : }
1071 :
1072 6 : for (auto &afnHVAC : afnNode.HVAC) {
1073 3 : switch (afnHVAC.zoneEquipType) {
1074 :
1075 0 : case DataZoneEquipment::ZoneEquipType::BaseboardWater: {
1076 : //'ZoneHVAC:Baseboard:RadiantConvective:Water' 13
1077 0 : SimHWBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1078 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1079 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1080 0 : } break;
1081 :
1082 0 : case DataZoneEquipment::ZoneEquipType::BaseboardSteam: {
1083 : // CASE(BBSteam_Num) !'ZoneHVAC:Baseboard:RadiantConvective:Steam' 14
1084 0 : SimSteamBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1085 :
1086 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1087 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1088 0 : } break;
1089 :
1090 0 : case DataZoneEquipment::ZoneEquipType::BaseboardConvectiveWater: {
1091 : // CASE(BBWaterConvective_Num) !'ZoneHVAC:Baseboard:Convective:Water' 16
1092 0 : SimBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1093 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1094 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1095 0 : } break;
1096 :
1097 0 : case DataZoneEquipment::ZoneEquipType::BaseboardConvectiveElectric: {
1098 : // CASE(BBElectricConvective_Num) !'ZoneHVAC:Baseboard:Convective:Electric' 15
1099 0 : SimElectricBaseboard(state, afnHVAC.Name, zoneNum, SysOutputProvided, afnHVAC.CompIndex);
1100 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1101 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1102 0 : } break;
1103 :
1104 0 : case DataZoneEquipment::ZoneEquipType::RefrigerationChillerSet: {
1105 : // CASE(RefrigerationAirChillerSet_Num) !'ZoneHVAC:RefrigerationChillerSet' 20
1106 0 : SimAirChillerSet(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, LatOutputProvided, afnHVAC.CompIndex);
1107 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1108 0 : } break;
1109 :
1110 0 : case DataZoneEquipment::ZoneEquipType::BaseboardElectric: {
1111 : // CASE(BBElectric_Num) !'ZoneHVAC:Baseboard:RadiantConvective:Electric' 12
1112 0 : SimElecBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1113 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1114 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1115 0 : } break;
1116 :
1117 0 : case DataZoneEquipment::ZoneEquipType::HighTemperatureRadiant: {
1118 : // CASE(BBElectric_Num) !'ZoneHVAC:HighTemperatureRadiant' 17
1119 0 : SimHighTempRadiantSystem(state, afnHVAC.Name, false, SysOutputProvided, afnHVAC.CompIndex);
1120 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1121 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1122 0 : } break;
1123 :
1124 3 : default: {
1125 3 : } break;
1126 : } // switch
1127 :
1128 : // Zone sum of system convective gains, collected via NonAirSystemResponse
1129 : }
1130 :
1131 : } // SumNonAirSystemResponseForNode
1132 :
1133 : //*****************************************************************************************
1134 :
1135 1 : void SumSystemDepResponseForNode(EnergyPlusData &state, int const zoneNum)
1136 : {
1137 : // SUBROUTINE INFORMATION:
1138 : // AUTHOR B.Griffith
1139 : // DATE WRITTEN aug 2005, Jan2004
1140 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
1141 :
1142 : // PURPOSE OF THIS SUBROUTINE:
1143 : // Sum system sensible loads used at the next time step
1144 :
1145 : // USE STATEMENTS:
1146 : using ZoneDehumidifier::SimZoneDehumidifier;
1147 :
1148 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1149 : Real64 LatOutputProvided;
1150 :
1151 : // TODO
1152 :
1153 1 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
1154 :
1155 : // SysDepZoneLoads saved to be added to zone heat balance next
1156 1 : Real64 SysOutputProvided = 0.0;
1157 3 : for (auto &afnNode : afnZoneInfo.Node) {
1158 2 : afnNode.SysDepZoneLoadsLaggedOld = 0.0;
1159 4 : for (auto &afnHVAC : afnNode.HVAC) {
1160 2 : if (afnHVAC.zoneEquipType == DataZoneEquipment::ZoneEquipType::DehumidifierDX) {
1161 0 : if (SysOutputProvided == 0.0) {
1162 0 : SimZoneDehumidifier(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, LatOutputProvided, afnHVAC.CompIndex);
1163 : }
1164 0 : if (SysOutputProvided > 0.0) {
1165 0 : break;
1166 : }
1167 : }
1168 : }
1169 : }
1170 :
1171 1 : if (SysOutputProvided > 0.0) {
1172 0 : for (auto &afnNode : afnZoneInfo.Node) {
1173 0 : for (auto const &afnHVAC : afnNode.HVAC) {
1174 0 : if (afnHVAC.zoneEquipType == DataZoneEquipment::ZoneEquipType::DehumidifierDX) {
1175 0 : afnNode.SysDepZoneLoadsLaggedOld += afnHVAC.SupplyFraction * SysOutputProvided;
1176 : }
1177 : }
1178 : }
1179 : }
1180 :
1181 1 : } // SumSystemDepResponseForNode
1182 :
1183 : //*****************************************************************************************
1184 :
1185 : } // namespace RoomAir
1186 :
1187 : } // namespace EnergyPlus
|