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