Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : // #include <ObjexxFCL/Fmath.hh>
54 : #include <ObjexxFCL/string.functions.hh>
55 :
56 : // EnergyPlus Headers
57 : #include <EnergyPlus/Autosizing/CoolingAirFlowSizing.hh>
58 : #include <EnergyPlus/Autosizing/CoolingCapacitySizing.hh>
59 : #include <EnergyPlus/Autosizing/HeatingAirFlowSizing.hh>
60 : #include <EnergyPlus/Autosizing/HeatingCapacitySizing.hh>
61 : #include <EnergyPlus/Data/EnergyPlusData.hh>
62 : #include <EnergyPlus/DataContaminantBalance.hh>
63 : #include <EnergyPlus/DataEnvironment.hh>
64 : #include <EnergyPlus/DataHVACGlobals.hh>
65 : #include <EnergyPlus/DataHeatBalFanSys.hh>
66 : #include <EnergyPlus/DataHeatBalance.hh>
67 : #include <EnergyPlus/DataIPShortCuts.hh>
68 : #include <EnergyPlus/DataLoopNode.hh>
69 : #include <EnergyPlus/DataSizing.hh>
70 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
71 : #include <EnergyPlus/DataZoneEquipment.hh>
72 : #include <EnergyPlus/EMSManager.hh>
73 : #include <EnergyPlus/General.hh>
74 : #include <EnergyPlus/GeneralRoutines.hh>
75 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
76 : #include <EnergyPlus/NodeInputManager.hh>
77 : #include <EnergyPlus/OutAirNodeManager.hh>
78 : #include <EnergyPlus/OutputProcessor.hh>
79 : #include <EnergyPlus/Psychrometrics.hh>
80 : #include <EnergyPlus/PurchasedAirManager.hh>
81 : #include <EnergyPlus/ScheduleManager.hh>
82 : #include <EnergyPlus/UtilityRoutines.hh>
83 : #include <EnergyPlus/ZonePlenum.hh>
84 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
85 :
86 : namespace EnergyPlus::PurchasedAirManager {
87 :
88 : // Module containing data and routines dealing with Ideal Loads Air System (formerly PURCHASED AIR).
89 :
90 : // MODULE INFORMATION:
91 : // AUTHOR Russ Taylor
92 : // DATE WRITTEN May 1997
93 : // MODIFIED Fred Buhl Dec 1999
94 : // B. Griffith Dec 2006. added OA lookup function, moved getinputflag up to Module
95 : // M. Witte June 2011, add new features including DCV, economizer, dehumidification and humidification
96 : // NOTE: MJW Sep 13, 2011: Still need to review checks for negative loads and impossible supply temps???
97 : // There are no Deallocate statements in here - should there be?
98 : // RE-ENGINEERED na
99 :
100 : // PURPOSE OF THIS MODULE:
101 : // To encapsulate the data and algorithms required to simulate the
102 : // Zone Ideal Loads Air System component. This component supplies hot or cold air
103 : // at a fixed or variable temperature to a zone to meet the zone load.
104 : // With the June 2011 enhancements it will also supply outdoor air with optional demand-controlled ventilation
105 : // and economizer controls, plus new options for controlling zone humidity.
106 :
107 : // METHODOLOGY EMPLOYED:
108 : // The user can choose via input the max/min hot and cold supply air
109 : // temperature and humidity ratio. The air mass flow rate is chosen
110 : // to meet the (remaining) zone load or based on the outdoor air flow requirement.
111 : // If the outdoor air flow sets the flow rate, the supply air temperature and
112 : // humidity ratio are adjusted to meet the zone load.
113 :
114 : // Using/Aliasing
115 : using Psychrometrics::PsyCpAirFnW;
116 : using Psychrometrics::PsyHFnTdbW;
117 : using Psychrometrics::PsyRhoAirFnPbTdbW;
118 : using Psychrometrics::PsyTdbFnHW;
119 : using Psychrometrics::PsyTsatFnHPb;
120 : using Psychrometrics::PsyWFnTdbH;
121 : using Psychrometrics::PsyWFnTdbRhPb;
122 :
123 : // Delta humidity ratio limit, 0.00025 equals delta between 45F dewpoint and 46F dewpoint
124 : // used to prevent dividing by near zero
125 : Real64 constexpr SmallDeltaHumRat(0.00025);
126 :
127 : constexpr std::array<std::string_view, (int)LimitType::Num> limitTypeNames = {
128 : "NoLimit", "LimitFlowRate", "LimitCapacity", "LimitFlowRateAndCapacity"};
129 : constexpr std::array<std::string_view, (int)LimitType::Num> limitTypeNamesUC = {
130 : "NOLIMIT", "LIMITFLOWRATE", "LIMITCAPACITY", "LIMITFLOWRATEANDCAPACITY"};
131 :
132 : constexpr std::array<std::string_view, (int)HumControl::Num> humControlNames = {
133 : "None", "ConstantSensibleHeatRatio", "Humidistat", "ConstantSupplyHumidityRatio"};
134 : constexpr std::array<std::string_view, (int)HumControl::Num> humControlNamesUC = {
135 : "NONE", "CONSTANTSENSIBLEHEATRATIO", "HUMIDISTAT", "CONSTANTSUPPLYHUMIDITYRATIO"};
136 :
137 : constexpr std::array<std::string_view, (int)DCV::Num> dcvNames = {"None", "OccupancySchedule", "CO2SetPoint"};
138 : constexpr std::array<std::string_view, (int)DCV::Num> dcvNamesUC = {"NONE", "OCCUPANCYSCHEDULE", "CO2SETPOINT"};
139 :
140 : constexpr std::array<std::string_view, (int)Econ::Num> econNames = {"NoEconomizer", "DifferentialDryBulb", "DifferentialEnthalpy"};
141 : constexpr std::array<std::string_view, (int)Econ::Num> econNamesUC = {"NOECONOMIZER", "DIFFERENTIALDRYBULB", "DIFFERENTIALENTHALPY"};
142 :
143 : constexpr std::array<std::string_view, (int)HeatRecovery::Num> heatRecoveryNames = {"None", "Sensible", "Enthalpy"};
144 : constexpr std::array<std::string_view, (int)HeatRecovery::Num> heatRecoveryNamesUC = {"NONE", "SENSIBLE", "ENTHALPY"};
145 :
146 761 : void SimPurchasedAir(EnergyPlusData &state,
147 : std::string const &PurchAirName,
148 : Real64 &SysOutputProvided,
149 : Real64 &MoistOutputProvided, // Moisture output provided (kg/s), dehumidification = negative
150 : bool const FirstHVACIteration,
151 : int const ControlledZoneNum,
152 : int &CompIndex)
153 : {
154 :
155 : // SUBROUTINE INFORMATION:
156 : // AUTHOR Russ Taylor
157 : // DATE WRITTEN May 1997
158 : // MODIFIED Don Shirey, Aug 2009 (LatOutputProvided - now MoistOutputProvided)
159 : // RE-ENGINEERED na
160 :
161 : // PURPOSE OF THIS SUBROUTINE:
162 : // This subroutine manages Purchased Air component simulation.
163 : // It is called from SimZoneEquipment in the ZoneEquipmentManager
164 : // at the system time step.
165 :
166 : int PurchAirNum;
167 :
168 761 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
169 9 : GetPurchasedAir(state);
170 9 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
171 : }
172 :
173 : // Find the correct PurchasedAir Equipment
174 761 : if (CompIndex == 0) {
175 9 : PurchAirNum = Util::FindItemInList(PurchAirName, state.dataPurchasedAirMgr->PurchAir);
176 9 : if (PurchAirNum == 0) {
177 0 : ShowFatalError(state, format("SimPurchasedAir: Unit not found={}", PurchAirName));
178 : }
179 9 : CompIndex = PurchAirNum;
180 : } else {
181 752 : PurchAirNum = CompIndex;
182 752 : if (PurchAirNum > state.dataPurchasedAirMgr->NumPurchAir || PurchAirNum < 1) {
183 0 : ShowFatalError(state,
184 0 : format("SimPurchasedAir: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
185 : PurchAirNum,
186 0 : state.dataPurchasedAirMgr->NumPurchAir,
187 : PurchAirName));
188 : }
189 752 : if (state.dataPurchasedAirMgr->CheckEquipName(PurchAirNum)) {
190 5 : if (PurchAirName != state.dataPurchasedAirMgr->PurchAir(PurchAirNum).Name) {
191 0 : ShowFatalError(state,
192 0 : format("SimPurchasedAir: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
193 : PurchAirNum,
194 : PurchAirName,
195 0 : state.dataPurchasedAirMgr->PurchAir(PurchAirNum).Name));
196 : }
197 5 : state.dataPurchasedAirMgr->CheckEquipName(PurchAirNum) = false;
198 : }
199 : }
200 :
201 761 : InitPurchasedAir(state, PurchAirNum, ControlledZoneNum);
202 :
203 761 : CalcPurchAirLoads(state, PurchAirNum, SysOutputProvided, MoistOutputProvided, ControlledZoneNum);
204 :
205 761 : UpdatePurchasedAir(state, PurchAirNum, FirstHVACIteration);
206 :
207 761 : ReportPurchasedAir(state, PurchAirNum);
208 761 : }
209 :
210 17 : void GetPurchasedAir(EnergyPlusData &state)
211 : {
212 :
213 : // SUBROUTINE INFORMATION:
214 : // AUTHOR Russ Taylor
215 : // DATE WRITTEN June 1997
216 : // MODIFIED M. Witte, June 2011, add new features including DCV, economizer, dehumidification
217 : // and humidification controls
218 : // RE-ENGINEERED na
219 :
220 : // PURPOSE OF THIS SUBROUTINE:
221 : // Get the input data for the Purchased Air objects.
222 : // Set up output variables.
223 :
224 : // Using/Aliasing
225 : using NodeInputManager::CheckUniqueNodeNames;
226 : using NodeInputManager::EndUniqueNodeCheck;
227 : using NodeInputManager::GetOnlySingleNode;
228 : using NodeInputManager::InitUniqueNodeCheck;
229 : using OutAirNodeManager::CheckAndAddAirNodeNumber;
230 : using namespace DataLoopNode;
231 : using ZonePlenum::GetReturnPlenumIndex;
232 :
233 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
234 : static constexpr std::string_view RoutineName("GetPurchasedAir: "); // include trailing blank space
235 : static constexpr std::string_view routineName = "GetPurchasedAir";
236 :
237 17 : bool ErrorsFound(false); // If errors detected in input
238 17 : auto &s_ipsc = state.dataIPShortCut;
239 :
240 17 : s_ipsc->cCurrentModuleObject = "ZoneHVAC:IdealLoadsAirSystem";
241 :
242 17 : auto &PurchAir(state.dataPurchasedAirMgr->PurchAir);
243 :
244 17 : state.dataPurchasedAirMgr->NumPurchAir = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
245 :
246 17 : PurchAir.allocate(state.dataPurchasedAirMgr->NumPurchAir);
247 17 : state.dataPurchasedAirMgr->CheckEquipName.allocate(state.dataPurchasedAirMgr->NumPurchAir);
248 17 : state.dataPurchasedAirMgr->PurchAirNumericFields.allocate(state.dataPurchasedAirMgr->NumPurchAir);
249 17 : state.dataPurchasedAirMgr->CheckEquipName = true;
250 :
251 17 : if (state.dataPurchasedAirMgr->NumPurchAir > 0) {
252 : int NumAlphas;
253 : int NumNums;
254 : int IOStat;
255 14 : InitUniqueNodeCheck(state, s_ipsc->cCurrentModuleObject);
256 28 : for (int PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
257 14 : PurchAir(PurchAirNum).cObjectName = s_ipsc->cCurrentModuleObject;
258 :
259 28 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
260 14 : s_ipsc->cCurrentModuleObject,
261 : PurchAirNum,
262 14 : s_ipsc->cAlphaArgs,
263 : NumAlphas,
264 14 : s_ipsc->rNumericArgs,
265 : NumNums,
266 : IOStat,
267 14 : s_ipsc->lNumericFieldBlanks,
268 14 : s_ipsc->lAlphaFieldBlanks,
269 14 : s_ipsc->cAlphaFieldNames,
270 14 : s_ipsc->cNumericFieldNames);
271 :
272 14 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
273 :
274 14 : state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames.allocate(NumNums);
275 14 : state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames = "";
276 14 : state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames = s_ipsc->cNumericFieldNames;
277 :
278 14 : PurchAir(PurchAirNum).Name = s_ipsc->cAlphaArgs(1);
279 : // get optional availability schedule
280 14 : if (s_ipsc->lAlphaFieldBlanks(2)) {
281 14 : PurchAir(PurchAirNum).availSched = Sched::GetScheduleAlwaysOn(state);
282 0 : } else if ((PurchAir(PurchAirNum).availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
283 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
284 0 : ErrorsFound = true;
285 : }
286 : // Purchased air supply air node is an outlet node
287 28 : PurchAir(PurchAirNum).ZoneSupplyAirNodeNum = GetOnlySingleNode(state,
288 14 : s_ipsc->cAlphaArgs(3),
289 : ErrorsFound,
290 : DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
291 14 : s_ipsc->cAlphaArgs(1),
292 : DataLoopNode::NodeFluidType::Air,
293 : DataLoopNode::ConnectionType::Outlet,
294 : NodeInputManager::CompFluidStream::Primary,
295 : ObjectIsNotParent);
296 14 : bool UniqueNodeError = false;
297 14 : CheckUniqueNodeNames(state, s_ipsc->cAlphaFieldNames(3), UniqueNodeError, s_ipsc->cAlphaArgs(3), s_ipsc->cAlphaArgs(1));
298 14 : if (UniqueNodeError) {
299 0 : ErrorsFound = true;
300 : }
301 : // If new (optional) exhaust air node name is present, then register it as inlet
302 14 : if (!s_ipsc->lAlphaFieldBlanks(4)) {
303 8 : if (s_ipsc->lAlphaFieldBlanks(5)) {
304 14 : PurchAir(PurchAirNum).ZoneExhaustAirNodeNum = GetOnlySingleNode(state,
305 7 : s_ipsc->cAlphaArgs(4),
306 : ErrorsFound,
307 : DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
308 7 : s_ipsc->cAlphaArgs(1),
309 : DataLoopNode::NodeFluidType::Air,
310 : DataLoopNode::ConnectionType::Inlet,
311 : NodeInputManager::CompFluidStream::Primary,
312 : ObjectIsNotParent);
313 : } else {
314 2 : PurchAir(PurchAirNum).ZoneExhaustAirNodeNum = GetOnlySingleNode(state,
315 1 : s_ipsc->cAlphaArgs(4),
316 : ErrorsFound,
317 : DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
318 1 : s_ipsc->cAlphaArgs(1),
319 : DataLoopNode::NodeFluidType::Air,
320 : DataLoopNode::ConnectionType::Outlet,
321 : NodeInputManager::CompFluidStream::Primary,
322 : ObjectIsNotParent);
323 : }
324 8 : UniqueNodeError = false;
325 8 : CheckUniqueNodeNames(state, s_ipsc->cAlphaFieldNames(4), UniqueNodeError, s_ipsc->cAlphaArgs(4), s_ipsc->cAlphaArgs(1));
326 8 : if (UniqueNodeError) {
327 0 : ErrorsFound = true;
328 : }
329 : }
330 14 : if (!s_ipsc->lAlphaFieldBlanks(5)) {
331 2 : PurchAir(PurchAirNum).PlenumExhaustAirNodeNum = GetOnlySingleNode(state,
332 1 : s_ipsc->cAlphaArgs(5),
333 : ErrorsFound,
334 : DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
335 1 : s_ipsc->cAlphaArgs(1),
336 : DataLoopNode::NodeFluidType::Air,
337 : DataLoopNode::ConnectionType::Inlet,
338 : NodeInputManager::CompFluidStream::Primary,
339 : ObjectIsNotParent);
340 : }
341 14 : PurchAir(PurchAirNum).MaxHeatSuppAirTemp = s_ipsc->rNumericArgs(1);
342 14 : PurchAir(PurchAirNum).MinCoolSuppAirTemp = s_ipsc->rNumericArgs(2);
343 14 : PurchAir(PurchAirNum).MaxHeatSuppAirHumRat = s_ipsc->rNumericArgs(3);
344 14 : PurchAir(PurchAirNum).MinCoolSuppAirHumRat = s_ipsc->rNumericArgs(4);
345 :
346 14 : PurchAir(PurchAirNum).HeatingLimit = static_cast<LimitType>(getEnumValue(limitTypeNamesUC, s_ipsc->cAlphaArgs(6)));
347 14 : switch (PurchAir(PurchAirNum).HeatingLimit) {
348 :
349 13 : case LimitType::None: {
350 13 : } break;
351 :
352 0 : case LimitType::FlowRate: {
353 0 : if (s_ipsc->lNumericFieldBlanks(5)) {
354 0 : PurchAir(PurchAirNum).HeatingLimit = LimitType::None;
355 : }
356 0 : } break;
357 :
358 1 : case LimitType::Capacity: {
359 1 : if (s_ipsc->lNumericFieldBlanks(6)) {
360 0 : PurchAir(PurchAirNum).HeatingLimit = LimitType::None;
361 : }
362 1 : } break;
363 :
364 0 : case LimitType::FlowRateAndCapacity: {
365 0 : if (s_ipsc->lNumericFieldBlanks(5) && s_ipsc->lNumericFieldBlanks(6)) {
366 0 : PurchAir(PurchAirNum).HeatingLimit = LimitType::None;
367 0 : } else if (s_ipsc->lNumericFieldBlanks(5)) {
368 0 : PurchAir(PurchAirNum).HeatingLimit = LimitType::Capacity;
369 0 : } else if (s_ipsc->lNumericFieldBlanks(6)) {
370 0 : PurchAir(PurchAirNum).HeatingLimit = LimitType::FlowRate;
371 : }
372 0 : } break;
373 :
374 0 : default: {
375 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
376 0 : ErrorsFound = true;
377 0 : } break;
378 : }
379 :
380 14 : PurchAir(PurchAirNum).MaxHeatVolFlowRate = s_ipsc->rNumericArgs(5);
381 14 : PurchAir(PurchAirNum).MaxHeatSensCap = s_ipsc->rNumericArgs(6);
382 :
383 14 : PurchAir(PurchAirNum).CoolingLimit = static_cast<LimitType>(getEnumValue(limitTypeNamesUC, s_ipsc->cAlphaArgs(7)));
384 :
385 14 : switch (PurchAir(PurchAirNum).CoolingLimit) {
386 14 : case LimitType::None: {
387 14 : } break;
388 :
389 0 : case LimitType::FlowRate: {
390 0 : if (s_ipsc->lNumericFieldBlanks(7)) {
391 0 : PurchAir(PurchAirNum).CoolingLimit = LimitType::None;
392 : }
393 0 : } break;
394 :
395 0 : case LimitType::Capacity: {
396 0 : if (s_ipsc->lNumericFieldBlanks(8)) {
397 0 : PurchAir(PurchAirNum).CoolingLimit = LimitType::None;
398 : }
399 0 : } break;
400 :
401 0 : case LimitType::FlowRateAndCapacity: {
402 0 : if (s_ipsc->lNumericFieldBlanks(7) && s_ipsc->lNumericFieldBlanks(8)) {
403 0 : PurchAir(PurchAirNum).CoolingLimit = LimitType::None;
404 0 : } else if (s_ipsc->lNumericFieldBlanks(7)) {
405 0 : PurchAir(PurchAirNum).CoolingLimit = LimitType::Capacity;
406 0 : } else if (s_ipsc->lNumericFieldBlanks(8)) {
407 0 : PurchAir(PurchAirNum).CoolingLimit = LimitType::FlowRate;
408 : }
409 0 : } break;
410 :
411 0 : default: {
412 0 : ShowSevereInvalidKey(state,
413 : eoh,
414 0 : s_ipsc->cAlphaFieldNames(6),
415 0 : s_ipsc->cAlphaArgs(6),
416 : "Valid entries are None, ConstantSensibleHeatRatio, Humidistat, or ConstantSupplyHumidityRatio");
417 0 : ErrorsFound = true;
418 0 : } break;
419 : }
420 :
421 14 : PurchAir(PurchAirNum).MaxCoolVolFlowRate = s_ipsc->rNumericArgs(7);
422 14 : PurchAir(PurchAirNum).MaxCoolTotCap = s_ipsc->rNumericArgs(8);
423 :
424 : // get optional heating availability schedule
425 14 : if (s_ipsc->lAlphaFieldBlanks(8)) {
426 14 : PurchAir(PurchAirNum).heatAvailSched = Sched::GetScheduleAlwaysOn(state);
427 0 : } else if ((PurchAir(PurchAirNum).heatAvailSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(8))) == nullptr) {
428 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8));
429 0 : ErrorsFound = true;
430 : }
431 :
432 : // get optional cooling availability schedule
433 14 : if (s_ipsc->lAlphaFieldBlanks(9)) {
434 14 : PurchAir(PurchAirNum).coolAvailSched = Sched::GetScheduleAlwaysOn(state);
435 0 : } else if ((PurchAir(PurchAirNum).coolAvailSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(9))) == nullptr) {
436 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(9), s_ipsc->cAlphaArgs(9));
437 0 : ErrorsFound = true;
438 : }
439 :
440 : // get Dehumidification control type
441 14 : PurchAir(PurchAirNum).DehumidCtrlType = static_cast<HumControl>(getEnumValue(humControlNamesUC, s_ipsc->cAlphaArgs(10)));
442 14 : if (PurchAir(PurchAirNum).DehumidCtrlType == HumControl::Invalid) {
443 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
444 0 : ErrorsFound = true;
445 : }
446 :
447 14 : PurchAir(PurchAirNum).CoolSHR = s_ipsc->rNumericArgs(9);
448 :
449 : // get Humidification control type
450 14 : PurchAir(PurchAirNum).HumidCtrlType = static_cast<HumControl>(getEnumValue(humControlNamesUC, s_ipsc->cAlphaArgs(11)));
451 28 : if (PurchAir(PurchAirNum).HumidCtrlType == HumControl::Invalid ||
452 14 : PurchAir(PurchAirNum).HumidCtrlType == HumControl::ConstantSensibleHeatRatio) {
453 0 : ShowSevereInvalidKey(state,
454 : eoh,
455 0 : s_ipsc->cAlphaFieldNames(11),
456 0 : s_ipsc->cAlphaArgs(11),
457 : "Valid entries are None, Humidistat, or ConstantSupplyHumidityRatio");
458 0 : ErrorsFound = true;
459 : }
460 :
461 : // get Design specification outdoor air object
462 14 : if (!s_ipsc->lAlphaFieldBlanks(12)) {
463 1 : PurchAir(PurchAirNum).OARequirementsPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(12), state.dataSize->OARequirements);
464 1 : if (PurchAir(PurchAirNum).OARequirementsPtr == 0) {
465 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(12), s_ipsc->cAlphaArgs(12));
466 0 : ErrorsFound = true;
467 : } else {
468 1 : PurchAir(PurchAirNum).OutdoorAir = true;
469 : }
470 : }
471 :
472 : // If outdoor air specified, then get Outdoor air inlet node and other outdoor air inputs
473 14 : if (PurchAir(PurchAirNum).OutdoorAir) {
474 1 : if (s_ipsc->lAlphaFieldBlanks(13)) {
475 : // If there is outdoor air and outdoor air inlet node is blank, then create one
476 1 : if (len(s_ipsc->cAlphaArgs(1)) < Constant::MaxNameLength - 23) { // protect against long name leading to > 100 chars
477 1 : s_ipsc->cAlphaArgs(13) = s_ipsc->cAlphaArgs(1) + " OUTDOOR AIR INLET NODE";
478 : } else {
479 0 : s_ipsc->cAlphaArgs(13) = s_ipsc->cAlphaArgs(1).substr(0, 75) + " OUTDOOR AIR INLET NODE";
480 : }
481 1 : if (state.dataGlobal->DisplayExtraWarnings) {
482 0 : ShowWarningError(state, format("{}{}=\"{} blank field", RoutineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
483 0 : ShowContinueError(state,
484 0 : format("{} is blank, but there is outdoor air requested for this system.", s_ipsc->cAlphaFieldNames(13)));
485 0 : ShowContinueError(state, format("Creating node name ={}", s_ipsc->cAlphaArgs(13)));
486 : }
487 : }
488 : // Register OA node
489 2 : PurchAir(PurchAirNum).OutdoorAirNodeNum = GetOnlySingleNode(state,
490 1 : s_ipsc->cAlphaArgs(13),
491 : ErrorsFound,
492 : DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
493 1 : s_ipsc->cAlphaArgs(1),
494 : DataLoopNode::NodeFluidType::Air,
495 : DataLoopNode::ConnectionType::Outlet,
496 : NodeInputManager::CompFluidStream::Primary,
497 : ObjectIsNotParent);
498 : // Check if OA node is initialized in OutdoorAir:Node or OutdoorAir:Nodelist
499 : bool IsOANodeListed; // Flag for OA node name listed in OutdoorAir:Node or Nodelist
500 1 : CheckAndAddAirNodeNumber(state, PurchAir(PurchAirNum).OutdoorAirNodeNum, IsOANodeListed);
501 1 : if ((!IsOANodeListed) && state.dataGlobal->DisplayExtraWarnings) {
502 0 : ShowWarningError(state, format("{}{}=\"{} missing data", RoutineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
503 0 : ShowContinueError(state,
504 0 : format("{} does not appear in an OutdoorAir:NodeList or as an OutdoorAir:Node.", s_ipsc->cAlphaArgs(13)));
505 0 : ShowContinueError(state, format("Adding OutdoorAir:Node={}", s_ipsc->cAlphaArgs(13)));
506 : }
507 1 : UniqueNodeError = false;
508 1 : CheckUniqueNodeNames(state, s_ipsc->cAlphaFieldNames(13), UniqueNodeError, s_ipsc->cAlphaArgs(13), s_ipsc->cAlphaArgs(1));
509 1 : if (UniqueNodeError) {
510 0 : ErrorsFound = true;
511 : }
512 :
513 : // get Demand controlled ventilation type
514 1 : PurchAir(PurchAirNum).DCVType = static_cast<DCV>(getEnumValue(dcvNamesUC, s_ipsc->cAlphaArgs(14)));
515 1 : if (PurchAir(PurchAirNum).DCVType == DCV::Invalid) {
516 0 : ShowSevereInvalidKey(state,
517 : eoh,
518 0 : s_ipsc->cAlphaFieldNames(14),
519 0 : s_ipsc->cAlphaArgs(14),
520 : "Valid entries are None, OccupancySchedule, or CO2Setpoint");
521 0 : ErrorsFound = true;
522 1 : } else if (PurchAir(PurchAirNum).DCVType == DCV::CO2SetPoint && !state.dataContaminantBalance->Contaminant.CO2Simulation) {
523 0 : PurchAir(PurchAirNum).DCVType = DCV::None;
524 0 : ShowWarningError(state, format("{}{}=\"{} invalid data", RoutineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
525 0 : ShowContinueError(state, format("{}={} but CO2 simulation is not active.", s_ipsc->cAlphaFieldNames(14), s_ipsc->cAlphaArgs(14)));
526 0 : ShowContinueError(state, format("Resetting {} to NoDCV", s_ipsc->cAlphaFieldNames(14)));
527 0 : ShowContinueError(state,
528 : "To activate CO2 simulation, use ZoneAirContaminantBalance object and specify \"Carbon Dioxide "
529 : "Concentration\"=\"Yes\".");
530 : }
531 :
532 : // get Outdoor air economizer type
533 1 : PurchAir(PurchAirNum).EconomizerType = static_cast<Econ>(getEnumValue(econNamesUC, s_ipsc->cAlphaArgs(15)));
534 1 : if (PurchAir(PurchAirNum).EconomizerType == Econ::Invalid) {
535 0 : ShowSevereInvalidKey(state,
536 : eoh,
537 0 : s_ipsc->cAlphaFieldNames(15),
538 0 : s_ipsc->cAlphaArgs(15),
539 : "Valid entries are NoEconomizer, DifferentialDryBulb, or DifferentialEnthalpy");
540 0 : ErrorsFound = true;
541 : }
542 :
543 : // get Outdoor air heat recovery type and effectiveness
544 1 : PurchAir(PurchAirNum).HtRecType = static_cast<HeatRecovery>(getEnumValue(heatRecoveryNamesUC, s_ipsc->cAlphaArgs(16)));
545 1 : if (PurchAir(PurchAirNum).HtRecType == HeatRecovery::Invalid) {
546 0 : ShowSevereInvalidKey(
547 0 : state, eoh, s_ipsc->cAlphaFieldNames(16), s_ipsc->cAlphaArgs(16), "Valid entries are None, Sensible, or Enthalpy");
548 0 : ErrorsFound = true;
549 : }
550 :
551 : } else { // No outdoorair
552 13 : PurchAir(PurchAirNum).DCVType = DCV::None;
553 13 : PurchAir(PurchAirNum).EconomizerType = Econ::NoEconomizer;
554 13 : PurchAir(PurchAirNum).HtRecType = HeatRecovery::None;
555 : }
556 :
557 14 : PurchAir(PurchAirNum).HtRecSenEff = s_ipsc->rNumericArgs(10);
558 14 : PurchAir(PurchAirNum).HtRecLatEff = s_ipsc->rNumericArgs(11);
559 :
560 28 : for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
561 14 : if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
562 1 : continue;
563 : }
564 26 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++NodeNum) {
565 13 : if (PurchAir(PurchAirNum).ZoneSupplyAirNodeNum == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(NodeNum)) {
566 13 : PurchAir(PurchAirNum).ZonePtr = CtrlZone;
567 : }
568 : }
569 : }
570 :
571 14 : PurchAir(PurchAirNum).HVACSizingIndex = 0;
572 14 : if (!s_ipsc->lAlphaFieldBlanks(17)) {
573 0 : PurchAir(PurchAirNum).HVACSizingIndex = Util::FindItemInList(s_ipsc->cAlphaArgs(17), state.dataSize->ZoneHVACSizing);
574 0 : if (PurchAir(PurchAirNum).HVACSizingIndex == 0) {
575 0 : ShowSevereError(state, format("{} = {} not found.", s_ipsc->cAlphaFieldNames(17), s_ipsc->cAlphaArgs(17)));
576 0 : ShowContinueError(state, format("Occurs in {} = {}", s_ipsc->cCurrentModuleObject, PurchAir(PurchAirNum).Name));
577 0 : ErrorsFound = true;
578 : }
579 : }
580 :
581 : // initialize the calculated and report values
582 14 : PurchAir(PurchAirNum).MaxHeatMassFlowRate = 0.0;
583 14 : PurchAir(PurchAirNum).MaxCoolMassFlowRate = 0.0;
584 14 : PurchAir(PurchAirNum).SenHeatEnergy = 0.0;
585 14 : PurchAir(PurchAirNum).LatHeatEnergy = 0.0;
586 14 : PurchAir(PurchAirNum).TotHeatEnergy = 0.0;
587 14 : PurchAir(PurchAirNum).SenCoolEnergy = 0.0;
588 14 : PurchAir(PurchAirNum).LatCoolEnergy = 0.0;
589 14 : PurchAir(PurchAirNum).TotCoolEnergy = 0.0;
590 14 : PurchAir(PurchAirNum).ZoneSenHeatEnergy = 0.0;
591 14 : PurchAir(PurchAirNum).ZoneLatHeatEnergy = 0.0;
592 14 : PurchAir(PurchAirNum).ZoneTotHeatEnergy = 0.0;
593 14 : PurchAir(PurchAirNum).ZoneSenCoolEnergy = 0.0;
594 14 : PurchAir(PurchAirNum).ZoneLatCoolEnergy = 0.0;
595 14 : PurchAir(PurchAirNum).ZoneTotCoolEnergy = 0.0;
596 14 : PurchAir(PurchAirNum).OASenHeatEnergy = 0.0;
597 14 : PurchAir(PurchAirNum).OALatHeatEnergy = 0.0;
598 14 : PurchAir(PurchAirNum).OATotHeatEnergy = 0.0;
599 14 : PurchAir(PurchAirNum).OASenCoolEnergy = 0.0;
600 14 : PurchAir(PurchAirNum).OALatCoolEnergy = 0.0;
601 14 : PurchAir(PurchAirNum).OATotCoolEnergy = 0.0;
602 14 : PurchAir(PurchAirNum).HtRecSenHeatEnergy = 0.0;
603 14 : PurchAir(PurchAirNum).HtRecLatHeatEnergy = 0.0;
604 14 : PurchAir(PurchAirNum).HtRecTotHeatEnergy = 0.0;
605 14 : PurchAir(PurchAirNum).HtRecSenCoolEnergy = 0.0;
606 14 : PurchAir(PurchAirNum).HtRecLatCoolEnergy = 0.0;
607 14 : PurchAir(PurchAirNum).HtRecTotCoolEnergy = 0.0;
608 14 : PurchAir(PurchAirNum).SenHeatRate = 0.0;
609 14 : PurchAir(PurchAirNum).LatHeatRate = 0.0;
610 14 : PurchAir(PurchAirNum).TotHeatRate = 0.0;
611 14 : PurchAir(PurchAirNum).SenCoolRate = 0.0;
612 14 : PurchAir(PurchAirNum).LatCoolRate = 0.0;
613 14 : PurchAir(PurchAirNum).TotCoolRate = 0.0;
614 14 : PurchAir(PurchAirNum).ZoneSenHeatRate = 0.0;
615 14 : PurchAir(PurchAirNum).ZoneLatHeatRate = 0.0;
616 14 : PurchAir(PurchAirNum).ZoneTotHeatRate = 0.0;
617 14 : PurchAir(PurchAirNum).ZoneSenCoolRate = 0.0;
618 14 : PurchAir(PurchAirNum).ZoneLatCoolRate = 0.0;
619 14 : PurchAir(PurchAirNum).ZoneTotCoolRate = 0.0;
620 14 : PurchAir(PurchAirNum).OASenHeatRate = 0.0;
621 14 : PurchAir(PurchAirNum).OALatHeatRate = 0.0;
622 14 : PurchAir(PurchAirNum).OATotHeatRate = 0.0;
623 14 : PurchAir(PurchAirNum).OASenCoolRate = 0.0;
624 14 : PurchAir(PurchAirNum).OALatCoolRate = 0.0;
625 14 : PurchAir(PurchAirNum).OATotCoolRate = 0.0;
626 14 : PurchAir(PurchAirNum).HtRecSenHeatRate = 0.0;
627 14 : PurchAir(PurchAirNum).HtRecLatHeatRate = 0.0;
628 14 : PurchAir(PurchAirNum).HtRecTotHeatRate = 0.0;
629 14 : PurchAir(PurchAirNum).HtRecSenCoolRate = 0.0;
630 14 : PurchAir(PurchAirNum).HtRecLatCoolRate = 0.0;
631 14 : PurchAir(PurchAirNum).HtRecTotCoolRate = 0.0;
632 :
633 14 : PurchAir(PurchAirNum).OutdoorAirMassFlowRate = 0.0;
634 14 : PurchAir(PurchAirNum).OutdoorAirVolFlowRateStdRho = 0.0;
635 14 : PurchAir(PurchAirNum).SupplyAirMassFlowRate = 0.0;
636 14 : PurchAir(PurchAirNum).SupplyAirVolFlowRateStdRho = 0.0;
637 : }
638 14 : EndUniqueNodeCheck(state, s_ipsc->cCurrentModuleObject);
639 : }
640 :
641 31 : for (int PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
642 :
643 : // Setup Output variables
644 : // energy variables
645 28 : SetupOutputVariable(state,
646 : "Zone Ideal Loads Supply Air Sensible Heating Energy",
647 : Constant::Units::J,
648 14 : PurchAir(PurchAirNum).SenHeatEnergy,
649 : OutputProcessor::TimeStepType::System,
650 : OutputProcessor::StoreType::Sum,
651 14 : PurchAir(PurchAirNum).Name);
652 28 : SetupOutputVariable(state,
653 : "Zone Ideal Loads Supply Air Latent Heating Energy",
654 : Constant::Units::J,
655 14 : PurchAir(PurchAirNum).LatHeatEnergy,
656 : OutputProcessor::TimeStepType::System,
657 : OutputProcessor::StoreType::Sum,
658 14 : PurchAir(PurchAirNum).Name);
659 28 : SetupOutputVariable(state,
660 : "Zone Ideal Loads Supply Air Total Heating Energy",
661 : Constant::Units::J,
662 14 : PurchAir(PurchAirNum).TotHeatEnergy,
663 : OutputProcessor::TimeStepType::System,
664 : OutputProcessor::StoreType::Sum,
665 14 : PurchAir(PurchAirNum).Name,
666 : Constant::eResource::DistrictHeatingWater,
667 : OutputProcessor::Group::HVAC,
668 : OutputProcessor::EndUseCat::Heating);
669 28 : SetupOutputVariable(state,
670 : "Zone Ideal Loads Supply Air Sensible Cooling Energy",
671 : Constant::Units::J,
672 14 : PurchAir(PurchAirNum).SenCoolEnergy,
673 : OutputProcessor::TimeStepType::System,
674 : OutputProcessor::StoreType::Sum,
675 14 : PurchAir(PurchAirNum).Name);
676 28 : SetupOutputVariable(state,
677 : "Zone Ideal Loads Supply Air Latent Cooling Energy",
678 : Constant::Units::J,
679 14 : PurchAir(PurchAirNum).LatCoolEnergy,
680 : OutputProcessor::TimeStepType::System,
681 : OutputProcessor::StoreType::Sum,
682 14 : PurchAir(PurchAirNum).Name);
683 28 : SetupOutputVariable(state,
684 : "Zone Ideal Loads Supply Air Total Cooling Energy",
685 : Constant::Units::J,
686 14 : PurchAir(PurchAirNum).TotCoolEnergy,
687 : OutputProcessor::TimeStepType::System,
688 : OutputProcessor::StoreType::Sum,
689 14 : PurchAir(PurchAirNum).Name,
690 : Constant::eResource::DistrictCooling,
691 : OutputProcessor::Group::HVAC,
692 : OutputProcessor::EndUseCat::Cooling);
693 28 : SetupOutputVariable(state,
694 : "Zone Ideal Loads Zone Sensible Heating Energy",
695 : Constant::Units::J,
696 14 : PurchAir(PurchAirNum).ZoneSenHeatEnergy,
697 : OutputProcessor::TimeStepType::System,
698 : OutputProcessor::StoreType::Sum,
699 14 : PurchAir(PurchAirNum).Name);
700 28 : SetupOutputVariable(state,
701 : "Zone Ideal Loads Zone Latent Heating Energy",
702 : Constant::Units::J,
703 14 : PurchAir(PurchAirNum).ZoneLatHeatEnergy,
704 : OutputProcessor::TimeStepType::System,
705 : OutputProcessor::StoreType::Sum,
706 14 : PurchAir(PurchAirNum).Name);
707 28 : SetupOutputVariable(state,
708 : "Zone Ideal Loads Zone Total Heating Energy",
709 : Constant::Units::J,
710 14 : PurchAir(PurchAirNum).ZoneTotHeatEnergy,
711 : OutputProcessor::TimeStepType::System,
712 : OutputProcessor::StoreType::Sum,
713 14 : PurchAir(PurchAirNum).Name);
714 28 : SetupOutputVariable(state,
715 : "Zone Ideal Loads Zone Sensible Cooling Energy",
716 : Constant::Units::J,
717 14 : PurchAir(PurchAirNum).ZoneSenCoolEnergy,
718 : OutputProcessor::TimeStepType::System,
719 : OutputProcessor::StoreType::Sum,
720 14 : PurchAir(PurchAirNum).Name);
721 28 : SetupOutputVariable(state,
722 : "Zone Ideal Loads Zone Latent Cooling Energy",
723 : Constant::Units::J,
724 14 : PurchAir(PurchAirNum).ZoneLatCoolEnergy,
725 : OutputProcessor::TimeStepType::System,
726 : OutputProcessor::StoreType::Sum,
727 14 : PurchAir(PurchAirNum).Name);
728 28 : SetupOutputVariable(state,
729 : "Zone Ideal Loads Zone Total Cooling Energy",
730 : Constant::Units::J,
731 14 : PurchAir(PurchAirNum).ZoneTotCoolEnergy,
732 : OutputProcessor::TimeStepType::System,
733 : OutputProcessor::StoreType::Sum,
734 14 : PurchAir(PurchAirNum).Name);
735 28 : SetupOutputVariable(state,
736 : "Zone Ideal Loads Outdoor Air Sensible Heating Energy",
737 : Constant::Units::J,
738 14 : PurchAir(PurchAirNum).OASenHeatEnergy,
739 : OutputProcessor::TimeStepType::System,
740 : OutputProcessor::StoreType::Sum,
741 14 : PurchAir(PurchAirNum).Name);
742 28 : SetupOutputVariable(state,
743 : "Zone Ideal Loads Outdoor Air Latent Heating Energy",
744 : Constant::Units::J,
745 14 : PurchAir(PurchAirNum).OALatHeatEnergy,
746 : OutputProcessor::TimeStepType::System,
747 : OutputProcessor::StoreType::Sum,
748 14 : PurchAir(PurchAirNum).Name);
749 28 : SetupOutputVariable(state,
750 : "Zone Ideal Loads Outdoor Air Total Heating Energy",
751 : Constant::Units::J,
752 14 : PurchAir(PurchAirNum).OATotHeatEnergy,
753 : OutputProcessor::TimeStepType::System,
754 : OutputProcessor::StoreType::Sum,
755 14 : PurchAir(PurchAirNum).Name);
756 28 : SetupOutputVariable(state,
757 : "Zone Ideal Loads Outdoor Air Sensible Cooling Energy",
758 : Constant::Units::J,
759 14 : PurchAir(PurchAirNum).OASenCoolEnergy,
760 : OutputProcessor::TimeStepType::System,
761 : OutputProcessor::StoreType::Sum,
762 14 : PurchAir(PurchAirNum).Name);
763 28 : SetupOutputVariable(state,
764 : "Zone Ideal Loads Outdoor Air Latent Cooling Energy",
765 : Constant::Units::J,
766 14 : PurchAir(PurchAirNum).OALatCoolEnergy,
767 : OutputProcessor::TimeStepType::System,
768 : OutputProcessor::StoreType::Sum,
769 14 : PurchAir(PurchAirNum).Name);
770 28 : SetupOutputVariable(state,
771 : "Zone Ideal Loads Outdoor Air Total Cooling Energy",
772 : Constant::Units::J,
773 14 : PurchAir(PurchAirNum).OATotCoolEnergy,
774 : OutputProcessor::TimeStepType::System,
775 : OutputProcessor::StoreType::Sum,
776 14 : PurchAir(PurchAirNum).Name);
777 28 : SetupOutputVariable(state,
778 : "Zone Ideal Loads Heat Recovery Sensible Heating Energy",
779 : Constant::Units::J,
780 14 : PurchAir(PurchAirNum).HtRecSenHeatEnergy,
781 : OutputProcessor::TimeStepType::System,
782 : OutputProcessor::StoreType::Sum,
783 14 : PurchAir(PurchAirNum).Name);
784 28 : SetupOutputVariable(state,
785 : "Zone Ideal Loads Heat Recovery Latent Heating Energy",
786 : Constant::Units::J,
787 14 : PurchAir(PurchAirNum).HtRecLatHeatEnergy,
788 : OutputProcessor::TimeStepType::System,
789 : OutputProcessor::StoreType::Sum,
790 14 : PurchAir(PurchAirNum).Name);
791 28 : SetupOutputVariable(state,
792 : "Zone Ideal Loads Heat Recovery Total Heating Energy",
793 : Constant::Units::J,
794 14 : PurchAir(PurchAirNum).HtRecTotHeatEnergy,
795 : OutputProcessor::TimeStepType::System,
796 : OutputProcessor::StoreType::Sum,
797 14 : PurchAir(PurchAirNum).Name);
798 28 : SetupOutputVariable(state,
799 : "Zone Ideal Loads Heat Recovery Sensible Cooling Energy",
800 : Constant::Units::J,
801 14 : PurchAir(PurchAirNum).HtRecSenCoolEnergy,
802 : OutputProcessor::TimeStepType::System,
803 : OutputProcessor::StoreType::Sum,
804 14 : PurchAir(PurchAirNum).Name);
805 28 : SetupOutputVariable(state,
806 : "Zone Ideal Loads Heat Recovery Latent Cooling Energy",
807 : Constant::Units::J,
808 14 : PurchAir(PurchAirNum).HtRecLatCoolEnergy,
809 : OutputProcessor::TimeStepType::System,
810 : OutputProcessor::StoreType::Sum,
811 14 : PurchAir(PurchAirNum).Name);
812 28 : SetupOutputVariable(state,
813 : "Zone Ideal Loads Heat Recovery Total Cooling Energy",
814 : Constant::Units::J,
815 14 : PurchAir(PurchAirNum).HtRecTotCoolEnergy,
816 : OutputProcessor::TimeStepType::System,
817 : OutputProcessor::StoreType::Sum,
818 14 : PurchAir(PurchAirNum).Name);
819 :
820 : // rate variables
821 28 : SetupOutputVariable(state,
822 : "Zone Ideal Loads Supply Air Sensible Heating Rate",
823 : Constant::Units::W,
824 14 : PurchAir(PurchAirNum).SenHeatRate,
825 : OutputProcessor::TimeStepType::System,
826 : OutputProcessor::StoreType::Average,
827 14 : PurchAir(PurchAirNum).Name);
828 28 : SetupOutputVariable(state,
829 : "Zone Ideal Loads Supply Air Latent Heating Rate",
830 : Constant::Units::W,
831 14 : PurchAir(PurchAirNum).LatHeatRate,
832 : OutputProcessor::TimeStepType::System,
833 : OutputProcessor::StoreType::Average,
834 14 : PurchAir(PurchAirNum).Name);
835 28 : SetupOutputVariable(state,
836 : "Zone Ideal Loads Supply Air Total Heating Rate",
837 : Constant::Units::W,
838 14 : PurchAir(PurchAirNum).TotHeatRate,
839 : OutputProcessor::TimeStepType::System,
840 : OutputProcessor::StoreType::Average,
841 14 : PurchAir(PurchAirNum).Name);
842 28 : SetupOutputVariable(state,
843 : "Zone Ideal Loads Supply Air Sensible Cooling Rate",
844 : Constant::Units::W,
845 14 : PurchAir(PurchAirNum).SenCoolRate,
846 : OutputProcessor::TimeStepType::System,
847 : OutputProcessor::StoreType::Average,
848 14 : PurchAir(PurchAirNum).Name);
849 28 : SetupOutputVariable(state,
850 : "Zone Ideal Loads Supply Air Latent Cooling Rate",
851 : Constant::Units::W,
852 14 : PurchAir(PurchAirNum).LatCoolRate,
853 : OutputProcessor::TimeStepType::System,
854 : OutputProcessor::StoreType::Average,
855 14 : PurchAir(PurchAirNum).Name);
856 28 : SetupOutputVariable(state,
857 : "Zone Ideal Loads Supply Air Total Cooling Rate",
858 : Constant::Units::W,
859 14 : PurchAir(PurchAirNum).TotCoolRate,
860 : OutputProcessor::TimeStepType::System,
861 : OutputProcessor::StoreType::Average,
862 14 : PurchAir(PurchAirNum).Name);
863 28 : SetupOutputVariable(state,
864 : "Zone Ideal Loads Zone Sensible Heating Rate",
865 : Constant::Units::W,
866 14 : PurchAir(PurchAirNum).ZoneSenHeatRate,
867 : OutputProcessor::TimeStepType::System,
868 : OutputProcessor::StoreType::Average,
869 14 : PurchAir(PurchAirNum).Name);
870 28 : SetupOutputVariable(state,
871 : "Zone Ideal Loads Zone Latent Heating Rate",
872 : Constant::Units::W,
873 14 : PurchAir(PurchAirNum).ZoneLatHeatRate,
874 : OutputProcessor::TimeStepType::System,
875 : OutputProcessor::StoreType::Average,
876 14 : PurchAir(PurchAirNum).Name);
877 28 : SetupOutputVariable(state,
878 : "Zone Ideal Loads Zone Total Heating Rate",
879 : Constant::Units::W,
880 14 : PurchAir(PurchAirNum).ZoneTotHeatRate,
881 : OutputProcessor::TimeStepType::System,
882 : OutputProcessor::StoreType::Average,
883 14 : PurchAir(PurchAirNum).Name);
884 28 : SetupOutputVariable(state,
885 : "Zone Ideal Loads Zone Sensible Cooling Rate",
886 : Constant::Units::W,
887 14 : PurchAir(PurchAirNum).ZoneSenCoolRate,
888 : OutputProcessor::TimeStepType::System,
889 : OutputProcessor::StoreType::Average,
890 14 : PurchAir(PurchAirNum).Name);
891 28 : SetupOutputVariable(state,
892 : "Zone Ideal Loads Zone Latent Cooling Rate",
893 : Constant::Units::W,
894 14 : PurchAir(PurchAirNum).ZoneLatCoolRate,
895 : OutputProcessor::TimeStepType::System,
896 : OutputProcessor::StoreType::Average,
897 14 : PurchAir(PurchAirNum).Name);
898 28 : SetupOutputVariable(state,
899 : "Zone Ideal Loads Zone Total Cooling Rate",
900 : Constant::Units::W,
901 14 : PurchAir(PurchAirNum).ZoneTotCoolRate,
902 : OutputProcessor::TimeStepType::System,
903 : OutputProcessor::StoreType::Average,
904 14 : PurchAir(PurchAirNum).Name);
905 28 : SetupOutputVariable(state,
906 : "Zone Ideal Loads Outdoor Air Sensible Heating Rate",
907 : Constant::Units::W,
908 14 : PurchAir(PurchAirNum).OASenHeatRate,
909 : OutputProcessor::TimeStepType::System,
910 : OutputProcessor::StoreType::Average,
911 14 : PurchAir(PurchAirNum).Name);
912 28 : SetupOutputVariable(state,
913 : "Zone Ideal Loads Outdoor Air Latent Heating Rate",
914 : Constant::Units::W,
915 14 : PurchAir(PurchAirNum).OALatHeatRate,
916 : OutputProcessor::TimeStepType::System,
917 : OutputProcessor::StoreType::Average,
918 14 : PurchAir(PurchAirNum).Name);
919 28 : SetupOutputVariable(state,
920 : "Zone Ideal Loads Outdoor Air Total Heating Rate",
921 : Constant::Units::W,
922 14 : PurchAir(PurchAirNum).OATotHeatRate,
923 : OutputProcessor::TimeStepType::System,
924 : OutputProcessor::StoreType::Average,
925 14 : PurchAir(PurchAirNum).Name);
926 28 : SetupOutputVariable(state,
927 : "Zone Ideal Loads Outdoor Air Sensible Cooling Rate",
928 : Constant::Units::W,
929 14 : PurchAir(PurchAirNum).OASenCoolRate,
930 : OutputProcessor::TimeStepType::System,
931 : OutputProcessor::StoreType::Average,
932 14 : PurchAir(PurchAirNum).Name);
933 28 : SetupOutputVariable(state,
934 : "Zone Ideal Loads Outdoor Air Latent Cooling Rate",
935 : Constant::Units::W,
936 14 : PurchAir(PurchAirNum).OALatCoolRate,
937 : OutputProcessor::TimeStepType::System,
938 : OutputProcessor::StoreType::Average,
939 14 : PurchAir(PurchAirNum).Name);
940 28 : SetupOutputVariable(state,
941 : "Zone Ideal Loads Outdoor Air Total Cooling Rate",
942 : Constant::Units::W,
943 14 : PurchAir(PurchAirNum).OATotCoolRate,
944 : OutputProcessor::TimeStepType::System,
945 : OutputProcessor::StoreType::Average,
946 14 : PurchAir(PurchAirNum).Name);
947 28 : SetupOutputVariable(state,
948 : "Zone Ideal Loads Heat Recovery Sensible Heating Rate",
949 : Constant::Units::W,
950 14 : PurchAir(PurchAirNum).HtRecSenHeatRate,
951 : OutputProcessor::TimeStepType::System,
952 : OutputProcessor::StoreType::Average,
953 14 : PurchAir(PurchAirNum).Name);
954 28 : SetupOutputVariable(state,
955 : "Zone Ideal Loads Heat Recovery Latent Heating Rate",
956 : Constant::Units::W,
957 14 : PurchAir(PurchAirNum).HtRecLatHeatRate,
958 : OutputProcessor::TimeStepType::System,
959 : OutputProcessor::StoreType::Average,
960 14 : PurchAir(PurchAirNum).Name);
961 28 : SetupOutputVariable(state,
962 : "Zone Ideal Loads Heat Recovery Total Heating Rate",
963 : Constant::Units::W,
964 14 : PurchAir(PurchAirNum).HtRecTotHeatRate,
965 : OutputProcessor::TimeStepType::System,
966 : OutputProcessor::StoreType::Average,
967 14 : PurchAir(PurchAirNum).Name);
968 28 : SetupOutputVariable(state,
969 : "Zone Ideal Loads Heat Recovery Sensible Cooling Rate",
970 : Constant::Units::W,
971 14 : PurchAir(PurchAirNum).HtRecSenCoolRate,
972 : OutputProcessor::TimeStepType::System,
973 : OutputProcessor::StoreType::Average,
974 14 : PurchAir(PurchAirNum).Name);
975 28 : SetupOutputVariable(state,
976 : "Zone Ideal Loads Heat Recovery Latent Cooling Rate",
977 : Constant::Units::W,
978 14 : PurchAir(PurchAirNum).HtRecLatCoolRate,
979 : OutputProcessor::TimeStepType::System,
980 : OutputProcessor::StoreType::Average,
981 14 : PurchAir(PurchAirNum).Name);
982 28 : SetupOutputVariable(state,
983 : "Zone Ideal Loads Heat Recovery Total Cooling Rate",
984 : Constant::Units::W,
985 14 : PurchAir(PurchAirNum).HtRecTotCoolRate,
986 : OutputProcessor::TimeStepType::System,
987 : OutputProcessor::StoreType::Average,
988 14 : PurchAir(PurchAirNum).Name);
989 :
990 28 : SetupOutputVariable(state,
991 : "Zone Ideal Loads Economizer Active Time",
992 : Constant::Units::hr,
993 14 : PurchAir(PurchAirNum).TimeEconoActive,
994 : OutputProcessor::TimeStepType::System,
995 : OutputProcessor::StoreType::Sum,
996 14 : PurchAir(PurchAirNum).Name);
997 28 : SetupOutputVariable(state,
998 : "Zone Ideal Loads Heat Recovery Active Time",
999 : Constant::Units::hr,
1000 14 : PurchAir(PurchAirNum).TimeHtRecActive,
1001 : OutputProcessor::TimeStepType::System,
1002 : OutputProcessor::StoreType::Sum,
1003 14 : PurchAir(PurchAirNum).Name);
1004 :
1005 14 : SetupOutputVariable(state,
1006 : "Zone Ideal Loads Hybrid Ventilation Available Status",
1007 : Constant::Units::None,
1008 14 : (int &)PurchAir(PurchAirNum).availStatus,
1009 : OutputProcessor::TimeStepType::System,
1010 : OutputProcessor::StoreType::Average,
1011 14 : PurchAir(PurchAirNum).Name);
1012 :
1013 : // air flows
1014 28 : SetupOutputVariable(state,
1015 : "Zone Ideal Loads Outdoor Air Mass Flow Rate",
1016 : Constant::Units::kg_s,
1017 14 : PurchAir(PurchAirNum).OutdoorAirMassFlowRate,
1018 : OutputProcessor::TimeStepType::System,
1019 : OutputProcessor::StoreType::Average,
1020 14 : PurchAir(PurchAirNum).Name);
1021 28 : SetupOutputVariable(state,
1022 : "Zone Ideal Loads Outdoor Air Standard Density Volume Flow Rate",
1023 : Constant::Units::m3_s,
1024 14 : PurchAir(PurchAirNum).OutdoorAirVolFlowRateStdRho,
1025 : OutputProcessor::TimeStepType::System,
1026 : OutputProcessor::StoreType::Average,
1027 14 : PurchAir(PurchAirNum).Name);
1028 28 : SetupOutputVariable(state,
1029 : "Zone Ideal Loads Supply Air Mass Flow Rate",
1030 : Constant::Units::kg_s,
1031 14 : PurchAir(PurchAirNum).SupplyAirMassFlowRate,
1032 : OutputProcessor::TimeStepType::System,
1033 : OutputProcessor::StoreType::Average,
1034 14 : PurchAir(PurchAirNum).Name);
1035 28 : SetupOutputVariable(state,
1036 : "Zone Ideal Loads Supply Air Standard Density Volume Flow Rate",
1037 : Constant::Units::m3_s,
1038 14 : PurchAir(PurchAirNum).SupplyAirVolFlowRateStdRho,
1039 : OutputProcessor::TimeStepType::System,
1040 : OutputProcessor::StoreType::Average,
1041 14 : PurchAir(PurchAirNum).Name);
1042 :
1043 : // Supply Air temperature
1044 28 : SetupOutputVariable(state,
1045 : "Zone Ideal Loads Supply Air Temperature",
1046 : Constant::Units::C,
1047 14 : PurchAir(PurchAirNum).SupplyTemp,
1048 : OutputProcessor::TimeStepType::System,
1049 : OutputProcessor::StoreType::Average,
1050 14 : PurchAir(PurchAirNum).Name);
1051 : // Supply Air Humidity Ratio
1052 28 : SetupOutputVariable(state,
1053 : "Zone Ideal Loads Supply Air Humidity Ratio",
1054 : Constant::Units::kgWater_kgDryAir,
1055 14 : PurchAir(PurchAirNum).SupplyHumRat,
1056 : OutputProcessor::TimeStepType::System,
1057 : OutputProcessor::StoreType::Average,
1058 14 : PurchAir(PurchAirNum).Name);
1059 :
1060 : // Mixed Air temperature
1061 28 : SetupOutputVariable(state,
1062 : "Zone Ideal Loads Mixed Air Temperature",
1063 : Constant::Units::C,
1064 14 : PurchAir(PurchAirNum).MixedAirTemp,
1065 : OutputProcessor::TimeStepType::System,
1066 : OutputProcessor::StoreType::Average,
1067 14 : PurchAir(PurchAirNum).Name);
1068 : // Mixed Air Humidity Ratio
1069 28 : SetupOutputVariable(state,
1070 : "Zone Ideal Loads Mixed Air Humidity Ratio",
1071 : Constant::Units::kgWater_kgDryAir,
1072 14 : PurchAir(PurchAirNum).MixedAirHumRat,
1073 : OutputProcessor::TimeStepType::System,
1074 : OutputProcessor::StoreType::Average,
1075 14 : PurchAir(PurchAirNum).Name);
1076 :
1077 14 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
1078 8 : SetupEMSActuator(state,
1079 : "Ideal Loads Air System",
1080 4 : PurchAir(PurchAirNum).Name,
1081 : "Air Mass Flow Rate",
1082 : "[kg/s]",
1083 4 : PurchAir(PurchAirNum).EMSOverrideMdotOn,
1084 4 : PurchAir(PurchAirNum).EMSValueMassFlowRate);
1085 8 : SetupEMSActuator(state,
1086 : "Ideal Loads Air System",
1087 4 : PurchAir(PurchAirNum).Name,
1088 : "Outdoor Air Mass Flow Rate",
1089 : "[kg/s]",
1090 4 : PurchAir(PurchAirNum).EMSOverrideOAMdotOn,
1091 4 : PurchAir(PurchAirNum).EMSValueOAMassFlowRate);
1092 8 : SetupEMSActuator(state,
1093 : "Ideal Loads Air System",
1094 4 : PurchAir(PurchAirNum).Name,
1095 : "Air Temperature",
1096 : "[C]",
1097 4 : PurchAir(PurchAirNum).EMSOverrideSupplyTempOn,
1098 4 : PurchAir(PurchAirNum).EMSValueSupplyTemp);
1099 8 : SetupEMSActuator(state,
1100 : "Ideal Loads Air System",
1101 4 : PurchAir(PurchAirNum).Name,
1102 : "Air Humidity Ratio",
1103 : "[kgWater/kgDryAir]",
1104 4 : PurchAir(PurchAirNum).EMSOverrideSupplyHumRatOn,
1105 4 : PurchAir(PurchAirNum).EMSValueSupplyHumRat);
1106 : }
1107 : }
1108 :
1109 17 : if (ErrorsFound) {
1110 0 : ShowFatalError(state, format("{}Errors found in input. Preceding conditions cause termination.", RoutineName));
1111 : }
1112 17 : }
1113 :
1114 766 : void InitPurchasedAir(EnergyPlusData &state, int const PurchAirNum, int const ControlledZoneNum)
1115 : {
1116 :
1117 : // SUBROUTINE INFORMATION:
1118 : // AUTHOR Russ Taylor
1119 : // DATE WRITTEN Nov 1997
1120 :
1121 : // PURPOSE OF THIS SUBROUTINE:
1122 : // Initialize the PurchAir data structure.
1123 :
1124 : // Using/Aliasing
1125 : using DataZoneEquipment::CheckZoneEquipmentList;
1126 : using General::FindNumberInList;
1127 : using ZonePlenum::GetReturnPlenumIndex;
1128 : using ZonePlenum::GetReturnPlenumName;
1129 :
1130 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1131 : bool UnitOn; // simple checks for error
1132 :
1133 : // Do the Begin Simulation initializations
1134 766 : if (state.dataPurchasedAirMgr->InitPurchasedAirMyOneTimeFlag) {
1135 13 : state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag.allocate(state.dataPurchasedAirMgr->NumPurchAir);
1136 13 : state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag.allocate(state.dataPurchasedAirMgr->NumPurchAir);
1137 13 : state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone.allocate(state.dataPurchasedAirMgr->NumPurchAir);
1138 13 : state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag = true;
1139 13 : state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag = true;
1140 13 : state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone = false;
1141 13 : state.dataPurchasedAirMgr->InitPurchasedAirMyOneTimeFlag = false;
1142 : }
1143 :
1144 : // need to check all units to see if they are on Zone Equipment List or issue warning
1145 766 : if (!state.dataPurchasedAirMgr->InitPurchasedAirZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
1146 9 : state.dataPurchasedAirMgr->InitPurchasedAirZoneEquipmentListChecked = true;
1147 18 : for (int Loop = 1; Loop <= state.dataPurchasedAirMgr->NumPurchAir; ++Loop) {
1148 9 : auto &PurchAirLoop = state.dataPurchasedAirMgr->PurchAir(Loop);
1149 :
1150 : // link with return plenum if used (i.e., PlenumExhaustAirNodeNum will be non-zero)
1151 9 : if (PurchAirLoop.PlenumExhaustAirNodeNum > 0) {
1152 1 : PurchAirLoop.ReturnPlenumIndex = GetReturnPlenumIndex(state, PurchAirLoop.PlenumExhaustAirNodeNum);
1153 1 : if (PurchAirLoop.ReturnPlenumIndex > 0) {
1154 1 : GetReturnPlenumName(state, PurchAirLoop.ReturnPlenumIndex, PurchAirLoop.ReturnPlenumName);
1155 1 : InitializePlenumArrays(state, Loop);
1156 : } else {
1157 0 : ShowSevereError(state,
1158 0 : format("InitPurchasedAir: {} = {} cannot find ZoneHVAC:ReturnPlenum. It will not be simulated.",
1159 0 : PurchAirLoop.cObjectName,
1160 0 : PurchAirLoop.Name));
1161 : }
1162 : }
1163 :
1164 9 : if (CheckZoneEquipmentList(state, PurchAirLoop.cObjectName, PurchAirLoop.Name)) {
1165 9 : continue;
1166 : }
1167 0 : ShowSevereError(state,
1168 0 : format("InitPurchasedAir: {} = {} is not on any ZoneHVAC:EquipmentList. It will not be simulated.",
1169 0 : PurchAirLoop.cObjectName,
1170 0 : PurchAirLoop.Name));
1171 : }
1172 : }
1173 :
1174 766 : auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
1175 : // one time inits for each unit - links PurchAirNum with static input data from ControlledZoneNum and ActualZoneNum
1176 766 : if (!state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone(PurchAirNum)) {
1177 13 : state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone(PurchAirNum) = true;
1178 :
1179 : // Is the supply node really a zone inlet node?
1180 : // this check has to be done here because of SimPurchasedAir passing in ControlledZoneNum
1181 13 : int SupplyNodeNum = PurchAir.ZoneSupplyAirNodeNum;
1182 13 : if (SupplyNodeNum > 0) {
1183 13 : int NodeIndex = FindNumberInList(SupplyNodeNum,
1184 13 : state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).InletNode,
1185 13 : state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumInletNodes);
1186 13 : if (NodeIndex == 0) {
1187 0 : ShowSevereError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
1188 0 : ShowContinueError(state,
1189 0 : format("Zone Supply Air Node Name={} is not a zone inlet node.", state.dataLoopNodes->NodeID(SupplyNodeNum)));
1190 0 : ShowContinueError(
1191 : state,
1192 0 : format("Check ZoneHVAC:EquipmentConnections for zone={}", state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneName));
1193 0 : ShowFatalError(state, "Preceding condition causes termination.");
1194 : }
1195 : }
1196 :
1197 : // Set recirculation node number
1198 : // If exhaust node is specified, then recirculation is exhaust node, otherwise use zone return node
1199 : // this check has to be done here because of SimPurchasedAir passing in ControlledZoneNum
1200 13 : bool UseReturnNode = false;
1201 13 : if (PurchAir.ZoneExhaustAirNodeNum > 0) {
1202 8 : int ExhaustNodeNum = PurchAir.ZoneExhaustAirNodeNum;
1203 8 : int NodeIndex = FindNumberInList(ExhaustNodeNum,
1204 8 : state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ExhaustNode,
1205 8 : state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumExhaustNodes);
1206 8 : if (NodeIndex == 0) {
1207 0 : ShowSevereError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
1208 0 : ShowContinueError(state,
1209 0 : format("Zone Exhaust Air Node Name={} is not a zone exhaust node.", state.dataLoopNodes->NodeID(ExhaustNodeNum)));
1210 0 : ShowContinueError(
1211 : state,
1212 0 : format("Check ZoneHVAC:EquipmentConnections for zone={}", state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneName));
1213 0 : ShowContinueError(state, "Zone return air node will be used for ideal loads recirculation air.");
1214 0 : UseReturnNode = true;
1215 : } else {
1216 8 : PurchAir.ZoneRecircAirNodeNum = PurchAir.ZoneExhaustAirNodeNum;
1217 : }
1218 : } else {
1219 5 : UseReturnNode = true;
1220 : }
1221 13 : if (UseReturnNode) {
1222 5 : if (state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumReturnNodes == 1) {
1223 5 : PurchAir.ZoneRecircAirNodeNum = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ReturnNode(1);
1224 0 : } else if (state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumReturnNodes > 1) {
1225 0 : ShowWarningError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
1226 0 : ShowContinueError(state,
1227 : "No Zone Exhaust Air Node Name has been specified for this system and the zone has more than one Return Air Node.");
1228 0 : ShowContinueError(state,
1229 0 : format("Using the first return air node ={}",
1230 0 : state.dataLoopNodes->NodeID(state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ReturnNode(1))));
1231 : } else {
1232 0 : ShowFatalError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
1233 0 : ShowContinueError(
1234 : state,
1235 : " Invalid recirculation node. No exhaust or return node has been specified for this zone in ZoneHVAC:EquipmentConnections.");
1236 0 : ShowFatalError(state, "Preceding condition causes termination.");
1237 : }
1238 : }
1239 : // If there is OA and economizer is active, then there must be a limit on cooling flow rate
1240 13 : if (PurchAir.OutdoorAir && (PurchAir.EconomizerType != Econ::NoEconomizer)) {
1241 0 : if ((PurchAir.CoolingLimit == LimitType::None) || (PurchAir.CoolingLimit == LimitType::Capacity)) {
1242 0 : ShowSevereError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
1243 0 : ShowContinueError(state, "There is outdoor air with economizer active but there is no limit on cooling air flow rate.");
1244 0 : ShowContinueError(state,
1245 : "Cooling Limit must be set to LimitFlowRate or LimitFlowRateAndCapacity, and Maximum Cooling Air Flow Rate "
1246 : "must be set to a value or autosize.");
1247 0 : ShowContinueError(state, "Simulation will proceed with no limit on outdoor air flow rate.");
1248 : }
1249 : }
1250 : }
1251 :
1252 766 : if (!state.dataGlobal->SysSizingCalc && state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag(PurchAirNum)) {
1253 :
1254 13 : SizePurchasedAir(state, PurchAirNum);
1255 :
1256 13 : state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag(PurchAirNum) = false;
1257 : }
1258 :
1259 : // Do the Begin Environment initializations
1260 766 : if (state.dataGlobal->BeginEnvrnFlag && state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag(PurchAirNum)) {
1261 :
1262 12 : if ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) {
1263 0 : PurchAir.MaxHeatMassFlowRate = state.dataEnvrn->StdRhoAir * PurchAir.MaxHeatVolFlowRate;
1264 : } else {
1265 12 : PurchAir.MaxHeatMassFlowRate = 0.0;
1266 : }
1267 12 : if ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
1268 0 : PurchAir.MaxCoolMassFlowRate = state.dataEnvrn->StdRhoAir * PurchAir.MaxCoolVolFlowRate;
1269 : } else {
1270 12 : PurchAir.MaxCoolMassFlowRate = 0.0;
1271 : }
1272 12 : state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag(PurchAirNum) = false;
1273 : }
1274 :
1275 766 : if (!state.dataGlobal->BeginEnvrnFlag) {
1276 730 : state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag(PurchAirNum) = true;
1277 : }
1278 :
1279 766 : auto const &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(ControlledZoneNum);
1280 :
1281 : // These initializations are done every iteration
1282 : // check that supply air temps can meet the zone thermostat setpoints
1283 766 : if (PurchAir.MinCoolSuppAirTemp > zoneTstatSetpt.setptHi && zoneTstatSetpt.setptHi != 0 && PurchAir.CoolingLimit == LimitType::None) {
1284 : // Check if the unit is scheduled off
1285 0 : UnitOn = true;
1286 : // IF (PurchAir(PurchAirNum)%AvailSchedPtr > 0) THEN
1287 0 : if (PurchAir.availSched->getCurrentVal() <= 0) {
1288 0 : UnitOn = false;
1289 : }
1290 : // END IF
1291 : // Check if cooling available
1292 0 : bool CoolOn = true;
1293 : // IF (PurchAir(PurchAirNum)%CoolSchedPtr > 0) THEN
1294 0 : if (PurchAir.coolAvailSched->getCurrentVal() <= 0) {
1295 0 : CoolOn = false;
1296 : }
1297 : // END IF
1298 0 : if (UnitOn && CoolOn) {
1299 0 : if (PurchAir.CoolErrIndex == 0) {
1300 0 : ShowSevereError(state,
1301 0 : format("InitPurchasedAir: For {} = {} serving Zone {}",
1302 0 : PurchAir.cObjectName,
1303 0 : PurchAir.Name,
1304 0 : state.dataHeatBal->Zone(ControlledZoneNum).Name));
1305 0 : ShowContinueError(state,
1306 0 : format("..the minimum supply air temperature for cooling [{:.2R}] is greater than the zone cooling mean air "
1307 : "temperature (MAT) setpoint [{:.2R}].",
1308 0 : PurchAir.MinCoolSuppAirTemp,
1309 0 : zoneTstatSetpt.setptHi));
1310 0 : ShowContinueError(state, "..For operative and comfort thermostat controls, the MAT setpoint is computed.");
1311 0 : ShowContinueError(state, "..This error may indicate that the mean radiant temperature or another comfort factor is too warm.");
1312 0 : ShowContinueError(state, "Unit availability is nominally ON and Cooling availability is nominally ON.");
1313 0 : ShowContinueError(state, format("Limit Cooling Capacity Type={}", limitTypeNames[(int)PurchAir.CoolingLimit]));
1314 : // could check for optemp control or comfort control here
1315 0 : ShowContinueErrorTimeStamp(state, "");
1316 : }
1317 0 : ShowRecurringSevereErrorAtEnd(state,
1318 0 : "InitPurchasedAir: For " + PurchAir.cObjectName + " = " + PurchAir.Name + " serving Zone " +
1319 0 : state.dataHeatBal->Zone(ControlledZoneNum).Name +
1320 : ", the minimum supply air temperature for cooling error continues",
1321 0 : PurchAir.CoolErrIndex,
1322 0 : PurchAir.MinCoolSuppAirTemp,
1323 0 : PurchAir.MinCoolSuppAirTemp,
1324 : _,
1325 : "C",
1326 : "C");
1327 : }
1328 : }
1329 :
1330 766 : if (PurchAir.MaxHeatSuppAirTemp < zoneTstatSetpt.setptLo && zoneTstatSetpt.setptLo != 0 && PurchAir.HeatingLimit == LimitType::None) {
1331 : // Check if the unit is scheduled off
1332 0 : UnitOn = true;
1333 : // IF (PurchAir(PurchAirNum)%AvailSchedPtr > 0) THEN
1334 0 : if (PurchAir.availSched->getCurrentVal() <= 0) {
1335 0 : UnitOn = false;
1336 : }
1337 : // END IF
1338 : // Check if heating and cooling available
1339 :
1340 0 : bool HeatOn = true;
1341 : // IF (PurchAir(PurchAirNum)%HeatSchedPtr > 0) THEN
1342 0 : if (PurchAir.heatAvailSched->getCurrentVal() <= 0) {
1343 0 : HeatOn = false;
1344 : }
1345 : // END IF
1346 0 : if (UnitOn && HeatOn) {
1347 0 : if (PurchAir.HeatErrIndex == 0) {
1348 0 : ShowSevereMessage(state,
1349 0 : format("InitPurchasedAir: For {} = {} serving Zone {}",
1350 0 : PurchAir.cObjectName,
1351 0 : PurchAir.Name,
1352 0 : state.dataHeatBal->Zone(ControlledZoneNum).Name));
1353 0 : ShowContinueError(state,
1354 0 : format("..the maximum supply air temperature for heating [{:.2R}] is less than the zone mean air temperature "
1355 : "heating setpoint [{:.2R}].",
1356 0 : PurchAir.MaxHeatSuppAirTemp,
1357 0 : zoneTstatSetpt.setptLo));
1358 0 : ShowContinueError(state, "..For operative and comfort thermostat controls, the MAT setpoint is computed.");
1359 0 : ShowContinueError(state, "..This error may indicate that the mean radiant temperature or another comfort factor is too cold.");
1360 0 : ShowContinueError(state, "Unit availability is nominally ON and Heating availability is nominally ON.");
1361 0 : ShowContinueError(state, format("Limit Heating Capacity Type={}", limitTypeNames[(int)PurchAir.HeatingLimit]));
1362 : // could check for optemp control or comfort control here
1363 0 : ShowContinueErrorTimeStamp(state, "");
1364 : }
1365 0 : ShowRecurringSevereErrorAtEnd(state,
1366 0 : "InitPurchasedAir: For " + PurchAir.cObjectName + " = " + PurchAir.Name + " serving Zone " +
1367 0 : state.dataHeatBal->Zone(ControlledZoneNum).Name +
1368 : ", maximum supply air temperature for heating error continues",
1369 0 : PurchAir.HeatErrIndex,
1370 0 : PurchAir.MaxHeatSuppAirTemp,
1371 0 : PurchAir.MaxHeatSuppAirTemp,
1372 : _,
1373 : "C",
1374 : "C");
1375 : }
1376 : }
1377 : // IF (ErrorsFound .and. .not. WarmupFlag) THEN
1378 : // CALL ShowFatalError(state, 'Preceding conditions cause termination.')
1379 : // ENDIF
1380 766 : }
1381 :
1382 15 : void SizePurchasedAir(EnergyPlusData &state, int const PurchAirNum)
1383 : {
1384 :
1385 : // SUBROUTINE INFORMATION:
1386 : // AUTHOR Fred Buhl
1387 : // DATE WRITTEN April 2003
1388 : // MODIFIED M. Witte, June 2011, add sizing for new capacity fields
1389 : // August 2013 Daeho Kang, add component sizing table entries
1390 : // RE-ENGINEERED na
1391 :
1392 : // PURPOSE OF THIS SUBROUTINE:
1393 : // This subroutine is for sizing Purchased Air Components for which flow rates have not been
1394 : // specified in the input.
1395 :
1396 : // METHODOLOGY EMPLOYED:
1397 : // Obtains flow rates from the zone sizing arrays.
1398 :
1399 : // Using/Aliasing
1400 : using namespace DataSizing;
1401 : using HVAC::CoolingCapacitySizing;
1402 : using HVAC::HeatingAirflowSizing;
1403 : using HVAC::HeatingCapacitySizing;
1404 : using Psychrometrics::CPCW;
1405 : using Psychrometrics::CPHW;
1406 : using Psychrometrics::PsyCpAirFnW;
1407 : using Psychrometrics::PsyHFnTdbW;
1408 : using Psychrometrics::RhoH2O;
1409 :
1410 : // SUBROUTINE PARAMETER DEFINITIONS:
1411 : static constexpr std::string_view RoutineName("SizePurchasedAir: "); // include trailing blank space
1412 :
1413 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1414 : Real64 MaxHeatVolFlowRateDes; // Autosized maximum heating air flow for reporting
1415 : Real64 MaxHeatVolFlowRateUser; // Hardsized maximum heating air flow for reporting
1416 : Real64 MaxCoolVolFlowRateDes; // Autosized maximum cooling air flow for reporting
1417 : Real64 MaxCoolVolFlowRateUser; // Hardsized maximum cooling air flow for reporting
1418 : Real64 MaxHeatSensCapDes; // Autosized maximum sensible heating capacity for reporting
1419 : Real64 MaxHeatSensCapUser; // Hardsized maximum sensible heating capacity for reporting
1420 : Real64 MaxCoolTotCapDes; // Autosized maximum sensible cooling capacity for reporting
1421 : Real64 MaxCoolTotCapUser; // Hardsized maximum sensible cooling capacity for reporting
1422 15 : std::string CompName; // component name
1423 15 : std::string CompType; // component type
1424 : Real64 TempSize; // autosized value of coil input field
1425 : // FractionOfAutosizedHeatingCapacity )
1426 15 : Real64 CoolingAirVolFlowDes(0.0); // cooling supply air flow rate
1427 15 : Real64 HeatingAirVolFlowDes(0.0); // heating supply air flow rate
1428 :
1429 15 : auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
1430 :
1431 15 : MaxHeatVolFlowRateDes = 0.0;
1432 15 : MaxHeatVolFlowRateUser = 0.0;
1433 15 : MaxCoolVolFlowRateDes = 0.0;
1434 15 : MaxCoolVolFlowRateUser = 0.0;
1435 15 : MaxHeatSensCapDes = 0.0;
1436 15 : MaxHeatSensCapUser = 0.0;
1437 15 : MaxCoolTotCapDes = 0.0;
1438 15 : MaxCoolTotCapUser = 0.0;
1439 :
1440 15 : state.dataSize->ZoneHeatingOnlyFan = false;
1441 15 : state.dataSize->ZoneCoolingOnlyFan = false;
1442 15 : CompType = PurchAir.cObjectName;
1443 15 : CompName = PurchAir.Name;
1444 :
1445 15 : if (state.dataSize->CurZoneEqNum > 0) {
1446 15 : auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(state.dataSize->CurZoneEqNum);
1447 15 : std::string SizingString; // input field sizing description (e.g., Nominal Capacity)
1448 : int FieldNum; // IDD numeric field number where input field description is found
1449 : bool PrintFlag; // TRUE when sizing information is reported in the eio file
1450 15 : bool ErrorsFound = false;
1451 15 : if (PurchAir.HVACSizingIndex > 0) {
1452 0 : state.dataSize->DataZoneNumber = PurchAir.ZonePtr;
1453 0 : int zoneHVACIndex = PurchAir.HVACSizingIndex;
1454 : int SAFMethod; // supply air flow rate sizing method (SupplyAirFlowRate, FlowPerFloorArea, FractionOfAutosizedCoolingAirflow,
1455 : // FractionOfAutosizedHeatingAirflow, HeatingCapacitySizing, etc.)
1456 : int CapSizingMethod; // capacity sizing methods (HeatingDesignCapacity, CapacityPerFloorArea, FractionOfAutosizedCoolingCapacity, and
1457 : int SizingMethod; // Integer representation of sizing method name (e.g., CoolingAirflowSizing, HeatingAirflowSizing,
1458 : // CoolingCapacitySizing)
1459 :
1460 0 : FieldNum = 5; // N5 , \field Maximum Heating Air Flow Rate
1461 0 : PrintFlag = true;
1462 0 : SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
1463 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod > 0) {
1464 0 : SizingMethod = HeatingAirflowSizing;
1465 0 : state.dataSize->ZoneHeatingOnlyFan = true;
1466 0 : SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod;
1467 0 : ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1468 0 : if (SAFMethod == SupplyAirFlowRate || SAFMethod == FlowPerFloorArea || SAFMethod == FractionOfAutosizedHeatingAirflow) {
1469 0 : if (SAFMethod == SupplyAirFlowRate) {
1470 0 : if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow == AutoSize) &&
1471 0 : ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
1472 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1473 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1474 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1475 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1476 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1477 0 : HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
1478 0 : } else {
1479 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow > 0.0) {
1480 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1481 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1482 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1483 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1484 : HeatingAirVolFlowDes =
1485 0 : sizingHeatingAirFlow.size(state, state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow, ErrorsFound);
1486 0 : }
1487 : }
1488 0 : } else if (SAFMethod == FlowPerFloorArea) {
1489 0 : ZoneEqSizing.SystemAirFlow = true;
1490 0 : ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow *
1491 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1492 0 : TempSize = ZoneEqSizing.AirVolFlow;
1493 0 : state.dataSize->DataScalableSizingON = true;
1494 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1495 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1496 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1497 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1498 0 : HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
1499 0 : } else if (SAFMethod == FractionOfAutosizedHeatingAirflow) {
1500 0 : state.dataSize->DataFracOfAutosizedHeatingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1501 0 : if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow == AutoSize) &&
1502 0 : ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
1503 0 : TempSize = AutoSize;
1504 0 : state.dataSize->DataScalableSizingON = true;
1505 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1506 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1507 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1508 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1509 0 : HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
1510 0 : }
1511 :
1512 : } else {
1513 : // Invalid sizing method
1514 : }
1515 0 : } else if (SAFMethod == FlowPerHeatingCapacity) {
1516 0 : SizingMethod = HeatingCapacitySizing;
1517 0 : TempSize = AutoSize;
1518 0 : PrintFlag = false;
1519 0 : if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow == AutoSize) &&
1520 0 : ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
1521 0 : TempSize = AutoSize;
1522 0 : state.dataSize->DataScalableSizingON = true;
1523 0 : HeatingCapacitySizer sizerHeatingCapacity;
1524 0 : sizerHeatingCapacity.overrideSizingString(SizingString);
1525 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1526 0 : state.dataSize->DataAutosizedHeatingCapacity = sizerHeatingCapacity.size(state, TempSize, ErrorsFound);
1527 0 : state.dataSize->DataFlowPerHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1528 0 : SizingMethod = HeatingAirflowSizing;
1529 0 : PrintFlag = true;
1530 0 : TempSize = AutoSize;
1531 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1532 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1533 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1534 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1535 0 : HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
1536 0 : }
1537 : }
1538 0 : MaxHeatVolFlowRateDes = max(0.0, HeatingAirVolFlowDes);
1539 0 : PurchAir.MaxHeatVolFlowRate = MaxHeatVolFlowRateDes;
1540 0 : state.dataSize->ZoneHeatingOnlyFan = false;
1541 :
1542 0 : CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
1543 0 : ZoneEqSizing.CapSizingMethod = CapSizingMethod;
1544 0 : if (CapSizingMethod == HeatingDesignCapacity || CapSizingMethod == CapacityPerFloorArea ||
1545 0 : CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
1546 0 : if (CapSizingMethod == HeatingDesignCapacity) {
1547 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity > 0.0) {
1548 0 : ZoneEqSizing.HeatingCapacity = true;
1549 0 : ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1550 : }
1551 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1552 0 : } else if (CapSizingMethod == CapacityPerFloorArea) {
1553 0 : ZoneEqSizing.HeatingCapacity = true;
1554 0 : ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
1555 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1556 0 : state.dataSize->DataScalableSizingON = true;
1557 0 : } else if (CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
1558 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1559 0 : TempSize = AutoSize;
1560 : }
1561 : }
1562 0 : SizingMethod = HeatingCapacitySizing;
1563 0 : SizingString = "";
1564 0 : state.dataSize->ZoneHeatingOnlyFan = true;
1565 0 : PrintFlag = false;
1566 0 : HeatingCapacitySizer sizerHeatingCapacity;
1567 0 : sizerHeatingCapacity.overrideSizingString(SizingString);
1568 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1569 0 : MaxHeatSensCapDes = sizerHeatingCapacity.size(state, TempSize, ErrorsFound);
1570 0 : state.dataSize->ZoneHeatingOnlyFan = false;
1571 0 : if (MaxHeatSensCapDes < HVAC::SmallLoad) {
1572 0 : MaxHeatSensCapDes = 0.0;
1573 : }
1574 0 : if (PurchAir.MaxHeatSensCap > 0.0 && MaxHeatSensCapDes > 0.0) {
1575 0 : MaxHeatSensCapUser = PurchAir.MaxHeatSensCap;
1576 0 : BaseSizer::reportSizerOutput(state,
1577 : PurchAir.cObjectName,
1578 : PurchAir.Name,
1579 : "Design Size Maximum Sensible Heating Capacity [W]",
1580 : MaxHeatSensCapDes,
1581 : "User-Specified Maximum Sensible Heating Capacity [W]",
1582 : MaxHeatSensCapUser);
1583 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1584 0 : if ((std::abs(MaxHeatSensCapDes - MaxHeatSensCapUser) / MaxHeatSensCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
1585 0 : ShowMessage(
1586 : state,
1587 0 : format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
1588 0 : ShowContinueError(state, format("...User-Specified Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapUser));
1589 0 : ShowContinueError(
1590 0 : state, format("...differs from Design Size Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapDes));
1591 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1592 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1593 : }
1594 : }
1595 : }
1596 0 : }
1597 :
1598 0 : PrintFlag = true;
1599 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingSAFMethod > 0) {
1600 0 : state.dataSize->ZoneCoolingOnlyFan = true;
1601 0 : SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingSAFMethod;
1602 0 : ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1603 0 : if (SAFMethod == SupplyAirFlowRate || SAFMethod == FlowPerFloorArea || SAFMethod == FractionOfAutosizedCoolingAirflow) {
1604 0 : if (SAFMethod == SupplyAirFlowRate) {
1605 0 : if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow == AutoSize) &&
1606 0 : ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
1607 0 : (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
1608 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1609 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1610 0 : sizingCoolingAirFlow.overrideSizingString(SizingString);
1611 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1612 0 : CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
1613 0 : } else {
1614 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow > 0.0) {
1615 0 : CoolingAirVolFlowDes = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1616 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1617 0 : sizingCoolingAirFlow.overrideSizingString(SizingString);
1618 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1619 0 : CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, CoolingAirVolFlowDes, ErrorsFound);
1620 0 : }
1621 : }
1622 0 : } else if (SAFMethod == FlowPerFloorArea) {
1623 0 : ZoneEqSizing.SystemAirFlow = true;
1624 0 : ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow *
1625 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1626 0 : TempSize = ZoneEqSizing.AirVolFlow;
1627 0 : state.dataSize->DataScalableSizingON = true;
1628 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1629 0 : std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
1630 0 : if (state.dataGlobal->isEpJSON) {
1631 0 : stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
1632 : }
1633 0 : sizingCoolingAirFlow.overrideSizingString(stringOverride);
1634 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1635 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1636 0 : CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
1637 0 : } else if (SAFMethod == FractionOfAutosizedCoolingAirflow) {
1638 0 : if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow == AutoSize) &&
1639 0 : ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
1640 0 : (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
1641 0 : state.dataSize->DataFracOfAutosizedCoolingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1642 0 : TempSize = AutoSize;
1643 0 : state.dataSize->DataScalableSizingON = true;
1644 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1645 0 : std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
1646 0 : if (state.dataGlobal->isEpJSON) {
1647 0 : stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
1648 : }
1649 0 : sizingCoolingAirFlow.overrideSizingString(stringOverride);
1650 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1651 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1652 0 : CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
1653 0 : }
1654 : } else {
1655 : // Invalid scalable sizing method
1656 : }
1657 0 : } else if (SAFMethod == FlowPerCoolingCapacity) {
1658 0 : if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow == AutoSize) &&
1659 0 : ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
1660 0 : (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
1661 0 : SizingMethod = CoolingCapacitySizing;
1662 0 : TempSize = AutoSize;
1663 0 : PrintFlag = false;
1664 0 : CoolingCapacitySizer sizerCoolingCapacity;
1665 0 : sizerCoolingCapacity.overrideSizingString(SizingString);
1666 0 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1667 0 : state.dataSize->DataAutosizedCoolingCapacity = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1668 0 : state.dataSize->DataFlowPerCoolingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1669 0 : PrintFlag = true;
1670 0 : TempSize = AutoSize;
1671 0 : state.dataSize->DataScalableSizingON = true;
1672 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1673 0 : std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
1674 0 : if (state.dataGlobal->isEpJSON) {
1675 0 : stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
1676 : }
1677 0 : sizingCoolingAirFlow.overrideSizingString(stringOverride);
1678 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1679 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1680 0 : CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
1681 0 : }
1682 : }
1683 0 : MaxCoolVolFlowRateDes = max(0.0, CoolingAirVolFlowDes);
1684 0 : PurchAir.MaxCoolVolFlowRate = MaxCoolVolFlowRateDes;
1685 0 : state.dataSize->ZoneCoolingOnlyFan = false;
1686 0 : state.dataSize->DataScalableSizingON = false;
1687 :
1688 0 : CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingCapMethod;
1689 0 : ZoneEqSizing.CapSizingMethod = CapSizingMethod;
1690 0 : if (CapSizingMethod == CoolingDesignCapacity || CapSizingMethod == CapacityPerFloorArea ||
1691 0 : CapSizingMethod == FractionOfAutosizedCoolingCapacity) {
1692 0 : if (CapSizingMethod == CoolingDesignCapacity) {
1693 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity > 0.0) {
1694 0 : ZoneEqSizing.CoolingCapacity = true;
1695 0 : ZoneEqSizing.DesCoolingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
1696 : } else {
1697 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolMassFlow;
1698 : }
1699 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
1700 0 : } else if (CapSizingMethod == CapacityPerFloorArea) {
1701 0 : ZoneEqSizing.CoolingCapacity = true;
1702 0 : ZoneEqSizing.DesCoolingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity *
1703 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1704 0 : state.dataSize->DataScalableSizingON = true;
1705 0 : } else if (CapSizingMethod == FractionOfAutosizedCoolingCapacity) {
1706 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
1707 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolMassFlow;
1708 0 : TempSize = AutoSize;
1709 : }
1710 : }
1711 : // SizingMethod = CoolingCapacitySizing;
1712 0 : SizingString = "";
1713 0 : state.dataSize->ZoneCoolingOnlyFan = true;
1714 0 : PrintFlag = false;
1715 0 : TempSize = PurchAir.MaxCoolTotCap;
1716 0 : CoolingCapacitySizer sizerCoolingCapacity;
1717 0 : sizerCoolingCapacity.overrideSizingString(SizingString);
1718 0 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1719 0 : MaxCoolTotCapDes = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1720 0 : state.dataSize->ZoneCoolingOnlyFan = false;
1721 0 : if (MaxCoolTotCapDes < HVAC::SmallLoad) {
1722 0 : MaxCoolTotCapDes = 0.0;
1723 : }
1724 0 : if (PurchAir.MaxCoolTotCap > 0.0 && MaxCoolTotCapDes > 0.0) {
1725 0 : MaxCoolTotCapUser = PurchAir.MaxCoolTotCap;
1726 0 : BaseSizer::reportSizerOutput(state,
1727 : PurchAir.cObjectName,
1728 : PurchAir.Name,
1729 : "Design Size Maximum Total Cooling Capacity [W]",
1730 : MaxCoolTotCapDes,
1731 : "User-Specified Maximum Total Cooling Capacity [W]",
1732 : MaxCoolTotCapUser);
1733 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1734 0 : if ((std::abs(MaxCoolTotCapDes - MaxCoolTotCapUser) / MaxCoolTotCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
1735 0 : ShowMessage(
1736 : state,
1737 0 : format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
1738 0 : ShowContinueError(state, format("User-Specified Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapUser));
1739 0 : ShowContinueError(state,
1740 0 : format("differs from Design Size Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapDes));
1741 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1742 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1743 : }
1744 : }
1745 : }
1746 0 : }
1747 :
1748 : } else {
1749 : // SizingString = "Maximum Heating Air Flow Rate [m3/s]";
1750 : // SizingMethod = HeatingAirflowSizing;
1751 15 : FieldNum = 5;
1752 15 : SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
1753 15 : bool IsAutoSize = false;
1754 15 : PrintFlag = true;
1755 15 : if ((PurchAir.MaxHeatVolFlowRate == AutoSize) &&
1756 14 : ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
1757 2 : IsAutoSize = true;
1758 : }
1759 15 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1760 0 : if (PurchAir.MaxHeatVolFlowRate > 0.0) {
1761 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1762 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1763 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1764 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1765 0 : PurchAir.MaxHeatVolFlowRate = sizingHeatingAirFlow.size(state, PurchAir.MaxHeatVolFlowRate, ErrorsFound);
1766 0 : }
1767 0 : MaxHeatVolFlowRateDes = 0.0;
1768 : } else {
1769 15 : state.dataSize->ZoneHeatingOnlyFan = true;
1770 15 : TempSize = PurchAir.MaxHeatVolFlowRate;
1771 15 : HeatingAirFlowSizer sizingHeatingAirFlow;
1772 15 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1773 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1774 15 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1775 15 : MaxHeatVolFlowRateDes = sizingHeatingAirFlow.size(state, PurchAir.MaxHeatVolFlowRate, ErrorsFound);
1776 15 : PurchAir.MaxHeatVolFlowRate = MaxHeatVolFlowRateDes;
1777 15 : state.dataSize->ZoneHeatingOnlyFan = false;
1778 15 : }
1779 :
1780 15 : IsAutoSize = false;
1781 : // SizingMethod = HeatingCapacitySizing;
1782 15 : FieldNum = 6; // N6, \field Maximum Sensible Heating Capacity
1783 15 : SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
1784 15 : if ((PurchAir.MaxHeatSensCap == AutoSize) &&
1785 2 : ((PurchAir.HeatingLimit == LimitType::Capacity) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
1786 2 : IsAutoSize = true;
1787 : }
1788 15 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1789 0 : if (PurchAir.MaxHeatSensCap > 0.0) {
1790 0 : HeatingCapacitySizer sizerHeatingCapacity;
1791 0 : sizerHeatingCapacity.overrideSizingString(SizingString);
1792 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1793 0 : MaxHeatSensCapDes = sizerHeatingCapacity.size(state, PurchAir.MaxHeatSensCap, ErrorsFound);
1794 0 : }
1795 : } else {
1796 15 : TempSize = PurchAir.MaxHeatSensCap;
1797 15 : ZoneEqSizing.OAVolFlow = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
1798 15 : state.dataSize->ZoneHeatingOnlyFan = true;
1799 15 : PrintFlag = false;
1800 15 : HeatingCapacitySizer sizerHeatingCapacity;
1801 15 : sizerHeatingCapacity.overrideSizingString(SizingString);
1802 15 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1803 15 : MaxHeatSensCapDes = sizerHeatingCapacity.size(state, TempSize, ErrorsFound);
1804 15 : state.dataSize->ZoneHeatingOnlyFan = false;
1805 15 : }
1806 15 : if (MaxHeatSensCapDes < HVAC::SmallLoad) {
1807 13 : MaxHeatSensCapDes = 0.0;
1808 : }
1809 15 : if (IsAutoSize) {
1810 2 : PurchAir.MaxHeatSensCap = MaxHeatSensCapDes;
1811 2 : BaseSizer::reportSizerOutput(
1812 : state, PurchAir.cObjectName, PurchAir.Name, "Design Size Maximum Sensible Heating Capacity [W]", MaxHeatSensCapDes);
1813 : // If there is OA, check if sizing calcs have OA>0, throw warning if not
1814 2 : if ((PurchAir.OutdoorAir) && (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA == 0.0)) {
1815 0 : ShowWarningError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
1816 0 : ShowContinueError(state, "There is outdoor air specified in this object, but the design outdoor air flow rate for this ");
1817 0 : ShowContinueError(state, "zone is zero. The Maximum Sensible Heating Capacity will be autosized for zero outdoor air flow. ");
1818 0 : ShowContinueError(state,
1819 0 : format("Check the outdoor air specifications in the Sizing:Zone object for zone {}.",
1820 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).ZoneName));
1821 : }
1822 : } else {
1823 13 : if (PurchAir.MaxHeatSensCap > 0.0 && MaxHeatSensCapDes > 0.0) {
1824 0 : MaxHeatSensCapUser = PurchAir.MaxHeatSensCap;
1825 0 : BaseSizer::reportSizerOutput(state,
1826 : PurchAir.cObjectName,
1827 : PurchAir.Name,
1828 : "Design Size Maximum Sensible Heating Capacity [W]",
1829 : MaxHeatSensCapDes,
1830 : "User-Specified Maximum Sensible Heating Capacity [W]",
1831 : MaxHeatSensCapUser);
1832 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1833 0 : if ((std::abs(MaxHeatSensCapDes - MaxHeatSensCapUser) / MaxHeatSensCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
1834 0 : ShowMessage(
1835 : state,
1836 0 : format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
1837 0 : ShowContinueError(state, format("...User-Specified Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapUser));
1838 0 : ShowContinueError(
1839 0 : state, format("...differs from Design Size Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapDes));
1840 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1841 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1842 : }
1843 : }
1844 : }
1845 : }
1846 :
1847 15 : PrintFlag = true;
1848 15 : IsAutoSize = false;
1849 15 : if ((PurchAir.MaxCoolVolFlowRate == AutoSize) &&
1850 14 : ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
1851 12 : (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
1852 2 : IsAutoSize = true;
1853 : }
1854 15 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1855 0 : if (PurchAir.MaxCoolVolFlowRate > 0.0) {
1856 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1857 0 : std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
1858 0 : if (state.dataGlobal->isEpJSON) {
1859 0 : stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
1860 : }
1861 0 : sizingCoolingAirFlow.overrideSizingString(stringOverride);
1862 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1863 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1864 0 : PurchAir.MaxCoolVolFlowRate = sizingCoolingAirFlow.size(state, PurchAir.MaxCoolVolFlowRate, ErrorsFound);
1865 0 : }
1866 : } else {
1867 15 : state.dataSize->ZoneCoolingOnlyFan = true;
1868 15 : TempSize = PurchAir.MaxCoolVolFlowRate;
1869 15 : CoolingAirFlowSizer sizingCoolingAirFlow;
1870 15 : std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
1871 15 : if (state.dataGlobal->isEpJSON) {
1872 0 : stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
1873 : }
1874 15 : sizingCoolingAirFlow.overrideSizingString(stringOverride);
1875 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1876 15 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1877 15 : MaxCoolVolFlowRateDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
1878 15 : PurchAir.MaxCoolVolFlowRate = MaxCoolVolFlowRateDes;
1879 15 : state.dataSize->ZoneCoolingOnlyFan = false;
1880 15 : }
1881 :
1882 15 : IsAutoSize = false;
1883 : // SizingMethod = CoolingCapacitySizing;
1884 15 : FieldNum = 8; // N8, \field Maximum Total Cooling Capacity
1885 15 : SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
1886 15 : if ((PurchAir.MaxCoolTotCap == AutoSize) &&
1887 2 : ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity))) {
1888 2 : IsAutoSize = true;
1889 : }
1890 15 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1891 0 : if (PurchAir.MaxCoolTotCap > 0.0) {
1892 0 : CoolingCapacitySizer sizerCoolingCapacity;
1893 0 : sizerCoolingCapacity.overrideSizingString(SizingString);
1894 0 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1895 0 : PurchAir.MaxCoolTotCap = sizerCoolingCapacity.size(state, PurchAir.MaxCoolTotCap, ErrorsFound);
1896 0 : }
1897 : } else {
1898 15 : state.dataSize->ZoneCoolingOnlyFan = true;
1899 15 : ZoneEqSizing.OAVolFlow = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
1900 15 : PrintFlag = false;
1901 15 : TempSize = PurchAir.MaxCoolTotCap;
1902 15 : CoolingCapacitySizer sizerCoolingCapacity;
1903 15 : sizerCoolingCapacity.overrideSizingString(SizingString);
1904 15 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1905 15 : MaxCoolTotCapDes = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1906 15 : state.dataSize->ZoneCoolingOnlyFan = false;
1907 15 : }
1908 15 : if (MaxCoolTotCapDes < HVAC::SmallLoad) {
1909 13 : MaxCoolTotCapDes = 0.0;
1910 : }
1911 15 : if (IsAutoSize) {
1912 2 : PurchAir.MaxCoolTotCap = MaxCoolTotCapDes;
1913 2 : BaseSizer::reportSizerOutput(
1914 : state, PurchAir.cObjectName, PurchAir.Name, "Design Size Maximum Total Cooling Capacity [W]", MaxCoolTotCapDes);
1915 : // If there is OA, check if sizing calcs have OA>0, throw warning if not
1916 2 : if ((PurchAir.OutdoorAir) && (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA == 0.0)) {
1917 0 : ShowWarningError(state, format("SizePurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
1918 0 : ShowContinueError(state, "There is outdoor air specified in this object, but the design outdoor air flow rate for this ");
1919 0 : ShowContinueError(state, "zone is zero. The Maximum Total Cooling Capacity will be autosized for zero outdoor air flow. ");
1920 0 : ShowContinueError(state,
1921 0 : format("Check the outdoor air specifications in the Sizing:Zone object for zone {}.",
1922 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).ZoneName));
1923 : }
1924 : } else {
1925 13 : if (PurchAir.MaxCoolTotCap > 0.0 && MaxCoolTotCapDes > 0.0) {
1926 0 : MaxCoolTotCapUser = PurchAir.MaxCoolTotCap;
1927 0 : BaseSizer::reportSizerOutput(state,
1928 : PurchAir.cObjectName,
1929 : PurchAir.Name,
1930 : "Design Size Maximum Total Cooling Capacity [W]",
1931 : MaxCoolTotCapDes,
1932 : "User-Specified Maximum Total Cooling Capacity [W]",
1933 : MaxCoolTotCapUser);
1934 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1935 0 : if ((std::abs(MaxCoolTotCapDes - MaxCoolTotCapUser) / MaxCoolTotCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
1936 0 : ShowMessage(
1937 : state,
1938 0 : format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
1939 0 : ShowContinueError(state, format("User-Specified Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapUser));
1940 0 : ShowContinueError(state,
1941 0 : format("differs from Design Size Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapDes));
1942 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1943 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1944 : }
1945 : }
1946 : }
1947 : }
1948 : }
1949 15 : }
1950 15 : }
1951 :
1952 766 : void CalcPurchAirLoads(EnergyPlusData &state,
1953 : int const PurchAirNum,
1954 : Real64 &SysOutputProvided, // Sensible output provided [W] cooling = negative
1955 : Real64 &MoistOutputProvided, // Moisture output provided [kg/s] dehumidification = negative
1956 : int const ControlledZoneNum)
1957 : {
1958 :
1959 : // SUBROUTINE INFORMATION:
1960 : // AUTHOR Russ Taylor
1961 : // DATE WRITTEN Nov 1997
1962 : // MODIFIED Shirey, Aug 2009 (LatOutputProvided - now MoistOutputProvided)
1963 : // M. Witte June 2011, add new features including DCV, economizer, dehumidification
1964 : // and humidification,
1965 : // July 2012, Chandan Sharma - FSEC: Added hybrid ventilation manager
1966 : // RE-ENGINEERED na
1967 :
1968 : // SUBROUTINE PARAMETER DEFINITIONS:
1969 : static constexpr std::string_view RoutineName("CalcPurchAirLoads");
1970 :
1971 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1972 : int InNodeNum; // Ideal loads supply node to zone
1973 : // INTEGER :: ExhNodeNum ! Ideal loads exhaust node from zone
1974 : int ZoneNodeNum; // Zone air node
1975 : int OANodeNum; // Outdoor air inlet node
1976 : int RecircNodeNum; // Return air or zone exhaust node
1977 : OpMode OperatingMode; // current operating mode, Off, Heat, Cool, or DeadBand
1978 : Real64 SupplyMassFlowRate; // System supply air mass flow rate [kg/s]
1979 : Real64 SupplyMassFlowRateForHumid; // System supply air mass flow rate required to meet humidification load [kg/s]
1980 : Real64 SupplyMassFlowRateForDehum; // System supply air mass flow rate required to meet dehumidification load [kg/s]
1981 : Real64 SupplyMassFlowRateForCool; // System supply air mass flow rate required to meet sensible cooling load[kg/s]
1982 : Real64 SupplyMassFlowRateForHeat; // System supply air mass flow rate required to meet sensible heating load[kg/s]
1983 : Real64 SupplyHumRatForHumid; // Supply air humidity ratio require to meet the humidification load [kgWater/kgDryAir]
1984 : Real64 SupplyHumRatForDehum; // Supply air humidity ratio require to meet the dehumidification load [kgWater/kgDryAir]
1985 : Real64 OAMassFlowRate; // Outdoor air mass flow rate [kg/s]
1986 : Real64 OAVolFlowRate; // Outdoor air volume flow rate at standard density [m3/s]
1987 : Real64 MinOASensOutput; // Minimum Outdoor air sensible output [W], <0 means OA is cooler than zone air
1988 : Real64 MinOALatOutput; // Minimum Outdoor air moisture load [kg/s]
1989 : Real64 SensOutput; // Sensible output [W] (positive means heating, negative means cooling)
1990 : Real64 HeatSensOutput; // Heating sensible output [W]
1991 : Real64 CoolSensOutput; // Cooling sensible output [W] (positive value means cooling)
1992 : Real64 LatOutput; // Latent output [W] (positive value means humidification, negative means dehumidification)
1993 : Real64 CoolLatOutput; // Cooling latent output [W] (positive value means dehumidification)
1994 : Real64 CoolTotOutput; // Cooling total output [W] (positive value means cooling)
1995 : Real64 DeltaT; // Delta temperature - reused in multiple places
1996 : Real64 DeltaHumRat; // Delta humidity ratio - reused in multiple places
1997 : Real64 QZnHeatSP; // Load required to meet heating setpoint [W] (>0 is a heating load)
1998 : Real64 QZnCoolSP; // Load required to meet cooling setpoint [W] (<0 is a cooling load)
1999 : Real64 MdotZnHumidSP; // Load required to meet humidifying setpoint [kgWater/s] (>0 = a humidify load)
2000 : Real64 MdotZnDehumidSP; // Load required to meet dehumidifying setpoint [kgWater/s] (<0 = a dehumidify load)
2001 : bool UnitOn;
2002 : bool HeatOn; // Flag for heating and humidification availability schedule, true if heating is on
2003 : bool CoolOn; // Flag for cooling and dehumidification availability schedule, true if cooling is on
2004 : bool EconoOn; // Flag for economizer operation, true if economizer is on
2005 : Real64 SupplyHumRatOrig; // Supply inlet to zone humidity ratio before saturation check [kgWater/kgDryAir]
2006 : Real64 SupplyHumRatSat; // Supply inlet to zone humidity ratio saturation at SupplyTemp [kgWater/kgDryAir]
2007 : Real64 SupplyEnthalpy; // Supply inlet to zone enthalpy [J/kg]
2008 : Real64 MixedAirEnthalpy; // Mixed air enthalpy [J/kg]
2009 : Real64 CpAir; // Specific heat [J/kg-C] reused in multiple places
2010 : // REAL(r64) :: SpecHumOut ! Specific humidity ratio of outlet air (kg moisture / kg moist air)
2011 : // REAL(r64) :: SpecHumIn ! Specific humidity ratio of inlet [zone] air (kg moisture / kg moist air)
2012 :
2013 766 : auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
2014 :
2015 : // Sign convention: SysOutputProvided <0 Supply air is heated on entering zone (zone is cooled)
2016 : // SysOutputProvided >0 Supply air is cooled on entering zone (zone is heated)
2017 766 : InNodeNum = PurchAir.ZoneSupplyAirNodeNum;
2018 766 : ZoneNodeNum = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneNode;
2019 766 : OANodeNum = PurchAir.OutdoorAirNodeNum;
2020 766 : RecircNodeNum = PurchAir.ZoneRecircAirNodeNum;
2021 766 : SupplyMassFlowRate = 0.0;
2022 766 : OAMassFlowRate = 0.0;
2023 766 : PurchAir.MinOAMassFlowRate = 0.0;
2024 766 : PurchAir.TimeEconoActive = 0.0;
2025 766 : PurchAir.TimeHtRecActive = 0.0;
2026 766 : SysOutputProvided = 0.0;
2027 766 : MoistOutputProvided = 0.0;
2028 766 : CoolSensOutput = 0.0;
2029 766 : CoolLatOutput = 0.0;
2030 766 : CoolTotOutput = 0.0;
2031 766 : HeatSensOutput = 0.0;
2032 766 : LatOutput = 0.0;
2033 :
2034 : // default unit to ON
2035 766 : UnitOn = true;
2036 766 : EconoOn = false;
2037 : // get current zone requirements
2038 766 : QZnHeatSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP;
2039 766 : QZnCoolSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP;
2040 :
2041 766 : if (allocated(state.dataAvail->ZoneComp)) {
2042 752 : auto &availMgr = state.dataAvail->ZoneComp(DataZoneEquipment::ZoneEquipType::PurchasedAir).ZoneCompAvailMgrs(PurchAirNum);
2043 752 : availMgr.ZoneNum = ControlledZoneNum;
2044 752 : PurchAir.availStatus = availMgr.availStatus;
2045 : // Check if the hybrid ventilation availability manager is turning the unit off
2046 752 : if (PurchAir.availStatus == Avail::Status::ForceOff) {
2047 0 : UnitOn = false;
2048 : }
2049 : }
2050 :
2051 : // Check if the unit is scheduled off
2052 766 : if (PurchAir.availSched->getCurrentVal() <= 0) {
2053 0 : UnitOn = false;
2054 : }
2055 : // END IF
2056 : // Check if heating and cooling available
2057 766 : HeatOn = true;
2058 766 : if (PurchAir.heatAvailSched->getCurrentVal() <= 0) {
2059 0 : HeatOn = false;
2060 : }
2061 :
2062 766 : CoolOn = true;
2063 766 : if (PurchAir.coolAvailSched->getCurrentVal() <= 0) {
2064 0 : CoolOn = false;
2065 : }
2066 : // END IF
2067 :
2068 766 : if (UnitOn) {
2069 766 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ControlledZoneNum);
2070 : // Calculate current minimum outdoor air flow rate based on design OA specifications and DCV or CO2 control
2071 766 : CalcPurchAirMinOAMassFlow(state, PurchAirNum, ControlledZoneNum, OAMassFlowRate);
2072 :
2073 : // EMS override point Purch air outdoor air massflow rate.....
2074 766 : if (PurchAir.EMSOverrideOAMdotOn) {
2075 0 : OAMassFlowRate = PurchAir.EMSValueOAMassFlowRate;
2076 : }
2077 :
2078 : // Calculate minimum outdoor air sensible and latent load
2079 766 : if (PurchAir.OutdoorAir) {
2080 1 : CpAir = PsyCpAirFnW(state.dataLoopNodes->Node(OANodeNum).HumRat);
2081 1 : MinOASensOutput = OAMassFlowRate * CpAir * (state.dataLoopNodes->Node(OANodeNum).Temp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
2082 1 : MinOALatOutput = OAMassFlowRate * (state.dataLoopNodes->Node(OANodeNum).HumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
2083 : } else {
2084 765 : MinOASensOutput = 0.0;
2085 765 : MinOALatOutput = 0.0;
2086 : }
2087 : // SupplyMassFlowRate = OAMassFlowRate;
2088 :
2089 : // Check if cooling of the supply air stream is required
2090 :
2091 : // Cooling operation
2092 766 : if ((MinOASensOutput >= QZnCoolSP) && (state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleHeat)) {
2093 358 : OperatingMode = OpMode::Cool;
2094 : // Calculate supply mass flow, temp and humidity with the following constraints:
2095 : // Min cooling supply temp
2096 : // Max total cooling capacity
2097 : // Max cooling airflow
2098 : // Min cooling supply humrat (and Max heating supply humrat)
2099 : // Min OA mass flow rate
2100 :
2101 : // Check if OA flow rate greater than max cooling airflow limit
2102 358 : if (((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
2103 0 : (OAMassFlowRate > PurchAir.MaxCoolMassFlowRate)) {
2104 0 : OAVolFlowRate = OAMassFlowRate / state.dataEnvrn->StdRhoAir;
2105 0 : if (PurchAir.OAFlowMaxCoolOutputError < 1) {
2106 0 : ++PurchAir.OAFlowMaxCoolOutputError;
2107 0 : ShowWarningError(state,
2108 0 : format("{} \"{}\" Requested outdoor air flow rate = {:.5T} [m3/s] exceeds limit.",
2109 0 : PurchAir.cObjectName,
2110 0 : PurchAir.Name,
2111 : OAVolFlowRate));
2112 0 : ShowContinueError(state,
2113 0 : format(" Will be reduced to the Maximum Cooling Air Flow Rate = {:.5T} [m3/s]", PurchAir.MaxCoolVolFlowRate));
2114 0 : ShowContinueErrorTimeStamp(state, "");
2115 : } else {
2116 0 : ShowRecurringWarningErrorAtEnd(
2117 : state,
2118 0 : PurchAir.cObjectName + " \"" + PurchAir.Name +
2119 : "\" Requested outdoor air flow rate [m3/s] reduced to Maximum Cooling Air Flow Rate warning continues...",
2120 0 : PurchAir.OAFlowMaxCoolOutputIndex,
2121 : OAVolFlowRate);
2122 : }
2123 0 : OAMassFlowRate = PurchAir.MaxCoolMassFlowRate;
2124 :
2125 : } else {
2126 : // Model economizer
2127 358 : if (PurchAir.EconomizerType != Econ::NoEconomizer) {
2128 0 : if (((PurchAir.EconomizerType == Econ::DifferentialDryBulb) &&
2129 0 : (state.dataLoopNodes->Node(OANodeNum).Temp < state.dataLoopNodes->Node(PurchAir.ZoneRecircAirNodeNum).Temp)) ||
2130 0 : ((PurchAir.EconomizerType == Econ::DifferentialEnthalpy) &&
2131 0 : (state.dataLoopNodes->Node(OANodeNum).Enthalpy < state.dataLoopNodes->Node(PurchAir.ZoneRecircAirNodeNum).Enthalpy))) {
2132 :
2133 : // Calculate supply MassFlowRate based on sensible load but limit to Max Cooling Supply Air Flow Rate if specified
2134 0 : CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
2135 0 : DeltaT = (state.dataLoopNodes->Node(OANodeNum).Temp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
2136 0 : if (DeltaT < -HVAC::SmallTempDiff) {
2137 0 : SupplyMassFlowRate = QZnCoolSP / CpAir / DeltaT;
2138 0 : if (((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
2139 0 : (PurchAir.MaxCoolMassFlowRate > 0.0)) {
2140 0 : SupplyMassFlowRate = min(max(SupplyMassFlowRate, 0.0), PurchAir.MaxCoolMassFlowRate);
2141 : }
2142 0 : if (SupplyMassFlowRate > OAMassFlowRate) {
2143 0 : EconoOn = true;
2144 0 : OAMassFlowRate = SupplyMassFlowRate;
2145 0 : PurchAir.TimeEconoActive = state.dataHVACGlobal->TimeStepSys;
2146 : }
2147 : }
2148 : }
2149 : }
2150 : }
2151 :
2152 : // Determine supply mass flow rate
2153 : // Mass flow rate to meet sensible load, at Minimum Cooling Supply Air Temperature
2154 358 : SupplyMassFlowRateForCool = 0.0;
2155 358 : if (CoolOn) {
2156 358 : CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
2157 358 : DeltaT = (PurchAir.MinCoolSuppAirTemp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
2158 358 : if (DeltaT < -HVAC::SmallTempDiff) {
2159 358 : SupplyMassFlowRateForCool = QZnCoolSP / CpAir / DeltaT;
2160 : }
2161 : }
2162 :
2163 : // Mass flow rate to meet dehumidification load, if applicable, at Minimum Cooling Supply Humidity Ratio
2164 358 : SupplyMassFlowRateForDehum = 0.0;
2165 358 : if (CoolOn) {
2166 358 : if (PurchAir.DehumidCtrlType == HumControl::Humidistat) {
2167 1 : MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
2168 1 : DeltaHumRat = (PurchAir.MinCoolSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
2169 1 : if ((DeltaHumRat < -SmallDeltaHumRat) && (MdotZnDehumidSP < 0.0)) {
2170 1 : SupplyMassFlowRateForDehum = MdotZnDehumidSP / DeltaHumRat;
2171 : }
2172 : }
2173 : }
2174 :
2175 : // Mass flow rate to meet humidification load, if applicable, at Maximum Heating Supply Humidity Ratio
2176 : // This section is the cooling section, so humidification should activate only if humidification control = humidistat
2177 : // and if dehumidification control = humidistat or none
2178 358 : SupplyMassFlowRateForHumid = 0.0;
2179 358 : if (HeatOn) {
2180 358 : if (PurchAir.HumidCtrlType == HumControl::Humidistat) {
2181 0 : if ((PurchAir.DehumidCtrlType == HumControl::Humidistat) || (PurchAir.DehumidCtrlType == HumControl::None)) {
2182 0 : MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
2183 0 : DeltaHumRat = (PurchAir.MaxHeatSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
2184 0 : if ((DeltaHumRat > SmallDeltaHumRat) && (MdotZnHumidSP > 0.0)) {
2185 0 : SupplyMassFlowRateForHumid = MdotZnHumidSP / DeltaHumRat;
2186 : }
2187 : }
2188 : }
2189 : }
2190 :
2191 : // If cooling capacity is limited to zero, SupplyMassFlowRate* should be set to zero
2192 358 : if (((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
2193 0 : (PurchAir.MaxCoolTotCap == 0)) {
2194 0 : SupplyMassFlowRateForCool = 0;
2195 0 : SupplyMassFlowRateForDehum = 0;
2196 0 : SupplyMassFlowRateForHumid = 0;
2197 : }
2198 :
2199 : // Supply mass flow is greatest of these, but limit to cooling max flow rate, if applicable
2200 358 : SupplyMassFlowRate = max(0.0, OAMassFlowRate, SupplyMassFlowRateForCool, SupplyMassFlowRateForDehum, SupplyMassFlowRateForHumid);
2201 : // EMS override point Purch air massflow rate..... but only if unit is on, i.e. SupplyMassFlowRate>0.0
2202 358 : if (PurchAir.EMSOverrideMdotOn) {
2203 1 : SupplyMassFlowRate = PurchAir.EMSValueMassFlowRate;
2204 1 : OAMassFlowRate = min(OAMassFlowRate, SupplyMassFlowRate);
2205 : }
2206 358 : if (((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
2207 0 : (PurchAir.MaxCoolMassFlowRate > 0.0)) {
2208 0 : SupplyMassFlowRate = min(SupplyMassFlowRate, PurchAir.MaxCoolMassFlowRate);
2209 : }
2210 :
2211 358 : if (SupplyMassFlowRate <= HVAC::VerySmallMassFlow) {
2212 0 : SupplyMassFlowRate = 0.0;
2213 : }
2214 :
2215 : // Calculate mixed air conditions
2216 358 : CalcPurchAirMixedAir(state,
2217 : PurchAirNum,
2218 : OAMassFlowRate,
2219 : SupplyMassFlowRate,
2220 358 : PurchAir.MixedAirTemp,
2221 358 : PurchAir.MixedAirHumRat,
2222 : MixedAirEnthalpy,
2223 : OperatingMode);
2224 :
2225 : // Calculate supply air conditions using final massflow rate, imposing capacity limits if specified
2226 : // If capacity limits are exceeded, keep massflow rate where it is and adjust supply temp
2227 : // In general, in the cooling section, don't let SupplyTemp be set to something that results in heating
2228 358 : if (SupplyMassFlowRate > 0.0) {
2229 : // Calculate supply temp at SupplyMassFlowRate and recheck limit on Minimum Cooling Supply Air Temperature
2230 358 : CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
2231 358 : PurchAir.SupplyTemp = QZnCoolSP / (CpAir * SupplyMassFlowRate) + state.dataLoopNodes->Node(ZoneNodeNum).Temp;
2232 358 : PurchAir.SupplyTemp = max(PurchAir.SupplyTemp, PurchAir.MinCoolSuppAirTemp);
2233 : // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
2234 358 : PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
2235 358 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2236 358 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2237 :
2238 : // Check sensible load vs max total cooling capacity, if specified, and adjust supply temp before applying humidity controls
2239 : // Will check again later, too
2240 358 : if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
2241 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2242 0 : CoolSensOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy);
2243 0 : if (CoolSensOutput >= PurchAir.MaxCoolTotCap) {
2244 0 : CoolSensOutput = PurchAir.MaxCoolTotCap;
2245 0 : SupplyEnthalpy = MixedAirEnthalpy - CoolSensOutput / SupplyMassFlowRate;
2246 0 : PurchAir.SupplyTemp = PsyTdbFnHW(SupplyEnthalpy, PurchAir.SupplyHumRat);
2247 : // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
2248 0 : PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
2249 : } // Capacity limit exceeded
2250 : }
2251 :
2252 : // Set supply humidity ratio for cooling/dehumidification
2253 358 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2254 358 : switch (PurchAir.DehumidCtrlType) {
2255 0 : case HumControl::None: {
2256 0 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat; // Unnecessary line?
2257 0 : } break;
2258 0 : case HumControl::ConstantSensibleHeatRatio: {
2259 : // SHR = CoolSensOutput/CoolTotOutput
2260 : // CoolTotOutput = CoolSensOutput/SHR
2261 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2262 0 : CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
2263 0 : CoolTotOutput = CoolSensOutput / PurchAir.CoolSHR;
2264 0 : SupplyEnthalpy = MixedAirEnthalpy - CoolTotOutput / SupplyMassFlowRate;
2265 : // Limit for overdrying (avoid Pysch errors which occur if SupplyEnthalpy is too low for SupplyTemp)
2266 0 : SupplyEnthalpy = max(SupplyEnthalpy, PsyHFnTdbW(PurchAir.SupplyTemp, 0.00001));
2267 0 : PurchAir.SupplyHumRat = min(PurchAir.SupplyHumRat, PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName));
2268 : // Apply min cooling humidity ratio limit
2269 0 : PurchAir.SupplyHumRat = max(PurchAir.SupplyHumRat, PurchAir.MinCoolSuppAirHumRat);
2270 : // But don't let it be higher than incoming MixedAirHumRat
2271 0 : PurchAir.SupplyHumRat = min(PurchAir.SupplyHumRat, PurchAir.MixedAirHumRat);
2272 0 : } break;
2273 1 : case HumControl::Humidistat: {
2274 1 : MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
2275 1 : SupplyHumRatForDehum = MdotZnDehumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
2276 1 : SupplyHumRatForDehum = max(SupplyHumRatForDehum, PurchAir.MinCoolSuppAirHumRat);
2277 1 : PurchAir.SupplyHumRat = min(PurchAir.MixedAirHumRat, SupplyHumRatForDehum);
2278 1 : } break;
2279 357 : case HumControl::ConstantSupplyHumidityRatio: {
2280 357 : PurchAir.SupplyHumRat = PurchAir.MinCoolSuppAirHumRat;
2281 357 : } break;
2282 0 : default: {
2283 0 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2284 0 : } break;
2285 : }
2286 :
2287 : // Check supply humidity ratio for humidification (SupplyHumRatForHum should always be < SupplyHumRatForDehum)
2288 : // This section is the cooling section, so humidification should activate only if humidification control = humidistat
2289 : // and if dehumidification control = humidistat or none
2290 358 : if (HeatOn) {
2291 358 : if (PurchAir.HumidCtrlType == HumControl::Humidistat) {
2292 0 : if ((PurchAir.DehumidCtrlType == HumControl::Humidistat) || (PurchAir.DehumidCtrlType == HumControl::None)) {
2293 0 : MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
2294 0 : SupplyHumRatForHumid = MdotZnHumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
2295 0 : SupplyHumRatForHumid = min(SupplyHumRatForHumid, PurchAir.MaxHeatSuppAirHumRat);
2296 0 : PurchAir.SupplyHumRat = max(PurchAir.SupplyHumRat, SupplyHumRatForHumid);
2297 : }
2298 : }
2299 : }
2300 :
2301 : // Limit supply humidity ratio to saturation at supply outlet temp
2302 :
2303 358 : SupplyHumRatOrig = PurchAir.SupplyHumRat;
2304 358 : SupplyHumRatSat = PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
2305 358 : PurchAir.SupplyHumRat = min(SupplyHumRatOrig, SupplyHumRatSat);
2306 358 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2307 :
2308 : // Check max total Cooling capacity, if specified
2309 358 : if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
2310 : // If dehumidifying, compare total cooling to the limit
2311 0 : if (PurchAir.SupplyHumRat < PurchAir.MixedAirHumRat) { // Dehumidifying
2312 0 : CoolTotOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy);
2313 0 : if ((CoolTotOutput) > PurchAir.MaxCoolTotCap) {
2314 0 : CoolTotOutput = PurchAir.MaxCoolTotCap;
2315 0 : SupplyEnthalpy = MixedAirEnthalpy - CoolTotOutput / SupplyMassFlowRate;
2316 : // Adjust output based on dehumidification control type
2317 0 : switch (PurchAir.DehumidCtrlType) {
2318 0 : case HumControl::ConstantSensibleHeatRatio: {
2319 : // Adjust both supply temp and humidity ratio to maintain SHR
2320 : // SHR = CoolSensOutput/CoolTotOutput
2321 : // CoolSensOutput = SHR*CoolTotOutput
2322 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2323 0 : CoolSensOutput = CoolTotOutput * PurchAir.CoolSHR;
2324 0 : PurchAir.SupplyTemp = PurchAir.MixedAirTemp - CoolSensOutput / (CpAir * SupplyMassFlowRate);
2325 : // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
2326 0 : PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
2327 : // Limit for overdrying (avoid Pysch errors which occur if SupplyEnthalpy is too low for SupplyTemp)
2328 0 : SupplyEnthalpy = max(SupplyEnthalpy, PsyHFnTdbW(PurchAir.SupplyTemp, 0.00001));
2329 0 : PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
2330 0 : } break;
2331 0 : case HumControl::Humidistat: {
2332 : // Keep supply temp and adjust humidity ratio to reduce load
2333 0 : PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
2334 0 : } break;
2335 0 : case HumControl::None:
2336 : case HumControl::ConstantSupplyHumidityRatio: {
2337 : // Keep humidity ratio and adjust supply temp
2338 : // Check if latent output exceeds capacity
2339 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2340 0 : CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
2341 0 : CoolLatOutput = CoolTotOutput - CoolSensOutput;
2342 0 : if (CoolLatOutput >= PurchAir.MaxCoolTotCap) {
2343 0 : PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
2344 0 : PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
2345 0 : CoolLatOutput = PurchAir.MaxCoolTotCap;
2346 : } else {
2347 0 : PurchAir.SupplyTemp = PsyTdbFnHW(SupplyEnthalpy, PurchAir.SupplyHumRat);
2348 : // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
2349 0 : PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
2350 : }
2351 0 : } break;
2352 0 : default:
2353 0 : break;
2354 : }
2355 : // Limit supply humidity ratio to saturation at supply outlet temp
2356 : // If saturation exceeded, then honor capacity limit and set to dew point at supply enthalpy
2357 :
2358 0 : SupplyHumRatOrig = PurchAir.SupplyHumRat;
2359 0 : SupplyHumRatSat = PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
2360 0 : if (SupplyHumRatSat < SupplyHumRatOrig) {
2361 0 : PurchAir.SupplyTemp = PsyTsatFnHPb(state, SupplyEnthalpy, state.dataEnvrn->OutBaroPress, RoutineName);
2362 :
2363 : // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
2364 0 : PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
2365 0 : PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
2366 0 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2367 : // CpAir = PsyCpAirFnW(MixedAirHumRat)
2368 : // CoolSensOutput = SupplyMassFlowRate * CpAir * (MixedAirTemp - SupplyTemp)
2369 : // CoolTotOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy)
2370 : }
2371 : } // Capacity limit exceeded
2372 : } else { // Not dehumidifying
2373 : // If not dehumidifying, compare sensible cooling to the limit
2374 : // This section will only increase supply temp, so no need to recheck for super-saturation
2375 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2376 0 : CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
2377 0 : if (CoolSensOutput >= PurchAir.MaxCoolTotCap) {
2378 0 : CoolSensOutput = PurchAir.MaxCoolTotCap;
2379 0 : PurchAir.SupplyTemp = PurchAir.MixedAirTemp - CoolSensOutput / (SupplyMassFlowRate * CpAir);
2380 : } // Capacity limit exceeded
2381 : } // Dehumidifying or not
2382 : } // Capacity limit active
2383 :
2384 : } else { // SupplyMassFlowRate is zero
2385 0 : SupplyEnthalpy = MixedAirEnthalpy;
2386 0 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2387 0 : PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
2388 0 : CoolSensOutput = 0.0;
2389 0 : CoolTotOutput = 0.0;
2390 : }
2391 : // Heating or no-load operation
2392 : } else { // Heating or no-load case
2393 408 : if ((MinOASensOutput < QZnHeatSP) && (state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleCool)) {
2394 276 : OperatingMode = OpMode::Heat;
2395 : } else { // DeadBand mode shuts off heat recovery and economizer
2396 132 : OperatingMode = OpMode::DeadBand;
2397 : }
2398 : // Calculate supply mass flow, temp and humidity with the following constraints:
2399 : // Max heating supply temp
2400 : // Max sensible heating capacity
2401 : // Max heating airflow
2402 : // Max heating supply humrat (and Min cooling supply humrat)
2403 : // Min OA mass flow rate
2404 :
2405 : // Check if OA flow rate greater than max heating airflow limit
2406 408 : if (((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) &&
2407 0 : (OAMassFlowRate > PurchAir.MaxHeatMassFlowRate)) {
2408 0 : OAVolFlowRate = OAMassFlowRate / state.dataEnvrn->StdRhoAir;
2409 0 : if (PurchAir.OAFlowMaxHeatOutputError < 1) {
2410 0 : ++PurchAir.OAFlowMaxHeatOutputError;
2411 0 : ShowWarningError(state,
2412 0 : format("{} \"{}\" Requested outdoor air flow rate = {:.5T} [m3/s] exceeds limit.",
2413 0 : PurchAir.cObjectName,
2414 0 : PurchAir.Name,
2415 : OAVolFlowRate));
2416 0 : ShowContinueError(state,
2417 0 : format(" Will be reduced to the Maximum Heating Air Flow Rate = {:.5T} [m3/s]", PurchAir.MaxHeatVolFlowRate));
2418 0 : ShowContinueErrorTimeStamp(state, "");
2419 : } else {
2420 0 : ShowRecurringWarningErrorAtEnd(
2421 : state,
2422 0 : PurchAir.cObjectName + " \"" + PurchAir.Name +
2423 : "\" Requested outdoor air flow rate [m3/s] reduced to Maximum Heating Air Flow Rate warning continues...",
2424 0 : PurchAir.OAFlowMaxHeatOutputIndex,
2425 : OAVolFlowRate);
2426 : }
2427 0 : OAMassFlowRate = PurchAir.MaxHeatMassFlowRate;
2428 : }
2429 :
2430 : // SupplyMassFlowRate = OAMassFlowRate;
2431 :
2432 : // Determine supply mass flow rate
2433 : // Mass flow rate to meet sensible load, at Minimum Cooling Supply Air Temperature
2434 408 : SupplyMassFlowRateForHeat = 0.0;
2435 408 : if ((HeatOn) && (OperatingMode == OpMode::Heat)) {
2436 276 : CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
2437 276 : DeltaT = (PurchAir.MaxHeatSuppAirTemp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
2438 276 : if (DeltaT > HVAC::SmallTempDiff) {
2439 276 : SupplyMassFlowRateForHeat = QZnHeatSP / CpAir / DeltaT;
2440 : }
2441 : }
2442 :
2443 : // Mass flow rate to meet dehumidification load, if applicable, at Minimum Cooling Supply Humidity Ratio
2444 : // This section is the heating/deadband section, so dehumidification should activate
2445 : // only if dehumidification control = humidistat
2446 : // and if humidification control = humidistat or none or if operating in deadband mode
2447 408 : SupplyMassFlowRateForDehum = 0.0;
2448 408 : if (CoolOn) {
2449 408 : if (PurchAir.DehumidCtrlType == HumControl::Humidistat) {
2450 0 : if ((PurchAir.HumidCtrlType == HumControl::Humidistat) || (PurchAir.HumidCtrlType == HumControl::None) ||
2451 : (OperatingMode == OpMode::DeadBand)) {
2452 0 : MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
2453 0 : DeltaHumRat = (PurchAir.MinCoolSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
2454 0 : if ((DeltaHumRat < -SmallDeltaHumRat) && (MdotZnDehumidSP < 0.0)) {
2455 0 : SupplyMassFlowRateForDehum = MdotZnDehumidSP / DeltaHumRat;
2456 : }
2457 : }
2458 : }
2459 : }
2460 :
2461 : // Mass flow rate to meet humidification load, if applicable, at Maximum Heating Supply Humidity Ratio
2462 408 : SupplyMassFlowRateForHumid = 0.0;
2463 408 : if (HeatOn) {
2464 408 : if (PurchAir.HumidCtrlType == HumControl::Humidistat) {
2465 0 : MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
2466 0 : DeltaHumRat = (PurchAir.MaxHeatSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
2467 0 : if ((DeltaHumRat > SmallDeltaHumRat) && (MdotZnHumidSP > 0.0)) {
2468 0 : SupplyMassFlowRateForHumid = MdotZnHumidSP / DeltaHumRat;
2469 : }
2470 : }
2471 : }
2472 :
2473 : // If heating capacity is limited to zero, SupplyMassFlowRate* should be set to zero
2474 408 : if (((PurchAir.HeatingLimit == LimitType::Capacity) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) &&
2475 2 : (PurchAir.MaxHeatSensCap == 0)) {
2476 2 : SupplyMassFlowRateForHeat = 0;
2477 2 : SupplyMassFlowRateForDehum = 0;
2478 2 : SupplyMassFlowRateForHumid = 0;
2479 : }
2480 :
2481 : // Supply mass flow is greatest of these, but limit to heating max flow rate, if applicable
2482 408 : SupplyMassFlowRate = max(0.0, OAMassFlowRate, SupplyMassFlowRateForHeat, SupplyMassFlowRateForDehum, SupplyMassFlowRateForHumid);
2483 : // EMS override point Purch air massflow rate..... but only if unit is on, i.e. SupplyMassFlowRate>0.0
2484 408 : if (PurchAir.EMSOverrideMdotOn) {
2485 3 : SupplyMassFlowRate = PurchAir.EMSValueMassFlowRate;
2486 3 : OAMassFlowRate = min(OAMassFlowRate, SupplyMassFlowRate);
2487 : }
2488 408 : if (((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) &&
2489 0 : (PurchAir.MaxHeatMassFlowRate > 0.0)) {
2490 0 : SupplyMassFlowRate = min(SupplyMassFlowRate, PurchAir.MaxHeatMassFlowRate);
2491 : }
2492 :
2493 408 : if (SupplyMassFlowRate <= HVAC::VerySmallMassFlow) {
2494 133 : SupplyMassFlowRate = 0.0;
2495 : }
2496 :
2497 : // Calculate mixed air conditions
2498 408 : CalcPurchAirMixedAir(state,
2499 : PurchAirNum,
2500 : OAMassFlowRate,
2501 : SupplyMassFlowRate,
2502 408 : PurchAir.MixedAirTemp,
2503 408 : PurchAir.MixedAirHumRat,
2504 : MixedAirEnthalpy,
2505 : OperatingMode);
2506 :
2507 : // Calculate supply air conditions using final massflow rate, imposing capacity limits if specified
2508 : // If capacity limits are exceeded, keep massflow rate where it is and adjust supply temp
2509 408 : if (SupplyMassFlowRate > 0.0) {
2510 275 : if ((HeatOn) && (OperatingMode == OpMode::Heat)) {
2511 : // Calculate supply temp at SupplyMassFlowRate and check limit on Maximum Heating Supply Air Temperature
2512 274 : CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
2513 274 : PurchAir.SupplyTemp = QZnHeatSP / (CpAir * SupplyMassFlowRate) + state.dataLoopNodes->Node(ZoneNodeNum).Temp;
2514 274 : PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MaxHeatSuppAirTemp);
2515 : // This is the heating mode, so SupplyTemp can't be less than MixedAirTemp
2516 274 : PurchAir.SupplyTemp = max(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
2517 : // Check max heating capacity, if specified
2518 274 : if ((PurchAir.HeatingLimit == LimitType::Capacity) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) {
2519 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2520 0 : HeatSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - PurchAir.MixedAirTemp);
2521 0 : if (HeatSensOutput > PurchAir.MaxHeatSensCap) {
2522 0 : PurchAir.SupplyTemp = PurchAir.MaxHeatSensCap / (SupplyMassFlowRate * CpAir) + PurchAir.MixedAirTemp;
2523 0 : HeatSensOutput = PurchAir.MaxHeatSensCap;
2524 : }
2525 : }
2526 : } else { // Heat is off or operating mode is deadband (i.e. don't do any heating)
2527 1 : PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
2528 : }
2529 :
2530 : // Set supply humidity ratio first for heating/humidification
2531 275 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2532 275 : switch (PurchAir.HumidCtrlType) {
2533 0 : case HumControl::None: {
2534 0 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2535 0 : } break;
2536 0 : case HumControl::Humidistat: {
2537 0 : MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
2538 0 : SupplyHumRatForHumid = MdotZnHumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
2539 0 : SupplyHumRatForHumid = min(SupplyHumRatForHumid, PurchAir.MaxHeatSuppAirHumRat);
2540 0 : PurchAir.SupplyHumRat = max(PurchAir.SupplyHumRat, SupplyHumRatForHumid);
2541 0 : } break;
2542 275 : case HumControl::ConstantSupplyHumidityRatio: {
2543 275 : if (OperatingMode == OpMode::Heat) {
2544 : // If this results in dehumidification, must check cooling capacity limit
2545 274 : if (PurchAir.MixedAirHumRat > PurchAir.MaxHeatSuppAirHumRat) {
2546 177 : if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
2547 0 : PurchAir.SupplyHumRat = PurchAir.MaxHeatSuppAirHumRat;
2548 0 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2549 0 : CoolTotOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy);
2550 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2551 0 : CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
2552 0 : CoolLatOutput = CoolTotOutput - CoolSensOutput;
2553 0 : if (CoolLatOutput >= PurchAir.MaxCoolTotCap) {
2554 0 : CoolLatOutput = PurchAir.MaxCoolTotCap;
2555 0 : CoolTotOutput = CoolSensOutput + CoolLatOutput;
2556 0 : SupplyEnthalpy = MixedAirEnthalpy - CoolTotOutput / SupplyMassFlowRate;
2557 0 : PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
2558 : }
2559 : } else {
2560 177 : PurchAir.SupplyHumRat = PurchAir.MaxHeatSuppAirHumRat;
2561 : }
2562 : } else {
2563 97 : PurchAir.SupplyHumRat = PurchAir.MaxHeatSuppAirHumRat;
2564 : }
2565 : } else {
2566 1 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2567 : }
2568 275 : } break;
2569 0 : default: {
2570 0 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2571 0 : } break;
2572 : }
2573 : // SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2574 :
2575 : // Check supply humidity ratio for dehumidification (SupplyHumRatForHumid should always be < SupplyHumRatForDehum)
2576 : // This section is the heating/deadband section, so dehumidification should activate
2577 : // only if dehumidification control = humidistat
2578 : // and if humidification control = humidistat or none or if operating in deadband mode
2579 275 : if (CoolOn) {
2580 275 : if (PurchAir.DehumidCtrlType == HumControl::Humidistat) {
2581 0 : if ((PurchAir.HumidCtrlType == HumControl::Humidistat) || (PurchAir.HumidCtrlType == HumControl::None) ||
2582 : (OperatingMode == OpMode::DeadBand)) {
2583 0 : MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
2584 0 : SupplyHumRatForDehum = MdotZnDehumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
2585 0 : SupplyHumRatForDehum = max(SupplyHumRatForDehum, PurchAir.MinCoolSuppAirHumRat);
2586 0 : PurchAir.SupplyHumRat = min(PurchAir.SupplyHumRat, SupplyHumRatForDehum);
2587 0 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2588 0 : if (PurchAir.SupplyHumRat < PurchAir.MixedAirHumRat) {
2589 : // At this point, the system is heating or deadband but dehumidifying, check max cooling cap limit
2590 0 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2591 0 : SensOutput = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - PurchAir.MixedAirTemp);
2592 0 : LatOutput = SupplyMassFlowRate * (SupplyEnthalpy - MixedAirEnthalpy) - SensOutput;
2593 0 : if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
2594 0 : if (LatOutput > PurchAir.MaxCoolTotCap) {
2595 0 : LatOutput = PurchAir.MaxCoolTotCap;
2596 0 : SupplyEnthalpy = MixedAirEnthalpy + (LatOutput + SensOutput) / SupplyMassFlowRate;
2597 0 : PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
2598 : }
2599 : }
2600 : }
2601 : }
2602 : }
2603 : }
2604 :
2605 : // Limit supply humidity ratio to saturation at supply outlet temp
2606 :
2607 275 : SupplyHumRatOrig = PurchAir.SupplyHumRat;
2608 275 : PurchAir.SupplyHumRat =
2609 275 : min(PurchAir.SupplyHumRat, PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName));
2610 275 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2611 :
2612 : } else { // SupplyMassFlowRate is zero
2613 133 : SupplyEnthalpy = MixedAirEnthalpy;
2614 133 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2615 133 : PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
2616 133 : HeatSensOutput = 0.0;
2617 : }
2618 :
2619 : } // Cooling or heating required
2620 :
2621 766 : if (SupplyMassFlowRate > 0.0) {
2622 : // EMS override point Purch air supply temp and humidity ratio ..... but only if unit is on, SupplyMassFlowRate>0.0
2623 633 : if (PurchAir.EMSOverrideSupplyTempOn) {
2624 1 : PurchAir.SupplyTemp = PurchAir.EMSValueSupplyTemp;
2625 : }
2626 633 : if (PurchAir.EMSOverrideSupplyHumRatOn) {
2627 1 : PurchAir.SupplyHumRat = PurchAir.EMSValueSupplyHumRat;
2628 : }
2629 633 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2630 :
2631 : // compute coil loads
2632 633 : if ((PurchAir.SupplyHumRat == PurchAir.MixedAirHumRat) && (PurchAir.SupplyTemp == PurchAir.MixedAirTemp)) {
2633 : // If no change in humrat or temp, then set loads to zero
2634 0 : PurchAir.SenCoilLoad = 0.0;
2635 0 : PurchAir.LatCoilLoad = 0.0;
2636 633 : } else if ((PurchAir.SupplyHumRat == PurchAir.MixedAirHumRat) && (PurchAir.SupplyTemp != PurchAir.MixedAirTemp)) {
2637 : // If no change in humrat, then set latent load to zero and use enthalpies to calculate sensible load
2638 0 : PurchAir.SenCoilLoad = SupplyMassFlowRate * (SupplyEnthalpy - MixedAirEnthalpy);
2639 0 : PurchAir.LatCoilLoad = 0.0;
2640 : } else {
2641 633 : CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
2642 633 : PurchAir.SenCoilLoad = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - PurchAir.MixedAirTemp);
2643 633 : PurchAir.LatCoilLoad = SupplyMassFlowRate * (SupplyEnthalpy - MixedAirEnthalpy) - PurchAir.SenCoilLoad;
2644 : }
2645 :
2646 : // Apply heating and cooling availability schedules to sensible load
2647 633 : if (((PurchAir.SenCoilLoad > 0.0) && !HeatOn) || ((PurchAir.SenCoilLoad < 0.0) && !CoolOn)) {
2648 : // Coil is off
2649 0 : PurchAir.SenCoilLoad = 0.0;
2650 0 : PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
2651 : }
2652 :
2653 : // Apply heating and cooling availability schedules to latent load
2654 633 : if (((PurchAir.LatCoilLoad > 0.0) && !HeatOn) || ((PurchAir.LatCoilLoad < 0.0) && !CoolOn)) {
2655 : // Coil is off
2656 0 : PurchAir.LatCoilLoad = 0.0;
2657 0 : PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
2658 : }
2659 :
2660 : // Double-check if saturation exceeded, then throw warning, shouldn't happen here, don't reset, just warn
2661 :
2662 633 : SupplyHumRatOrig = PurchAir.SupplyHumRat;
2663 633 : SupplyHumRatSat = PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
2664 :
2665 633 : DeltaHumRat = SupplyHumRatOrig - SupplyHumRatSat;
2666 633 : if (DeltaHumRat > SmallDeltaHumRat) {
2667 0 : if (PurchAir.SaturationOutputError < 1) {
2668 0 : ++PurchAir.SaturationOutputError;
2669 0 : ShowWarningError(state,
2670 0 : format("{} \"{}\" Supply humidity ratio = {:.5T} exceeds saturation limit {:.5T} [kgWater/kgDryAir]",
2671 0 : PurchAir.cObjectName,
2672 0 : PurchAir.Name,
2673 : SupplyHumRatOrig,
2674 : SupplyHumRatSat));
2675 0 : ShowContinueError(state, " Simulation continuing . . . ");
2676 0 : ShowContinueErrorTimeStamp(state, "");
2677 : } else {
2678 0 : ShowRecurringWarningErrorAtEnd(
2679 : state,
2680 0 : PurchAir.cObjectName + " \"" + PurchAir.Name +
2681 : "\" Supply humidity ratio exceeds saturation limit warning continues, delta max/min [kgWater/kgDryAir]...",
2682 0 : PurchAir.SaturationOutputIndex,
2683 : DeltaHumRat,
2684 : DeltaHumRat);
2685 : }
2686 : }
2687 :
2688 633 : SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
2689 :
2690 633 : CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
2691 633 : SysOutputProvided = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
2692 633 : MoistOutputProvided = SupplyMassFlowRate * (PurchAir.SupplyHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat); // Latent rate, kg/s
2693 :
2694 633 : PurchAir.SenOutputToZone = SysOutputProvided;
2695 633 : PurchAir.LatOutputToZone =
2696 633 : SupplyMassFlowRate * (SupplyEnthalpy - state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy) - PurchAir.SenOutputToZone;
2697 :
2698 633 : CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
2699 633 : if (PurchAir.OutdoorAir) {
2700 1 : PurchAir.OASenOutput =
2701 1 : OAMassFlowRate * CpAir * (state.dataLoopNodes->Node(OANodeNum).Temp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
2702 1 : PurchAir.OALatOutput =
2703 1 : OAMassFlowRate * (state.dataLoopNodes->Node(OANodeNum).Enthalpy - state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy) -
2704 1 : PurchAir.OASenOutput;
2705 : } else {
2706 632 : PurchAir.OASenOutput = 0.0;
2707 632 : PurchAir.OALatOutput = 0.0;
2708 : }
2709 633 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
2710 0 : if (PurchAir.OutdoorAir) {
2711 0 : state.dataLoopNodes->Node(InNodeNum).CO2 = ((SupplyMassFlowRate - OAMassFlowRate) * state.dataLoopNodes->Node(RecircNodeNum).CO2 +
2712 0 : OAMassFlowRate * state.dataLoopNodes->Node(OANodeNum).CO2) /
2713 : SupplyMassFlowRate;
2714 : } else {
2715 0 : state.dataLoopNodes->Node(InNodeNum).CO2 = state.dataLoopNodes->Node(RecircNodeNum).CO2;
2716 : }
2717 : }
2718 633 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
2719 0 : if (PurchAir.OutdoorAir) {
2720 0 : state.dataLoopNodes->Node(InNodeNum).GenContam =
2721 0 : ((SupplyMassFlowRate - OAMassFlowRate) * state.dataLoopNodes->Node(RecircNodeNum).GenContam +
2722 0 : OAMassFlowRate * state.dataLoopNodes->Node(OANodeNum).GenContam) /
2723 : SupplyMassFlowRate;
2724 : } else {
2725 0 : state.dataLoopNodes->Node(InNodeNum).GenContam = state.dataLoopNodes->Node(RecircNodeNum).GenContam;
2726 : }
2727 : }
2728 : } else { // SupplyMassFlowRate = 0.0
2729 133 : SysOutputProvided = 0.0;
2730 133 : MoistOutputProvided = 0.0;
2731 :
2732 133 : PurchAir.SenOutputToZone = 0.0;
2733 133 : PurchAir.LatOutputToZone = 0.0;
2734 133 : PurchAir.SenCoilLoad = 0.0;
2735 133 : PurchAir.LatCoilLoad = 0.0;
2736 133 : PurchAir.OASenOutput = 0.0;
2737 133 : PurchAir.OALatOutput = 0.0;
2738 :
2739 133 : PurchAir.MixedAirTemp = state.dataLoopNodes->Node(RecircNodeNum).Temp;
2740 133 : PurchAir.MixedAirHumRat = state.dataLoopNodes->Node(RecircNodeNum).HumRat;
2741 133 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
2742 :
2743 0 : state.dataLoopNodes->Node(InNodeNum).CO2 = state.dataLoopNodes->Node(ZoneNodeNum).CO2;
2744 : }
2745 133 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
2746 0 : state.dataLoopNodes->Node(InNodeNum).GenContam = state.dataLoopNodes->Node(ZoneNodeNum).GenContam;
2747 : }
2748 : }
2749 :
2750 766 : state.dataLoopNodes->Node(InNodeNum).Temp = PurchAir.SupplyTemp;
2751 766 : state.dataLoopNodes->Node(InNodeNum).HumRat = PurchAir.SupplyHumRat;
2752 766 : state.dataLoopNodes->Node(InNodeNum).Enthalpy = SupplyEnthalpy;
2753 766 : state.dataLoopNodes->Node(InNodeNum).MassFlowRate = SupplyMassFlowRate;
2754 766 : if (PurchAir.OutdoorAir) {
2755 1 : state.dataLoopNodes->Node(OANodeNum).MassFlowRate = OAMassFlowRate;
2756 : }
2757 :
2758 : } else { // purchased air OFF
2759 :
2760 0 : SysOutputProvided = 0.0;
2761 0 : MoistOutputProvided = 0.0;
2762 0 : SupplyMassFlowRate = 0.0;
2763 0 : OAMassFlowRate = 0.0;
2764 0 : state.dataLoopNodes->Node(InNodeNum).Temp = state.dataLoopNodes->Node(ZoneNodeNum).Temp;
2765 0 : state.dataLoopNodes->Node(InNodeNum).HumRat = state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
2766 0 : state.dataLoopNodes->Node(InNodeNum).Enthalpy = state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy;
2767 0 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
2768 0 : state.dataLoopNodes->Node(InNodeNum).CO2 = state.dataLoopNodes->Node(ZoneNodeNum).CO2;
2769 : }
2770 0 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
2771 0 : state.dataLoopNodes->Node(InNodeNum).GenContam = state.dataLoopNodes->Node(ZoneNodeNum).GenContam;
2772 : }
2773 :
2774 0 : state.dataLoopNodes->Node(InNodeNum).MassFlowRate = 0.0;
2775 0 : if (PurchAir.OutdoorAir) {
2776 0 : state.dataLoopNodes->Node(OANodeNum).MassFlowRate = 0.0;
2777 : }
2778 0 : PurchAir.SenHeatRate = 0.0;
2779 0 : PurchAir.SenCoolRate = 0.0;
2780 0 : PurchAir.TotCoolRate = 0.0;
2781 :
2782 0 : PurchAir.SenOutputToZone = 0.0;
2783 0 : PurchAir.LatOutputToZone = 0.0;
2784 0 : PurchAir.SenCoilLoad = 0.0;
2785 0 : PurchAir.LatCoilLoad = 0.0;
2786 0 : PurchAir.OASenOutput = 0.0;
2787 0 : PurchAir.OALatOutput = 0.0;
2788 0 : PurchAir.MixedAirTemp = state.dataLoopNodes->Node(RecircNodeNum).Temp;
2789 0 : PurchAir.MixedAirHumRat = state.dataLoopNodes->Node(RecircNodeNum).HumRat;
2790 0 : PurchAir.SupplyTemp = state.dataLoopNodes->Node(InNodeNum).Temp;
2791 0 : PurchAir.SupplyHumRat = state.dataLoopNodes->Node(InNodeNum).HumRat;
2792 : }
2793 :
2794 766 : PurchAir.OutdoorAirMassFlowRate = OAMassFlowRate;
2795 766 : PurchAir.OutdoorAirVolFlowRateStdRho = OAMassFlowRate / state.dataEnvrn->StdRhoAir;
2796 766 : PurchAir.SupplyAirMassFlowRate = SupplyMassFlowRate;
2797 :
2798 766 : PurchAir.SupplyAirVolFlowRateStdRho = SupplyMassFlowRate / state.dataEnvrn->StdRhoAir;
2799 :
2800 766 : if (PurchAir.PlenumExhaustAirNodeNum > 0) {
2801 1 : state.dataLoopNodes->Node(PurchAir.PlenumExhaustAirNodeNum).MassFlowRate = SupplyMassFlowRate;
2802 : }
2803 766 : state.dataLoopNodes->Node(RecircNodeNum).MassFlowRate = SupplyMassFlowRate;
2804 766 : }
2805 :
2806 766 : void CalcPurchAirMinOAMassFlow(EnergyPlusData &state,
2807 : int const PurchAirNum, // index to ideal loads unit
2808 : int const ZoneNum, // index to zone
2809 : Real64 &OAMassFlowRate // outside air mass flow rate [kg/s] from volume flow using std density
2810 : )
2811 : {
2812 :
2813 : // SUBROUTINE INFORMATION:
2814 : // AUTHOR M. Witte (GARD)
2815 : // DATE WRITTEN Jun 2011 (taken from HVACSingleDuctSystem.cc and adapted for Ideal Loads System)
2816 :
2817 : // PURPOSE OF THIS SUBROUTINE:
2818 : // Calculates the amount of outside air required based on optional user input.
2819 : // Zone multipliers have been applied in GetInput.
2820 :
2821 : // METHODOLOGY EMPLOYED:
2822 : // User input defines method used to calculate OA.
2823 :
2824 : // FUNCTION PARAMETER DEFINITIONS:
2825 766 : bool constexpr UseMinOASchFlag = true; // Always use min OA schedule in calculations.
2826 :
2827 766 : auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
2828 :
2829 766 : if (PurchAir.OutdoorAir) {
2830 : bool UseOccSchFlag; // TRUE = use actual occupancy, FALSE = use total zone people
2831 :
2832 1 : if (PurchAir.DCVType == DCV::OccupancySchedule) {
2833 0 : UseOccSchFlag = true;
2834 : } else {
2835 1 : UseOccSchFlag = false;
2836 : }
2837 : Real64 OAVolumeFlowRate =
2838 1 : DataSizing::calcDesignSpecificationOutdoorAir(state, PurchAir.OARequirementsPtr, ZoneNum, UseOccSchFlag, UseMinOASchFlag);
2839 1 : OAMassFlowRate = OAVolumeFlowRate * state.dataEnvrn->StdRhoAir;
2840 :
2841 : // If DCV with CO2SetPoint then check required OA flow to meet CO2 setpoint
2842 1 : if (PurchAir.DCVType == DCV::CO2SetPoint) {
2843 0 : OAMassFlowRate = max(OAMassFlowRate, state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToCO2SP);
2844 : }
2845 :
2846 1 : if (OAMassFlowRate <= HVAC::VerySmallMassFlow) {
2847 1 : OAMassFlowRate = 0.0;
2848 : }
2849 :
2850 : } else { // No outdoor air
2851 765 : OAMassFlowRate = 0.0;
2852 : }
2853 766 : PurchAir.MinOAMassFlowRate = OAMassFlowRate;
2854 766 : }
2855 :
2856 767 : void CalcPurchAirMixedAir(EnergyPlusData &state,
2857 : int const PurchAirNum, // index to ideal loads unit
2858 : Real64 const OAMassFlowRate, // outside air mass flow rate [kg/s]
2859 : Real64 const SupplyMassFlowRate, // supply air mass flow rate [kg/s]
2860 : Real64 &MixedAirTemp, // Mixed air dry bulb temperature [C]
2861 : Real64 &MixedAirHumRat, // Mixed air humidity ratio [kgWater/kgDryAir]
2862 : Real64 &MixedAirEnthalpy, // Mixed air enthalpy [J/kg]
2863 : OpMode const OperatingMode // current operating mode, Off, Heating, Cooling, or DeadBand
2864 : )
2865 : {
2866 :
2867 : // SUBROUTINE INFORMATION:
2868 : // AUTHOR M. Witte (GARD)
2869 : // DATE WRITTEN Sep 2011
2870 : // MODIFIED na
2871 : // RE-ENGINEERED na
2872 :
2873 : // PURPOSE OF THIS SUBROUTINE:
2874 : // Calculates the mixed air conditions, accounting for heat recovery.
2875 :
2876 : // SUBROUTINE PARAMETER DEFINITIONS:
2877 : static constexpr std::string_view RoutineName("CalcPurchAirMixedAir");
2878 :
2879 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2880 : int RecircNodeNum; // Zone return air node
2881 : int OANodeNum; // Outdoor air inlet node
2882 : Real64 RecircTemp; // Recirculated air from zone dry bulb temperature [C]
2883 : Real64 RecircHumRat; // Recirculated air from zone humidity ratio [kgWater/kgDryAir]
2884 : Real64 RecircEnthalpy; // Recirculated air from zone enthalpy [J/kg]
2885 : Real64 RecircMassFlowRate; // Recirculated air mass flow rate [kg/s]
2886 : Real64 OAInletTemp; // Outdoor air inlet dry bulb temperature [C]
2887 : Real64 OAInletHumRat; // Outdoor air inlet humidity ratio [kgWater/kgDryAir]
2888 : Real64 OAInletEnthalpy; // Outdoor air inlet enthalpy [J/kg]
2889 : Real64 OAAfterHtRecTemp; // Outdoor air after heat recovery to mixing box dry bulb temperature [C]
2890 : Real64 OAAfterHtRecHumRat; // Outdoor air after heat recovery to mixing box humidity ratio [kgWater/kgDryAir]
2891 : Real64 OAAfterHtRecEnthalpy; // Outdoor air after heat recovery to mixing box enthalpy [J/kg]
2892 : bool HeatRecOn;
2893 : Real64 CpAir; // Specific heat [J/kg-C] reused in multiple places
2894 :
2895 767 : auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
2896 :
2897 : // Initializations
2898 767 : OANodeNum = PurchAir.OutdoorAirNodeNum;
2899 767 : RecircNodeNum = PurchAir.ZoneRecircAirNodeNum;
2900 :
2901 767 : RecircMassFlowRate = 0.0;
2902 767 : RecircTemp = state.dataLoopNodes->Node(RecircNodeNum).Temp;
2903 767 : RecircHumRat = state.dataLoopNodes->Node(RecircNodeNum).HumRat;
2904 767 : RecircEnthalpy = state.dataLoopNodes->Node(RecircNodeNum).Enthalpy;
2905 767 : if (PurchAir.OutdoorAir) {
2906 2 : OAInletTemp = state.dataLoopNodes->Node(OANodeNum).Temp;
2907 2 : OAInletHumRat = state.dataLoopNodes->Node(OANodeNum).HumRat;
2908 2 : OAInletEnthalpy = state.dataLoopNodes->Node(OANodeNum).Enthalpy;
2909 2 : OAAfterHtRecTemp = OAInletTemp;
2910 2 : OAAfterHtRecHumRat = OAInletHumRat;
2911 2 : OAAfterHtRecEnthalpy = OAInletEnthalpy;
2912 : } else {
2913 765 : OAInletTemp = 0.0;
2914 765 : OAInletHumRat = 0.0;
2915 765 : OAInletEnthalpy = 0.0;
2916 765 : OAAfterHtRecTemp = OAInletTemp;
2917 765 : OAAfterHtRecHumRat = OAInletHumRat;
2918 765 : OAAfterHtRecEnthalpy = OAInletEnthalpy;
2919 : }
2920 767 : HeatRecOn = false;
2921 :
2922 767 : if (PurchAir.OutdoorAir && (OAMassFlowRate > 0.0)) {
2923 : // Determine if heat recovery is beneficial
2924 1 : if (PurchAir.HtRecType == HeatRecovery::Sensible) {
2925 1 : if ((OperatingMode == OpMode::Heat) && (RecircTemp > OAInletTemp)) {
2926 0 : HeatRecOn = true;
2927 : }
2928 1 : if ((OperatingMode == OpMode::Cool) && (RecircTemp < OAInletTemp)) {
2929 0 : HeatRecOn = true;
2930 : }
2931 : }
2932 1 : if (PurchAir.HtRecType == HeatRecovery::Enthalpy) {
2933 0 : if ((OperatingMode == OpMode::Heat) && (RecircEnthalpy > OAInletEnthalpy)) {
2934 0 : HeatRecOn = true;
2935 : }
2936 0 : if ((OperatingMode == OpMode::Cool) && (RecircEnthalpy < OAInletEnthalpy)) {
2937 0 : HeatRecOn = true;
2938 : }
2939 : }
2940 : // Calculate heat recovery if active
2941 1 : if (HeatRecOn) {
2942 0 : PurchAir.TimeHtRecActive = state.dataHVACGlobal->TimeStepSys;
2943 0 : OAAfterHtRecTemp = OAInletTemp + PurchAir.HtRecSenEff * (RecircTemp - OAInletTemp);
2944 0 : if (PurchAir.HtRecType == HeatRecovery::Enthalpy) {
2945 0 : OAAfterHtRecHumRat = OAInletHumRat + PurchAir.HtRecLatEff * (RecircHumRat - OAInletHumRat);
2946 : }
2947 0 : OAAfterHtRecEnthalpy = PsyHFnTdbW(OAAfterHtRecTemp, OAAfterHtRecHumRat);
2948 : // Check for saturation in supply outlet and reset temp, then humidity ratio at constant enthalpy
2949 0 : if (PsyTsatFnHPb(state, OAAfterHtRecEnthalpy, state.dataEnvrn->OutBaroPress, RoutineName) > OAAfterHtRecTemp) {
2950 0 : OAAfterHtRecTemp = PsyTsatFnHPb(state, OAAfterHtRecEnthalpy, state.dataEnvrn->OutBaroPress, RoutineName);
2951 0 : OAAfterHtRecHumRat = PsyWFnTdbH(state, OAAfterHtRecTemp, OAAfterHtRecEnthalpy, RoutineName);
2952 : }
2953 : }
2954 :
2955 1 : if (SupplyMassFlowRate > OAMassFlowRate) {
2956 1 : RecircMassFlowRate = SupplyMassFlowRate - OAMassFlowRate;
2957 1 : MixedAirEnthalpy =
2958 1 : (RecircMassFlowRate * state.dataLoopNodes->Node(RecircNodeNum).Enthalpy + OAMassFlowRate * OAAfterHtRecEnthalpy) / SupplyMassFlowRate;
2959 1 : MixedAirHumRat =
2960 1 : (RecircMassFlowRate * state.dataLoopNodes->Node(RecircNodeNum).HumRat + OAMassFlowRate * OAAfterHtRecHumRat) / SupplyMassFlowRate;
2961 : // Mixed air temperature is calculated from the mixed air enthalpy and humidity ratio.
2962 1 : MixedAirTemp = PsyTdbFnHW(MixedAirEnthalpy, MixedAirHumRat);
2963 : } else {
2964 0 : RecircMassFlowRate = 0.0;
2965 0 : MixedAirEnthalpy = OAAfterHtRecEnthalpy;
2966 0 : MixedAirHumRat = OAAfterHtRecHumRat;
2967 0 : MixedAirTemp = OAAfterHtRecTemp;
2968 : }
2969 :
2970 : // Calculate OA and heat recovery sensible and latent rates
2971 1 : CpAir = PsyCpAirFnW(OAInletHumRat);
2972 1 : PurchAir.HtRecSenOutput = OAMassFlowRate * CpAir * (OAAfterHtRecTemp - OAInletTemp);
2973 1 : PurchAir.HtRecLatOutput = OAMassFlowRate * (OAAfterHtRecEnthalpy - OAInletEnthalpy) - PurchAir.HtRecSenOutput;
2974 :
2975 : } else { // No outdoor air
2976 766 : RecircMassFlowRate = SupplyMassFlowRate;
2977 766 : MixedAirTemp = RecircTemp;
2978 766 : MixedAirHumRat = RecircHumRat;
2979 766 : MixedAirEnthalpy = RecircEnthalpy;
2980 766 : PurchAir.HtRecSenOutput = 0.0;
2981 766 : PurchAir.HtRecLatOutput = 0.0;
2982 : }
2983 767 : }
2984 :
2985 761 : void UpdatePurchasedAir(EnergyPlusData &state, int const PurchAirNum, bool const FirstHVACIteration)
2986 : {
2987 :
2988 : // SUBROUTINE INFORMATION:
2989 : // AUTHOR M. J. Witte
2990 : // DATE WRITTEN Sep 2011
2991 : // MODIFIED R. Raustad, July 2017, added return plenum
2992 : // RE-ENGINEERED na
2993 :
2994 : // PURPOSE OF THIS SUBROUTINE:
2995 : // Update node data for Ideal Loads (purchased air) system
2996 :
2997 : // USE STATEMENTS:
2998 : using ZonePlenum::SimAirZonePlenum;
2999 :
3000 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3001 : // na
3002 : bool FirstCall;
3003 : bool SupPathInletChanged;
3004 :
3005 761 : FirstCall = true; // just used to avoid redundant calculations
3006 761 : SupPathInletChanged = false; // don't care if something changes
3007 :
3008 761 : auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
3009 :
3010 761 : if (PurchAir.ReturnPlenumIndex > 0) {
3011 :
3012 : // if connected to a return plenum, set the flag that this ideal loads air system was simulated
3013 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(PurchAir.ReturnPlenumIndex).IsSimulated(PurchAir.PurchAirArrayIndex) = true;
3014 :
3015 : // if all ideal loads air systems connected to the same plenum have been simulated, simulate the zone air plenum
3016 1 : if (all(state.dataPurchasedAirMgr->PurchAirPlenumArrays(PurchAir.ReturnPlenumIndex).IsSimulated)) {
3017 2 : SimAirZonePlenum(state,
3018 : PurchAir.ReturnPlenumName,
3019 : DataZoneEquipment::AirLoopHVACZone::ReturnPlenum,
3020 1 : PurchAir.ReturnPlenumIndex,
3021 : FirstHVACIteration,
3022 : FirstCall,
3023 : SupPathInletChanged);
3024 : // reset this plenums flags for next iteration
3025 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(PurchAir.ReturnPlenumIndex).IsSimulated = false;
3026 : }
3027 : }
3028 761 : }
3029 :
3030 761 : void ReportPurchasedAir(EnergyPlusData &state, int const PurchAirNum)
3031 : {
3032 :
3033 : // SUBROUTINE INFORMATION:
3034 : // AUTHOR Russ Taylor
3035 : // DATE WRITTEN Nov 1997
3036 : // MODIFIED na
3037 : // RE-ENGINEERED na
3038 :
3039 : // PURPOSE OF THIS SUBROUTINE:
3040 : // Calculate values of report variables, if necessary.
3041 :
3042 : // Using/Aliasing
3043 761 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
3044 :
3045 761 : auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
3046 :
3047 : // Sort out heating and cooling rates
3048 761 : PurchAir.SenHeatRate = max(PurchAir.SenCoilLoad, 0.0);
3049 761 : PurchAir.SenCoolRate = std::abs(min(PurchAir.SenCoilLoad, 0.0));
3050 761 : PurchAir.LatHeatRate = max(PurchAir.LatCoilLoad, 0.0);
3051 761 : PurchAir.LatCoolRate = std::abs(min(PurchAir.LatCoilLoad, 0.0));
3052 761 : PurchAir.TotHeatRate = PurchAir.SenHeatRate + PurchAir.LatHeatRate;
3053 761 : PurchAir.TotCoolRate = PurchAir.SenCoolRate + PurchAir.LatCoolRate;
3054 :
3055 761 : PurchAir.ZoneSenHeatRate = max(PurchAir.SenOutputToZone, 0.0);
3056 761 : PurchAir.ZoneSenCoolRate = std::abs(min(PurchAir.SenOutputToZone, 0.0));
3057 761 : PurchAir.ZoneLatHeatRate = max(PurchAir.LatOutputToZone, 0.0);
3058 761 : PurchAir.ZoneLatCoolRate = std::abs(min(PurchAir.LatOutputToZone, 0.0));
3059 761 : PurchAir.ZoneTotHeatRate = PurchAir.ZoneSenHeatRate + PurchAir.ZoneLatHeatRate;
3060 761 : PurchAir.ZoneTotCoolRate = PurchAir.ZoneSenCoolRate + PurchAir.ZoneLatCoolRate;
3061 :
3062 : // Sort out outdoor air "loads"
3063 : // OASenOutput = Outdoor air sensible output relative to zone conditions [W], <0 means OA is cooler than zone air
3064 : // OALatOutput = Outdoor air latent output relative to zone conditions [W], <0 means OA is drier than zone air
3065 761 : if (PurchAir.SenCoilLoad > 0.0) { // Heating is active
3066 274 : PurchAir.OASenHeatRate = std::abs(min(PurchAir.OASenOutput, 0.0));
3067 : } else {
3068 487 : PurchAir.OASenHeatRate = 0.0;
3069 : }
3070 761 : if (PurchAir.SenCoilLoad < 0.0) { // Cooling is active
3071 357 : PurchAir.OASenCoolRate = max(PurchAir.OASenOutput, 0.0);
3072 : } else {
3073 404 : PurchAir.OASenCoolRate = 0.0;
3074 : }
3075 761 : if (PurchAir.LatCoilLoad > 0.0) { // Humidification is active
3076 131 : PurchAir.OALatHeatRate = std::abs(min(PurchAir.OALatOutput, 0.0));
3077 : } else {
3078 630 : PurchAir.OALatHeatRate = 0.0;
3079 : }
3080 761 : if (PurchAir.LatCoilLoad < 0.0) { // Dehumidification is active
3081 500 : PurchAir.OALatCoolRate = max(PurchAir.OALatOutput, 0.0);
3082 : } else {
3083 261 : PurchAir.OALatCoolRate = 0.0;
3084 : }
3085 :
3086 761 : PurchAir.OATotHeatRate = PurchAir.OASenHeatRate + PurchAir.OALatHeatRate;
3087 761 : PurchAir.OATotCoolRate = PurchAir.OASenCoolRate + PurchAir.OALatCoolRate;
3088 :
3089 761 : PurchAir.HtRecSenHeatRate = max(PurchAir.HtRecSenOutput, 0.0);
3090 761 : PurchAir.HtRecSenCoolRate = std::abs(min(PurchAir.HtRecSenOutput, 0.0));
3091 761 : PurchAir.HtRecLatHeatRate = max(PurchAir.HtRecLatOutput, 0.0);
3092 761 : PurchAir.HtRecLatCoolRate = std::abs(min(PurchAir.HtRecLatOutput, 0.0));
3093 761 : PurchAir.HtRecTotHeatRate = PurchAir.HtRecSenHeatRate + PurchAir.HtRecLatHeatRate;
3094 761 : PurchAir.HtRecTotCoolRate = PurchAir.HtRecSenCoolRate + PurchAir.HtRecLatCoolRate;
3095 :
3096 761 : PurchAir.SenHeatEnergy = PurchAir.SenHeatRate * TimeStepSysSec;
3097 761 : PurchAir.SenCoolEnergy = PurchAir.SenCoolRate * TimeStepSysSec;
3098 761 : PurchAir.LatHeatEnergy = PurchAir.LatHeatRate * TimeStepSysSec;
3099 761 : PurchAir.LatCoolEnergy = PurchAir.LatCoolRate * TimeStepSysSec;
3100 761 : PurchAir.TotHeatEnergy = PurchAir.TotHeatRate * TimeStepSysSec;
3101 761 : PurchAir.TotCoolEnergy = PurchAir.TotCoolRate * TimeStepSysSec;
3102 :
3103 761 : PurchAir.ZoneSenHeatEnergy = PurchAir.ZoneSenHeatRate * TimeStepSysSec;
3104 761 : PurchAir.ZoneSenCoolEnergy = PurchAir.ZoneSenCoolRate * TimeStepSysSec;
3105 761 : PurchAir.ZoneLatHeatEnergy = PurchAir.ZoneLatHeatRate * TimeStepSysSec;
3106 761 : PurchAir.ZoneLatCoolEnergy = PurchAir.ZoneLatCoolRate * TimeStepSysSec;
3107 761 : PurchAir.ZoneTotHeatEnergy = PurchAir.ZoneTotHeatRate * TimeStepSysSec;
3108 761 : PurchAir.ZoneTotCoolEnergy = PurchAir.ZoneTotCoolRate * TimeStepSysSec;
3109 :
3110 761 : PurchAir.OASenHeatEnergy = PurchAir.OASenHeatRate * TimeStepSysSec;
3111 761 : PurchAir.OASenCoolEnergy = PurchAir.OASenCoolRate * TimeStepSysSec;
3112 761 : PurchAir.OALatHeatEnergy = PurchAir.OALatHeatRate * TimeStepSysSec;
3113 761 : PurchAir.OALatCoolEnergy = PurchAir.OALatCoolRate * TimeStepSysSec;
3114 761 : PurchAir.OATotHeatEnergy = PurchAir.OATotHeatRate * TimeStepSysSec;
3115 761 : PurchAir.OATotCoolEnergy = PurchAir.OATotCoolRate * TimeStepSysSec;
3116 :
3117 761 : PurchAir.HtRecSenHeatEnergy = PurchAir.HtRecSenHeatRate * TimeStepSysSec;
3118 761 : PurchAir.HtRecSenCoolEnergy = PurchAir.HtRecSenCoolRate * TimeStepSysSec;
3119 761 : PurchAir.HtRecLatHeatEnergy = PurchAir.HtRecLatHeatRate * TimeStepSysSec;
3120 761 : PurchAir.HtRecLatCoolEnergy = PurchAir.HtRecLatCoolRate * TimeStepSysSec;
3121 761 : PurchAir.HtRecTotHeatEnergy = PurchAir.HtRecTotHeatRate * TimeStepSysSec;
3122 761 : PurchAir.HtRecTotCoolEnergy = PurchAir.HtRecTotCoolRate * TimeStepSysSec;
3123 761 : }
3124 :
3125 49 : Real64 GetPurchasedAirOutAirMassFlow(EnergyPlusData &state, int const PurchAirNum)
3126 : {
3127 :
3128 : // FUNCTION INFORMATION:
3129 : // AUTHOR B Griffith
3130 : // DATE WRITTEN Dec 2006
3131 : // MODIFIED na
3132 : // RE-ENGINEERED na
3133 :
3134 : // PURPOSE OF THIS FUNCTION:
3135 : // lookup function for OA inlet mass flow for ventilation rate reporting
3136 :
3137 : // METHODOLOGY EMPLOYED:
3138 : // most analogous functions look up an outside air node but this function
3139 : // gets the actual mass flow of outdoor air, following the features of the model
3140 :
3141 49 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
3142 0 : GetPurchasedAir(state);
3143 0 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
3144 : }
3145 49 : return state.dataPurchasedAirMgr->PurchAir(PurchAirNum).OutdoorAirMassFlowRate;
3146 : }
3147 :
3148 49 : int GetPurchasedAirZoneInletAirNode(EnergyPlusData &state, int const PurchAirNum)
3149 : {
3150 :
3151 : // FUNCTION INFORMATION:
3152 : // AUTHOR B Griffith
3153 : // DATE WRITTEN Dec 2006
3154 : // MODIFIED Adapted for purchased air by M.J. Witte, Oct 2013
3155 : // RE-ENGINEERED na
3156 :
3157 : // PURPOSE OF THIS FUNCTION:
3158 : // lookup function for zone inlet node for ventilation rate reporting
3159 :
3160 49 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
3161 0 : GetPurchasedAir(state);
3162 0 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
3163 : }
3164 :
3165 49 : int GetPurchasedAirZoneInletAirNode = 0;
3166 49 : if (PurchAirNum > 0 && PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir) {
3167 49 : GetPurchasedAirZoneInletAirNode = state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ZoneSupplyAirNodeNum;
3168 : }
3169 :
3170 49 : return GetPurchasedAirZoneInletAirNode;
3171 : }
3172 :
3173 49 : int GetPurchasedAirReturnAirNode(EnergyPlusData &state, int const PurchAirNum)
3174 : {
3175 :
3176 : // FUNCTION INFORMATION:
3177 : // AUTHOR B Griffith
3178 : // DATE WRITTEN Dec 2006
3179 : // MODIFIED Adapted for purchased air by M.J. Witte, Oct 2013
3180 : // RE-ENGINEERED na
3181 :
3182 : // PURPOSE OF THIS FUNCTION:
3183 : // lookup function for recirculation air node for ventilation rate reporting
3184 :
3185 49 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
3186 0 : GetPurchasedAir(state);
3187 0 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
3188 : }
3189 :
3190 49 : int GetPurchasedAirReturnAirNode = 0;
3191 49 : if (PurchAirNum > 0 && PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir) {
3192 49 : GetPurchasedAirReturnAirNode = state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ZoneRecircAirNodeNum;
3193 : }
3194 :
3195 49 : return GetPurchasedAirReturnAirNode;
3196 : }
3197 :
3198 3 : int getPurchasedAirIndex(EnergyPlusData &state, std::string_view PurchAirName)
3199 : {
3200 3 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
3201 0 : GetPurchasedAir(state);
3202 0 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
3203 : }
3204 :
3205 3 : for (int PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
3206 3 : if (Util::SameString(state.dataPurchasedAirMgr->PurchAir(PurchAirNum).Name, PurchAirName)) {
3207 3 : return PurchAirNum;
3208 : }
3209 : }
3210 :
3211 0 : return 0;
3212 : }
3213 :
3214 49 : Real64 GetPurchasedAirMixedAirTemp(EnergyPlusData &state, int const PurchAirNum)
3215 : {
3216 :
3217 : // FUNCTION INFORMATION:
3218 : // AUTHOR B Griffith
3219 : // DATE WRITTEN Dec 2006
3220 : // MODIFIED Adapted for purchased air by M.J. Witte, Oct 2013
3221 : // RE-ENGINEERED na
3222 :
3223 : // PURPOSE OF THIS FUNCTION:
3224 : // lookup function for mixed air Temp for ventilation rate reporting
3225 :
3226 : // METHODOLOGY EMPLOYED:
3227 : // most analogous functions look up an outside air node but this function
3228 : // gets the actual mass flow of outdoor air, following the features of the model
3229 :
3230 49 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
3231 0 : GetPurchasedAir(state);
3232 0 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
3233 : }
3234 :
3235 49 : return state.dataPurchasedAirMgr->PurchAir(PurchAirNum).MixedAirTemp;
3236 : }
3237 :
3238 49 : Real64 GetPurchasedAirMixedAirHumRat(EnergyPlusData &state, int const PurchAirNum)
3239 : {
3240 :
3241 : // FUNCTION INFORMATION:
3242 : // AUTHOR B Griffith
3243 : // DATE WRITTEN Dec 2006
3244 : // MODIFIED Adapted for purchased air by M.J. Witte, Oct 2013
3245 : // RE-ENGINEERED na
3246 :
3247 : // PURPOSE OF THIS FUNCTION:
3248 : // lookup function for mixed air HumRat for ventilation rate reporting
3249 :
3250 : // METHODOLOGY EMPLOYED:
3251 : // most analogous functions look up an outside air node but this function
3252 : // gets the actual mass flow of outdoor air, following the features of the model
3253 :
3254 49 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
3255 0 : GetPurchasedAir(state);
3256 0 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
3257 : }
3258 :
3259 49 : return state.dataPurchasedAirMgr->PurchAir(PurchAirNum).MixedAirHumRat;
3260 : }
3261 :
3262 6 : bool CheckPurchasedAirForReturnPlenum(EnergyPlusData &state, int const ReturnPlenumIndex)
3263 : {
3264 :
3265 : // FUNCTION INFORMATION:
3266 : // AUTHOR R Raustad
3267 : // DATE WRITTEN July 2017
3268 :
3269 : // PURPOSE OF THIS FUNCTION:
3270 : // lookup function to check if return plenum is used
3271 :
3272 : // Return value
3273 : bool CheckPurchasedAirForReturnPlenum;
3274 :
3275 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
3276 : int PurchAirNum;
3277 :
3278 6 : if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
3279 3 : GetPurchasedAir(state);
3280 3 : state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
3281 : }
3282 :
3283 6 : CheckPurchasedAirForReturnPlenum = false;
3284 6 : for (PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
3285 0 : if (ReturnPlenumIndex != state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ReturnPlenumIndex) {
3286 0 : continue;
3287 : }
3288 0 : CheckPurchasedAirForReturnPlenum = true;
3289 : }
3290 :
3291 6 : return CheckPurchasedAirForReturnPlenum;
3292 : }
3293 :
3294 1 : void InitializePlenumArrays(EnergyPlusData &state, int const PurchAirNum)
3295 : {
3296 : // FUNCTION INFORMATION:
3297 : // AUTHOR R Raustad
3298 : // DATE WRITTEN July 2017
3299 :
3300 : // PURPOSE OF THIS FUNCTION:
3301 : // to initialize arrays needed to manage ideal load air system used with return plenums
3302 : //
3303 : // Example:
3304 : // NumPlenumArrays = 2 (same as there are two ZoneHVAC:ReturnPlenums objects connected to two or more ideal loads air systems
3305 : // In this example ideal loads air system #4 is not connected to a zone return plenum
3306 : //
3307 : // ZoneHVAC:ReturnPlenum( 1 ) = ReturnPlenum1 is not connected to any ideal loads air systems
3308 : // ZoneHVAC:ReturnPlenum( 2 ) = ReturnPlenum2 is connected to PurchAirPlenumArrays( 1 )
3309 : // ZoneHVAC:ReturnPlenum( 3 ) = ReturnPlenum3 is connected to PurchAirPlenumArrays( 2 )
3310 : //
3311 : // PurchAirPlenumArrays( 1 )
3312 : // PurchAirPlenumArrays( 1 ).NumPurchAir = 2, there are 2 ideal loads air systems connected to this plenum
3313 : // PurchAirPlenumArrays( 1 ).PurchAirArray( 1 ) = 1, ideal loads air system #1 is attached to this plenum
3314 : // PurchAirPlenumArrays( 1 ).PurchAirArray( 2 ) = 3, ideal loads air system #3 is attached to this plenum
3315 : // PurchAirPlenumArrays( 1 ).IsSimulated( 1 ) = true, ideal loads air system #1 has been simulated this iteration
3316 : // PurchAirPlenumArrays( 1 ).IsSimulated( 2 ) = false, ideal loads air system #3 has not yet been simulated this iteration
3317 : //
3318 : // Ideal loads air systems keep track of which plenum they are connected to
3319 : // PurchAir( 1 ).PlenumArrayIndex = 1
3320 : // PurchAir( 1 ).ReturnPlenumName = ReturnPlenum2;
3321 : // PurchAir( 3 ).PlenumArrayIndex = 1
3322 : // PurchAir( 3 ).ReturnPlenumName = ReturnPlenum2;
3323 : //
3324 : // The ideal loads air systems also keep track of which item they are in the int and bool arrays
3325 : // PurchAir( 1 ).PurchAirArrayIndex = 1
3326 : // PurchAir( 3 ).PurchAirArrayIndex = 2
3327 : //
3328 : // PurchAirPlenumArrays( 2 )
3329 : // PurchAirPlenumArrays( 2 ).NumPurchAir = 3, there are 3 ideal loads air systems connected to this plenum
3330 : // PurchAirPlenumArrays( 2 ).PurchAirArray( 1 ) = 2, ideal loads air system #2 is attached to this plenum
3331 : // PurchAirPlenumArrays( 2 ).PurchAirArray( 2 ) = 5, ideal loads air system #5 is attached to this plenum
3332 : // PurchAirPlenumArrays( 2 ).PurchAirArray( 3 ) = 6, ideal loads air system #6 is attached to this plenum
3333 : // PurchAirPlenumArrays( 2 ).IsSimulated( 1 ) = true, ideal loads air system #4 has been simulated this iteration
3334 : // PurchAirPlenumArrays( 2 ).IsSimulated( 2 ) = false, ideal loads air system #5 has not yet been simulated this iteration
3335 : // PurchAirPlenumArrays( 2 ).IsSimulated( 3 ) = false, ideal loads air system #6 has not yet been simulated this iteration
3336 : //
3337 : // Ideal loads air systems keep track of which plenum they are connected to
3338 : // PurchAir( 2 ).PlenumArrayIndex = 2;
3339 : // PurchAir( 2 ).ReturnPlenumName = ReturnPlenum3;
3340 : // PurchAir( 5 ).PlenumArrayIndex = 2;
3341 : // PurchAir( 5 ).ReturnPlenumName = ReturnPlenum3;
3342 : // PurchAir( 6 ).PlenumArrayIndex = 2;
3343 : // PurchAir( 6 ).ReturnPlenumName = ReturnPlenum3;
3344 : //
3345 : // The ideal loads air systems also keep track of which item they are in the int and bool arrays
3346 : // PurchAir( 2 ).PurchAirArrayIndex = 1;
3347 : // PurchAir( 5 ).PurchAirArrayIndex = 2;
3348 : // PurchAir( 6 ).PurchAirArrayIndex = 3;
3349 : //
3350 : // Given these connections, the data in the IsSimulated array can be set (or checked) according to this syntax:
3351 : //
3352 : // Each time an ideal loads air system is simulated the IsSimulated flag is set to true
3353 : // PurchAirPlenumArrays( PurchAir( PurchNum ).PlenumArrayIndex ).IsSimulated( PurchAir( PurchNum ).PurchAirArrayIndex ) = true;
3354 : //
3355 : // if all ideal loads air systems connected to the same plenum have been simulated, simulate the zone air return plenum (once per set of
3356 : // ideal loads systems) if ( all( PurchAirPlenumArrays( PurchAir( PurchAirNum ).ReturnPlenumIndex ).IsSimulated ) ) {
3357 : // SimAirZonePlenum( PurchAir( PurchAirNum ).ReturnPlenumName, DataZoneEquipment::ZoneReturnPlenum_Type, PurchAir( PurchAirNum
3358 : // ).ReturnPlenumIndex, FirstHVACIteration, FirstCall, SupPathInletChanged ); reset all IsSimulated flags for next iteration
3359 : // PurchAirPlenumArrays( PurchAir( PurchAirNum ).ReturnPlenumIndex ).IsSimulated = false;
3360 : // }
3361 :
3362 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
3363 : int ReturnPlenumIndex; // index to ZoneHVAC:ReturnPlenum object
3364 : bool PlenumNotFound; // logical to determine if same plenum is used by other ideal loads air systems
3365 1 : Array1D_int TempPurchArray; // temporary array used for dynamic allocation
3366 1 : Array1D_bool TempIsSimulated; // temporary array used for dynamic allocation
3367 :
3368 : // index to ZoneHVAC:ReturnPlenum object
3369 1 : ReturnPlenumIndex = state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ReturnPlenumIndex;
3370 1 : PlenumNotFound = true;
3371 :
3372 : // if first time through, set up arrays
3373 1 : if (!state.dataPurchasedAirMgr->PurchAirPlenumArrays.allocated()) {
3374 :
3375 : // the ideal loads air system keeps track of which item this system is in a list
3376 1 : state.dataPurchasedAirMgr->PurchAir(PurchAirNum).PurchAirArrayIndex = 1;
3377 : // keep track of how many arrays (i.e., how many different plenums are attached to different ideal loads air systems
3378 1 : state.dataPurchasedAirMgr->NumPlenumArrays = 1;
3379 :
3380 : // allocate new array
3381 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays.allocate(state.dataPurchasedAirMgr->NumPlenumArrays);
3382 : // set counter for how many ideal loads air systems are attached to this plenum
3383 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).NumPurchAir =
3384 : 1; // keeps track of how many ideal loads air system are connected to this return plenum
3385 : // keep track of which plenum this is ( i.e., PurchAirPlenumArrays(1) is ZoneHVAC:ReturnPlenum #4 )
3386 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).ReturnPlenumIndex =
3387 : ReturnPlenumIndex; // stores index of return plenum (e.g., 4 of 5)
3388 : // allocate array holding index to one or more ideal loads air systems
3389 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray.allocate(1);
3390 : // allocate boolean to keep track of whether or not this ideal loads air system has been simulated
3391 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated.allocate(1);
3392 : // save the data
3393 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray(1) = PurchAirNum;
3394 1 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated(1) = false;
3395 :
3396 : } else {
3397 :
3398 : // find the correct index to PurchAirPlenumArrays
3399 0 : for (int ReturnPlenumNum = 1; ReturnPlenumNum <= state.dataPurchasedAirMgr->NumPlenumArrays; ++ReturnPlenumNum) {
3400 0 : if (ReturnPlenumIndex != state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).ReturnPlenumIndex) {
3401 0 : continue;
3402 : }
3403 :
3404 : // allocate temporary arrays and save existing data
3405 0 : TempPurchArray.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
3406 0 : TempIsSimulated.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
3407 : // these are the member arrays in an existing PurchAirPlenumArrays
3408 0 : TempPurchArray = state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).PurchAirArray;
3409 0 : TempIsSimulated = state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).IsSimulated;
3410 :
3411 : // if this array has been used before, we need to increase member array space to save new PurchAir data
3412 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir += 1;
3413 : // save the location of this ideal loads air system in the member arrays
3414 0 : state.dataPurchasedAirMgr->PurchAir(PurchAirNum).PurchAirArrayIndex =
3415 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir;
3416 :
3417 : // allocate more space, this will wipe out data previously stored
3418 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
3419 0 : .PurchAirArray.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
3420 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
3421 0 : .IsSimulated.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
3422 :
3423 : // re-initialize previous data
3424 0 : for (int Loop = 1; Loop < state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir; ++Loop) {
3425 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).PurchAirArray(Loop) = TempPurchArray(Loop);
3426 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).IsSimulated(Loop) = TempIsSimulated(Loop);
3427 : }
3428 : // delete temporary array
3429 0 : TempPurchArray.deallocate();
3430 0 : TempIsSimulated.deallocate();
3431 :
3432 : // save new data in expanded member array
3433 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
3434 0 : .PurchAirArray(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir) = PurchAirNum;
3435 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
3436 0 : .IsSimulated(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir) = false;
3437 :
3438 0 : PlenumNotFound = false;
3439 0 : break;
3440 : }
3441 :
3442 0 : if (PlenumNotFound) {
3443 :
3444 : // need to allocate additional space for new plenum array
3445 : // keep track of how many arrays (i.e., how many different plenums are attached to different ideal loads air systems)
3446 0 : state.dataPurchasedAirMgr->NumPlenumArrays += 1;
3447 :
3448 : // allocate temporary array and save existing data
3449 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays.allocate(state.dataPurchasedAirMgr->NumPlenumArrays);
3450 0 : for (int Loop = 1; Loop < state.dataPurchasedAirMgr->NumPlenumArrays; ++Loop) {
3451 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).NumPurchAir =
3452 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir;
3453 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).ReturnPlenumIndex =
3454 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).ReturnPlenumIndex;
3455 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).PurchAirArray.allocate(
3456 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir);
3457 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).IsSimulated.allocate(
3458 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir);
3459 0 : for (int Loop2 = 1; Loop2 <= state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir; ++Loop2) {
3460 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).PurchAirArray(Loop2) =
3461 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).PurchAirArray(Loop2);
3462 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).IsSimulated(Loop2) =
3463 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).IsSimulated(Loop2);
3464 : }
3465 : }
3466 :
3467 : // delete primary array (probably could just re-allocate, but this is only done a few times per simulation)
3468 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays.deallocate();
3469 : // reallocate to new size
3470 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays.allocate(state.dataPurchasedAirMgr->NumPlenumArrays);
3471 :
3472 : // allocate member arrays to same size as before
3473 0 : for (int Loop = 1; Loop < state.dataPurchasedAirMgr->NumPlenumArrays; ++Loop) {
3474 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).PurchAirArray.allocate(
3475 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).NumPurchAir);
3476 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).IsSimulated.allocate(
3477 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).NumPurchAir);
3478 : }
3479 :
3480 : // save the data
3481 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays = state.dataPurchasedAirMgr->TempPurchAirPlenumArrays;
3482 : // delete temporary data
3483 0 : state.dataPurchasedAirMgr->TempPurchAirPlenumArrays.deallocate();
3484 :
3485 : // save the index to where this ideal loads air system data is stored
3486 0 : state.dataPurchasedAirMgr->PurchAir(PurchAirNum).PurchAirArrayIndex = 1;
3487 : // save the number of ideal loads air systems stored in these arrays
3488 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).NumPurchAir = 1;
3489 : // save the index the the ZoneHVAC:ReturnPlenum
3490 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).ReturnPlenumIndex = ReturnPlenumIndex;
3491 : // allocate member array and store data
3492 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray.allocate(1);
3493 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray(1) = PurchAirNum;
3494 : // allocate member array and store data
3495 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated.allocate(1);
3496 0 : state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated(1) = false;
3497 : }
3498 : }
3499 1 : }
3500 :
3501 : } // namespace EnergyPlus::PurchasedAirManager
|