Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, 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 : // C++ Headers
49 : #include <cmath>
50 : #include <string>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Array1D.hh>
55 : #include <ObjexxFCL/Array2D.hh>
56 : #include <ObjexxFCL/Fmath.hh>
57 : #include <ObjexxFCL/member.functions.hh>
58 :
59 : // EnergyPlus Headers
60 : #include <EnergyPlus/BaseboardRadiator.hh>
61 : #include <EnergyPlus/Construction.hh>
62 : #include <EnergyPlus/ConvectionCoefficients.hh>
63 : #include <EnergyPlus/Data/EnergyPlusData.hh>
64 : #include <EnergyPlus/DataAirLoop.hh>
65 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
66 : #include <EnergyPlus/DataEnvironment.hh>
67 : #include <EnergyPlus/DataHVACGlobals.hh>
68 : #include <EnergyPlus/DataHeatBalSurface.hh>
69 : #include <EnergyPlus/DataHeatBalance.hh>
70 : #include <EnergyPlus/DataLoopNode.hh>
71 : #include <EnergyPlus/DataSizing.hh>
72 : #include <EnergyPlus/DataSurfaces.hh>
73 : #include <EnergyPlus/DataZoneEquipment.hh>
74 : #include <EnergyPlus/ExhaustAirSystemManager.hh>
75 : #include <EnergyPlus/FanCoilUnits.hh>
76 : #include <EnergyPlus/GeneralRoutines.hh>
77 : #include <EnergyPlus/HVACSingleDuctInduc.hh>
78 : #include <EnergyPlus/HWBaseboardRadiator.hh>
79 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
80 : #include <EnergyPlus/Material.hh>
81 : #include <EnergyPlus/MixerComponent.hh>
82 : #include <EnergyPlus/OutdoorAirUnit.hh>
83 : #include <EnergyPlus/PlantUtilities.hh>
84 : #include <EnergyPlus/PoweredInductionUnits.hh>
85 : #include <EnergyPlus/Psychrometrics.hh>
86 : #include <EnergyPlus/PurchasedAirManager.hh>
87 : #include <EnergyPlus/ScheduleManager.hh>
88 : #include <EnergyPlus/SolarCollectors.hh>
89 : #include <EnergyPlus/SplitterComponent.hh>
90 : #include <EnergyPlus/SteamBaseboardRadiator.hh>
91 : #include <EnergyPlus/UnitHeater.hh>
92 : #include <EnergyPlus/UnitVentilator.hh>
93 : #include <EnergyPlus/UtilityRoutines.hh>
94 : #include <EnergyPlus/VentilatedSlab.hh>
95 : #include <EnergyPlus/WaterCoils.hh>
96 : #include <EnergyPlus/ZonePlenum.hh>
97 :
98 : namespace EnergyPlus {
99 :
100 : // Integer constants for different system types handled by the routines in this file
101 : enum GeneralRoutinesEquipNums
102 : {
103 : ParallelPIUReheatNum = 1,
104 : SeriesPIUReheatNum = 2,
105 : HeatingCoilWaterNum = 3,
106 : BBWaterConvOnlyNum = 4,
107 : BBSteamRadConvNum = 5,
108 : BBWaterRadConvNum = 6,
109 : FourPipeFanCoilNum = 7,
110 : OutdoorAirUnitNum = 8,
111 : UnitHeaterNum = 9,
112 : UnitVentilatorNum = 10,
113 : VentilatedSlabNum = 11
114 : };
115 :
116 : enum class AirLoopHVACCompType
117 : {
118 : Invalid = -1,
119 : SupplyPlenum,
120 : ZoneSplitter,
121 : ZoneMixer,
122 : ReturnPlenum,
123 : Num
124 : };
125 :
126 : constexpr std::array<std::string_view, static_cast<int>(AirLoopHVACCompType::Num)> AirLoopHVACCompTypeNamesUC{
127 : "AIRLOOPHVAC:SUPPLYPLENUM", "AIRLOOPHVAC:ZONESPLITTER", "AIRLOOPHVAC:ZONEMIXER", "AIRLOOPHVAC:RETURNPLENUM"};
128 :
129 11202809 : void ControlCompOutput(EnergyPlusData &state,
130 : std::string const &CompName, // the component Name
131 : std::string const &CompType, // Type of component
132 : int &CompNum, // Index of component in component array
133 : bool const FirstHVACIteration, // flag for 1st HVAV iteration in the time step
134 : Real64 const QZnReq, // zone load to be met
135 : int const ActuatedNode, // node that controls unit output
136 : Real64 const MaxFlow, // maximum water flow
137 : Real64 const MinFlow, // minimum water flow
138 : Real64 const ControlOffset, // really the tolerance
139 : int &ControlCompTypeNum, // Internal type num for CompType
140 : int &CompErrIndex, // for Recurring error call
141 : Optional_int_const TempInNode, // inlet node for output calculation
142 : Optional_int_const TempOutNode, // outlet node for output calculation
143 : Optional<Real64 const> AirMassFlow, // air mass flow rate
144 : Optional_int_const Action, // 1=reverse; 2=normal
145 : Optional_int_const EquipIndex, // Identifier for equipment of Outdoor Air Unit "ONLY"
146 : PlantLocation const &plantLoc, // for plant components, Location
147 : Optional_int_const ControlledZoneIndex // controlled zone index for the zone containing the component
148 : )
149 : {
150 :
151 : // SUBROUTINE INFORMATION:
152 : // AUTHOR Richard J. Liesen
153 : // DATE WRITTEN April 2000
154 : // MODIFIED Brent Griffith, Sept 2010 update plant interactions
155 : // RE-ENGINEERED na
156 :
157 : // PURPOSE OF THIS SUBROUTINE:
158 : // The purpose of this subroutine is to control the output of heating or cooling
159 : // meet the zone load.
160 :
161 : // METHODOLOGY EMPLOYED:
162 : // Currently this is using an intervasl halving scheme to a control tolerance
163 :
164 : // Using/Aliasing
165 : using namespace DataLoopNode;
166 : using BaseboardRadiator::SimHWConvective;
167 : using FanCoilUnits::Calc4PipeFanCoil;
168 :
169 : using HWBaseboardRadiator::CalcHWBaseboard;
170 : using PlantUtilities::SetActuatedBranchFlowRate;
171 : using Psychrometrics::PsyCpAirFnW;
172 : using SteamBaseboardRadiator::CalcSteamBaseboard;
173 : using UnitHeater::CalcUnitHeaterComponents;
174 : using UnitVentilator::CalcUnitVentilatorComponents;
175 : using VentilatedSlab::CalcVentilatedSlabComps;
176 : using WaterCoils::SimulateWaterCoilComponents;
177 :
178 : // SUBROUTINE PARAMETER DEFINITIONS:
179 : // Iteration maximum for reheat control
180 : static int constexpr MaxIter(25);
181 : static Real64 const iter_fac(1.0 / std::pow(2, MaxIter - 3));
182 11202809 : int constexpr iReverseAction(1);
183 11202809 : int constexpr iNormalAction(2);
184 :
185 : // Note - order in routine must match order below
186 : // Plus -- order in ListOfComponents array must be in sorted order.
187 11202809 : int constexpr NumComponents(11);
188 : static Array1D_string const ListOfComponents(NumComponents,
189 : {"AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT",
190 : "AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT",
191 : "COIL:HEATING:WATER",
192 : "ZONEHVAC:BASEBOARD:CONVECTIVE:WATER",
193 : "ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:STEAM",
194 : "ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER",
195 : "ZONEHVAC:FOURPIPEFANCOIL",
196 : "ZONEHVAC:OUTDOORAIRUNIT",
197 : "ZONEHVAC:UNITHEATER",
198 : "ZONEHVAC:UNITVENTILATOR",
199 11202809 : "ZONEHVAC:VENTILATEDSLAB"});
200 :
201 : // DERIVED TYPE DEFINITIONS
202 : // Interval Half Type used for Controller
203 :
204 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
205 11202809 : int Iter(0); // Iteration limit for the interval halving process
206 : Real64 CpAir; // specific heat of air (J/kg-C)
207 : bool Converged;
208 : Real64 Denom; // the denominator of the control signal
209 : Real64 LoadMet; // Actual output of unit (watts)
210 : // INTEGER, SAVE :: ErrCount=0 ! Number of times that the maximum iterations was exceeded
211 : // INTEGER, SAVE :: ErrCount1=0 ! for recurring error
212 : bool WaterCoilAirFlowControl; // True if controlling air flow through water coil, water flow fixed
213 : int SimCompNum; // internal number for case statement
214 11202809 : Real64 HalvingPrec(0.0); // precision of halving algorithm
215 : bool BBConvergeCheckFlag; // additional check on convergence specifically for radiant/convective baseboard units
216 :
217 : // Object Data
218 11202809 : auto &ZoneInterHalf = state.dataGeneralRoutines->ZoneInterHalf;
219 11202809 : auto &ZoneController = state.dataGeneralRoutines->ZoneController;
220 :
221 11202809 : if (ControlCompTypeNum != 0) {
222 11200718 : SimCompNum = ControlCompTypeNum;
223 : } else {
224 2091 : SimCompNum = UtilityRoutines::FindItemInSortedList(CompType, ListOfComponents, NumComponents);
225 2091 : ControlCompTypeNum = SimCompNum;
226 : }
227 :
228 11202809 : Iter = 0;
229 11202809 : Converged = false;
230 11202809 : WaterCoilAirFlowControl = false;
231 11202809 : LoadMet = 0.0;
232 11202809 : HalvingPrec = 0.0;
233 :
234 : // At the beginning of every time step the value is reset to the User Input
235 11202809 : ZoneController.SetPoint = 0.0;
236 :
237 : // Set to converged controller
238 11202809 : ZoneInterHalf.MaxFlowCalc = true;
239 11202809 : ZoneInterHalf.MinFlowCalc = false;
240 11202809 : ZoneInterHalf.NormFlowCalc = false;
241 11202809 : ZoneInterHalf.MinFlowResult = false;
242 11202809 : ZoneInterHalf.MaxResult = 1.0;
243 11202809 : ZoneInterHalf.MinResult = 0.0;
244 :
245 : // Start the Solution Iteration
246 153371051 : while (!Converged) {
247 :
248 78132205 : if (FirstHVACIteration) {
249 32568867 : state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail = MaxFlow;
250 32568867 : state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail = MinFlow;
251 : // Check to make sure that the Minimum Flow rate is less than the max.
252 32568867 : if (MinFlow > MaxFlow) {
253 0 : ShowSevereError(state, "ControlCompOutput:" + CompType + ':' + CompName + ", Min Control Flow is > Max Control Flow");
254 0 : ShowContinueError(
255 0 : state, format("Acuated Node={} MinFlow=[{:.3T}], Max Flow={:.3T}", state.dataLoopNodes->NodeID(ActuatedNode), MinFlow, MaxFlow));
256 0 : ShowContinueErrorTimeStamp(state, "");
257 0 : ShowFatalError(state, "Program terminates due to preceding condition.");
258 : }
259 : } // End of FirstHVACIteration Conditional If
260 : // The interface managers can reset the Max or Min to available values during the time step
261 : // and these will then be the new setpoint limits for the controller to work within.
262 78132205 : if ((SimCompNum == 3) && (!present(AirMassFlow))) {
263 6057749 : ZoneController.MaxSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail;
264 6057749 : ZoneController.MinSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail;
265 : } else {
266 72074456 : ZoneController.MaxSetPoint =
267 72074456 : min(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMax);
268 72074456 : ZoneController.MinSetPoint =
269 72074456 : max(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMin);
270 : }
271 : // The first time through run at maximum flow rate and find results
272 78132205 : if (ZoneInterHalf.MaxFlowCalc) {
273 11202809 : ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
274 11202809 : ZoneInterHalf.MaxFlow = ZoneController.MaxSetPoint;
275 11202809 : ZoneInterHalf.MaxFlowCalc = false;
276 11202809 : ZoneInterHalf.MinFlowCalc = true;
277 : // Record the maximum flow rates and set the flow to the minimum and find results
278 66929396 : } else if (ZoneInterHalf.MinFlowCalc) {
279 11104366 : ZoneInterHalf.MaxResult = ZoneController.SensedValue;
280 11104366 : ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
281 11104366 : ZoneInterHalf.MinFlow = ZoneController.MinSetPoint;
282 11104366 : ZoneInterHalf.MinFlowCalc = false;
283 11104366 : ZoneInterHalf.MinFlowResult = true;
284 : // Record the minimum results and set flow to half way between the max and min and find results
285 55825030 : } else if (ZoneInterHalf.MinFlowResult) {
286 11053427 : ZoneInterHalf.MinResult = ZoneController.SensedValue;
287 11053427 : HalvingPrec = (ZoneInterHalf.MaxResult - ZoneInterHalf.MinResult) * iter_fac;
288 11053427 : ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
289 11053427 : ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
290 11053427 : ZoneInterHalf.MinFlowResult = false;
291 11053427 : ZoneInterHalf.NormFlowCalc = true;
292 : // Record the Mid results and check all possibilities and start interval halving procedure
293 44771603 : } else if (ZoneInterHalf.NormFlowCalc) {
294 44771603 : ZoneInterHalf.MidResult = ZoneController.SensedValue;
295 :
296 : // First check to see if the component is running; if not converge and return
297 44771603 : if (ZoneInterHalf.MaxResult == ZoneInterHalf.MinResult) {
298 : // Set to converged controller
299 1150330 : Converged = true;
300 1150330 : ZoneInterHalf.MaxFlowCalc = true;
301 1150330 : ZoneInterHalf.MinFlowCalc = false;
302 1150330 : ZoneInterHalf.NormFlowCalc = false;
303 1150330 : ZoneInterHalf.MinFlowResult = false;
304 1150330 : ZoneInterHalf.MaxResult = 1.0;
305 1150330 : ZoneInterHalf.MinResult = 0.0;
306 1150330 : if ((SimCompNum >= 4) && (SimCompNum <= 6)) { // hot water baseboards use min flow
307 19111 : ZoneController.CalculatedSetPoint = 0.0; // CR7253
308 : } else {
309 1131219 : ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow; // CR7253
310 : }
311 : // Set the Actuated node MassFlowRate with zero value
312 1150330 : if (plantLoc.loopNum) { // this is a plant component
313 487411 : SetActuatedBranchFlowRate(state,
314 : ZoneController.CalculatedSetPoint,
315 : ActuatedNode,
316 : plantLoc,
317 : false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
318 : } else { // assume not a plant component
319 662919 : state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
320 : }
321 1150330 : return;
322 : }
323 :
324 : // The next series of checks is to determine what interval the current solution is in
325 : // comparison to the setpoint and then respond appropriately.
326 :
327 : // Normal controller assumes that MaxResult will be greater than MinResult. First check
328 : // to make sure that this is the case
329 43621273 : if (ZoneInterHalf.MaxResult <= ZoneInterHalf.MinResult) {
330 135336 : if (WaterCoilAirFlowControl) {
331 135290 : ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
332 : } else {
333 46 : ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
334 : }
335 : // set to converged controller
336 135336 : Converged = true;
337 135336 : ZoneInterHalf.MaxFlowCalc = true;
338 135336 : ZoneInterHalf.MinFlowCalc = false;
339 135336 : ZoneInterHalf.NormFlowCalc = false;
340 135336 : ZoneInterHalf.MinFlowResult = false;
341 135336 : ZoneInterHalf.MaxResult = 1.0;
342 135336 : ZoneInterHalf.MinResult = 0.0;
343 : // MaxResult is greater than MinResult so simulation control algorithm may proceed normally
344 43485937 : } else if (ZoneInterHalf.MaxResult > ZoneInterHalf.MinResult) {
345 : // Now check to see if the setpoint is outside the endpoints of the control range
346 : // First check to see if the water is too cold and if so set to the minimum flow.
347 43485937 : if (ZoneController.SetPoint <= ZoneInterHalf.MinResult) {
348 7007 : ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
349 : // Set to Converged Controller
350 7007 : Converged = true;
351 7007 : ZoneInterHalf.MaxFlowCalc = true;
352 7007 : ZoneInterHalf.MinFlowCalc = false;
353 7007 : ZoneInterHalf.NormFlowCalc = false;
354 7007 : ZoneInterHalf.MinFlowResult = false;
355 7007 : ZoneInterHalf.MaxResult = 1.0;
356 7007 : ZoneInterHalf.MinResult = 0.0;
357 : // Then check if too hot and if so set it to the maximum flow
358 43478930 : } else if (ZoneController.SetPoint >= ZoneInterHalf.MaxResult) {
359 4012365 : ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
360 : // Set to Converged Controller
361 4012365 : Converged = true;
362 4012365 : ZoneInterHalf.MaxFlowCalc = true;
363 4012365 : ZoneInterHalf.MinFlowCalc = false;
364 4012365 : ZoneInterHalf.NormFlowCalc = false;
365 4012365 : ZoneInterHalf.MinFlowResult = false;
366 4012365 : ZoneInterHalf.MaxResult = 1.0;
367 4012365 : ZoneInterHalf.MinResult = 0.0;
368 : // If between the max and mid set to new flow and raise min to mid
369 39466565 : } else if ((ZoneController.SetPoint < ZoneInterHalf.MaxResult) && (ZoneController.SetPoint >= ZoneInterHalf.MidResult)) {
370 17199952 : ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
371 17199952 : ZoneInterHalf.MinFlow = ZoneInterHalf.MidFlow;
372 17199952 : ZoneInterHalf.MinResult = ZoneInterHalf.MidResult;
373 17199952 : ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
374 : // If between the min and mid set to new flow and lower Max to mid
375 22266613 : } else if ((ZoneController.SetPoint < ZoneInterHalf.MidResult) && (ZoneController.SetPoint > ZoneInterHalf.MinResult)) {
376 22266613 : ZoneController.CalculatedSetPoint = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
377 22266613 : ZoneInterHalf.MaxFlow = ZoneInterHalf.MidFlow;
378 22266613 : ZoneInterHalf.MaxResult = ZoneInterHalf.MidResult;
379 22266613 : ZoneInterHalf.MidFlow = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
380 :
381 : } // End of the Conditional for the actual interval halving scheme itself
382 : } // end of max > min check
383 :
384 : } // End of the Conditinal for the first 3 iterations for the interval halving
385 :
386 : // Make sure that the Calculated setpoint falls between the minimum and maximum allowed
387 76981875 : if (ZoneController.CalculatedSetPoint > ZoneController.MaxSetPoint) {
388 0 : ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
389 0 : Converged = true;
390 0 : ZoneInterHalf.MaxFlowCalc = true;
391 0 : ZoneInterHalf.MinFlowCalc = false;
392 0 : ZoneInterHalf.NormFlowCalc = false;
393 0 : ZoneInterHalf.MinFlowResult = false;
394 0 : ZoneInterHalf.MaxResult = 1.0;
395 0 : ZoneInterHalf.MinResult = 0.0;
396 76981875 : } else if (ZoneController.CalculatedSetPoint < ZoneController.MinSetPoint) {
397 17 : ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
398 17 : Converged = true;
399 17 : ZoneInterHalf.MaxFlowCalc = true;
400 17 : ZoneInterHalf.MinFlowCalc = false;
401 17 : ZoneInterHalf.NormFlowCalc = false;
402 17 : ZoneInterHalf.MinFlowResult = false;
403 17 : ZoneInterHalf.MaxResult = 1.0;
404 17 : ZoneInterHalf.MinResult = 0.0;
405 : }
406 :
407 : // check if hunting down around the limit of a significant mass flow in systems.
408 76981875 : if ((Iter > MaxIter / 2) && (ZoneController.CalculatedSetPoint < DataBranchAirLoopPlant::MassFlowTolerance)) {
409 0 : ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
410 0 : Converged = true;
411 0 : ZoneInterHalf.MaxFlowCalc = true;
412 0 : ZoneInterHalf.MinFlowCalc = false;
413 0 : ZoneInterHalf.NormFlowCalc = false;
414 0 : ZoneInterHalf.MinFlowResult = false;
415 0 : ZoneInterHalf.MaxResult = 1.0;
416 0 : ZoneInterHalf.MinResult = 0.0;
417 : }
418 :
419 : // Set the Actuated node MassFlowRate with the new value
420 76981875 : if (plantLoc.loopNum) { // this is a plant component
421 71587045 : SetActuatedBranchFlowRate(state,
422 : ZoneController.CalculatedSetPoint,
423 : ActuatedNode,
424 : plantLoc,
425 : false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
426 : } else { // assume not a plant component, leave alone
427 5394830 : state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
428 : }
429 :
430 : // The denominator of the control signal should be no less than 100 watts
431 76981875 : Denom = sign(max(std::abs(QZnReq), 100.0), QZnReq);
432 76981875 : if (present(Action)) {
433 764795 : if (Action == iNormalAction) {
434 540287 : Denom = max(std::abs(QZnReq), 100.0);
435 224508 : } else if (Action == iReverseAction) {
436 224508 : Denom = -max(std::abs(QZnReq), 100.0);
437 : } else {
438 0 : ShowFatalError(state, format("ControlCompOutput: Illegal Action argument =[{}]", Action));
439 : }
440 : }
441 :
442 76981875 : switch (SimCompNum) { // Tuned If block changed to switch
443 107839 : case ParallelPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT'
444 : // simulate series piu reheat coil
445 107839 : SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
446 : // Calculate the control signal (the variable we are forcing to zero)
447 107839 : CpAir =
448 107839 : PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
449 215678 : LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
450 215678 : (state.dataLoopNodes->Node(TempOutNode).Temp -
451 107839 : state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
452 107839 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
453 107839 : break;
454 :
455 610245 : case SeriesPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT'
456 : // simulate series piu reheat coil
457 610245 : SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
458 : // Calculate the control signal (the variable we are forcing to zero)
459 610245 : CpAir =
460 610245 : PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
461 1220490 : LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
462 1220490 : (state.dataLoopNodes->Node(TempOutNode).Temp -
463 610245 : state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
464 610245 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
465 610245 : break;
466 :
467 73629611 : case HeatingCoilWaterNum: // 'COIL:HEATING:WATER'
468 : // Simulate reheat coil for the VAV system
469 73629611 : SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
470 : // Calculate the control signal (the variable we are forcing to zero)
471 73629611 : CpAir = PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat);
472 73629611 : if (present(AirMassFlow)) {
473 68234781 : LoadMet = AirMassFlow * CpAir * state.dataLoopNodes->Node(TempOutNode).Temp;
474 68234781 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
475 : } else {
476 5394830 : WaterCoilAirFlowControl = true;
477 10789660 : LoadMet = state.dataLoopNodes->Node(TempOutNode).MassFlowRate * CpAir *
478 10789660 : (state.dataLoopNodes->Node(TempOutNode).Temp -
479 5394830 : state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
480 5394830 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
481 : }
482 73629611 : break;
483 :
484 729231 : case BBWaterConvOnlyNum: // 'ZONEHVAC:BASEBOARD:CONVECTIVE:WATER'
485 : // Simulate baseboard
486 729231 : SimHWConvective(state, CompNum, LoadMet);
487 : // Calculate the control signal (the variable we are forcing to zero)
488 729231 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
489 729231 : break;
490 :
491 27946 : case BBSteamRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:STEAM'
492 : // Simulate baseboard
493 27946 : CalcSteamBaseboard(state, CompNum, LoadMet);
494 : // Calculate the control signal (the variable we are forcing to zero)
495 27946 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
496 27946 : break;
497 :
498 162274 : case BBWaterRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER'
499 : // Simulate baseboard
500 162274 : CalcHWBaseboard(state, CompNum, LoadMet);
501 : // Calculate the control signal (the variable we are forcing to zero)
502 162274 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
503 162274 : break;
504 :
505 0 : case FourPipeFanCoilNum: // 'ZONEHVAC:FOURPIPEFANCOIL'
506 : // Simulate fancoil unit
507 0 : Calc4PipeFanCoil(state, CompNum, ControlledZoneIndex, FirstHVACIteration, LoadMet);
508 : // Calculate the control signal (the variable we are forcing to zero)
509 0 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
510 0 : break;
511 :
512 764795 : case OutdoorAirUnitNum: //'ZONEHVAC:OUTDOORAIRUNIT'
513 : // Simulate outdoor air unit components
514 764795 : OutdoorAirUnit::CalcOAUnitCoilComps(
515 764795 : state, CompNum, FirstHVACIteration, EquipIndex, LoadMet); // Autodesk:OPTIONAL EquipIndex used without PRESENT check
516 : // Calculate the control signal (the variable we are forcing to zero)
517 764795 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
518 764795 : break;
519 :
520 166647 : case UnitHeaterNum: // 'ZONEHVAC:UNITHEATER'
521 : // Simulate unit heater components
522 166647 : CalcUnitHeaterComponents(state, CompNum, FirstHVACIteration, LoadMet);
523 : // Calculate the control signal (the variable we are forcing to zero)
524 166647 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
525 166647 : break;
526 :
527 351716 : case UnitVentilatorNum: // 'ZONEHVAC:UNITVENTILATOR'
528 : // Simulate unit ventilator components
529 351716 : CalcUnitVentilatorComponents(state, CompNum, FirstHVACIteration, LoadMet);
530 : // Calculate the control signal (the variable we are forcing to zero)
531 351716 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
532 351716 : break;
533 :
534 431571 : case VentilatedSlabNum: // 'ZONEHVAC:VENTILATEDSLAB'
535 : // Simulate unit ventilator components
536 431571 : CalcVentilatedSlabComps(state, CompNum, FirstHVACIteration, LoadMet);
537 : // Calculate the control signal (the variable we are forcing to zero)
538 431571 : ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
539 431571 : break;
540 :
541 0 : default:
542 0 : ShowFatalError(state, format("ControlCompOutput: Illegal Component Number argument =[{}]", SimCompNum));
543 0 : break;
544 : }
545 :
546 : // Check for Controller convergence to see if within the offset
547 76981875 : if (std::abs(ZoneController.SensedValue) <= ControlOffset || std::abs(ZoneController.SensedValue) <= HalvingPrec) {
548 : // Set to converged controller
549 5831977 : Converged = true;
550 5831977 : ZoneInterHalf.MaxFlowCalc = true;
551 5831977 : ZoneInterHalf.MinFlowCalc = false;
552 5831977 : ZoneInterHalf.NormFlowCalc = false;
553 5831977 : ZoneInterHalf.MinFlowResult = false;
554 5831977 : ZoneInterHalf.MaxResult = 1.0;
555 5831977 : ZoneInterHalf.MinResult = 0.0;
556 5831977 : break;
557 : }
558 71149898 : if (!Converged) {
559 66995173 : BBConvergeCheckFlag = BBConvergeCheck(SimCompNum, ZoneInterHalf.MaxFlow, ZoneInterHalf.MinFlow);
560 66995173 : if (BBConvergeCheckFlag) {
561 : // Set to converged controller
562 65777 : Converged = true;
563 65777 : ZoneInterHalf.MaxFlowCalc = true;
564 65777 : ZoneInterHalf.MinFlowCalc = false;
565 65777 : ZoneInterHalf.NormFlowCalc = false;
566 65777 : ZoneInterHalf.MinFlowResult = false;
567 65777 : ZoneInterHalf.MaxResult = 1.0;
568 65777 : ZoneInterHalf.MinResult = 0.0;
569 65777 : break;
570 : }
571 : }
572 :
573 71084121 : ++Iter;
574 71084121 : if ((Iter > MaxIter) && (!state.dataGlobal->WarmupFlag)) {
575 : // if ( CompErrIndex == 0 ) {
576 0 : ShowWarningMessage(state, "ControlCompOutput: Maximum iterations exceeded for " + CompType + " = " + CompName);
577 0 : ShowContinueError(state, format("... Load met = {:.5T} W.", LoadMet));
578 0 : ShowContinueError(state, format("... Load requested = {:.5T} W.", QZnReq));
579 0 : ShowContinueError(state, format("... Error = {:.8T} %.", std::abs((LoadMet - QZnReq) * 100.0 / Denom)));
580 0 : ShowContinueError(state, format("... Tolerance = {:.8T} %.", ControlOffset * 100.0));
581 0 : ShowContinueError(state, "... Error = (Load met - Load requested) / MAXIMUM(Load requested, 100)");
582 0 : ShowContinueError(state, format("... Actuated Node Mass Flow Rate ={:.9R} kg/s", state.dataLoopNodes->Node(ActuatedNode).MassFlowRate));
583 0 : ShowContinueErrorTimeStamp(state, "");
584 0 : ShowRecurringWarningErrorAtEnd(state,
585 0 : "ControlCompOutput: Maximum iterations error for " + CompType + " = " + CompName,
586 : CompErrIndex,
587 0 : std::abs((LoadMet - QZnReq) * 100.0 / Denom),
588 0 : std::abs((LoadMet - QZnReq) * 100.0 / Denom),
589 : _,
590 : "%",
591 : "%");
592 : //}
593 0 : ShowRecurringWarningErrorAtEnd(state,
594 0 : "ControlCompOutput: Maximum iterations error for " + CompType + " = " + CompName,
595 : CompErrIndex,
596 0 : std::abs((LoadMet - QZnReq) * 100.0 / Denom),
597 0 : std::abs((LoadMet - QZnReq) * 100.0 / Denom),
598 : _,
599 : "%",
600 : "%");
601 0 : break; // It will not converge this time
602 71084121 : } else if (Iter > MaxIter * 2) {
603 0 : break;
604 : }
605 :
606 : } // End of the Convergence Iteration
607 : }
608 :
609 66995173 : bool BBConvergeCheck(int const SimCompNum, Real64 const MaxFlow, Real64 const MinFlow)
610 : {
611 :
612 : // FUNCTION INFORMATION:
613 : // AUTHOR Rick Strand
614 : // DATE WRITTEN November 2017
615 :
616 : // PURPOSE OF THIS SUBROUTINE:
617 : // This is an additional check for the radiant/convective baseboard units
618 : // to see if they are converged or the flow is sufficiently converged to
619 : // procede with the simulation. With the radiant component to these systems,
620 : // the impact on the load met is more difficult to calculate and the impact
621 : // on the actual system output is not as well behaved as for convective
622 : // systems. This additional check avoids excessive iterations and max
623 : // iteration warnings and provides sufficiently converged results. It is
624 : // only called from ControlCompOutput.
625 :
626 : // Return Value
627 : bool BBConvergeCheck;
628 :
629 : // SUBROUTINE PARAMETER DEFINITIONS:
630 : static Real64 constexpr BBIterLimit(0.00001);
631 :
632 66995173 : if (SimCompNum != BBSteamRadConvNum && SimCompNum != BBWaterRadConvNum) {
633 : // For all zone equipment except radiant/convective baseboard (steam and water) units:
634 66813598 : BBConvergeCheck = false;
635 : } else {
636 : // For steam and water radiant/convective baseboard units:
637 181575 : if ((MaxFlow - MinFlow) > BBIterLimit) {
638 115798 : BBConvergeCheck = false;
639 : } else {
640 65777 : BBConvergeCheck = true;
641 : }
642 : }
643 :
644 66995173 : return BBConvergeCheck;
645 : }
646 :
647 6716 : void CheckSysSizing(EnergyPlusData &state,
648 : std::string const &CompType, // Component Type (e.g. Chiller:Electric)
649 : std::string const &CompName // Component Name (e.g. Big Chiller)
650 : )
651 : {
652 :
653 : // SUBROUTINE INFORMATION:
654 : // AUTHOR Fred Buhl
655 : // DATE WRITTEN October 2002
656 : // MODIFIED na
657 : // RE-ENGINEERED na
658 :
659 : // PURPOSE OF THIS SUBROUTINE:
660 : // This routine is called when an "autosize" input is encountered in a component
661 : // sizing routine to check that the system sizing calculations have been done.
662 :
663 : // METHODOLOGY EMPLOYED:
664 : // Checks SysSizingRunDone flag. If false throws a fatal error.
665 :
666 6716 : if (!state.dataSize->SysSizingRunDone) {
667 0 : ShowSevereError(state, "For autosizing of " + CompType + ' ' + CompName + ", a system sizing run must be done.");
668 0 : if (state.dataSize->NumSysSizInput == 0) {
669 0 : ShowContinueError(state, "No \"Sizing:System\" objects were entered.");
670 : }
671 0 : if (!state.dataGlobal->DoSystemSizing) {
672 0 : ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do System Sizing Calculation" set to Yes.)");
673 : }
674 0 : ShowFatalError(state, "Program terminates due to previously shown condition(s).");
675 : }
676 6716 : }
677 :
678 4815 : void CheckThisAirSystemForSizing(EnergyPlusData &state, int const AirLoopNum, bool &AirLoopWasSized)
679 : {
680 :
681 : // SUBROUTINE INFORMATION:
682 : // AUTHOR B. Griffith
683 : // DATE WRITTEN October 2013
684 : // MODIFIED na
685 : // RE-ENGINEERED na
686 :
687 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
688 : int ThisAirSysSizineInputLoop;
689 :
690 4815 : AirLoopWasSized = false;
691 4815 : if (state.dataSize->SysSizingRunDone) {
692 24990 : for (ThisAirSysSizineInputLoop = 1; ThisAirSysSizineInputLoop <= state.dataSize->NumSysSizInput; ++ThisAirSysSizineInputLoop) {
693 24982 : if (state.dataSize->SysSizInput(ThisAirSysSizineInputLoop).AirLoopNum == AirLoopNum) {
694 4465 : AirLoopWasSized = true;
695 4465 : break;
696 : }
697 : }
698 : }
699 4815 : }
700 :
701 12785 : void CheckZoneSizing(EnergyPlusData &state,
702 : std::string const &CompType, // Component Type (e.g. Chiller:Electric)
703 : std::string const &CompName // Component Name (e.g. Big Chiller)
704 : )
705 : {
706 :
707 : // SUBROUTINE INFORMATION:
708 : // AUTHOR Fred Buhl
709 : // DATE WRITTEN October 2002
710 : // MODIFIED na
711 : // RE-ENGINEERED na
712 :
713 : // PURPOSE OF THIS SUBROUTINE:
714 : // This routine is called when an "autosize" input is encountered in a component
715 : // sizing routine to check that the zone sizing calculations have been done.
716 :
717 : // METHODOLOGY EMPLOYED:
718 : // Checks ZoneSizingRunDone flag. If false throws a fatal error.
719 :
720 12785 : if (!state.dataSize->ZoneSizingRunDone) {
721 0 : ShowSevereError(state, "For autosizing of " + CompType + ' ' + CompName + ", a zone sizing run must be done.");
722 0 : if (state.dataSize->NumZoneSizingInput == 0) {
723 0 : ShowContinueError(state, "No \"Sizing:Zone\" objects were entered.");
724 : }
725 0 : if (!state.dataGlobal->DoZoneSizing) {
726 0 : ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do Zone Sizing Calculation" set to Yes.)");
727 : }
728 0 : ShowFatalError(state, "Program terminates due to previously shown condition(s).");
729 : }
730 12785 : }
731 :
732 603 : void CheckThisZoneForSizing(EnergyPlusData &state,
733 : int const ZoneNum, // zone index to be checked
734 : bool &ZoneWasSized)
735 : {
736 :
737 : // SUBROUTINE INFORMATION:
738 : // AUTHOR B. Griffith
739 : // DATE WRITTEN Oct 2013
740 : // MODIFIED na
741 : // RE-ENGINEERED na
742 :
743 : // PURPOSE OF THIS SUBROUTINE:
744 : // utility routine to see if a particular zone has a Sizing:Zone object for it
745 : // and that sizing was done.
746 :
747 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
748 : int ThisSizingInput;
749 :
750 603 : ZoneWasSized = false;
751 603 : if (state.dataSize->ZoneSizingRunDone) {
752 8305 : for (ThisSizingInput = 1; ThisSizingInput <= state.dataSize->NumZoneSizingInput; ++ThisSizingInput) {
753 8305 : if (state.dataSize->ZoneSizingInput(ThisSizingInput).ZoneNum == ZoneNum) {
754 597 : ZoneWasSized = true;
755 597 : break;
756 : }
757 : }
758 : }
759 603 : }
760 :
761 38147 : void ValidateComponent(EnergyPlusData &state,
762 : std::string_view CompType, // Component Type (e.g. Chiller:Electric)
763 : std::string const &CompName, // Component Name (e.g. Big Chiller)
764 : bool &IsNotOK, // .TRUE. if this component pair is invalid
765 : std::string const &CallString // Context of this pair -- for error message
766 : )
767 : {
768 :
769 : // SUBROUTINE INFORMATION:
770 : // AUTHOR Linda Lawrie
771 : // DATE WRITTEN October 2002
772 : // MODIFIED na
773 : // RE-ENGINEERED na
774 :
775 : // PURPOSE OF THIS SUBROUTINE:
776 : // This subroutine can be called to validate the component type-name pairs that
777 : // are so much a part of the EnergyPlus input. The main drawback to this validation
778 : // has been that the "GetInput" routine may not have been called and/or exists in
779 : // another module from the one with the list. This means that validation must be
780 : // done later, perhaps after simulation has already started or perhaps raises an
781 : // array bound error instead.
782 :
783 : // METHODOLOGY EMPLOYED:
784 : // Uses existing routines in InputProcessor. GetObjectItemNum uses the "standard"
785 : // convention of the Name of the item/object being the first Alpha Argument.
786 :
787 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
788 : int ItemNum;
789 :
790 38147 : IsNotOK = false;
791 :
792 38147 : ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, std::string{CompType}, CompName);
793 :
794 38147 : if (ItemNum < 0) {
795 0 : ShowSevereError(state, format("During {} Input, Invalid Component Type input={}", CallString, CompType));
796 0 : ShowContinueError(state, "Component name=" + CompName);
797 0 : IsNotOK = true;
798 38147 : } else if (ItemNum == 0) {
799 0 : ShowSevereError(state, "During " + CallString + " Input, Invalid Component Name input=" + CompName);
800 0 : ShowContinueError(state, format("Component type={}", CompType));
801 0 : IsNotOK = true;
802 : }
803 38147 : }
804 :
805 15 : void ValidateComponent(EnergyPlusData &state,
806 : std::string const &CompType, // Component Type (e.g. Chiller:Electric)
807 : std::string const &CompValType, // Component "name" field type
808 : std::string const &CompName, // Component Name (e.g. Big Chiller)
809 : bool &IsNotOK, // .TRUE. if this component pair is invalid
810 : std::string const &CallString // Context of this pair -- for error message
811 : )
812 : {
813 :
814 : // SUBROUTINE INFORMATION:
815 : // AUTHOR Linda Lawrie
816 : // DATE WRITTEN October 2002
817 : // MODIFIED na
818 : // RE-ENGINEERED na
819 :
820 : // PURPOSE OF THIS SUBROUTINE:
821 : // This subroutine can be called to validate the component type-name pairs that
822 : // are so much a part of the EnergyPlus input. The main drawback to this validation
823 : // has been that the "GetInput" routine may not have been called and/or exists in
824 : // another module from the one with the list. This means that validation must be
825 : // done later, perhaps after simulation has already started or perhaps raises an
826 : // array bound error instead.
827 :
828 : // METHODOLOGY EMPLOYED:
829 : // Uses existing routines in InputProcessor. GetObjectItemNum uses the "standard"
830 : // convention of the Name of the item/object being the first Alpha Argument.
831 :
832 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
833 : int ItemNum;
834 :
835 15 : IsNotOK = false;
836 :
837 15 : ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, CompType, CompValType, CompName);
838 :
839 15 : if (ItemNum < 0) {
840 0 : ShowSevereError(state, "During " + CallString + " Input, Invalid Component Type input=" + CompType);
841 0 : ShowContinueError(state, "Component name=" + CompName);
842 0 : IsNotOK = true;
843 15 : } else if (ItemNum == 0) {
844 0 : ShowSevereError(state, "During " + CallString + " Input, Invalid Component Name input=" + CompName);
845 0 : ShowContinueError(state, "Component type=" + CompType);
846 0 : IsNotOK = true;
847 : }
848 15 : }
849 :
850 55347 : void CalcPassiveExteriorBaffleGap(EnergyPlusData &state,
851 : const Array1D_int &SurfPtrARR, // Array of indexes pointing to Surface structure in DataSurfaces
852 : Real64 const VentArea, // Area available for venting the gap [m2]
853 : Real64 const Cv, // Orifice coefficient for volume-based discharge, wind-driven [--]
854 : Real64 const Cd, // Orifice coefficient for discharge, buoyancy-driven [--]
855 : Real64 const HdeltaNPL, // Height difference from neutral pressure level [m]
856 : Real64 const SolAbs, // solar absorptivity of baffle [--]
857 : Real64 const AbsExt, // thermal absorptance/emittance of baffle material [--]
858 : Real64 const Tilt, // Tilt of gap [Degrees]
859 : Real64 const AspRat, // aspect ratio of gap Height/gap [--]
860 : Real64 const GapThick, // Thickness of air space between baffle and underlying heat transfer surface
861 : DataSurfaces::SurfaceRoughness const Roughness, // Roughness index (1-6), see DataHeatBalance parameters
862 : Real64 const QdotSource, // Source/sink term, e.g. electricity exported from solar cell [W]
863 : Real64 &TsBaffle, // Temperature of baffle (both sides) use lagged value on input [C]
864 : Real64 &TaGap, // Temperature of air gap (assumed mixed) use lagged value on input [C]
865 : Optional<Real64> HcGapRpt,
866 : Optional<Real64> HrGapRpt,
867 : Optional<Real64> IscRpt,
868 : Optional<Real64> MdotVentRpt,
869 : Optional<Real64> VdotWindRpt,
870 : Optional<Real64> VdotBuoyRpt)
871 : {
872 :
873 : // SUBROUTINE INFORMATION:
874 : // AUTHOR B.T. Griffith
875 : // DATE WRITTEN November 2004
876 : // MODIFIED BG March 2007 outdoor conditions from surface for height-dependent conditions
877 : // RE-ENGINEERED na
878 :
879 : // PURPOSE OF THIS SUBROUTINE:
880 : // model the effect of the a ventilated baffle covering the outside of a heat transfer surface.
881 : // return calculated temperatures and certain intermediate values for reporting
882 :
883 : // METHODOLOGY EMPLOYED:
884 : // Heat balances on baffle and air space.
885 : // Natural ventilation calculations use buoyancy and wind.
886 :
887 : // REFERENCES:
888 : // Nat. Vent. equations from ASHRAE HoF 2001 Chapt. 26
889 :
890 : // Using/Aliasing
891 : using ConvectionCoefficients::InitExteriorConvectionCoeff;
892 : using DataSurfaces::SurfaceData;
893 : using Psychrometrics::PsyCpAirFnW;
894 : using Psychrometrics::PsyRhoAirFnPbTdbW;
895 : using Psychrometrics::PsyWFnTdbTwbPb;
896 :
897 : // SUBROUTINE PARAMETER DEFINITIONS:
898 55347 : Real64 constexpr g(9.807); // gravitational constant (m/s**2)
899 55347 : Real64 constexpr nu(15.66e-6); // kinematic viscosity (m**2/s) for air at 300 K (Mills 1999 Heat Transfer)
900 55347 : Real64 constexpr k(0.0267); // thermal conductivity (W/m K) for air at 300 K (Mills 1999 Heat Transfer)
901 55347 : Real64 constexpr Sigma(5.6697e-08); // Stefan-Boltzmann constant
902 : static constexpr std::string_view RoutineName("CalcPassiveExteriorBaffleGap");
903 : // INTERFACE BLOCK SPECIFICATIONS:
904 :
905 : // DERIVED TYPE DEFINITIONS:
906 :
907 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
908 :
909 : // following arrays are used to temporarily hold results from multiple underlying surfaces
910 110694 : Array1D<Real64> HSkyARR;
911 110694 : Array1D<Real64> HGroundARR;
912 110694 : Array1D<Real64> HAirARR;
913 110694 : Array1D<Real64> HPlenARR;
914 110694 : Array1D<Real64> HExtARR;
915 110694 : Array1D<Real64> LocalWindArr;
916 :
917 : // local working variables
918 : Real64 RhoAir; // density of air
919 : Real64 CpAir; // specific heat of air
920 : Real64 Tamb; // outdoor drybulb
921 : Real64 A; // projected area of baffle from sum of underlying surfaces
922 : Real64 HcPlen; // surface convection heat transfer coefficient for plenum surfaces
923 : int ThisSurf; // do loop counter
924 : int NumSurfs; // number of underlying HT surfaces associated with UTSC
925 : Real64 TmpTsBaf; // baffle temperature
926 : int SurfPtr; // index of surface in main surface structure
927 : Real64 HMovInsul; // dummy for call to InitExteriorConvectionCoeff
928 : Real64 HExt; // dummy for call to InitExteriorConvectionCoeff
929 : int ConstrNum; // index of construction in main construction structure
930 : Real64 AbsThermSurf; // thermal emmittance of underlying wall.
931 : Real64 TsoK; // underlying surface temperature in Kelvin
932 : Real64 TsBaffK; // baffle temperature in Kelvin (lagged)
933 : Real64 Vwind; // localized, and area-weighted average for wind speed
934 : Real64 HrSky; // radiation coeff for sky, area-weighted average
935 : Real64 HrGround; // radiation coeff for ground, area-weighted average
936 : Real64 HrAtm; // radiation coeff for air (bulk atmosphere), area-weighted average
937 : Real64 Isc; // Incoming combined solar radiation, area-weighted average
938 : Real64 HrPlen; // radiation coeff for plenum surfaces, area-weighted average
939 : Real64 Tso; // temperature of underlying surface, area-weighted average
940 : Real64 TmeanK; // average of surface temps , for Beta in Grashoff no.
941 : Real64 Gr; // Grasshof number for natural convection calc
942 : Real64 VdotWind; // volume flow rate of nat. vent due to wind
943 : Real64 VdotThermal; // Volume flow rate of nat. vent due to buoyancy
944 : Real64 VdotVent; // total volume flow rate of nat vent
945 : Real64 MdotVent; // total mass flow rate of nat vent
946 : Real64 NuPlen; // Nusselt No. for plenum Gap
947 : Real64 LocalOutDryBulbTemp; // OutDryBulbTemp for here
948 : Real64 LocalWetBulbTemp; // OutWetBulbTemp for here
949 : Real64 LocalOutHumRat; // OutHumRat for here
950 55347 : bool ICSCollectorIsOn(false); // ICS collector has OSCM on
951 : int CollectorNum; // current solar collector index
952 : Real64 ICSWaterTemp; // ICS solar collector water temp
953 : Real64 ICSULossbottom; // ICS solar collector bottom loss Conductance
954 55347 : Real64 sum_area = 0.0;
955 55347 : Real64 sum_produc_area_drybulb = 0.0;
956 55347 : Real64 sum_produc_area_wetbulb = 0.0;
957 130893 : for (int SurfNum : SurfPtrARR) {
958 75546 : sum_area += state.dataSurface->Surface(SurfNum).Area;
959 75546 : sum_produc_area_drybulb += state.dataSurface->Surface(SurfNum).Area * state.dataSurface->SurfOutDryBulbTemp(SurfNum);
960 75546 : sum_produc_area_wetbulb += state.dataSurface->Surface(SurfNum).Area * state.dataSurface->SurfOutWetBulbTemp(SurfNum);
961 : }
962 : // LocalOutDryBulbTemp = sum( Surface( SurfPtrARR ).Area * Surface( SurfPtrARR ).OutDryBulbTemp ) / sum( Surface( SurfPtrARR ).Area );
963 55347 : LocalOutDryBulbTemp = sum_produc_area_drybulb / sum_area; // Autodesk:F2C++ Functions handle array subscript usage
964 : // LocalWetBulbTemp = sum( Surface( SurfPtrARR ).Area * Surface( SurfPtrARR ).OutWetBulbTemp ) / sum( Surface( SurfPtrARR ).Area );
965 55347 : LocalWetBulbTemp = sum_produc_area_wetbulb / sum_area;
966 :
967 55347 : LocalOutHumRat = PsyWFnTdbTwbPb(state, LocalOutDryBulbTemp, LocalWetBulbTemp, state.dataEnvrn->OutBaroPress, RoutineName);
968 :
969 55347 : RhoAir = PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, LocalOutDryBulbTemp, LocalOutHumRat, RoutineName);
970 55347 : CpAir = PsyCpAirFnW(LocalOutHumRat);
971 55347 : if (!state.dataEnvrn->IsRain) {
972 55347 : Tamb = LocalOutDryBulbTemp;
973 : } else { // when raining we use wetbulb not drybulb
974 0 : Tamb = LocalWetBulbTemp;
975 : }
976 : // A = sum( Surface( SurfPtrARR ).Area ); //Autodesk:F2C++ Array subscript usage: Replaced by below
977 55347 : A = sum_area;
978 55347 : TmpTsBaf = TsBaffle;
979 :
980 : // loop through underlying surfaces and collect needed data
981 55347 : NumSurfs = size(SurfPtrARR);
982 55347 : HSkyARR.dimension(NumSurfs, 0.0);
983 55347 : HGroundARR.dimension(NumSurfs, 0.0);
984 55347 : HAirARR.dimension(NumSurfs, 0.0);
985 55347 : LocalWindArr.dimension(NumSurfs, 0.0);
986 55347 : HPlenARR.dimension(NumSurfs, 0.0);
987 55347 : HExtARR.dimension(NumSurfs, 0.0);
988 :
989 130893 : for (ThisSurf = 1; ThisSurf <= NumSurfs; ++ThisSurf) {
990 75546 : SurfPtr = SurfPtrARR(ThisSurf);
991 : // Initializations for this surface
992 75546 : HMovInsul = 0.0;
993 75546 : LocalWindArr(ThisSurf) = state.dataSurface->SurfOutWindSpeed(SurfPtr);
994 75546 : InitExteriorConvectionCoeff(
995 75546 : state, SurfPtr, HMovInsul, Roughness, AbsExt, TmpTsBaf, HExtARR(ThisSurf), HSkyARR(ThisSurf), HGroundARR(ThisSurf), HAirARR(ThisSurf));
996 75546 : ConstrNum = state.dataSurface->Surface(SurfPtr).Construction;
997 75546 : AbsThermSurf = state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).AbsorpThermal;
998 75546 : TsoK = state.dataHeatBalSurf->SurfOutsideTempHist(1)(SurfPtr) + DataGlobalConstants::KelvinConv;
999 75546 : TsBaffK = TmpTsBaf + DataGlobalConstants::KelvinConv;
1000 75546 : if (TsBaffK == TsoK) { // avoid divide by zero
1001 0 : HPlenARR(ThisSurf) = 0.0; // no net heat transfer if same temperature
1002 : } else {
1003 75546 : HPlenARR(ThisSurf) = Sigma * AbsExt * AbsThermSurf * (pow_4(TsBaffK) - pow_4(TsoK)) / (TsBaffK - TsoK);
1004 : }
1005 : // Added for ICS collector OSCM
1006 75546 : if (state.dataSurface->SurfIsICS(SurfPtr)) {
1007 12168 : ICSCollectorIsOn = true;
1008 12168 : CollectorNum = state.dataSurface->SurfICSPtr(SurfPtr);
1009 : }
1010 : }
1011 :
1012 55347 : if (ICSCollectorIsOn) {
1013 12168 : if (state.dataGlobal->BeginEnvrnFlag && state.dataGeneralRoutines->MyICSEnvrnFlag) {
1014 36 : ICSULossbottom = 0.40;
1015 36 : ICSWaterTemp = 20.0;
1016 : } else {
1017 12132 : if (!state.dataSolarCollectors->Collector.allocated()) {
1018 0 : ICSULossbottom = 0.40;
1019 0 : ICSWaterTemp = 20.0;
1020 : } else {
1021 12132 : ICSULossbottom = state.dataSolarCollectors->Collector(CollectorNum).UbLoss;
1022 12132 : ICSWaterTemp = state.dataSolarCollectors->Collector(CollectorNum).TempOfWater;
1023 12132 : state.dataGeneralRoutines->MyICSEnvrnFlag = false;
1024 : }
1025 : }
1026 : }
1027 55347 : if (!state.dataGlobal->BeginEnvrnFlag) {
1028 54913 : state.dataGeneralRoutines->MyICSEnvrnFlag = true;
1029 : }
1030 : if (A == 0.0) { // should have been caught earlier
1031 : }
1032 55347 : auto Area(array_sub(state.dataSurface->Surface,
1033 : &SurfaceData::Area,
1034 110694 : SurfPtrARR)); // Autodesk:F2C++ Copy of subscripted Area array for use below: This makes a copy so review wrt performance
1035 : // now figure area-weighted averages from underlying surfaces.
1036 : // Vwind = sum( LocalWindArr * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1037 55347 : Vwind = sum(LocalWindArr * Area) / A;
1038 55347 : LocalWindArr.deallocate();
1039 : // HrSky = sum( HSkyARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1040 55347 : HrSky = sum(HSkyARR * Area) / A;
1041 55347 : HSkyARR.deallocate();
1042 : // HrGround = sum( HGroundARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1043 55347 : HrGround = sum(HGroundARR * Area) / A;
1044 55347 : HGroundARR.deallocate();
1045 : // HrAtm = sum( HAirARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1046 55347 : HrAtm = sum(HAirARR * Area) / A;
1047 55347 : HAirARR.deallocate();
1048 : // HrPlen = sum( HPlenARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1049 55347 : HrPlen = sum(HPlenARR * Area) / A;
1050 55347 : HPlenARR.deallocate();
1051 : // HExt = sum( HExtARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1052 55347 : HExt = sum(HExtARR * Area) / A;
1053 55347 : HExtARR.deallocate();
1054 :
1055 55347 : if (state.dataEnvrn->IsRain) HExt = 1000.0;
1056 :
1057 : // Tso = sum( TH( 1, 1, SurfPtrARR ) * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1058 55347 : Tso = sum_product_sub(state.dataHeatBalSurf->SurfOutsideTempHist(1), state.dataSurface->Surface, &SurfaceData::Area, SurfPtrARR) /
1059 : A; // Autodesk:F2C++ Functions handle array subscript usage
1060 : // Isc = sum( SurfQRadSWOutIncident( SurfPtrARR ) * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
1061 55347 : Isc = sum_product_sub(state.dataHeatBal->SurfQRadSWOutIncident, state.dataSurface->Surface, &SurfaceData::Area, SurfPtrARR) /
1062 : A; // Autodesk:F2C++ Functions handle array subscript usage
1063 :
1064 55347 : TmeanK = 0.5 * (TmpTsBaf + Tso) + DataGlobalConstants::KelvinConv;
1065 :
1066 55347 : Gr = g * pow_3(GapThick) * std::abs(Tso - TmpTsBaf) * pow_2(RhoAir) / (TmeanK * pow_2(nu));
1067 :
1068 55347 : PassiveGapNusseltNumber(AspRat, Tilt, TmpTsBaf, Tso, Gr, NuPlen); // intentionally switch Tso to Tsi
1069 :
1070 55347 : HcPlen = NuPlen * (k / GapThick);
1071 :
1072 : // now model natural ventilation of plenum gap.
1073 55347 : VdotWind = Cv * (VentArea / 2.0) * Vwind;
1074 :
1075 55347 : if (TaGap > Tamb) {
1076 43314 : VdotThermal = Cd * (VentArea / 2.0) * std::sqrt(2.0 * g * HdeltaNPL * (TaGap - Tamb) / (TaGap + DataGlobalConstants::KelvinConv));
1077 12033 : } else if (TaGap == Tamb) {
1078 0 : VdotThermal = 0.0;
1079 : } else {
1080 12033 : if ((std::abs(Tilt) < 5.0) || (std::abs(Tilt - 180.0) < 5.0)) {
1081 8793 : VdotThermal = 0.0; // stable buoyancy situation
1082 : } else {
1083 3240 : VdotThermal = Cd * (VentArea / 2.0) * std::sqrt(2.0 * g * HdeltaNPL * (Tamb - TaGap) / (Tamb + DataGlobalConstants::KelvinConv));
1084 : }
1085 : }
1086 :
1087 55347 : VdotVent = VdotWind + VdotThermal;
1088 55347 : MdotVent = VdotVent * RhoAir;
1089 :
1090 : // now calculate baffle temperature
1091 55347 : if (!ICSCollectorIsOn) {
1092 86358 : TsBaffle = (Isc * SolAbs + HExt * Tamb + HrAtm * Tamb + HrSky * state.dataEnvrn->SkyTemp + HrGround * Tamb + HrPlen * Tso + HcPlen * TaGap +
1093 43179 : QdotSource) /
1094 43179 : (HExt + HrAtm + HrSky + HrGround + HrPlen + HcPlen);
1095 : } else {
1096 :
1097 12168 : TsBaffle = (ICSULossbottom * ICSWaterTemp + HrPlen * Tso + HcPlen * TaGap + QdotSource) / (ICSULossbottom + HrPlen + HcPlen);
1098 : }
1099 : // now calculate gap air temperature
1100 :
1101 55347 : TaGap = (HcPlen * A * Tso + MdotVent * CpAir * Tamb + HcPlen * A * TsBaffle) / (HcPlen * A + MdotVent * CpAir + HcPlen * A);
1102 :
1103 55347 : if (present(HcGapRpt)) HcGapRpt = HcPlen;
1104 55347 : if (present(HrGapRpt)) HrGapRpt = HrPlen;
1105 55347 : if (present(IscRpt)) IscRpt = Isc;
1106 55347 : if (present(MdotVentRpt)) MdotVentRpt = MdotVent;
1107 55347 : if (present(VdotWindRpt)) VdotWindRpt = VdotWind;
1108 55347 : if (present(VdotBuoyRpt)) VdotBuoyRpt = VdotThermal;
1109 55347 : }
1110 :
1111 : //****************************************************************************
1112 :
1113 55347 : void PassiveGapNusseltNumber(Real64 const AspRat, // Aspect Ratio of Gap height to gap width
1114 : Real64 const Tilt, // Tilt of gap, degrees
1115 : Real64 const Tso, // Temperature of gap surface closest to outside (K)
1116 : Real64 const Tsi, // Temperature of gap surface closest to zone (K)
1117 : Real64 const Gr, // Gap gas Grashof number
1118 : Real64 &gNu // Gap gas Nusselt number
1119 : )
1120 : {
1121 :
1122 : // SUBROUTINE INFORMATION:
1123 : // AUTHOR Adapted by B. Griffith from Fred Winkelmann's from NusseltNumber in WindowManager.cc
1124 : // DATE WRITTEN September 2001
1125 : // MODIFIED B. Griffith November 2004 (same models but slightly different for general use)
1126 : // RE-ENGINEERED na
1127 :
1128 : // PURPOSE OF THIS SUBROUTINE:
1129 : // Finds the Nusselt number for air-filled gaps between isothermal solid layers.
1130 :
1131 : // METHODOLOGY EMPLOYED:
1132 : // Based on methodology in Chapter 5 of the July 18, 2001 draft of ISO 15099,
1133 : // "Thermal Performance of Windows, Doors and Shading Devices--Detailed Calculations."
1134 : // The equation numbers below correspond to those in the standard.
1135 :
1136 : // REFERENCES:
1137 : // Window5 source code; ISO 15099
1138 :
1139 : // Using/Aliasing
1140 : // Locals
1141 : // SUBROUTINE ARGUMENT DEFINITIONS:
1142 :
1143 : // SUBROUTINE PARAMETER DEFINITIONS:
1144 55347 : Real64 constexpr Pr(0.71); // Prandtl number for air
1145 :
1146 : // INTERFACE BLOCK SPECIFICATIONS
1147 :
1148 : // DERIVED TYPE DEFINITIONS
1149 :
1150 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS
1151 : Real64 Ra; // Rayleigh number
1152 : Real64 gnu901; // Nusselt number temporary variables for
1153 : Real64 gnu902;
1154 : Real64 gnu90;
1155 : Real64 gnu601;
1156 : Real64 gnu602; // different tilt and Ra ranges
1157 : Real64 gnu60;
1158 : Real64 gnu601a;
1159 : Real64 gnua;
1160 : Real64 gnub;
1161 : Real64 cra; // Temporary variables
1162 : Real64 a;
1163 : Real64 b;
1164 : Real64 g;
1165 : Real64 ang;
1166 : Real64 tiltr;
1167 :
1168 55347 : tiltr = Tilt * DataGlobalConstants::DegToRadians;
1169 55347 : Ra = Gr * Pr;
1170 :
1171 : if (Ra > 2.0e6) {
1172 :
1173 : // write(*,*)' error, outside range of Rayleigh number'
1174 : }
1175 :
1176 55347 : if (Ra <= 1.0e4) {
1177 12277 : gnu901 = 1.0 + 1.7596678e-10 * std::pow(Ra, 2.2984755); // eq. 51
1178 : }
1179 55347 : if (Ra > 1.0e4 && Ra <= 5.0e4) gnu901 = 0.028154 * std::pow(Ra, 0.4134); // eq. 50
1180 55347 : if (Ra > 5.0e4) gnu901 = 0.0673838 * std::pow(Ra, 1.0 / 3.0); // eq. 49
1181 :
1182 55347 : gnu902 = 0.242 * std::pow(Ra / AspRat, 0.272); // eq. 52
1183 55347 : gnu90 = max(gnu901, gnu902);
1184 :
1185 55347 : if (Tso > Tsi) { // window heated from above
1186 20821 : gNu = 1.0 + (gnu90 - 1.0) * std::sin(tiltr); // eq. 53
1187 : } else { // window heated from below
1188 34526 : if (Tilt >= 60.0) {
1189 18484 : g = 0.5 * std::pow(1.0 + std::pow(Ra / 3160.0, 20.6), -0.1); // eq. 47
1190 18484 : gnu601a = 1.0 + pow_7(0.0936 * std::pow(Ra, 0.314) / (1.0 + g)); // eq. 45
1191 18484 : gnu601 = std::pow(gnu601a, 0.142857);
1192 :
1193 : // For any aspect ratio
1194 18484 : gnu602 = (0.104 + 0.175 / AspRat) * std::pow(Ra, 0.283); // eq. 46
1195 18484 : gnu60 = max(gnu601, gnu602);
1196 :
1197 : // linear interpolation for layers inclined at angles between 60 and 90 deg
1198 18484 : gNu = ((90.0 - Tilt) * gnu60 + (Tilt - 60.0) * gnu90) / 30.0;
1199 : }
1200 34526 : if (Tilt < 60.0) { // eq. 42
1201 16042 : cra = Ra * std::cos(tiltr);
1202 16042 : a = 1.0 - 1708.0 / cra;
1203 16042 : b = std::pow(cra / 5830.0, 0.33333) - 1.0;
1204 16042 : gnua = (std::abs(a) + a) / 2.0;
1205 16042 : gnub = (std::abs(b) + b) / 2.0;
1206 16042 : ang = 1708.0 * std::pow(std::sin(1.8 * tiltr), 1.6);
1207 16042 : gNu = 1.0 + 1.44 * gnua * (1.0 - ang / cra) + gnub;
1208 : }
1209 : }
1210 55347 : }
1211 :
1212 4297913 : void CalcBasinHeaterPower(EnergyPlusData &state,
1213 : Real64 const Capacity, // Basin heater capacity per degree C below setpoint (W/C)
1214 : int const SchedulePtr, // Pointer to basin heater schedule
1215 : Real64 const SetPointTemp, // setpoint temperature for basin heater operation (C)
1216 : Real64 &Power // Basin heater power (W)
1217 : )
1218 : {
1219 :
1220 : // SUBROUTINE INFORMATION:
1221 : // AUTHOR Chandan Sharma, FSEC
1222 : // DATE WRITTEN Feb 2010
1223 : // MODIFIED na
1224 : // RE-ENGINEERED na
1225 :
1226 : // PURPOSE OF THIS SUBROUTINE:
1227 : // To calculate basin heater power when the evaporative cooled equipment is not operating
1228 : // and outdoor air dry-bulb temperature is below the set-point
1229 :
1230 : // METHODOLOGY EMPLOYED:
1231 : // Checks to see whether schedule for basin heater exists or not. If the schedule exists,
1232 : // the basin heater is operated for the schedule specified otherwise the heater runs
1233 : // for the entire simulation timestep whenever the outdoor temperature is below setpoint
1234 : // and water is not flowing through the evaporative cooled equipment.
1235 :
1236 : // REFERENCES:
1237 : // na
1238 :
1239 : // Using/Aliasing
1240 : using ScheduleManager::GetCurrentScheduleValue;
1241 :
1242 : // Locals
1243 : // SUBROUTINE ARGUMENT DEFINITIONS:
1244 :
1245 : // SUBROUTINE PARAMETER DEFINITIONS:
1246 : // na
1247 :
1248 : // INTERFACE BLOCK SPECIFICATIONS
1249 : // na
1250 :
1251 : // DERIVED TYPE DEFINITIONS
1252 : // na
1253 :
1254 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1255 : Real64 BasinHeaterSch; // Schedule for basin heater operation
1256 :
1257 4297913 : Power = 0.0;
1258 : // Operate basin heater anytime outdoor temperature is below setpoint and water is not flowing through the equipment
1259 : // IF schedule exists, basin heater performance can be scheduled OFF
1260 4297913 : if (SchedulePtr > 0) {
1261 24445 : BasinHeaterSch = GetCurrentScheduleValue(state, SchedulePtr);
1262 24445 : if (Capacity > 0.0 && BasinHeaterSch > 0.0) {
1263 24439 : Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
1264 : }
1265 : } else {
1266 : // IF schedule does not exist, basin heater operates anytime outdoor dry-bulb temp is below setpoint
1267 4273468 : if (Capacity > 0.0) {
1268 134696 : Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
1269 : }
1270 : }
1271 4297913 : }
1272 :
1273 769 : void TestAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
1274 : {
1275 :
1276 : // SUBROUTINE INFORMATION:
1277 : // AUTHOR Linda Lawrie
1278 : // DATE WRITTEN March 2003
1279 : // MODIFIED na
1280 : // RE-ENGINEERED na
1281 :
1282 : // PURPOSE OF THIS SUBROUTINE:
1283 : // This subroutine tests supply, return and overall air path integrity.
1284 :
1285 : // METHODOLOGY EMPLOYED:
1286 : // na
1287 :
1288 : // REFERENCES:
1289 : // na
1290 :
1291 : // Using/Aliasing
1292 : using namespace DataLoopNode;
1293 769 : auto &NumPrimaryAirSys = state.dataHVACGlobal->NumPrimaryAirSys;
1294 :
1295 : // Locals
1296 : // SUBROUTINE ARGUMENT DEFINITIONS:
1297 :
1298 : // SUBROUTINE PARAMETER DEFINITIONS:
1299 : // na
1300 :
1301 : // INTERFACE BLOCK SPECIFICATIONS
1302 : // COMPILER-GENERATED INTERFACE MODULE: Thu Sep 29 07:54:46 2011
1303 :
1304 : // DERIVED TYPE DEFINITIONS
1305 : // na
1306 :
1307 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1308 : int Loop;
1309 : int Loop1;
1310 : int Loop2;
1311 : int Loop3;
1312 : int Count;
1313 : int TestNode;
1314 : bool errFlag;
1315 1538 : Array2D_int ValRetAPaths;
1316 1538 : Array2D_int NumRAPNodes;
1317 1538 : Array2D_int ValSupAPaths;
1318 1538 : Array2D_int NumSAPNodes;
1319 :
1320 769 : NumSAPNodes.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
1321 769 : NumRAPNodes.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
1322 769 : ValRetAPaths.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
1323 769 : ValSupAPaths.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
1324 769 : NumSAPNodes = 0;
1325 769 : NumRAPNodes = 0;
1326 769 : ValRetAPaths = 0;
1327 769 : ValSupAPaths = 0;
1328 :
1329 769 : TestSupplyAirPathIntegrity(state, errFlag);
1330 769 : if (errFlag) ErrFound = true;
1331 769 : TestReturnAirPathIntegrity(state, errFlag, ValRetAPaths);
1332 769 : if (errFlag) ErrFound = true;
1333 :
1334 : // Final tests, look for duplicate nodes
1335 1941 : for (Loop = 1; Loop <= NumPrimaryAirSys; ++Loop) {
1336 1172 : if (ValRetAPaths(1, Loop) != 0) continue;
1337 13 : if (state.dataAirLoop->AirToZoneNodeInfo(Loop).NumReturnNodes <= 0) continue;
1338 13 : ValRetAPaths(1, Loop) = state.dataAirLoop->AirToZoneNodeInfo(Loop).ZoneEquipReturnNodeNum(1);
1339 : }
1340 :
1341 1941 : for (Loop = 1; Loop <= NumPrimaryAirSys; ++Loop) {
1342 356164 : for (Loop1 = 1; Loop1 <= state.dataLoopNodes->NumOfNodes; ++Loop1) {
1343 354992 : TestNode = ValRetAPaths(Loop1, Loop);
1344 354992 : Count = 0;
1345 15282950 : for (Loop2 = 1; Loop2 <= NumPrimaryAirSys; ++Loop2) {
1346 48814463 : for (Loop3 = 1; Loop3 <= state.dataLoopNodes->NumOfNodes; ++Loop3) {
1347 48814463 : if (Loop2 == Loop && Loop1 == Loop3) continue; // Don't count test node
1348 48808588 : if (ValRetAPaths(Loop3, Loop2) == 0) break;
1349 33880630 : if (ValRetAPaths(Loop3, Loop2) == TestNode) ++Count;
1350 : }
1351 : }
1352 354992 : if (Count > 0) {
1353 0 : ShowSevereError(state, "Duplicate Node detected in Return Air Paths");
1354 0 : ShowContinueError(state, "Test Node=" + state.dataLoopNodes->NodeID(TestNode));
1355 0 : ShowContinueError(state, "In Air Path=" + state.dataAirLoop->AirToZoneNodeInfo(Loop).AirLoopName);
1356 0 : ErrFound = true;
1357 : }
1358 : }
1359 : }
1360 :
1361 769 : NumSAPNodes.deallocate();
1362 769 : NumRAPNodes.deallocate();
1363 769 : ValRetAPaths.deallocate();
1364 769 : ValSupAPaths.deallocate();
1365 769 : }
1366 :
1367 769 : void TestSupplyAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
1368 : {
1369 :
1370 : // SUBROUTINE INFORMATION:
1371 : // AUTHOR Linda Lawrie
1372 : // DATE WRITTEN March 2003
1373 : // MODIFIED na
1374 : // RE-ENGINEERED na
1375 :
1376 : // PURPOSE OF THIS SUBROUTINE:
1377 : // This subroutine tests supply air path integrity and displays the loop for each branch.
1378 : // Also, input and output nodes.
1379 :
1380 : // Using/Aliasing
1381 : using namespace DataLoopNode;
1382 769 : auto &GetZoneSplitterInput(SplitterComponent::GetSplitterInput);
1383 : using namespace DataZoneEquipment;
1384 769 : auto &NumPrimaryAirSys = state.dataHVACGlobal->NumPrimaryAirSys;
1385 :
1386 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1387 : int Count;
1388 1538 : std::string AirPathNodeName; // Air Path Inlet Node Name
1389 1538 : std::string PrimaryAirLoopName; // Air Loop to which this supply air path is connected
1390 1538 : Array1D_bool FoundSupplyPlenum;
1391 1538 : Array1D_bool FoundZoneSplitter;
1392 1538 : Array1D_string FoundNames;
1393 769 : int NumErr(0); // Error Counter //Autodesk:Init Initialization added
1394 : int BCount;
1395 : int Found;
1396 : int Count1;
1397 : int Count2;
1398 :
1399 : // Do by Paths
1400 769 : ShowMessage(state, "Testing Individual Supply Air Path Integrity");
1401 769 : ErrFound = false;
1402 :
1403 769 : print(state.files.bnd, "{}\n", "! ===============================================================");
1404 : static constexpr std::string_view Format_700("! <#Supply Air Paths>,<Number of Supply Air Paths>");
1405 769 : print(state.files.bnd, "{}\n", Format_700);
1406 769 : print(state.files.bnd, " #Supply Air Paths,{}\n", state.dataZoneEquip->NumSupplyAirPaths);
1407 : static constexpr std::string_view Format_702("! <Supply Air Path>,<Supply Air Path Count>,<Supply Air Path Name>,<AirLoopHVAC Name>");
1408 769 : print(state.files.bnd, "{}\n", Format_702);
1409 : static constexpr std::string_view Format_703("! <#Components on Supply Air Path>,<Number of Components>");
1410 769 : print(state.files.bnd, "{}\n", Format_703);
1411 : static constexpr std::string_view Format_704(
1412 : "! <Supply Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
1413 769 : print(state.files.bnd, "{}\n", Format_704);
1414 : static constexpr std::string_view Format_707("! <#Outlet Nodes on Supply Air Path Component>,<Number of Nodes>");
1415 769 : print(state.files.bnd, "{}\n", Format_707);
1416 : static constexpr std::string_view Format_708(
1417 : "! <Supply Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
1418 : "Node Name>,<AirLoopHVAC Name>");
1419 769 : print(state.files.bnd, "{}\n", Format_708);
1420 :
1421 1945 : for (BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
1422 :
1423 : // Determine which air loop this supply air path is connected to
1424 1176 : Found = 0;
1425 6469 : for (Count1 = 1; Count1 <= NumPrimaryAirSys; ++Count1) {
1426 6469 : PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
1427 6469 : Found = 0;
1428 12954 : for (Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumSupplyNodes; ++Count2) {
1429 12970 : if (state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum ==
1430 6485 : state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipSupplyNodeNum(Count2))
1431 1176 : Found = Count2;
1432 : }
1433 6469 : if (Found != 0) break;
1434 : }
1435 1176 : if (Found == 0) PrimaryAirLoopName = "**Unknown**";
1436 :
1437 1176 : print(state.files.bnd, " Supply Air Path,{},{},{}\n", BCount, state.dataZoneEquip->SupplyAirPath(BCount).Name, PrimaryAirLoopName);
1438 1176 : print(state.files.bnd, " #Components on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents);
1439 :
1440 1176 : AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum);
1441 :
1442 2359 : for (Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
1443 :
1444 3549 : print(state.files.bnd,
1445 : " Supply Air Path Component,{},{},{},{}\n",
1446 : Count,
1447 1183 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
1448 1183 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
1449 1183 : PrimaryAirLoopName);
1450 :
1451 2366 : AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(getEnumerationValue(
1452 3549 : AirLoopHVACCompTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count))));
1453 :
1454 1183 : switch (CompType) {
1455 8 : case AirLoopHVACCompType::SupplyPlenum: {
1456 20 : for (Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count2) {
1457 24 : if (state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName !=
1458 24 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
1459 4 : continue;
1460 8 : if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode)) {
1461 0 : ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
1462 0 : ShowContinueError(state,
1463 0 : format("For AirLoopHVAC:SupplyPlenum={}", state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName));
1464 0 : ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
1465 0 : ShowContinueError(state,
1466 0 : format("Encountered node name (supply plenum)={}",
1467 0 : state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(1))));
1468 0 : ErrFound = true;
1469 0 : ++NumErr;
1470 : }
1471 16 : print(state.files.bnd,
1472 : " #Outlet Nodes on Supply Air Path Component,{}\n",
1473 16 : state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes);
1474 20 : for (Count1 = 1; Count1 <= state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes; ++Count1) {
1475 84 : print(state.files.bnd,
1476 : " Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
1477 : Count1,
1478 12 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
1479 12 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
1480 24 : state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode),
1481 24 : state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(Count1)),
1482 12 : PrimaryAirLoopName);
1483 : }
1484 : }
1485 8 : } break;
1486 1175 : case AirLoopHVACCompType::ZoneSplitter: {
1487 12952 : for (Count2 = 1; Count2 <= state.dataSplitterComponent->NumSplitters; ++Count2) {
1488 23554 : if (state.dataSplitterComponent->SplitterCond(Count2).SplitterName !=
1489 23554 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
1490 10602 : continue;
1491 1175 : if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)) {
1492 0 : ShowSevereError(state, "Error in AirLoopHVAC:SupplyPath=" + state.dataZoneEquip->SupplyAirPath(BCount).Name);
1493 0 : ShowContinueError(state, "For AirLoopHVAC:ZoneSplitter=" + state.dataSplitterComponent->SplitterCond(Count2).SplitterName);
1494 0 : ShowContinueError(state, "Expected inlet node (supply air path)=" + AirPathNodeName);
1495 0 : ShowContinueError(state,
1496 0 : "Encountered node name (zone splitter)=" +
1497 0 : state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode));
1498 0 : ErrFound = true;
1499 0 : ++NumErr;
1500 : }
1501 2350 : print(state.files.bnd,
1502 : " #Outlet Nodes on Supply Air Path Component,{}\n",
1503 2350 : state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes);
1504 4650 : for (Count1 = 1; Count1 <= state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes; ++Count1) {
1505 24325 : print(state.files.bnd,
1506 : " Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
1507 : Count1,
1508 3475 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
1509 3475 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
1510 6950 : state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode),
1511 6950 : state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).OutletNode(Count1)),
1512 3475 : PrimaryAirLoopName);
1513 : }
1514 : }
1515 1175 : } break;
1516 0 : default: {
1517 0 : ShowSevereError(state,
1518 0 : "Invalid Component Type in Supply Air Path=" + state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count));
1519 0 : ErrFound = true;
1520 0 : ++NumErr;
1521 0 : } break;
1522 : }
1523 : }
1524 :
1525 1176 : if (state.dataZoneEquip->SupplyAirPath(BCount).NumNodes > 0) {
1526 : static constexpr std::string_view Format_705("! <#Nodes on Supply Air Path>,<Number of Nodes>");
1527 1176 : print(state.files.bnd, "{}\n", Format_705);
1528 : static constexpr std::string_view Format_706("! <Supply Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
1529 1176 : print(state.files.bnd, "{}\n", Format_706);
1530 1176 : print(state.files.bnd, "#Nodes on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumNodes);
1531 5839 : for (Count2 = 1; Count2 <= state.dataZoneEquip->SupplyAirPath(BCount).NumNodes; ++Count2) {
1532 4663 : if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::PathInlet) {
1533 3528 : print(state.files.bnd,
1534 : " Supply Air Path Node,Inlet Node,{},{},{}\n",
1535 : Count2,
1536 2352 : state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
1537 1176 : PrimaryAirLoopName);
1538 3487 : } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Intermediate) {
1539 21 : print(state.files.bnd,
1540 : " Supply Air Path Node,Through Node,{},{},{}\n",
1541 : Count2,
1542 14 : state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
1543 7 : PrimaryAirLoopName);
1544 3480 : } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Outlet) {
1545 10440 : print(state.files.bnd,
1546 : " Supply Air Path Node,Outlet Node,{},{},{}\n",
1547 : Count2,
1548 6960 : state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
1549 3480 : PrimaryAirLoopName);
1550 : }
1551 : }
1552 : }
1553 : }
1554 :
1555 769 : if (state.dataSplitterComponent->NumSplitters == 0) {
1556 248 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneSplitter") > 0) {
1557 0 : GetZoneSplitterInput(state);
1558 : }
1559 : }
1560 769 : if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
1561 583 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:SupplyPlenum") > 0) {
1562 0 : ZonePlenum::GetZonePlenumInput(state);
1563 : }
1564 : }
1565 :
1566 : // now the reverse. is every zone splitter and supply plenum on supply air path
1567 769 : FoundSupplyPlenum.dimension(state.dataZonePlenum->NumZoneSupplyPlenums, false);
1568 769 : FoundZoneSplitter.dimension(state.dataSplitterComponent->NumSplitters, false);
1569 769 : FoundNames.allocate(state.dataZonePlenum->NumZoneSupplyPlenums);
1570 777 : for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
1571 20 : for (BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
1572 35 : for (Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
1573 31 : if (state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
1574 8 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:SUPPLYPLENUM")
1575 15 : continue;
1576 8 : if (FoundSupplyPlenum(Count1)) {
1577 0 : ShowSevereError(
1578 0 : state, "AirLoopHVAC:SupplyPlenum=\"" + state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName + "\", duplicate entry.");
1579 0 : ShowContinueError(state, "already exists on AirLoopHVAC:SupplyPath=\"" + FoundNames(Count1) + "\".");
1580 0 : ErrFound = true;
1581 : } else {
1582 : // record use
1583 8 : FoundSupplyPlenum(Count1) = true;
1584 8 : FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
1585 : }
1586 : }
1587 : }
1588 : }
1589 769 : FoundNames.deallocate();
1590 769 : FoundNames.allocate(state.dataSplitterComponent->NumSplitters);
1591 1944 : for (Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
1592 12952 : for (BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
1593 23565 : for (Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
1594 35364 : if (state.dataSplitterComponent->SplitterCond(Count1).SplitterName !=
1595 36539 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
1596 1175 : state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONESPLITTER")
1597 10613 : continue;
1598 1175 : if (FoundZoneSplitter(Count1)) {
1599 0 : ShowSevereError(state,
1600 0 : "AirLoopHVAC:ZoneSplitter=\"" + state.dataSplitterComponent->SplitterCond(Count1).SplitterName +
1601 : "\", duplicate entry.");
1602 0 : ShowContinueError(state, "already exists on AirLoopHVAC:SupplyPath=\"" + FoundNames(Count1) + "\".");
1603 0 : ErrFound = true;
1604 : } else {
1605 : // record use
1606 1175 : FoundZoneSplitter(Count1) = true;
1607 1175 : FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
1608 : }
1609 : }
1610 : }
1611 : }
1612 769 : FoundNames.deallocate();
1613 :
1614 769 : if (!all(FoundSupplyPlenum)) {
1615 0 : for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
1616 0 : if (FoundSupplyPlenum(Count1)) continue;
1617 0 : ShowSevereError(state,
1618 0 : "AirLoopHVAC:SupplyPlenum=\"" + state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName +
1619 : "\", not found on any AirLoopHVAC:SupplyPath.");
1620 : // ErrFound=.TRUE.
1621 : }
1622 : }
1623 :
1624 769 : if (!all(FoundZoneSplitter)) {
1625 0 : for (Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
1626 0 : if (FoundZoneSplitter(Count1)) continue;
1627 0 : ShowSevereError(state,
1628 0 : "AirLoopHVAC:ZoneSplitter=\"" + state.dataSplitterComponent->SplitterCond(Count1).SplitterName +
1629 : "\", not found on any AirLoopHVAC:SupplyPath.");
1630 : // ErrFound=.TRUE.
1631 : }
1632 : }
1633 :
1634 769 : FoundSupplyPlenum.deallocate();
1635 769 : FoundZoneSplitter.deallocate();
1636 :
1637 769 : if (ErrFound) {
1638 0 : ShowSevereError(state, "Supply Air Path(s) did not pass integrity testing");
1639 : } else {
1640 769 : ShowMessage(state, "All Supply Air Paths passed integrity testing");
1641 : }
1642 769 : }
1643 :
1644 769 : void TestReturnAirPathIntegrity(EnergyPlusData &state, bool &ErrFound, Array2S_int ValRetAPaths)
1645 : {
1646 :
1647 : // SUBROUTINE INFORMATION:
1648 : // AUTHOR Linda Lawrie
1649 : // DATE WRITTEN March 2003
1650 :
1651 : // PURPOSE OF THIS SUBROUTINE:
1652 : // This subroutine tests return air path integrity and displays the loop for each branch.
1653 : // Also, input and output nodes.
1654 :
1655 : // REFERENCES:
1656 : // Return Air Path Validity Rules:
1657 : // Last component (zone mixer or zone return plenum) must resolve to
1658 : // be the outlet node for the return air path. Inlets to this component must be outlets from
1659 : // previous components or "controlled zone outlets"?.
1660 : // (though converse not true -- each outlet in previous components do not
1661 : // have to be inlets on this item -- though they must be inputs somewhere in the stream).
1662 : // If multiple components and no mixer, then a zone return plenums "outlet" must
1663 : // be represented as an inlet on a later plenum. i.e. some zone return plenums are
1664 : // really acting as "mixers" in a sense. These do not need to be stepwise in succession.
1665 : // Same caveat for inlets from previous item.
1666 : // If multiple components and mixer, then prior condition (nested plenums) is allowed as long as
1667 : // those aren't duplicated as mixer inlets. (i.e. zone rp 1 => zone rp 2 => zone mixer but
1668 : // zone rp 1 outlet should not also be inlet to mixer.
1669 : // Can have (nzrp -- nested zone return plenum, pzrp -- parallel zone return plenum):
1670 : // nzrp 1 => nzrp 2 & pzrp 3 => zm (inlets from nzrp 2 and pzrp 3). Or, likewise:
1671 : // pzrp 1 & pzrp 2 => zm => pzrp 3 (outlets from pzrp 1/2 are inlets to zm whose outlet is an
1672 : // inlet to pzrp 3 whose outlet is the outlet for the return air path.
1673 :
1674 : // Cannot have duplicate nodes in the "inlet" stream? (i.e. cannot have same zone feeding two independent
1675 : // plenums, for example). Similarly, Same return plenum can't be in two air loops nor as two independent
1676 : // return plenums in one return air path.
1677 :
1678 : // Using/Aliasing
1679 : using namespace DataLoopNode;
1680 : using namespace DataZoneEquipment;
1681 : using namespace ZonePlenum;
1682 769 : auto &NumPrimaryAirSys = state.dataHVACGlobal->NumPrimaryAirSys;
1683 769 : auto &GetZoneMixerInput(MixerComponent::GetMixerInput);
1684 : using HVACSingleDuctInduc::FourPipeInductionUnitHasMixer;
1685 : using PoweredInductionUnits::PIUnitHasMixer;
1686 : using PurchasedAirManager::CheckPurchasedAirForReturnPlenum;
1687 :
1688 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1689 : int Loop;
1690 : int Count;
1691 1538 : std::string AirPathNodeName; // Air Path Inlet Node Name
1692 1538 : std::string PrimaryAirLoopName; // Air Loop to which this return air path is connected
1693 1538 : Array1D_bool FoundReturnPlenum;
1694 1538 : Array1D_bool FoundZoneMixer;
1695 1538 : Array1D_string FoundNames;
1696 : int NumErr; // Error Counter
1697 : int BCount;
1698 : int Found;
1699 : int Count1;
1700 : int Count2;
1701 1538 : Array1D_int AllNodes;
1702 : int MixerCount;
1703 : int Count3;
1704 : int NumComp;
1705 : int CountNodes;
1706 :
1707 : // Formats
1708 :
1709 : // Do by Paths
1710 769 : ShowMessage(state, "Testing Individual Return Air Path Integrity");
1711 769 : ErrFound = false;
1712 769 : NumErr = 0;
1713 :
1714 769 : print(state.files.bnd, "{}\n", "! ===============================================================");
1715 : static constexpr std::string_view Format_700("! <#Return Air Paths>,<Number of Return Air Paths>");
1716 769 : print(state.files.bnd, "{}\n", Format_700);
1717 769 : print(state.files.bnd, " #Return Air Paths,{}\n", state.dataZoneEquip->NumReturnAirPaths);
1718 : static constexpr std::string_view Format_702("! <Return Air Path>,<Return Air Path Count>,<Return Air Path Name>,<AirLoopHVAC Name>");
1719 769 : print(state.files.bnd, "{}\n", Format_702);
1720 : static constexpr std::string_view Format_703("! <#Components on Return Air Path>,<Number of Components>");
1721 769 : print(state.files.bnd, "{}\n", Format_703);
1722 : static constexpr std::string_view Format_704(
1723 : "! <Return Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
1724 769 : print(state.files.bnd, "{}\n", Format_704);
1725 : static constexpr std::string_view Format_707("! <#Inlet Nodes on Return Air Path Component>,<Number of Nodes>");
1726 769 : print(state.files.bnd, "{}\n", Format_707);
1727 : static constexpr std::string_view Format_708(
1728 : "! <Return Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
1729 : "Node Name>,<AirLoopHVAC Name>");
1730 769 : print(state.files.bnd, "{}\n", Format_708);
1731 :
1732 769 : AllNodes.allocate(state.dataLoopNodes->NumOfNodes);
1733 :
1734 1928 : for (BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
1735 : // Determine which air loop this supply air path is connected to
1736 1159 : Found = 0;
1737 6438 : for (Count1 = 1; Count1 <= NumPrimaryAirSys; ++Count1) {
1738 6438 : PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
1739 6438 : Found = 0;
1740 12876 : for (Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumReturnNodes; ++Count2) {
1741 12876 : if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum ==
1742 6438 : state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipReturnNodeNum(Count2))
1743 1159 : Found = Count2;
1744 : }
1745 6438 : if (Found != 0) break;
1746 : }
1747 1159 : if (Found == 0) PrimaryAirLoopName = "**Unknown**";
1748 :
1749 1159 : print(state.files.bnd, " Return Air Path,{},{},{}\n", BCount, state.dataZoneEquip->ReturnAirPath(BCount).Name, PrimaryAirLoopName);
1750 :
1751 1159 : NumComp = state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents;
1752 1159 : print(state.files.bnd, " #Components on Return Air Path,{}\n", NumComp);
1753 :
1754 1159 : AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum);
1755 :
1756 1159 : MixerCount = 0;
1757 2427 : for (Count = 1; Count <= NumComp; ++Count) {
1758 3804 : print(state.files.bnd,
1759 : " Return Air Path Component,{},{},{},{}\n",
1760 : Count,
1761 1268 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count),
1762 1268 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count),
1763 1268 : PrimaryAirLoopName);
1764 :
1765 1268 : if (UtilityRoutines::SameString(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count), "AirLoopHVAC:ZoneMixer")) {
1766 997 : ++MixerCount;
1767 : }
1768 : }
1769 :
1770 1159 : if (MixerCount > 1) {
1771 0 : ShowSevereError(state, "Too many zone mixers in Return Air Path=" + state.dataZoneEquip->ReturnAirPath(BCount).Name);
1772 0 : ErrFound = true;
1773 0 : ++NumErr;
1774 0 : continue;
1775 : }
1776 :
1777 1159 : AllNodes = 0;
1778 1159 : CountNodes = 0;
1779 :
1780 1159 : if (NumComp > 0) {
1781 :
1782 2318 : AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(getEnumerationValue(
1783 3477 : AirLoopHVACCompTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp))));
1784 :
1785 1159 : switch (CompType) {
1786 997 : case AirLoopHVACCompType::ZoneMixer: {
1787 12326 : for (Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
1788 11329 : if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) != state.dataMixerComponent->MixerCond(Count2).MixerName)
1789 10332 : continue;
1790 : // Found correct Mixer (by name), check outlet node vs. return air path outlet node
1791 997 : if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)) {
1792 0 : ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
1793 0 : ShowContinueError(state, format("For Connector:Mixer={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
1794 0 : ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
1795 0 : ShowContinueError(state,
1796 0 : format("Encountered node name (mixer)={}",
1797 0 : state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)));
1798 0 : ErrFound = true;
1799 0 : ++NumErr;
1800 : } else {
1801 997 : ++CountNodes;
1802 997 : AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).OutletNode;
1803 3282 : for (Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
1804 2285 : ++CountNodes;
1805 2285 : AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
1806 : }
1807 : }
1808 1994 : print(state.files.bnd,
1809 : " #Inlet Nodes on Return Air Path Component,{}\n",
1810 1994 : state.dataMixerComponent->MixerCond(Count2).NumInletNodes);
1811 3282 : for (Count1 = 1; Count1 <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Count1) {
1812 15995 : print(state.files.bnd,
1813 : " Return Air Path Component Nodes,{},{},{},{},{},{}\n",
1814 : Count1,
1815 2285 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
1816 2285 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
1817 4570 : state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).InletNode(Count1)),
1818 4570 : state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode),
1819 2285 : PrimaryAirLoopName);
1820 : }
1821 : }
1822 997 : } break;
1823 162 : case AirLoopHVACCompType::ReturnPlenum: {
1824 506 : for (Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
1825 344 : if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) !=
1826 344 : state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
1827 182 : continue;
1828 162 : if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)) {
1829 0 : ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
1830 0 : ShowContinueError(
1831 0 : state, format("For AirLoopHVAC:ReturnPlenum={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
1832 0 : ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
1833 0 : ShowContinueError(state,
1834 0 : format("Encountered node name (zone return plenum)={}",
1835 0 : state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)));
1836 0 : ErrFound = true;
1837 0 : ++NumErr;
1838 : } else {
1839 162 : ++CountNodes;
1840 162 : AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode;
1841 882 : for (Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
1842 720 : ++CountNodes;
1843 720 : AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
1844 : }
1845 : }
1846 324 : print(state.files.bnd,
1847 : " #Inlet Nodes on Return Air Path Component,{}\n",
1848 324 : state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes);
1849 882 : for (Count1 = 1; Count1 <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Count1) {
1850 5040 : print(state.files.bnd,
1851 : " Return Air Path Component Nodes,{},{},{},{},{},{}\n",
1852 : Count1,
1853 720 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
1854 720 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
1855 1440 : state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Count1)),
1856 1440 : state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode),
1857 720 : PrimaryAirLoopName);
1858 : }
1859 : }
1860 162 : } break;
1861 0 : default: // This already validated in GetReturnAirPath
1862 0 : break;
1863 : }
1864 : }
1865 :
1866 1159 : if (NumComp > 1) {
1867 217 : for (Count3 = 1; Count3 <= NumComp - 1; ++Count3) {
1868 :
1869 218 : AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(getEnumerationValue(
1870 327 : AirLoopHVACCompTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count3))));
1871 :
1872 109 : switch (CompType) {
1873 0 : case AirLoopHVACCompType::ZoneMixer: {
1874 0 : for (Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
1875 0 : if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) != state.dataMixerComponent->MixerCond(Count2).MixerName)
1876 0 : continue;
1877 0 : for (Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
1878 0 : ++CountNodes;
1879 0 : AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
1880 : }
1881 : }
1882 0 : } break;
1883 109 : case AirLoopHVACCompType::ReturnPlenum: {
1884 430 : for (Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
1885 321 : if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) !=
1886 321 : state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
1887 212 : continue;
1888 639 : for (Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
1889 530 : ++CountNodes;
1890 530 : AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
1891 : }
1892 : }
1893 109 : } break;
1894 0 : default: // This already validated in GetReturnAirPath
1895 0 : break;
1896 : }
1897 : }
1898 : }
1899 1159 : if (CountNodes > 0) {
1900 : static constexpr std::string_view Format_705("! <#Nodes on Return Air Path>,<Number of Nodes>");
1901 1159 : print(state.files.bnd, "{}\n", Format_705);
1902 : static constexpr std::string_view Format_706("! <Return Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
1903 1159 : print(state.files.bnd, "{}\n", Format_706);
1904 1159 : print(state.files.bnd, " #Nodes on Return Air Path,{}\n", CountNodes);
1905 5853 : for (Count2 = 1; Count2 <= CountNodes; ++Count2) {
1906 4694 : if (Count2 == 1) {
1907 3477 : print(state.files.bnd,
1908 : " Return Air Path Node,Outlet Node,{},{},{}\n",
1909 : Count2,
1910 2318 : state.dataLoopNodes->NodeID(AllNodes(Count2)),
1911 1159 : PrimaryAirLoopName);
1912 : } else {
1913 10605 : print(state.files.bnd,
1914 : " Return Air Path Node,Inlet Node,{},{},{}\n",
1915 : Count2,
1916 7070 : state.dataLoopNodes->NodeID(AllNodes(Count2)),
1917 3535 : PrimaryAirLoopName);
1918 : }
1919 : }
1920 : }
1921 : // Determine Air Loop this Return Air Path is on
1922 6438 : for (Count2 = 1; Count2 <= NumPrimaryAirSys; ++Count2) {
1923 6438 : if (state.dataAirLoop->AirToZoneNodeInfo(Count2).NumReturnNodes > 0) {
1924 6438 : if (AllNodes(1) == state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipReturnNodeNum(1)) {
1925 1159 : const auto WAirLoop = Count2;
1926 1159 : ValRetAPaths(_, WAirLoop) = 0;
1927 1159 : ValRetAPaths({1, CountNodes}, WAirLoop) = AllNodes({1, CountNodes});
1928 1159 : break;
1929 : }
1930 : } else {
1931 0 : ShowWarningError(state,
1932 0 : "TestReturnAirPathIntegrity: Air Loop has no Zone Equipment Return Node=" +
1933 0 : state.dataAirLoop->AirToZoneNodeInfo(Count2).AirLoopName);
1934 : }
1935 : }
1936 : }
1937 :
1938 769 : AllNodes.deallocate();
1939 :
1940 769 : if (state.dataMixerComponent->NumMixers == 0) {
1941 377 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneMixer") > 0) {
1942 0 : GetZoneMixerInput(state);
1943 : }
1944 : }
1945 769 : if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
1946 583 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ReturnPlenum") > 0) {
1947 0 : GetZonePlenumInput(state);
1948 : }
1949 : }
1950 :
1951 : // now the reverse. is every zone Mixer and Return plenum on Return air path
1952 769 : FoundReturnPlenum.dimension(state.dataZonePlenum->NumZoneReturnPlenums, false);
1953 769 : FoundZoneMixer.dimension(state.dataMixerComponent->NumMixers, false);
1954 769 : FoundNames.allocate(state.dataZonePlenum->NumZoneReturnPlenums);
1955 1041 : for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
1956 1060 : for (BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
1957 1897 : for (Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
1958 1380 : if (state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
1959 271 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:RETURNPLENUM")
1960 838 : continue;
1961 271 : if (FoundReturnPlenum(Count1)) {
1962 0 : ShowSevereError(
1963 0 : state, "AirLoopHVAC:ReturnPlenum=\"" + state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName + "\", duplicate entry.");
1964 0 : ShowContinueError(state, "already exists on AirLoopHVAC:ReturnPath=\"" + FoundNames(Count1) + "\".");
1965 0 : ErrFound = true;
1966 : } else {
1967 : // record use
1968 271 : FoundReturnPlenum(Count1) = true;
1969 271 : FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
1970 : }
1971 : }
1972 : }
1973 272 : if (CheckPurchasedAirForReturnPlenum(state, Count1)) FoundReturnPlenum(Count1) = true;
1974 : }
1975 769 : FoundNames.deallocate();
1976 769 : FoundNames.allocate(state.dataMixerComponent->NumMixers);
1977 1806 : for (Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
1978 12405 : for (BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
1979 23175 : for (Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
1980 12804 : if (state.dataMixerComponent->MixerCond(Count1).MixerName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
1981 997 : state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONEMIXER")
1982 10810 : continue;
1983 997 : if (FoundZoneMixer(Count1)) {
1984 0 : ShowSevereError(state,
1985 0 : "AirLoopHVAC:ZoneMixer=\"" + state.dataMixerComponent->MixerCond(Count1).MixerName + "\", duplicate entry.");
1986 0 : ShowContinueError(state, "already exists on AirLoopHVAC:ReturnPath=\"" + FoundNames(Count1) + "\".");
1987 0 : ErrFound = true;
1988 : } else {
1989 : // record use
1990 997 : FoundZoneMixer(Count1) = true;
1991 997 : FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
1992 : }
1993 : }
1994 : }
1995 1037 : if (!FoundZoneMixer(Count1)) { // could be as child on other items
1996 : // PIU Units
1997 40 : if (PIUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) FoundZoneMixer(Count1) = true;
1998 : }
1999 1037 : if (!FoundZoneMixer(Count1)) { // could be as child on other items
2000 : // fourPipeInduction units
2001 6 : if (FourPipeInductionUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) FoundZoneMixer(Count1) = true;
2002 : }
2003 1037 : if (!FoundZoneMixer(Count1)) { // could be as child on other items
2004 : // Exhaust Systems
2005 2 : if (ExhaustAirSystemManager::ExhaustSystemHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName))
2006 2 : FoundZoneMixer(Count1) = true;
2007 : }
2008 : }
2009 769 : FoundNames.deallocate();
2010 :
2011 769 : if (!all(FoundReturnPlenum)) {
2012 0 : for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
2013 0 : if (FoundReturnPlenum(Count1)) continue;
2014 0 : ShowSevereError(state,
2015 0 : "AirLoopHVAC:ReturnPlenum=\"" + state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName +
2016 : "\", not found on any AirLoopHVAC:ReturnPath.");
2017 : // ErrFound=.TRUE.
2018 : }
2019 : }
2020 :
2021 769 : if (!all(FoundZoneMixer)) {
2022 0 : for (Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
2023 0 : if (FoundZoneMixer(Count1)) continue;
2024 0 : ShowSevereError(state,
2025 0 : "AirLoopHVAC:ZoneMixer=\"" + state.dataMixerComponent->MixerCond(Count1).MixerName +
2026 : "\", not found on any AirLoopHVAC:ReturnPath, AirLoopHVAC:ExhaustSystem, AirTerminal:SingleDuct:SeriesPIU:Reheat,");
2027 0 : ShowContinueError(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat or AirTerminal:SingleDuct:ConstantVolume:FourPipeInduction.");
2028 : // ErrFound=.TRUE.
2029 : }
2030 : }
2031 :
2032 769 : FoundReturnPlenum.deallocate();
2033 769 : FoundZoneMixer.deallocate();
2034 :
2035 769 : if (ErrFound) {
2036 0 : ShowSevereError(state, "Return Air Path(s) did not pass integrity testing");
2037 : } else {
2038 769 : ShowMessage(state, "All Return Air Paths passed integrity testing");
2039 : }
2040 769 : }
2041 :
2042 33029470 : void CalcComponentSensibleLatentOutput(Real64 const MassFlow, // air mass flow rate, {kg/s}
2043 : Real64 const TDB2, // dry-bulb temperature at state 2 {C}
2044 : Real64 const W2, // humidity ratio at state 2
2045 : Real64 const TDB1, // dry-bulb temperature at at state 1 {C}
2046 : Real64 const W1, // humidity ratio at state 1
2047 : Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
2048 : Real64 &LatentOutput, // latent output rate (state 2 -> State 1), {W}
2049 : Real64 &TotalOutput // total = sensible + latent putput rate (state 2 -> State 1), {W}
2050 : )
2051 : {
2052 :
2053 : // Purpose:
2054 : // returns total, sensible and latent heat rate of change of moist air transitioning
2055 : // between two states. The moist air energy transfer can be cooling or heating process
2056 : // across a cooling, a heating coil, or an HVAC component.
2057 :
2058 : // Methodology:
2059 : // Q_total = m_dot * (h2 - h1)
2060 : // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1);
2061 : // or Q_sensible = m_dot * cp_moistair_MinHumRat * (TDB2 - TDB1)
2062 : // cp_moistair_MinHumRat = Psychrometrics::PsyCpAirFnW(min(W2, W1));
2063 : // Q_latent = Q_total - Q_latent;
2064 :
2065 33029470 : TotalOutput = 0.0;
2066 33029470 : LatentOutput = 0.0;
2067 33029470 : SensibleOutput = 0.0;
2068 33029470 : if (MassFlow > 0.0) {
2069 32209248 : TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDB2, W2) - Psychrometrics::PsyHFnTdbW(TDB1, W1)); // total addition/removal rate, {W};
2070 32209248 : SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1); // sensible addition/removal rate, {W};
2071 32209248 : LatentOutput = TotalOutput - SensibleOutput; // latent addition/removal rate, {W}
2072 : }
2073 33029470 : }
2074 :
2075 51169907 : void CalcZoneSensibleLatentOutput(Real64 const MassFlow, // air mass flow rate, {kg/s}
2076 : Real64 const TDBEquip, // dry-bulb temperature at equipment outlet {C}
2077 : Real64 const WEquip, // humidity ratio at equipment outlet
2078 : Real64 const TDBZone, // dry-bulb temperature at zone air node {C}
2079 : Real64 const WZone, // humidity ratio at zone air node
2080 : Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
2081 : Real64 &LatentOutput, // latent output rate (state 2 -> State 1), {W}
2082 : Real64 &TotalOutput // total = sensible + latent putput rate (state 2 -> State 1), {W}
2083 : )
2084 : {
2085 :
2086 : // Purpose:
2087 : // returns total, sensible and latent heat rate of transfer between the supply air zone inlet
2088 : // node and zone air node. The moist air energy transfer can be cooling or heating depending
2089 : // on the supply air zone inlet node and zone air node conditions.
2090 :
2091 : // Methodology:
2092 : // Q_total = m_dot * (hEquip - hZone)
2093 : // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
2094 : // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
2095 : // cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
2096 : // Q_latent = Q_total - Q_latent;
2097 :
2098 51169907 : TotalOutput = 0.0;
2099 51169907 : LatentOutput = 0.0;
2100 51169907 : SensibleOutput = 0.0;
2101 51169907 : if (MassFlow > 0.0) {
2102 79454620 : TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDBEquip, WEquip) -
2103 39727310 : Psychrometrics::PsyHFnTdbW(TDBZone, WZone)); // total addition/removal rate, {W};
2104 39727310 : SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
2105 39727310 : LatentOutput = TotalOutput - SensibleOutput; // latent addition/removal rate, {W}
2106 : }
2107 51169907 : }
2108 :
2109 48112698 : Real64 calcZoneSensibleOutput(Real64 const MassFlow, // air mass flow rate, {kg/s}
2110 : Real64 const TDBEquip, // dry-bulb temperature at equipment outlet {C}
2111 : Real64 const TDBZone, // dry-bulb temperature at zone air node {C}
2112 : Real64 const WZone // humidity ratio at zone air node
2113 : )
2114 : {
2115 :
2116 : // Purpose:
2117 : // returns sensible heat rate of transfer between the supply air zone inlet node and
2118 : // zone air node. The moist air energy transfer can be cooling or heating depending
2119 : // on the supply air zone inlet node and zone air node conditions.
2120 :
2121 : // Methodology:
2122 : // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
2123 : // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
2124 : // cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
2125 :
2126 48112698 : Real64 sensibleOutput = 0.0; // sensible output rate (state 2 -> State 1), {W}
2127 48112698 : if (MassFlow > 0.0) {
2128 39006409 : sensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
2129 : }
2130 48112698 : return sensibleOutput;
2131 : }
2132 2313 : } // namespace EnergyPlus
|