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) continue;
186 1 : int NumSurfs = 0; // NumSurfs isn't used anywhere?
187 2 : for (int spaceNum : state.dataHeatBal->Zone(iZone).spaceIndexes) {
188 1 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
189 1 : NumSurfs += thisSpace.HTSurfaceLast - thisSpace.HTSurfaceFirst + 1;
190 : }
191 :
192 3 : for (auto &afnNode : afnZoneInfo.Node) {
193 : // calculate volume of air in node's control volume
194 2 : afnNode.AirVolume = state.dataHeatBal->Zone(iZone).Volume * afnNode.ZoneVolumeFraction;
195 :
196 4 : SetupOutputVariable(state,
197 : "RoomAirflowNetwork Node NonAirSystemResponse",
198 : Constant::Units::W,
199 2 : afnNode.NonAirSystemResponse,
200 : OutputProcessor::TimeStepType::System,
201 : OutputProcessor::StoreType::Average,
202 2 : afnNode.Name);
203 4 : SetupOutputVariable(state,
204 : "RoomAirflowNetwork Node SysDepZoneLoadsLagged",
205 : Constant::Units::W,
206 2 : afnNode.SysDepZoneLoadsLagged,
207 : OutputProcessor::TimeStepType::System,
208 : OutputProcessor::StoreType::Average,
209 2 : afnNode.Name);
210 4 : SetupOutputVariable(state,
211 : "RoomAirflowNetwork Node SumIntSensibleGain",
212 : Constant::Units::W,
213 2 : afnNode.SumIntSensibleGain,
214 : OutputProcessor::TimeStepType::System,
215 : OutputProcessor::StoreType::Average,
216 2 : afnNode.Name);
217 4 : SetupOutputVariable(state,
218 : "RoomAirflowNetwork Node SumIntLatentGain",
219 : Constant::Units::W,
220 2 : afnNode.SumIntLatentGain,
221 : OutputProcessor::TimeStepType::System,
222 : OutputProcessor::StoreType::Average,
223 2 : afnNode.Name);
224 : }
225 : }
226 1 : state.dataRoomAirflowNetModel->OneTimeFlag = false;
227 : }
228 :
229 3 : if (state.dataRoomAirflowNetModel->OneTimeFlagConf) { // then do one - time setup inits
230 2 : if (allocated(state.dataZoneEquip->ZoneEquipConfig) && allocated(state.dataZoneEquip->ZoneEquipList)) {
231 2 : int MaxNodeNum = 0;
232 2 : int MaxEquipNum = 0;
233 2 : bool ErrorsFound = false;
234 4 : for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
235 2 : if (!state.dataHeatBal->Zone(iZone).IsControlled) continue;
236 2 : MaxEquipNum = max(MaxEquipNum, state.dataZoneEquip->ZoneEquipList(iZone).NumOfEquipTypes);
237 2 : MaxNodeNum = max(MaxNodeNum, state.dataZoneEquip->ZoneEquipConfig(iZone).NumInletNodes);
238 : }
239 2 : if (MaxNodeNum > 0) {
240 2 : NodeFound.allocate(MaxNodeNum);
241 2 : NodeFound = false;
242 : }
243 2 : if (MaxEquipNum > 0) {
244 2 : EquipFound.allocate(MaxEquipNum);
245 2 : SupplyFrac.allocate(MaxEquipNum);
246 2 : ReturnFrac.allocate(MaxEquipNum);
247 2 : EquipFound = false;
248 2 : SupplyFrac = 0.0;
249 2 : ReturnFrac = 0.0;
250 : }
251 :
252 : // loop over all zones with RoomAirflowNetwork model
253 4 : for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
254 2 : auto const &zone = state.dataHeatBal->Zone(iZone);
255 2 : if (!zone.IsControlled) continue;
256 :
257 2 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(iZone);
258 2 : if (!afnZoneInfo.IsUsed) continue;
259 2 : afnZoneInfo.ActualZoneID = iZone;
260 2 : SupplyFrac = 0.0;
261 2 : ReturnFrac = 0.0;
262 2 : NodeFound = false;
263 2 : int numAirDistUnits = 0;
264 :
265 2 : auto const &zoneEquipList = state.dataZoneEquip->ZoneEquipList(iZone);
266 2 : auto const &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(iZone);
267 :
268 : // find supply air node number
269 6 : for (auto &afnNode : afnZoneInfo.Node) {
270 8 : for (auto &afnHVAC : afnNode.HVAC) {
271 8 : for (int I = 1; I <= zoneEquipList.NumOfEquipTypes; ++I) { // loop over all equip types
272 4 : if (zoneEquipList.EquipType(I) == DataZoneEquipment::ZoneEquipType::AirDistributionUnit) {
273 2 : if (numAirDistUnits == 0)
274 : numAirDistUnits =
275 1 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ZoneHVAC:AirDistributionUnit");
276 2 : if (state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag) {
277 0 : ZoneAirLoopEquipmentManager::GetZoneAirLoopEquipment(state);
278 0 : state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag = false;
279 : }
280 :
281 4 : for (int AirDistUnitNum = 1; AirDistUnitNum <= numAirDistUnits; ++AirDistUnitNum) {
282 2 : if (zoneEquipList.EquipName(I) == state.dataDefineEquipment->AirDistUnit(AirDistUnitNum).Name) {
283 2 : if (afnHVAC.Name == state.dataDefineEquipment->AirDistUnit(AirDistUnitNum).EquipName(1)) {
284 2 : if (afnHVAC.EquipConfigIndex == 0) {
285 0 : afnHVAC.EquipConfigIndex = I;
286 : }
287 2 : EquipFound(I) = true;
288 2 : SupplyFrac(I) += afnHVAC.SupplyFraction;
289 2 : ReturnFrac(I) += afnHVAC.ReturnFraction;
290 : }
291 : }
292 : }
293 2 : } else if (Util::SameString(zoneEquipList.EquipName(I), afnHVAC.Name)) {
294 2 : if (afnHVAC.EquipConfigIndex == 0) {
295 2 : afnHVAC.EquipConfigIndex = I;
296 : }
297 2 : EquipFound(I) = true;
298 2 : SupplyFrac(I) += afnHVAC.SupplyFraction;
299 2 : ReturnFrac(I) += afnHVAC.ReturnFraction;
300 : }
301 : }
302 4 : for (int iNode = 1; iNode <= state.dataLoopNodes->NumOfNodes; ++iNode) { // loop over all nodes to find supply node ID
303 4 : if (Util::SameString(state.dataLoopNodes->NodeID(iNode), afnHVAC.SupplyNodeName)) {
304 4 : afnHVAC.SupNodeNum = iNode;
305 4 : break;
306 : }
307 : }
308 : // Verify inlet nodes
309 4 : int inletNodeIndex = 0;
310 4 : for (int iNode = 1; iNode <= zoneEquipConfig.NumInletNodes;
311 : ++iNode) { // loop over all supply inlet nodes in a single zone
312 : // !Get node conditions
313 4 : if (zoneEquipConfig.InletNode(iNode) == afnHVAC.SupNodeNum) {
314 4 : NodeFound(iNode) = true;
315 4 : inletNodeIndex = iNode;
316 4 : break;
317 : }
318 : }
319 :
320 4 : if (afnHVAC.SupNodeNum > 0 && afnHVAC.ReturnNodeName.empty()) {
321 : // Find matching return node
322 0 : for (int retNode = 1; retNode <= zoneEquipConfig.NumReturnNodes; ++retNode) {
323 0 : if ((zoneEquipConfig.ReturnNodeInletNum(retNode) == inletNodeIndex) &&
324 0 : (zoneEquipConfig.ReturnNode(retNode) > 0)) {
325 0 : afnHVAC.RetNodeNum = zoneEquipConfig.ReturnNode(retNode); // Zone return node
326 0 : break;
327 : }
328 : }
329 : }
330 :
331 4 : if (afnHVAC.RetNodeNum == 0) {
332 4 : for (int iNode = 1; iNode <= state.dataLoopNodes->NumOfNodes; ++iNode) { // loop over all nodes to find return node ID
333 4 : if (Util::SameString(state.dataLoopNodes->NodeID(iNode), afnHVAC.ReturnNodeName)) {
334 2 : afnHVAC.RetNodeNum = iNode;
335 2 : break;
336 : }
337 : }
338 : }
339 8 : SetupOutputVariable(state,
340 : "RoomAirflowNetwork Node HVAC Supply Fraction",
341 : Constant::Units::None,
342 4 : afnHVAC.SupplyFraction,
343 : OutputProcessor::TimeStepType::System,
344 : OutputProcessor::StoreType::Average,
345 4 : afnHVAC.Name);
346 8 : SetupOutputVariable(state,
347 : "RoomAirflowNetwork Node HVAC Return Fraction",
348 : Constant::Units::None,
349 4 : afnHVAC.ReturnFraction,
350 : OutputProcessor::TimeStepType::System,
351 : OutputProcessor::StoreType::Average,
352 4 : afnHVAC.Name);
353 : }
354 : }
355 : // Count node with.TRUE.
356 2 : int ISum = 0;
357 4 : for (int iNode = 1; iNode <= MaxNodeNum; ++iNode) { // loop over all supply inlet nodes in a single zone
358 2 : if (NodeFound(iNode)) ++ISum;
359 : }
360 : // Provide error messages with incorrect supplu node inputs
361 2 : if (ISum != zoneEquipConfig.NumInletNodes) {
362 0 : if (ISum > zoneEquipConfig.NumInletNodes) {
363 0 : ShowSevereError(
364 : state, "GetRoomAirflowNetworkData: The number of equipment listed in RoomAirflowNetwork:Node:HVACEquipment objects");
365 0 : ShowContinueError(state, format("is greater than the number of zone configuration inlet nodes in {}", zone.Name));
366 0 : ShowContinueError(state, "Please check inputs of both objects.");
367 0 : ErrorsFound = true;
368 : } else {
369 0 : ShowSevereError(
370 : state, "GetRoomAirflowNetworkData: The number of equipment listed in RoomAirflowNetwork:Node:HVACEquipment objects");
371 0 : ShowContinueError(state, format("is less than the number of zone configuration inlet nodes in {}", zone.Name));
372 0 : ShowContinueError(state, "Please check inputs of both objects.");
373 0 : ErrorsFound = true;
374 : }
375 : }
376 :
377 : // Check equipment names to ensure they are used in RoomAirflowNetwork : Node : HVACEquipment objects
378 4 : for (int I = 1; I <= zoneEquipList.NumOfEquipTypes; ++I) { // loop over all equip types
379 2 : if (!EquipFound(I)) {
380 0 : ShowSevereError(state,
381 : "GetRoomAirflowNetworkData: The equipment listed in ZoneEquipList is not found in the lsit of "
382 : "RoomAir:Node:AirflowNetwork:HVACEquipment objects =");
383 0 : ShowContinueError(state, format("{}. Please check inputs of both objects.", zoneEquipList.EquipName(I)));
384 0 : ErrorsFound = true;
385 : }
386 : }
387 :
388 : // Check fraction to ensure sum = 1.0 for every equipment
389 4 : for (int I = 1; I <= zoneEquipList.NumOfEquipTypes; ++I) { // loop over all equip types
390 2 : if (std::abs(SupplyFrac(I) - 1.0) > 0.001) {
391 0 : ShowSevereError(state, "GetRoomAirflowNetworkData: Invalid, zone supply fractions do not sum to 1.0");
392 0 : ShowContinueError(
393 0 : state, format("Entered in {} defined in RoomAir:Node:AirflowNetwork:HVACEquipment", zoneEquipList.EquipName(I)));
394 0 : ShowContinueError(state,
395 : "The Fraction of supply fraction values across all the roomair nodes in a zone needs to sum to 1.0.");
396 0 : ShowContinueError(state, format("The sum of fractions entered = {:.3R}", SupplyFrac(I)));
397 0 : ErrorsFound = true;
398 : }
399 2 : if (std::abs(ReturnFrac(I) - 1.0) > 0.001) {
400 0 : ShowSevereError(state, "GetRoomAirflowNetworkData: Invalid, zone return fractions do not sum to 1.0");
401 0 : ShowContinueError(
402 0 : state, format("Entered in {} defined in RoomAir:Node:AirflowNetwork:HVACEquipment", zoneEquipList.EquipName(I)));
403 0 : ShowContinueError(state,
404 : "The Fraction of return fraction values across all the roomair nodes in a zone needs to sum to 1.0.");
405 0 : ShowContinueError(state, format("The sum of fractions entered = {:.3R}", ReturnFrac(I)));
406 0 : ErrorsFound = true;
407 : }
408 : }
409 : }
410 2 : state.dataRoomAirflowNetModel->OneTimeFlagConf = false;
411 2 : if (allocated(NodeFound)) NodeFound.deallocate();
412 2 : if (ErrorsFound) {
413 0 : ShowFatalError(state, "GetRoomAirflowNetworkData: Errors found getting air model input. Program terminates.");
414 : }
415 : } // if (allocated)
416 : } // if (OneTimeFlagConf)
417 :
418 3 : if (state.dataGlobal->BeginEnvrnFlag && state.dataRoomAirflowNetModel->EnvrnFlag) {
419 2 : for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
420 1 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(iZone);
421 1 : if (!afnZoneInfo.IsUsed) continue;
422 3 : for (auto &afnNode : afnZoneInfo.Node) {
423 2 : afnNode.AirTemp = 23.0;
424 2 : afnNode.AirTempX = {23.0, 23.0, 23.0, 23.0};
425 2 : afnNode.AirTempDSX = {23.0, 23.0, 23.0, 23.0};
426 2 : afnNode.AirTempT1 = 23.0;
427 2 : afnNode.AirTempTX = 23.0;
428 2 : afnNode.AirTempT2 = 23.0;
429 :
430 2 : afnNode.HumRat = 0.0;
431 2 : afnNode.HumRatX = {0.0, 0.0, 0.0, 0.0};
432 2 : afnNode.HumRatDSX = {0.0, 0.0, 0.0, 0.0};
433 2 : afnNode.HumRatT1 = 0.0;
434 2 : afnNode.HumRatTX = 0.0;
435 2 : afnNode.HumRatT2 = 0.0;
436 :
437 2 : afnNode.SysDepZoneLoadsLagged = 0.0;
438 2 : afnNode.SysDepZoneLoadsLaggedOld = 0.0;
439 : }
440 : }
441 1 : state.dataRoomAirflowNetModel->EnvrnFlag = false;
442 : }
443 3 : if (!state.dataGlobal->BeginEnvrnFlag) {
444 0 : state.dataRoomAirflowNetModel->EnvrnFlag = true;
445 : }
446 :
447 : // reuse code in ZoneTempPredictorCorrector for sensible components.
448 3 : CalcNodeSums(state, zoneNum, roomAirNodeNum);
449 :
450 3 : SumNonAirSystemResponseForNode(state, zoneNum, roomAirNodeNum);
451 :
452 : // latent gains.
453 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
454 3 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
455 :
456 3 : if (allocated(afnNode.SurfMask)) {
457 3 : CalcSurfaceMoistureSums(state, zoneNum, roomAirNodeNum, afnNode.SumHmAW, afnNode.SumHmARa, afnNode.SumHmARaW, afnNode.SurfMask);
458 : }
459 :
460 : // prepare AirflowNetwor flow rates and temperatures
461 3 : Real64 SumLinkMCp = 0.0;
462 3 : Real64 SumLinkMCpT = 0.0;
463 3 : Real64 SumLinkM = 0.0;
464 3 : Real64 SumLinkMW = 0.0;
465 :
466 3 : if (afnNode.AFNNodeID > 0) {
467 12 : for (int iLink = 1; iLink <= afnNode.NumOfAirflowLinks; ++iLink) {
468 9 : auto &afnLink = afnNode.Link(iLink);
469 9 : int linkNum = afnLink.AFNSimuID;
470 9 : if (state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[0] == afnNode.AFNNodeID) { // incoming flow
471 7 : int nodeInNum = state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[1];
472 7 : afnLink.TempIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).TZ;
473 7 : afnLink.HumRatIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).WZ;
474 7 : afnLink.MdotIn = state.afn->AirflowNetworkLinkSimu(linkNum).FLOW2;
475 : }
476 9 : if (state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[1] == afnNode.AFNNodeID) { // outgoing flow
477 2 : int nodeInNum = state.afn->AirflowNetworkLinkageData(linkNum).NodeNums[0];
478 2 : afnLink.TempIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).TZ;
479 2 : afnLink.HumRatIn = state.afn->AirflowNetworkNodeSimu(nodeInNum).WZ;
480 2 : afnLink.MdotIn = state.afn->AirflowNetworkLinkSimu(linkNum).FLOW;
481 : }
482 : }
483 :
484 12 : for (int iLink = 1; iLink <= afnNode.NumOfAirflowLinks; ++iLink) {
485 9 : auto &afnLink = afnNode.Link(iLink);
486 9 : Real64 CpAir = PsyCpAirFnW(afnLink.HumRatIn);
487 9 : SumLinkMCp += CpAir * afnLink.MdotIn;
488 9 : SumLinkMCpT += CpAir * afnLink.MdotIn * afnLink.TempIn;
489 9 : SumLinkM += afnLink.MdotIn;
490 9 : SumLinkMW += afnLink.MdotIn * afnLink.HumRatIn;
491 : }
492 : }
493 :
494 3 : afnNode.SumLinkMCp = SumLinkMCp;
495 3 : afnNode.SumLinkMCpT = SumLinkMCpT;
496 3 : afnNode.SumLinkM = SumLinkM;
497 3 : afnNode.SumLinkMW = SumLinkMW;
498 3 : afnNode.SysDepZoneLoadsLagged = afnNode.SysDepZoneLoadsLaggedOld;
499 :
500 3 : afnNode.RhoAir = PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, afnNode.AirTemp, afnNode.HumRat, "InitRoomAirModelAirflowNetwork");
501 :
502 3 : afnNode.CpAir = PsyCpAirFnW(afnNode.HumRat);
503 :
504 3 : } // InitRoomAirModelAirflowNetwork
505 :
506 : //*****************************************************************************************
507 :
508 2 : void CalcRoomAirModelAFN(EnergyPlusData &state, int const zoneNum,
509 : int const roomAirNodeNum) // index number for the specified zone and node
510 : {
511 :
512 : // SUBROUTINE INFORMATION:
513 : // AUTHOR Brent Griffith
514 : // DATE WRITTEN November 2009
515 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
516 :
517 : // PURPOSE OF THIS SUBROUTINE:
518 : // calculate new values for temperature and humidity ratio for room air node
519 :
520 : // METHODOLOGY EMPLOYED:
521 : // take terms(updated in init routine) and use classic air balance equations
522 : // solved for state variables. Store results in structure.
523 :
524 : // Using/Aliasing
525 2 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
526 : using Psychrometrics::PsyHgAirFnWTdb;
527 : using Psychrometrics::PsyRhFnTdbWPb;
528 :
529 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
530 : std::array<Real64, 3> NodeTempX;
531 : std::array<Real64, 3> NodeHumRatX;
532 : Real64 AirTempT1;
533 : Real64 HumRatT1;
534 :
535 2 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
536 2 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
537 :
538 2 : if (state.dataHVACGlobal->UseZoneTimeStepHistory) {
539 2 : NodeTempX[0] = afnNode.AirTempX[0];
540 2 : NodeTempX[1] = afnNode.AirTempX[1];
541 2 : NodeTempX[2] = afnNode.AirTempX[2];
542 :
543 2 : NodeHumRatX[0] = afnNode.HumRatX[0];
544 2 : NodeHumRatX[1] = afnNode.HumRatX[1];
545 2 : NodeHumRatX[2] = afnNode.HumRatX[2];
546 : } else { // use down - stepped history
547 0 : NodeTempX[0] = afnNode.AirTempDSX[0];
548 0 : NodeTempX[1] = afnNode.AirTempDSX[1];
549 0 : NodeTempX[2] = afnNode.AirTempDSX[2];
550 :
551 0 : NodeHumRatX[0] = afnNode.HumRatDSX[0];
552 0 : NodeHumRatX[1] = afnNode.HumRatDSX[1];
553 0 : NodeHumRatX[2] = afnNode.HumRatDSX[2];
554 : }
555 :
556 2 : if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
557 0 : AirTempT1 = afnNode.AirTempT1;
558 0 : HumRatT1 = afnNode.HumRatT1;
559 : }
560 : // solve for node drybulb temperature
561 2 : Real64 TempDepCoef = afnNode.SumHA + afnNode.SumLinkMCp + afnNode.SumSysMCp;
562 2 : Real64 TempIndCoef = afnNode.SumIntSensibleGain + afnNode.SumHATsurf - afnNode.SumHATref + afnNode.SumLinkMCpT + afnNode.SumSysMCpT +
563 2 : afnNode.NonAirSystemResponse + afnNode.SysDepZoneLoadsLagged;
564 2 : Real64 AirCap = afnNode.AirVolume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpSens * afnNode.RhoAir * afnNode.CpAir / TimeStepSysSec;
565 :
566 2 : if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
567 0 : if (TempDepCoef == 0.0) { // B=0
568 0 : afnNode.AirTemp = AirTempT1 + TempIndCoef / AirCap;
569 : } else {
570 0 : afnNode.AirTemp = (AirTempT1 - TempIndCoef / TempDepCoef) * std::exp(min(700.0, -TempDepCoef / AirCap)) + TempIndCoef / TempDepCoef;
571 : }
572 2 : } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::EulerMethod) {
573 0 : afnNode.AirTemp = (AirCap * AirTempT1 + TempIndCoef) / (AirCap + TempDepCoef);
574 : } else {
575 2 : afnNode.AirTemp = (TempIndCoef + AirCap * (3.0 * NodeTempX[0] - (3.0 / 2.0) * NodeTempX[1] + (1.0 / 3.0) * NodeTempX[2])) /
576 2 : ((11.0 / 6.0) * AirCap + TempDepCoef);
577 : }
578 :
579 : // solve for node humidity ratio using 3 algorithms
580 2 : Real64 H2OHtOfVap = PsyHgAirFnWTdb(afnNode.HumRat, afnNode.AirTemp);
581 2 : Real64 A = afnNode.SumLinkM + afnNode.SumHmARa + afnNode.SumSysM;
582 2 : Real64 B = (afnNode.SumIntLatentGain / H2OHtOfVap) + afnNode.SumSysMW + afnNode.SumLinkMW + afnNode.SumHmARaW;
583 2 : Real64 C = afnNode.RhoAir * afnNode.AirVolume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpMoist / TimeStepSysSec;
584 :
585 : // Exact solution
586 2 : if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
587 0 : if (A == 0.0) { // B=0
588 0 : afnNode.HumRat = HumRatT1 + B / C;
589 : } else {
590 0 : afnNode.HumRat = (HumRatT1 - B / A) * std::exp(min(700., -A / C)) + B / A;
591 : }
592 2 : } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::EulerMethod) {
593 0 : afnNode.HumRat = (C * HumRatT1 + B) / (C + A);
594 : } else {
595 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);
596 : }
597 :
598 2 : afnNode.AirCap = AirCap;
599 2 : afnNode.AirHumRat = C;
600 :
601 2 : afnNode.RelHumidity =
602 2 : PsyRhFnTdbWPb(state, afnNode.AirTemp, afnNode.HumRat, state.dataEnvrn->OutBaroPress, "CalcRoomAirModelAirflowNetwork") * 100.0;
603 :
604 2 : } // CalcRoomAirModelAirflowNetwork
605 :
606 1 : void UpdateRoomAirModelAFN(EnergyPlusData &state, int const zoneNum)
607 : {
608 :
609 : // SUBROUTINE INFORMATION:
610 : // AUTHOR B Griffith
611 : // DATE WRITTEN November 2009
612 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
613 :
614 : // PURPOSE OF THIS SUBROUTINE:
615 : // update variables
616 1 : auto const &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
617 :
618 1 : if (!afnZoneInfo.IsUsed) return;
619 :
620 1 : if (!state.dataGlobal->ZoneSizingCalc) SumSystemDepResponseForNode(state, zoneNum);
621 :
622 : // Update return node conditions
623 2 : for (int I = 1; I <= state.dataZoneEquip->ZoneEquipList(zoneNum).NumOfEquipTypes; ++I) { // loop over all equip types
624 1 : Real64 SumMass = 0.0;
625 1 : Real64 SumMassT = 0.0;
626 1 : Real64 SumMassW = 0.0;
627 1 : int RetNodeNum = 0;
628 3 : for (auto const &afnNode : afnZoneInfo.Node) {
629 4 : for (auto const &afnHVAC : afnNode.HVAC) {
630 2 : if (afnHVAC.EquipConfigIndex == I && afnHVAC.SupNodeNum > 0 && afnHVAC.RetNodeNum > 0) {
631 2 : Real64 NodeMass = state.dataLoopNodes->Node(afnHVAC.SupNodeNum).MassFlowRate * afnHVAC.ReturnFraction;
632 2 : SumMass += NodeMass;
633 2 : SumMassT += NodeMass * afnNode.AirTemp;
634 2 : SumMassW += NodeMass * afnNode.HumRat;
635 2 : RetNodeNum = afnHVAC.RetNodeNum;
636 : }
637 : }
638 : }
639 1 : if (SumMass > 0.0) {
640 1 : state.dataLoopNodes->Node(RetNodeNum).Temp = SumMassT / SumMass;
641 1 : state.dataLoopNodes->Node(RetNodeNum).HumRat = SumMassW / SumMass;
642 : }
643 : }
644 : } // UpdateRoomAirModelAirflowNetwork
645 :
646 3 : void CalcNodeSums(EnergyPlusData &state, int const zoneNum, int const roomAirNodeNum)
647 : {
648 :
649 : // SUBROUTINE INFORMATION:
650 : // AUTHOR B Griffith
651 : // DATE WRITTEN August 2009
652 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
653 : // RE - ENGINEERED na
654 :
655 : // PURPOSE OF THIS SUBROUTINE :
656 : // This subroutine calculates the various sums that go into the zone heat balance
657 : // equation.This replaces the SUMC, SUMHA, and SUMHAT calculations that were
658 : // previously done in various places throughout the program.
659 : // The SumHAT portion of the code is reproduced in RadiantSystemHighTemp and
660 : // RadiantSystemLowTemp and should be updated accordingly.
661 : //
662 : // A reference temperature(Tref) is specified for use with the ceiling diffuser
663 : // convection correlation.A bogus value of Tref = -999.9 defaults to using
664 : // the zone air(i.e.outlet) temperature for the reference temperature.
665 : // If Tref is applied to all surfaces, SumHA = 0, and SumHATref /= 0.
666 : // If Tref is not used at all, SumHATref = 0, and SumHA /= 0.
667 : //
668 :
669 : // USE STATEMENTS:
670 : using InternalHeatGains::SumInternalConvectionGainsByIndices;
671 : using InternalHeatGains::SumInternalLatentGainsByIndices;
672 : using InternalHeatGains::SumReturnAirConvectionGainsByIndices;
673 : using InternalHeatGains::SumReturnAirConvectionGainsByTypes;
674 : using Psychrometrics::PsyCpAirFnW;
675 : using Psychrometrics::PsyRhoAirFnPbTdbW;
676 :
677 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
678 : Real64 HA; // !Hc*Area
679 : Real64 Area; // !Effective surface area
680 : Real64 RefAirTemp; // !Reference air temperature for surface convection calculations
681 : bool Found; //
682 :
683 3 : Real64 SumIntGain = 0.0; // node sum of convective internal gains
684 3 : Real64 SumHA = 0.0; // Zone sum of Hc*Area
685 3 : Real64 SumHATsurf = 0.0; // Zone sum of Hc*Area*Tsurf
686 3 : Real64 SumHATref = 0.0; // Zone sum of Hc*Area*Tref, for ceiling diffuser convection correlation
687 3 : Real64 SumSysMCp = 0.0; // Zone sum of air system MassFlowRate*Cp
688 3 : Real64 SumSysMCpT = 0.0; // Zone sum of air system MassFlowRate*Cp*T
689 3 : Real64 SumSysM = 0.0; // Zone sum of air system MassFlowRate
690 3 : Real64 SumSysMW = 0.0; // Zone sum of air system MassFlowRate*W
691 :
692 3 : auto const &zone = state.dataHeatBal->Zone(zoneNum);
693 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
694 3 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
695 : // Sum all convective internal gains: SumIntGain
696 6 : afnNode.SumIntSensibleGain = SumInternalConvectionGainsByIndices(
697 3 : state, afnNode.NumIntGains, afnNode.intGainsDeviceSpaces, afnNode.IntGainsDeviceIndices, afnNode.IntGainsFractions);
698 :
699 6 : afnNode.SumIntLatentGain = SumInternalLatentGainsByIndices(
700 3 : state, afnNode.NumIntGains, afnNode.intGainsDeviceSpaces, afnNode.IntGainsDeviceIndices, afnNode.IntGainsFractions);
701 : // Add heat to return air if zonal system(no return air) or cycling system(return air frequently very low or zero)
702 3 : if (state.dataHeatBal->Zone(zoneNum).NoHeatToReturnAir) {
703 : // *******************************************
704 0 : SumIntGain = SumReturnAirConvectionGainsByIndices(
705 0 : state, afnNode.NumIntGains, afnNode.intGainsDeviceSpaces, afnNode.IntGainsDeviceIndices, afnNode.IntGainsFractions);
706 0 : afnNode.SumIntSensibleGain += SumIntGain;
707 : }
708 :
709 : // Check to see if this is a controlled zone
710 : // Check to see if this is a plenum zone
711 3 : int zoneRetPlenumNum = 0;
712 3 : for (int iPlenum = 1; iPlenum <= state.dataZonePlenum->NumZoneReturnPlenums; ++iPlenum) {
713 0 : if (state.dataZonePlenum->ZoneRetPlenCond(iPlenum).ActualZoneNum != zoneNum) continue;
714 0 : zoneRetPlenumNum = iPlenum;
715 0 : break;
716 : }
717 3 : bool zoneSupPlenumNum = false;
718 3 : for (int iPlenum = 1; iPlenum <= state.dataZonePlenum->NumZoneSupplyPlenums; ++iPlenum) {
719 0 : if (state.dataZonePlenum->ZoneSupPlenCond(iPlenum).ActualZoneNum != zoneNum) continue;
720 0 : zoneSupPlenumNum = iPlenum;
721 0 : break;
722 : }
723 :
724 : // Plenum and controlled zones have a different set of inlet nodes which must be calculated.
725 3 : auto &zoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(zoneNum);
726 3 : if (zone.IsControlled) {
727 3 : auto &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
728 6 : for (int iNode = 1; iNode <= zoneEquipConfig.NumInletNodes; ++iNode) {
729 : // Get node conditions
730 : // this next block is of interest to irratic system loads... maybe nodes are not accurate at time of call ?
731 : // how can we tell ? predict step must be lagged ? correct step, systems have run.
732 3 : auto const &inletNode = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(iNode));
733 6 : for (auto const &afnHVAC : afnNode.HVAC) {
734 3 : if (afnHVAC.SupNodeNum == zoneEquipConfig.InletNode(iNode)) {
735 3 : Real64 MassFlowRate = inletNode.MassFlowRate * afnHVAC.SupplyFraction;
736 3 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
737 3 : SumSysMCp += MassFlowRate * CpAir;
738 3 : SumSysMCpT += MassFlowRate * CpAir * inletNode.Temp;
739 3 : SumSysM += MassFlowRate;
740 3 : SumSysMW += MassFlowRate * inletNode.HumRat;
741 : }
742 : } // EquipLoop
743 : } // NodeNum
744 0 : } else if (zoneRetPlenumNum != 0) {
745 0 : auto const &zoneRetPlenum = state.dataZonePlenum->ZoneRetPlenCond(zoneRetPlenumNum);
746 0 : for (int iNode = 1; iNode <= zoneRetPlenum.NumInletNodes; ++iNode) {
747 : // Get node conditions
748 0 : auto const &zoneRetPlenumNode = state.dataLoopNodes->Node(zoneRetPlenum.InletNode(iNode));
749 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
750 0 : SumSysMCp += zoneRetPlenumNode.MassFlowRate * CpAir;
751 0 : SumSysMCpT += zoneRetPlenumNode.MassFlowRate * CpAir * zoneRetPlenumNode.Temp;
752 : } // NodeNum
753 : // add in the leaks
754 0 : for (int iADU = 1; iADU <= zoneRetPlenum.NumADUs; ++iADU) {
755 0 : int ADUNum = zoneRetPlenum.ADUIndex(iADU);
756 0 : auto const &adu = state.dataDefineEquipment->AirDistUnit(ADUNum);
757 0 : if (adu.UpStreamLeak) {
758 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
759 0 : SumSysMCp += adu.MassFlowRateUpStrLk * CpAir;
760 0 : SumSysMCpT += adu.MassFlowRateUpStrLk * CpAir * state.dataLoopNodes->Node(adu.InletNodeNum).Temp;
761 : }
762 0 : if (adu.DownStreamLeak) {
763 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
764 0 : SumSysMCp += adu.MassFlowRateDnStrLk * CpAir;
765 0 : SumSysMCpT += adu.MassFlowRateDnStrLk * CpAir * state.dataLoopNodes->Node(adu.OutletNodeNum).Temp;
766 : }
767 : } // ADUListIndex
768 0 : } else if (zoneSupPlenumNum != 0) {
769 : // Get node conditions
770 0 : auto const &zoneSupPlenum = state.dataZonePlenum->ZoneSupPlenCond(zoneSupPlenumNum);
771 0 : auto const &inletNode = state.dataLoopNodes->Node(zoneSupPlenum.InletNode);
772 0 : Real64 CpAir = PsyCpAirFnW(zoneHB.airHumRat);
773 0 : SumSysMCp += inletNode.MassFlowRate * CpAir;
774 0 : SumSysMCpT += inletNode.MassFlowRate * CpAir * inletNode.Temp;
775 : }
776 :
777 3 : int ZoneMult = zone.Multiplier * zone.ListMultiplier;
778 :
779 3 : SumSysMCp /= ZoneMult;
780 3 : SumSysMCpT /= ZoneMult;
781 3 : SumSysM /= ZoneMult;
782 3 : SumSysMW /= ZoneMult;
783 :
784 : // Sum all surface convection : SumHA, SumHATsurf, SumHATref(and additional contributions to SumIntGain)
785 : // Modified by Gu to include assigned surfaces only shown in the surface lsit
786 3 : if (!afnNode.HasSurfacesAssigned) return;
787 :
788 3 : int surfCount = 0;
789 6 : for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
790 3 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
791 9 : for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
792 6 : ++surfCount;
793 6 : if (afnZoneInfo.ControlAirNodeID == roomAirNodeNum) {
794 2 : Found = false;
795 5 : for (int Loop = 1; Loop <= afnZoneInfo.NumOfAirNodes; ++Loop) {
796 4 : if (Loop != roomAirNodeNum) {
797 2 : if (afnZoneInfo.Node(Loop).SurfMask(surfCount)) {
798 1 : Found = true;
799 1 : break;
800 : }
801 : }
802 : }
803 2 : if (Found) continue;
804 : } else {
805 4 : if (!afnNode.SurfMask(surfCount)) continue;
806 : }
807 :
808 3 : HA = 0.0;
809 3 : Area = state.dataSurface->Surface(SurfNum).Area; // For windows, this is the glazing area
810 :
811 3 : if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) {
812 :
813 : // Add to the convective internal gains
814 0 : if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
815 : // The shade area covers the area of the glazing plus the area of the dividers.
816 0 : Area += state.dataSurface->SurfWinDividerArea(SurfNum);
817 0 : SumIntGain += state.dataSurface->SurfWinDividerHeatGain(SurfNum);
818 : }
819 :
820 : // Convective heat gain from natural convection in gap between glass and interior shade or blind
821 0 : if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum)))
822 0 : SumIntGain += state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum);
823 :
824 : // Convective heat gain from airflow window
825 0 : if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
826 0 : SumIntGain += state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum);
827 0 : if (zone.NoHeatToReturnAir) {
828 0 : SumIntGain += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
829 0 : state.dataSurface->SurfWinHeatGain(SurfNum) += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
830 0 : if (state.dataSurface->SurfWinHeatGain(SurfNum) >= 0.0) {
831 0 : state.dataSurface->SurfWinHeatGainRep(SurfNum) = state.dataSurface->SurfWinHeatGain(SurfNum);
832 0 : state.dataSurface->SurfWinHeatGainRepEnergy(SurfNum) =
833 0 : state.dataSurface->SurfWinHeatGainRep(SurfNum) * state.dataGlobal->TimeStepZone * Constant::rSecsInHour;
834 : } else {
835 0 : state.dataSurface->SurfWinHeatLossRep(SurfNum) = -state.dataSurface->SurfWinHeatGain(SurfNum);
836 0 : state.dataSurface->SurfWinHeatLossRepEnergy(SurfNum) =
837 0 : state.dataSurface->SurfWinHeatLossRep(SurfNum) * state.dataGlobal->TimeStepZone * Constant::rSecsInHour;
838 : }
839 0 : state.dataSurface->SurfWinHeatTransferRepEnergy(SurfNum) =
840 0 : state.dataSurface->SurfWinHeatGain(SurfNum) * state.dataGlobal->TimeStepZone * Constant::rSecsInHour;
841 : }
842 : }
843 :
844 : // Add to the surface convection sums
845 0 : if (state.dataSurface->SurfWinFrameArea(SurfNum) > 0.0) {
846 : // Window frame contribution
847 0 : SumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
848 0 : (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum)) * state.dataSurface->SurfWinFrameTempIn(SurfNum);
849 0 : HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
850 0 : (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum));
851 : }
852 :
853 0 : if (state.dataSurface->SurfWinDividerArea(SurfNum) > 0.0 &&
854 0 : !ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
855 : // Window divider contribution(only from shade or blind for window with divider and interior shade or blind)
856 0 : SumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
857 0 : (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum)) *
858 0 : state.dataSurface->SurfWinDividerTempIn(SurfNum);
859 0 : HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
860 0 : (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum));
861 : }
862 :
863 : } // End of check if window
864 :
865 3 : HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area;
866 3 : SumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(SurfNum);
867 :
868 3 : if (state.dataSurface->SurfTAirRef(SurfNum) == DataSurfaces::RefAirTemp::ZoneMeanAirTemp) {
869 : // The zone air is the reference temperature(which is to be solved for in CorrectZoneAirTemp).
870 3 : RefAirTemp = zoneHB.MAT;
871 3 : SumHA += HA;
872 0 : } else if (state.dataSurface->SurfTAirRef(SurfNum) == DataSurfaces::RefAirTemp::AdjacentAirTemp) {
873 0 : RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(SurfNum);
874 0 : SumHATref += HA * RefAirTemp;
875 0 : } else if (state.dataSurface->SurfTAirRef(SurfNum) == DataSurfaces::RefAirTemp::ZoneSupplyAirTemp) {
876 : // check whether this zone is a controlled zone or not
877 0 : if (!zone.IsControlled) {
878 0 : ShowFatalError(state,
879 0 : format("Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone {}", zone.Name));
880 0 : return;
881 : }
882 : // determine supply air temperature as a weighted average of the inlet temperatures.
883 0 : RefAirTemp = SumSysMCpT / SumSysMCp;
884 0 : SumHATref += HA * RefAirTemp;
885 : } else {
886 0 : RefAirTemp = zoneHB.MAT;
887 0 : SumHA += HA;
888 : }
889 :
890 : } // SurfNum
891 : }
892 : // Assemble values
893 3 : afnNode.SumHA = SumHA;
894 3 : afnNode.SumHATsurf = SumHATsurf;
895 3 : afnNode.SumHATref = SumHATref;
896 3 : afnNode.SumSysMCp = SumSysMCp;
897 3 : afnNode.SumSysMCpT = SumSysMCpT;
898 3 : afnNode.SumSysM = SumSysM;
899 3 : afnNode.SumSysMW = SumSysMW;
900 :
901 : } // CalcNodeSums
902 :
903 3 : void CalcSurfaceMoistureSums(EnergyPlusData &state,
904 : int const zoneNum,
905 : int const roomAirNodeNum,
906 : Real64 &SumHmAW,
907 : Real64 &SumHmARa,
908 : Real64 &SumHmARaW,
909 : [[maybe_unused]] Array1D<bool> const &SurfMask)
910 : {
911 :
912 : // SUBROUTINE INFORMATION:
913 : // AUTHOR B Griffith
914 : // derived from P. Biddulph-- HAMT, L. Gu -- EPMD,
915 : // DATE WRITTEN November 2009
916 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
917 :
918 : // PURPOSE OF THIS SUBROUTINE:
919 : // Breakout summation of surface moisture interaction terms
920 :
921 : // Using/Aliasing
922 :
923 : using HeatBalanceHAMTManager::UpdateHeatBalHAMT;
924 : using MoistureBalanceEMPDManager::UpdateMoistureBalanceEMPD;
925 : using Psychrometrics::PsyRhFnTdbRhov;
926 : using Psychrometrics::PsyRhFnTdbRhovLBnd0C;
927 : using Psychrometrics::PsyRhoAirFnPbTdbW;
928 : using Psychrometrics::PsyWFnTdbRhPb;
929 :
930 3 : SumHmAW = 0.0;
931 3 : SumHmARa = 0.0;
932 3 : SumHmARaW = 0.0;
933 :
934 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
935 :
936 3 : int surfCount = 1;
937 6 : for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
938 3 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
939 9 : for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum, ++surfCount) {
940 6 : auto const &surf = state.dataSurface->Surface(SurfNum);
941 6 : if (surf.Class == SurfaceClass::Window) continue;
942 :
943 6 : if (afnZoneInfo.ControlAirNodeID == roomAirNodeNum) {
944 2 : bool Found = false;
945 6 : for (int Loop = 1; Loop <= afnZoneInfo.NumOfAirNodes && !Found; ++Loop) {
946 : // None - assigned surfaces belong to the zone node
947 4 : Found = (Loop != roomAirNodeNum) && afnZoneInfo.Node(Loop).SurfMask(surfCount);
948 : }
949 2 : if (Found) continue;
950 : } else {
951 4 : if (!afnZoneInfo.Node(roomAirNodeNum).SurfMask(surfCount)) continue;
952 : }
953 :
954 3 : auto const &HMassConvInFD = state.dataMstBal->HMassConvInFD(SurfNum);
955 3 : auto &RhoVaporSurfIn = state.dataMstBal->RhoVaporSurfIn(SurfNum);
956 3 : auto &RhoVaporAirIn = state.dataMstBal->RhoVaporAirIn(SurfNum);
957 3 : if (surf.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::HAMT) {
958 0 : UpdateHeatBalHAMT(state, SurfNum);
959 :
960 0 : SumHmAW += HMassConvInFD * surf.Area * (RhoVaporSurfIn - RhoVaporAirIn);
961 :
962 0 : Real64 RhoAirZone = PsyRhoAirFnPbTdbW(
963 : state,
964 0 : state.dataEnvrn->OutBaroPress,
965 0 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(surf.Zone).MAT,
966 0 : PsyRhFnTdbRhov(state,
967 0 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataSurface->Surface(SurfNum).Zone).MAT,
968 : RhoVaporAirIn,
969 : "RhoAirZone"));
970 :
971 0 : Real64 Wsurf = PsyWFnTdbRhPb(state,
972 0 : state.dataHeatBalSurf->SurfTempInTmp(SurfNum),
973 0 : PsyRhFnTdbRhov(state, state.dataHeatBalSurf->SurfTempInTmp(SurfNum), RhoVaporSurfIn, "Wsurf"),
974 0 : state.dataEnvrn->OutBaroPress);
975 :
976 0 : SumHmARa += HMassConvInFD * surf.Area * RhoAirZone;
977 0 : SumHmARaW += HMassConvInFD * surf.Area * RhoAirZone * Wsurf;
978 : }
979 :
980 3 : else if (surf.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::EMPD) {
981 :
982 3 : UpdateMoistureBalanceEMPD(state, SurfNum);
983 3 : RhoVaporSurfIn = state.dataMstBalEMPD->RVSurface(SurfNum);
984 :
985 3 : SumHmAW += HMassConvInFD * surf.Area * (RhoVaporSurfIn - RhoVaporAirIn);
986 3 : SumHmARa +=
987 6 : HMassConvInFD * surf.Area *
988 6 : PsyRhoAirFnPbTdbW(state,
989 3 : state.dataEnvrn->OutBaroPress,
990 3 : state.dataHeatBalSurf->SurfTempInTmp(SurfNum),
991 3 : PsyWFnTdbRhPb(state,
992 3 : state.dataHeatBalSurf->SurfTempInTmp(SurfNum),
993 3 : PsyRhFnTdbRhovLBnd0C(state, state.dataHeatBalSurf->SurfTempInTmp(SurfNum), RhoVaporAirIn),
994 3 : state.dataEnvrn->OutBaroPress));
995 3 : SumHmARaW += HMassConvInFD * surf.Area * RhoVaporSurfIn;
996 : }
997 : } // for (SurfNum)
998 : } // for (spaceNum)
999 :
1000 3 : } // CalcSurfaceMoistureSums
1001 :
1002 3 : void SumNonAirSystemResponseForNode(EnergyPlusData &state, int const zoneNum, int const roomAirNodeNum)
1003 : {
1004 :
1005 : // SUBROUTINE INFORMATION:
1006 : // AUTHOR B. Griffith
1007 : // DATE WRITTEN June 2012
1008 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
1009 :
1010 : // PURPOSE OF THIS SUBROUTINE:
1011 : // Sum system response from none air systems
1012 :
1013 : // USE STATEMENTS:
1014 : using BaseboardElectric::SimElectricBaseboard;
1015 : using BaseboardRadiator::SimBaseboard;
1016 : using ElectricBaseboardRadiator::SimElecBaseboard;
1017 : using HighTempRadiantSystem::SimHighTempRadiantSystem;
1018 : using HWBaseboardRadiator::SimHWBaseboard;
1019 : using RefrigeratedCase::SimAirChillerSet;
1020 : using SteamBaseboardRadiator::SimSteamBaseboard;
1021 :
1022 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1023 : Real64 SysOutputProvided;
1024 : Real64 LatOutputProvided;
1025 :
1026 : // TODO
1027 3 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
1028 3 : auto &afnNode = afnZoneInfo.Node(roomAirNodeNum);
1029 :
1030 3 : afnNode.NonAirSystemResponse = 0.0;
1031 :
1032 3 : if (!allocated(state.dataZoneEquip->ZoneEquipConfig)) return;
1033 :
1034 6 : for (auto &afnHVAC : afnNode.HVAC) {
1035 3 : switch (afnHVAC.zoneEquipType) {
1036 :
1037 0 : case DataZoneEquipment::ZoneEquipType::BaseboardWater: {
1038 : //'ZoneHVAC:Baseboard:RadiantConvective:Water' 13
1039 0 : SimHWBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1040 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1041 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1042 0 : } break;
1043 :
1044 0 : case DataZoneEquipment::ZoneEquipType::BaseboardSteam: {
1045 : // CASE(BBSteam_Num) !'ZoneHVAC:Baseboard:RadiantConvective:Steam' 14
1046 0 : SimSteamBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1047 :
1048 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1049 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1050 0 : } break;
1051 :
1052 0 : case DataZoneEquipment::ZoneEquipType::BaseboardConvectiveWater: {
1053 : // CASE(BBWaterConvective_Num) !'ZoneHVAC:Baseboard:Convective:Water' 16
1054 0 : SimBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1055 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1056 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1057 0 : } break;
1058 :
1059 0 : case DataZoneEquipment::ZoneEquipType::BaseboardConvectiveElectric: {
1060 : // CASE(BBElectricConvective_Num) !'ZoneHVAC:Baseboard:Convective:Electric' 15
1061 0 : SimElectricBaseboard(state, afnHVAC.Name, zoneNum, SysOutputProvided, afnHVAC.CompIndex);
1062 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1063 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1064 0 : } break;
1065 :
1066 0 : case DataZoneEquipment::ZoneEquipType::RefrigerationChillerSet: {
1067 : // CASE(RefrigerationAirChillerSet_Num) !'ZoneHVAC:RefrigerationChillerSet' 20
1068 0 : SimAirChillerSet(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, LatOutputProvided, afnHVAC.CompIndex);
1069 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1070 0 : } break;
1071 :
1072 0 : case DataZoneEquipment::ZoneEquipType::BaseboardElectric: {
1073 : // CASE(BBElectric_Num) !'ZoneHVAC:Baseboard:RadiantConvective:Electric' 12
1074 0 : SimElecBaseboard(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, afnHVAC.CompIndex);
1075 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1076 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1077 0 : } break;
1078 :
1079 0 : case DataZoneEquipment::ZoneEquipType::HighTemperatureRadiant: {
1080 : // CASE(BBElectric_Num) !'ZoneHVAC:HighTemperatureRadiant' 17
1081 0 : SimHighTempRadiantSystem(state, afnHVAC.Name, false, SysOutputProvided, afnHVAC.CompIndex);
1082 0 : afnNode.NonAirSystemResponse += afnHVAC.SupplyFraction * SysOutputProvided;
1083 : // LatOutputProvided = 0.0d0 !This baseboard does not add / remove any latent heat
1084 0 : } break;
1085 :
1086 3 : default: {
1087 3 : } break;
1088 : } // switch
1089 :
1090 : // Zone sum of system convective gains, collected via NonAirSystemResponse
1091 : }
1092 :
1093 : } // SumNonAirSystemResponseForNode
1094 :
1095 : //*****************************************************************************************
1096 :
1097 1 : void SumSystemDepResponseForNode(EnergyPlusData &state, int const zoneNum)
1098 : {
1099 : // SUBROUTINE INFORMATION:
1100 : // AUTHOR B.Griffith
1101 : // DATE WRITTEN aug 2005, Jan2004
1102 : // MODIFIED Lixing Gu, Aug. 2015 for v8.4 replease
1103 :
1104 : // PURPOSE OF THIS SUBROUTINE:
1105 : // Sum system sensible loads used at the next time step
1106 :
1107 : // USE STATEMENTS:
1108 : using ZoneDehumidifier::SimZoneDehumidifier;
1109 :
1110 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1111 : Real64 LatOutputProvided;
1112 :
1113 : // TODO
1114 :
1115 1 : auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
1116 :
1117 : // SysDepZoneLoads saved to be added to zone heat balance next
1118 1 : Real64 SysOutputProvided = 0.0;
1119 3 : for (auto &afnNode : afnZoneInfo.Node) {
1120 2 : afnNode.SysDepZoneLoadsLaggedOld = 0.0;
1121 4 : for (auto &afnHVAC : afnNode.HVAC) {
1122 2 : if (afnHVAC.zoneEquipType == DataZoneEquipment::ZoneEquipType::DehumidifierDX) {
1123 0 : if (SysOutputProvided == 0.0)
1124 0 : SimZoneDehumidifier(state, afnHVAC.Name, zoneNum, false, SysOutputProvided, LatOutputProvided, afnHVAC.CompIndex);
1125 0 : if (SysOutputProvided > 0.0) break;
1126 : }
1127 : }
1128 : }
1129 :
1130 1 : if (SysOutputProvided > 0.0) {
1131 0 : for (auto &afnNode : afnZoneInfo.Node) {
1132 0 : for (auto const &afnHVAC : afnNode.HVAC) {
1133 0 : if (afnHVAC.zoneEquipType == DataZoneEquipment::ZoneEquipType::DehumidifierDX) {
1134 0 : afnNode.SysDepZoneLoadsLaggedOld += afnHVAC.SupplyFraction * SysOutputProvided;
1135 : }
1136 : }
1137 : }
1138 : }
1139 :
1140 1 : } // SumSystemDepResponseForNode
1141 :
1142 : //*****************************************************************************************
1143 :
1144 : } // namespace RoomAir
1145 :
1146 : } // namespace EnergyPlus
|