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 : #include <string>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Fmath.hh>
55 :
56 : // EnergyPlus Headers
57 : #include <EnergyPlus/Autosizing/Base.hh>
58 : #include <EnergyPlus/BranchNodeConnections.hh>
59 : #include <EnergyPlus/CurveManager.hh>
60 : #include <EnergyPlus/Data/EnergyPlusData.hh>
61 : #include <EnergyPlus/DataContaminantBalance.hh>
62 : #include <EnergyPlus/DataDefineEquip.hh>
63 : #include <EnergyPlus/DataEnvironment.hh>
64 : #include <EnergyPlus/DataHVACGlobals.hh>
65 : #include <EnergyPlus/DataHeatBalance.hh>
66 : #include <EnergyPlus/DataIPShortCuts.hh>
67 : #include <EnergyPlus/DataLoopNode.hh>
68 : #include <EnergyPlus/DataSizing.hh>
69 : #include <EnergyPlus/DataZoneControls.hh>
70 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
71 : #include <EnergyPlus/DataZoneEquipment.hh>
72 : #include <EnergyPlus/DesiccantDehumidifiers.hh>
73 : #include <EnergyPlus/EMSManager.hh>
74 : #include <EnergyPlus/EvaporativeCoolers.hh>
75 : #include <EnergyPlus/Fans.hh>
76 : #include <EnergyPlus/FaultsManager.hh>
77 : #include <EnergyPlus/General.hh>
78 : #include <EnergyPlus/GeneralRoutines.hh>
79 : #include <EnergyPlus/GlobalNames.hh>
80 : #include <EnergyPlus/HVACControllers.hh>
81 : #include <EnergyPlus/HVACDXHeatPumpSystem.hh>
82 : #include <EnergyPlus/HVACHXAssistedCoolingCoil.hh>
83 : #include <EnergyPlus/HVACVariableRefrigerantFlow.hh>
84 : #include <EnergyPlus/HeatRecovery.hh>
85 : #include <EnergyPlus/HeatingCoils.hh>
86 : #include <EnergyPlus/Humidifiers.hh>
87 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
88 : #include <EnergyPlus/MixedAir.hh>
89 : #include <EnergyPlus/NodeInputManager.hh>
90 : #include <EnergyPlus/OutAirNodeManager.hh>
91 : #include <EnergyPlus/OutputProcessor.hh>
92 : #include <EnergyPlus/OutputReportPredefined.hh>
93 : #include <EnergyPlus/PhotovoltaicThermalCollectors.hh>
94 : #include <EnergyPlus/Psychrometrics.hh>
95 : #include <EnergyPlus/ScheduleManager.hh>
96 : #include <EnergyPlus/SetPointManager.hh>
97 : #include <EnergyPlus/SteamCoils.hh>
98 : #include <EnergyPlus/TranspiredCollector.hh>
99 : #include <EnergyPlus/UnitarySystem.hh>
100 : #include <EnergyPlus/UserDefinedComponents.hh>
101 : #include <EnergyPlus/UtilityRoutines.hh>
102 : #include <EnergyPlus/WaterCoils.hh>
103 :
104 : namespace EnergyPlus::MixedAir {
105 :
106 : // Module containing the routines dealing with the mixed air portion
107 : // of the HVAC air loop.
108 :
109 : // MODULE INFORMATION:
110 : // AUTHOR Fred Buhl
111 : // DATE WRITTEN October 1998
112 : // MODIFIED Shirey/Raustad FSEC, June/Aug 2003, Jan 2004
113 : // Lawrie, March 2006 - Module order (per template)
114 : // Craig Wray 22Aug2010 - Added Fan ComponentModel
115 : // Chandan Sharma, FSEC, 25Aug 2011 - Added ProportionalControl
116 : // to enhance CO2 based DCV control
117 : // Feb 2013 Bereket Nigusse, FSEC
118 : // Added DX Coil Model For 100% OA systems
119 : // RE-ENGINEERED na
120 :
121 : // PURPOSE OF THIS MODULE:
122 : // To encapsulate the data and algorithms required to
123 : // simulate the mixed air portion of the EPlus air loop.
124 :
125 : // METHODOLOGY EMPLOYED:
126 : // An algorithmic controller will be employed - there is no attempt to
127 : // simulate real controllers for the economizer. The mixed air controller
128 : // will sense various node conditions and set some node flow rates. Mixed
129 : // air components will operate with predetermined flow rates.
130 :
131 : // Using/Aliasing
132 : using namespace DataLoopNode;
133 : using namespace DataAirLoop;
134 : using namespace DataEnvironment;
135 : using namespace DataSizing;
136 : using namespace FaultsManager;
137 :
138 : constexpr std::array<std::string_view, static_cast<int>(ControllerKind::Num)> ControllerKindNamesUC{"CONTROLLER:WATERCOIL", "CONTROLLER:OUTDOORAIR"};
139 :
140 : constexpr std::array<std::string_view, static_cast<int>(MixedAirControllerType::Num)> MixedAirControllerTypeNames{
141 : "Controller:OutdoorAir", "ZoneHVAC:EnergyRecoveryVentilator:Controller"};
142 :
143 : constexpr std::array<std::string_view, static_cast<int>(CMO::Num)> CurrentModuleObjects{"None",
144 : "AirLoopHVAC:OutdoorAirSystem",
145 : "AirLoopHVAC:OutdoorAirSystem:EquipmentList",
146 : "AirLoopHVAC:ControllerList",
147 : "AvailabilityManagerAssignmentList",
148 : "Controller:OutdoorAir",
149 : "ZoneHVAC:EnergyRecoveryVentilator:Controller",
150 : "Controller:MechanicalVentilation",
151 : "OutdoorAir:Mixer"};
152 :
153 : constexpr std::array<std::string_view, static_cast<int>(DataSizing::SysOAMethod::Num)> SOAMNamesUC{"ZONESUM",
154 : "STANDARD62.1VENTILATIONRATEPROCEDURE",
155 : "INDOORAIRQUALITYPROCEDURE",
156 : "PROPORTIONALCONTROLBASEDONOCCUPANCYSCHEDULE",
157 : "INDOORAIRQUALITYPROCEDUREGENERICCONTAMINANT",
158 : "INDOORAIRQUALITYPROCEDURECOMBINED",
159 : "PROPORTIONALCONTROLBASEDONDESIGNOCCUPANCY",
160 : "PROPORTIONALCONTROLBASEDONDESIGNOARATE",
161 : "STANDARD62.1SIMPLIFIEDPROCEDURE",
162 : "STANDARD62.1VENTILATIONRATEPROCEDUREWITHLIMIT"};
163 :
164 : constexpr std::array<std::string_view, static_cast<int>(SimAirServingZones::CompType::Num)> CompTypeNamesUC{
165 : "OUTDOORAIR:MIXER",
166 : "FAN:CONSTANTVOLUME",
167 : "FAN:VARIABLEVOLUME",
168 : "COIL:COOLING:WATER",
169 : "COIL:HEATING:WATER",
170 : "COIL:HEATING:STEAM",
171 : "COIL:COOLING:WATER:DETAILEDGEOMETRY",
172 : "COIL:HEATING:ELECTRIC",
173 : "COIL:HEATING:FUEL",
174 : "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED",
175 : "COIL:HEATING:DESUPERHEATER",
176 : "COILSYSTEM:COOLING:DX",
177 : "HEATEXCHANGER:AIRTOAIR:FLATPLATE",
178 : "DEHUMIDIFIER:DESICCANT:NOFANS",
179 : "SOLARCOLLECTOR:UNGLAZEDTRANSPIRED",
180 : "EVAPORATIVECOOLER:DIRECT:CELDEKPAD",
181 : "AIRLOOPHVAC:UNITARY:FURNACE:HEATONLY",
182 : "AIRLOOPHVAC:UNITARY:FURNACE:HEATCOOL",
183 : "HUMIDIFIER:STEAM:ELECTRIC",
184 : "DUCT",
185 : "AIRLOOPHVAC:UNITARYHEATCOOL:VAVCHANGEOVERBYPASS",
186 : "AIRLOOPHVAC:UNITARYHEATPUMP:AIRTOAIR:MULTISPEED",
187 : "FAN:COMPONENTMODEL",
188 : "COILSYSTEM:HEATING:DX",
189 : "COIL:USERDEFINED",
190 : "FAN:SYSTEMMODEL",
191 : "AIRLOOPHVAC:UNITARYSYSTEM",
192 : "ZONEHVAC:TERMINALUNIT:VARIABLEREFRIGERANTFLOW",
193 : "SOLARCOLLECTOR:FLATPLATE:PHOTOVOLTAICTHERMAL",
194 : "COILSYSTEM:COOLING:WATER"};
195 :
196 : static constexpr std::array<std::string_view, static_cast<int>(DataSizing::SysOAMethod::Num)> printSysOAMethod{
197 : "ZoneSum,",
198 : "Standard62.1VentilationRateProcedure,",
199 : "IndoorAirQualityProcedure,",
200 : "ProportionalControlBasedOnOccupancySchedule,",
201 : "IndoorAirQualityGenericContaminant,",
202 : "IndoorAirQualityProcedureCombined,",
203 : "ProportionalControlBasedOnDesignOccupancy,",
204 : "ProportionalControlBasedOnDesignOARate,",
205 : "Standard62.1SimplifiedProcedure,",
206 : "Standard62.1VentilationRateProcedureWithLimit,"};
207 :
208 977 : Real64 OAGetFlowRate(EnergyPlusData &state, int OAPtr)
209 : {
210 977 : Real64 FlowRate(0);
211 977 : if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers) && (state.dataEnvrn->StdRhoAir != 0)) {
212 977 : FlowRate = state.dataMixedAir->OAController(OAPtr).OAMassFlow / state.dataEnvrn->StdRhoAir;
213 : }
214 977 : return FlowRate;
215 : }
216 0 : Real64 OAGetMinFlowRate(EnergyPlusData &state, int OAPtr)
217 : {
218 0 : Real64 MinFlowRate(0);
219 0 : if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers)) {
220 0 : MinFlowRate = state.dataMixedAir->OAController(OAPtr).MinOA;
221 : }
222 0 : return MinFlowRate;
223 : }
224 84 : void OASetDemandManagerVentilationState(EnergyPlusData &state, int OAPtr, bool aState)
225 : {
226 84 : if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers)) {
227 84 : state.dataMixedAir->OAController(OAPtr).ManageDemand = aState;
228 : }
229 84 : }
230 42 : void OASetDemandManagerVentilationFlow(EnergyPlusData &state, int OAPtr, Real64 aFlow)
231 : {
232 42 : if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers)) {
233 42 : state.dataMixedAir->OAController(OAPtr).DemandLimitFlowRate = aFlow * state.dataEnvrn->StdRhoAir;
234 : }
235 42 : }
236 1077 : int GetOAController(EnergyPlusData &state, std::string const &OAName)
237 : {
238 1077 : int CurrentOAController(0);
239 13271 : for (int i = 1; i <= state.dataMixedAir->NumOAControllers; i++) {
240 13271 : if (OAName == state.dataMixedAir->OAController(i).Name) {
241 1077 : CurrentOAController = i;
242 1077 : break;
243 : }
244 : }
245 1077 : return CurrentOAController;
246 : }
247 :
248 27043260 : void ManageOutsideAirSystem(EnergyPlusData &state, std::string const &OASysName, bool const FirstHVACIteration, int const AirLoopNum, int &OASysNum)
249 : {
250 :
251 : // SUBROUTINE INFORMATION:
252 : // AUTHOR Fred Buhl
253 : // DATE WRITTEN Oct 1998
254 :
255 : // PURPOSE OF THIS SUBROUTINE
256 : // Manage the outside air system
257 :
258 27043260 : if (state.dataMixedAir->GetOASysInputFlag) {
259 0 : GetOutsideAirSysInputs(state);
260 0 : state.dataMixedAir->GetOASysInputFlag = false;
261 : }
262 :
263 27043260 : if (OASysNum == 0) {
264 1067 : OASysNum = Util::FindItemInList(OASysName, state.dataAirLoop->OutsideAirSys);
265 1067 : if (OASysNum == 0) {
266 0 : ShowFatalError(state, format("ManageOutsideAirSystem: AirLoopHVAC:OutdoorAirSystem not found={}", OASysName));
267 : }
268 : }
269 :
270 27043260 : InitOutsideAirSys(state, OASysNum, AirLoopNum);
271 :
272 27043260 : SimOutsideAirSys(state, OASysNum, FirstHVACIteration, AirLoopNum);
273 27043260 : }
274 :
275 27118012 : void SimOASysComponents(EnergyPlusData &state, int const OASysNum, bool const FirstHVACIteration, int const AirLoopNum)
276 : {
277 27118012 : auto &CompType = state.dataMixedAir->CompType;
278 27118012 : auto &CompName = state.dataMixedAir->CompName;
279 27118012 : bool ReSim(false);
280 27118012 : bool Sim(true);
281 27118012 : bool OAHeatCoil(false);
282 27118012 : bool OACoolCoil(false);
283 27118012 : bool OAHX(false);
284 :
285 60050076 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents; ++CompNum) {
286 32932064 : CompType = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(CompNum);
287 32932064 : CompName = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum);
288 131728256 : SimOAComponent(state,
289 : CompType,
290 : CompName,
291 32932064 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum),
292 : FirstHVACIteration,
293 32932064 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentIndex(CompNum),
294 : AirLoopNum,
295 : Sim,
296 : OASysNum,
297 : OAHeatCoil,
298 : OACoolCoil,
299 : OAHX);
300 32932064 : if (OAHX) {
301 668052 : ReSim = true;
302 : }
303 : }
304 : // if there were heat exchangers and/or desiccant wheel in the OA path, need to simulate again in reverse
305 : // order to propagate the air flow and conditions out the relief air path to the relief air exit node
306 27118012 : if (ReSim) {
307 1275022 : for (int CompNum = state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents - 1; CompNum >= 1; --CompNum) {
308 692864 : CompType = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(CompNum);
309 692864 : CompName = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum);
310 2771456 : SimOAComponent(state,
311 : CompType,
312 : CompName,
313 692864 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum),
314 : FirstHVACIteration,
315 692864 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentIndex(CompNum),
316 : AirLoopNum,
317 : Sim,
318 : OASysNum,
319 : OAHeatCoil,
320 : OACoolCoil,
321 : OAHX);
322 : }
323 : // now simulate again propagate current temps back through OA system
324 1857180 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents; ++CompNum) {
325 1275022 : CompType = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(CompNum);
326 1275022 : CompName = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum);
327 5100088 : SimOAComponent(state,
328 : CompType,
329 : CompName,
330 1275022 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum),
331 : FirstHVACIteration,
332 1275022 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentIndex(CompNum),
333 : AirLoopNum,
334 : Sim,
335 : OASysNum,
336 : OAHeatCoil,
337 : OACoolCoil,
338 : OAHX);
339 : }
340 : }
341 27118012 : }
342 :
343 27043260 : void SimOutsideAirSys(EnergyPlusData &state, int const OASysNum, bool const FirstHVACIteration, int const AirLoopNum)
344 : {
345 :
346 : // SUBROUTINE INFORMATION:
347 : // AUTHOR Fred Buhl
348 : // DATE WRITTEN Oct 1998
349 :
350 : // PURPOSE OF THIS SUBROUTINE
351 : // Simulate the controllers and components in the outside air system.
352 :
353 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
354 27043260 : state.dataSize->CurOASysNum = OASysNum;
355 27043260 : auto &CurrentOASystem(state.dataAirLoop->OutsideAirSys(OASysNum));
356 27043260 : if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum == -1) {
357 27032378 : SimOAController(state, CurrentOASystem.OAControllerName, CurrentOASystem.OAControllerIndex, FirstHVACIteration, AirLoopNum);
358 : }
359 27043260 : SimOASysComponents(state, OASysNum, FirstHVACIteration, AirLoopNum);
360 :
361 27043260 : if (state.dataMixedAir->MyOneTimeErrorFlag(OASysNum)) {
362 1068 : bool FatalErrorFlag(false);
363 1068 : if (CurrentOASystem.NumControllers - CurrentOASystem.NumSimpleControllers > 1) {
364 0 : ShowWarningError(
365 : state,
366 0 : format("AirLoopHVAC:OutdoorAirSystem {} has more than 1 outside air controller; only the 1st will be used", CurrentOASystem.Name));
367 : }
368 2273 : for (int CompNum = 1; CompNum <= CurrentOASystem.NumComponents; ++CompNum) {
369 1205 : auto &CompType = CurrentOASystem.ComponentType(CompNum);
370 1205 : auto &CompName = CurrentOASystem.ComponentName(CompNum);
371 1205 : if (Util::SameString(CompType, "OutdoorAir:Mixer")) {
372 1067 : int OAMixerNum = Util::FindItemInList(CompName, state.dataMixedAir->OAMixer);
373 1067 : int OAControllerNum = CurrentOASystem.OAControllerIndex;
374 1067 : if (state.dataMixedAir->OAController(OAControllerNum).MixNode != state.dataMixedAir->OAMixer(OAMixerNum).MixNode) {
375 0 : ShowSevereError(
376 0 : state, format("The mixed air node of Controller:OutdoorAir=\"{}\"", state.dataMixedAir->OAController(OAControllerNum).Name));
377 0 : ShowContinueError(state,
378 0 : format("should be the same node as the mixed air node of OutdoorAir:Mixer=\"{}\".",
379 0 : state.dataMixedAir->OAMixer(OAMixerNum).Name));
380 0 : ShowContinueError(state,
381 0 : format("Controller:OutdoorAir mixed air node=\"{}\".",
382 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAController(OAControllerNum).MixNode)));
383 0 : ShowContinueError(state,
384 0 : format("OutdoorAir:Mixer mixed air node=\"{}\".",
385 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OAMixerNum).MixNode)));
386 0 : FatalErrorFlag = true;
387 : }
388 1067 : if (state.dataMixedAir->OAController(OAControllerNum).RelNode != state.dataMixedAir->OAMixer(OAMixerNum).RelNode) {
389 0 : ShowSevereError(
390 0 : state, format("The relief air node of Controller:OutdoorAir=\"{}\"", state.dataMixedAir->OAController(OAControllerNum).Name));
391 0 : ShowContinueError(state,
392 0 : format("should be the same node as the relief air node of OutdoorAir:Mixer=\"{}\".",
393 0 : state.dataMixedAir->OAMixer(OAMixerNum).Name));
394 0 : ShowContinueError(state,
395 0 : format("Controller:OutdoorAir relief air node=\"{}\".",
396 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAController(OAControllerNum).RelNode)));
397 0 : ShowContinueError(state,
398 0 : format("OutdoorAir:Mixer relief air node=\"{}\".",
399 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OAMixerNum).RelNode)));
400 0 : FatalErrorFlag = true;
401 : }
402 1067 : if (state.dataMixedAir->OAController(OAControllerNum).RetNode != state.dataMixedAir->OAMixer(OAMixerNum).RetNode) {
403 0 : ShowSevereError(
404 0 : state, format("The return air node of Controller:OutdoorAir=\"{}\"", state.dataMixedAir->OAController(OAControllerNum).Name));
405 0 : ShowContinueError(state,
406 0 : format("should be the same node as the return air node of OutdoorAir:Mixer=\"{}\".",
407 0 : state.dataMixedAir->OAMixer(OAMixerNum).Name));
408 0 : ShowContinueError(state,
409 0 : format("Controller:OutdoorAir return air node=\"{}\".",
410 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAController(OAControllerNum).RetNode)));
411 0 : ShowContinueError(state,
412 0 : format("OutdoorAir:Mixer return air node=\"{}\".",
413 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OAMixerNum).RetNode)));
414 0 : FatalErrorFlag = true;
415 : }
416 : }
417 : }
418 1068 : state.dataMixedAir->MyOneTimeErrorFlag(OASysNum) = false;
419 1068 : if (FatalErrorFlag) {
420 0 : ShowFatalError(state, "Previous severe error(s) cause program termination");
421 : }
422 : }
423 :
424 27043260 : state.dataSize->CurOASysNum = 0;
425 27043260 : if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum == -1) {
426 27032378 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysComponentsSimulated = true;
427 : }
428 27043260 : }
429 :
430 34902364 : void SimOAComponent(EnergyPlusData &state,
431 : std::string const &CompType, // the component type
432 : std::string const &CompName, // the component Name
433 : SimAirServingZones::CompType const CompTypeNum, // Component Type -- Integerized for this module
434 : bool const FirstHVACIteration,
435 : int &CompIndex,
436 : int const AirLoopNum, // air loop index for economizer lockout coordination
437 : bool const Sim, // if TRUE, simulate component; if FALSE, just set the coil existence flags
438 : int const OASysNum, // index to outside air system
439 : bool &OAHeatingCoil, // TRUE indicates a heating coil has been found
440 : bool &OACoolingCoil, // TRUE indicates a cooling coil has been found
441 : bool &OAHX) // TRUE indicates a heat exchanger has been found
442 : {
443 :
444 : // SUBROUTINE INFORMATION
445 : // AUTHOR: Russ Taylor, Dan Fisher, Fred Buhl
446 : // DATE WRITTEN: Oct 1997
447 : // MODIFIED: Dec 1997 Fred Buhl, D Shirey Feb/Sept 2003
448 : // Nov 2004 M. J. Witte, GARD Analytics, Inc.
449 : // Add DXSystem:AirLoop as valid OA system equipment
450 : // Work supported by ASHRAE research project 1254-RP
451 :
452 : // PURPOSE OF THIS SUBROUTINE:
453 : // Calls the individual air loop component simulation routines
454 :
455 : // SUBROUTINE LOCAL VARIABLE DEFINITIONS
456 34902364 : OAHeatingCoil = false;
457 34902364 : OACoolingCoil = false;
458 34902364 : OAHX = false;
459 : HVAC::FanOp fanOp;
460 34902364 : Real64 sensOut = 0.0;
461 34902364 : int constexpr zoneOAUnitNum = -1;
462 34902364 : Real64 constexpr OAUCoilOutTemp = 0.0;
463 34902364 : bool constexpr ZoneEquipFlag = false;
464 34902364 : bool HeatingActive = false; // why isn't this returning that a coil is active?
465 34902364 : bool CoolingActive = false;
466 :
467 34902364 : switch (CompTypeNum) {
468 27691432 : case SimAirServingZones::CompType::OAMixer_Num: { // OutdoorAir:Mixer
469 27691432 : if (Sim) {
470 27689288 : SimOAMixer(state, CompName, CompIndex);
471 : }
472 27691432 : break;
473 : }
474 46356 : case SimAirServingZones::CompType::Fan_Simple_CV: // Fan:ConstantVolume
475 : case SimAirServingZones::CompType::Fan_Simple_VAV: // Fan:VariableVolume
476 : case SimAirServingZones::CompType::Fan_System_Object: // Fan:SystemModel
477 : case SimAirServingZones::CompType::Fan_ComponentModel: { // Fan:ComponentModel
478 46356 : if (Sim) {
479 46352 : if (CompIndex == 0) { // TODO: get rid of this stuff
480 2 : CompIndex = Fans::GetFanIndex(state, CompName);
481 2 : assert(CompIndex > 0);
482 : }
483 :
484 46352 : state.dataFans->fans(CompIndex)->simulate(state, FirstHVACIteration);
485 : }
486 46356 : } break;
487 1316240 : case SimAirServingZones::CompType::WaterCoil_Cooling: { // Coil:Cooling:Water
488 1316240 : if (Sim) {
489 : // get water coil and controller data if not called previously
490 1316182 : if (CompIndex == 0) {
491 30 : WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
492 : }
493 : // iterate on OA sys controller and water coil at the same time
494 1316182 : if (!state.dataWaterCoils->WaterCoil(CompIndex).heatRecoveryCoil) {
495 2600684 : SimAirServingZones::SolveWaterCoilController(state,
496 : FirstHVACIteration,
497 : AirLoopNum,
498 : CompName,
499 : CompIndex,
500 1300342 : state.dataWaterCoils->WaterCoil(CompIndex).ControllerName,
501 1300342 : state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex,
502 : false);
503 : // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
504 1300342 : state.dataHVACControllers->ControllerProps(state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex).BypassControllerCalc = true;
505 : } else {
506 15840 : WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
507 : }
508 : } else {
509 : // This is not working as intended ... don't want to include the HR coil in sizing.
510 : // But if the water coil is called to get this index, then the controller is called to set the
511 : // controller index and the simulation sizes the controller before the cooling coil.
512 : // Pushing this aspect forward to a follow up issue where the
513 : // controller index call is moved out of water coils getInput.
514 : // if (CompIndex == 0) {
515 : // bool errFound = false;
516 : // CompIndex = WaterCoils::GetWaterCoilIndex(state, CompType, CompName, errFound);
517 : // if (errFound) ShowFatalError(state, "SimOAComponent: Program terminates for preceding reason.");
518 : // }
519 : // if (!state.dataWaterCoils->WaterCoil(CompIndex).heatRecoveryCoil) OACoolingCoil = true;
520 : // should not include heat recovery coils in sizing since heat transfer at peak cooling is minimal.
521 58 : OACoolingCoil = true;
522 : }
523 1316240 : } break;
524 1327114 : case SimAirServingZones::CompType::WaterCoil_SimpleHeat: { // Coil:Heating:Water
525 1327114 : if (Sim) {
526 : // get water coil and controller data if not called previously
527 1327054 : if (CompIndex == 0) {
528 31 : WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
529 : }
530 : // iterate on OA sys controller and water coil at the same time
531 2654108 : SimAirServingZones::SolveWaterCoilController(state,
532 : FirstHVACIteration,
533 : AirLoopNum,
534 : CompName,
535 : CompIndex,
536 1327054 : state.dataWaterCoils->WaterCoil(CompIndex).ControllerName,
537 1327054 : state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex,
538 : false);
539 : // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
540 1327054 : state.dataHVACControllers->ControllerProps(state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex).BypassControllerCalc = true;
541 : }
542 1327114 : OAHeatingCoil = true;
543 1327114 : } break;
544 0 : case SimAirServingZones::CompType::SteamCoil_AirHeat: { // Coil:Heating:Steam
545 0 : if (Sim) {
546 0 : SteamCoils::SimulateSteamCoilComponents(state, CompName, FirstHVACIteration, CompIndex, 0.0);
547 : }
548 0 : OAHeatingCoil = true;
549 0 : } break;
550 0 : case SimAirServingZones::CompType::WaterCoil_DetailedCool: { // Coil:Cooling:Water:DetailedGeometry
551 0 : if (Sim) {
552 : // get water coil and controller data if not called previously
553 0 : if (CompIndex == 0) {
554 0 : WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
555 : }
556 : // iterate on OA sys controller and water coil at the same time
557 0 : SimAirServingZones::SolveWaterCoilController(state,
558 : FirstHVACIteration,
559 : AirLoopNum,
560 : CompName,
561 : CompIndex,
562 0 : state.dataWaterCoils->WaterCoil(CompIndex).ControllerName,
563 0 : state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex,
564 : false);
565 : // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
566 0 : state.dataHVACControllers->ControllerProps(state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex).BypassControllerCalc = true;
567 : }
568 0 : OACoolingCoil = true;
569 0 : } break;
570 26588 : case SimAirServingZones::CompType::Coil_ElectricHeat: // Coil:Heating:Electric
571 : case SimAirServingZones::CompType::Coil_GasHeat: { // Coil:Heating:Fuel
572 26588 : if (Sim) {
573 : // stand-alone coils are temperature controlled (do not pass QCoilReq in argument list, QCoilReq overrides temp SP)
574 26586 : HeatingCoils::SimulateHeatingCoilComponents(state, CompName, FirstHVACIteration, _, CompIndex);
575 : }
576 26588 : OAHeatingCoil = true;
577 26588 : } break;
578 0 : case SimAirServingZones::CompType::WaterCoil_CoolingHXAsst: { // CoilSystem:Cooling:Water:HeatExchangerAssisted
579 0 : if (Sim) {
580 : // get water coil and controller data if not called previously
581 0 : if (CompIndex == 0) {
582 0 : HVACHXAssistedCoolingCoil::SimHXAssistedCoolingCoil(
583 : state, CompName, FirstHVACIteration, HVAC::CompressorOp::On, 0.0, CompIndex, HVAC::FanOp::Continuous);
584 : }
585 : // iterate on OA sys controller and water coil at the same time
586 0 : SimAirServingZones::SolveWaterCoilController(state,
587 : FirstHVACIteration,
588 : AirLoopNum,
589 : CompName,
590 : CompIndex,
591 0 : state.dataHVACAssistedCC->HXAssistedCoil(CompIndex).ControllerName,
592 0 : state.dataHVACAssistedCC->HXAssistedCoil(CompIndex).ControllerIndex,
593 : true);
594 : // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
595 0 : state.dataHVACControllers->ControllerProps(state.dataHVACAssistedCC->HXAssistedCoil(CompIndex).ControllerIndex).BypassControllerCalc =
596 : true;
597 : }
598 0 : OACoolingCoil = true;
599 0 : } break;
600 19512 : case SimAirServingZones::CompType::DXSystem: // CoilSystem:Cooling:DX
601 : case SimAirServingZones::CompType::CoilSystemWater: // CoilSystem:Cooling:Water
602 : case SimAirServingZones::CompType::UnitarySystemModel: { // AirloopHVAC:UnitarySystem
603 19512 : if (Sim) {
604 19508 : Real64 latOut = 0.0; // does the air loop not need to know what the latent capacity is?
605 19508 : int compNum = CompIndex; // use local so return value of compNum from simulate call does not overwrite CompIndex
606 19508 : state.dataAirLoop->OutsideAirSys(OASysNum).compPointer[compNum]->simulate(state,
607 : CompName,
608 : FirstHVACIteration,
609 : AirLoopNum,
610 : compNum,
611 : HeatingActive,
612 : CoolingActive,
613 : zoneOAUnitNum,
614 : OAUCoilOutTemp,
615 : ZoneEquipFlag,
616 : sensOut,
617 : latOut);
618 : }
619 19512 : if (state.dataMixedAir->MyOneTimeCheckUnitarySysFlag(OASysNum) && CompTypeNum == SimAirServingZones::CompType::UnitarySystemModel) {
620 0 : UnitarySystems::UnitarySys::getUnitarySysHeatCoolCoil(state, CompName, OACoolingCoil, OAHeatingCoil, 0);
621 0 : UnitarySystems::UnitarySys::checkUnitarySysCoilInOASysExists(state, CompName, 0);
622 0 : if (Sim) {
623 0 : state.dataMixedAir->MyOneTimeCheckUnitarySysFlag(OASysNum) = false;
624 : }
625 : } else {
626 19512 : OACoolingCoil = true;
627 : }
628 19512 : } break;
629 0 : case SimAirServingZones::CompType::DXHeatPumpSystem: { // CoilSystem:IntegratedHeatPump:AirSource
630 0 : if (Sim) {
631 0 : HVACDXHeatPumpSystem::SimDXHeatPumpSystem(state, CompName, FirstHVACIteration, AirLoopNum, CompIndex);
632 : }
633 0 : OAHeatingCoil = true;
634 0 : } break;
635 26716 : case SimAirServingZones::CompType::CoilUserDefined: { // Coil:UserDefined
636 26716 : if (Sim) {
637 26712 : UserDefinedComponents::SimCoilUserDefined(state, CompName, CompIndex, AirLoopNum, OAHeatingCoil, OACoolingCoil);
638 : }
639 26716 : } break;
640 1746532 : case SimAirServingZones::CompType::HeatXchngr: {
641 : // HeatExchanger:AirToAir:FlatPlate, HeatExchanger:AirToAir:SensibleAndLatent, HeatExchanger:Desiccant:BalancedFlow
642 1746532 : if (Sim) {
643 1746474 : Real64 AirloopPLR = 1;
644 1746474 : if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum > -1) {
645 0 : fanOp = HVAC::FanOp::Continuous;
646 : } else {
647 1746474 : if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).fanOp == HVAC::FanOp::Cycling) {
648 0 : fanOp = HVAC::FanOp::Cycling;
649 : } else {
650 1746474 : fanOp = HVAC::FanOp::Continuous;
651 : }
652 :
653 1746474 : if (fanOp == HVAC::FanOp::Cycling) {
654 : // HX's in the OA system can be troublesome given that the OA flow rate is not necessarily proportional to air loop PLR
655 : // adding that user input for branch flow rate, HX nominal flow rate, OA system min/max flow rate will not necessarily be
656 : // perfectly input, a compromise is used for OA sys HX's as the ratio of flow to max. Issue #4298.
657 : // AirloopPLR = AirLoopFlow( AirLoopNum ).FanPLR;
658 0 : AirloopPLR = state.dataMixedAir->OAController(OASysNum).OAMassFlow / state.dataMixedAir->OAController(OASysNum).MaxOAMassFlowRate;
659 : }
660 : }
661 1746474 : if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum > -1) {
662 0 : HeatRecovery::SimHeatRecovery(state, CompName, FirstHVACIteration, CompIndex, fanOp, AirloopPLR, _, _, _, _, _);
663 : } else {
664 3492948 : HeatRecovery::SimHeatRecovery(state,
665 : CompName,
666 : FirstHVACIteration,
667 : CompIndex,
668 : fanOp,
669 : AirloopPLR,
670 : _,
671 : _,
672 : _,
673 1746474 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass,
674 1746474 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HighHumCtrlActive);
675 : }
676 : }
677 1746532 : OAHX = true;
678 1746532 : } break;
679 257692 : case SimAirServingZones::CompType::Desiccant: { // Dehumidifier:Desiccant:NoFans, Dehumidifier:Desiccant:NoFans, Dehumidifier:Desiccant:System
680 257692 : if (Sim) {
681 257682 : DesiccantDehumidifiers::SimDesiccantDehumidifier(state, CompName, FirstHVACIteration, CompIndex);
682 : }
683 257692 : OAHX = true;
684 257692 : } break;
685 47852 : case SimAirServingZones::CompType::Humidifier: { // Humidifier:Steam:Electric Humidifier:Steam:Gas
686 47852 : if (Sim) {
687 47850 : Humidifiers::SimHumidifier(state, CompName, FirstHVACIteration, CompIndex);
688 : }
689 47852 : } break;
690 41455 : case SimAirServingZones::CompType::Unglazed_SolarCollector: { // SolarCollector:UnglazedTranspired
691 41455 : if (Sim) {
692 41437 : TranspiredCollector::SimTranspiredCollector(state, CompName, CompIndex);
693 : }
694 41455 : } break;
695 93252 : case SimAirServingZones::CompType::PVT_AirBased: { // SolarCollector:FlatPlate:PhotovoltaicThermal
696 93252 : if (Sim) {
697 93232 : if (CompIndex == 0) {
698 10 : CompIndex = PhotovoltaicThermalCollectors::getPVTindexFromName(state, CompName);
699 : }
700 93232 : PhotovoltaicThermalCollectors::simPVTfromOASys(state, CompIndex, FirstHVACIteration);
701 : }
702 93252 : } break;
703 2258433 : case SimAirServingZones::CompType::EvapCooler: { // EvaporativeCooler:Direct:CelDekPad, EvaporativeCooler:Indirect:CelDekPad
704 : // EvaporativeCooler:Indirect:WetCoil, EvaporativeCooler:Indirect:ResearchSpecial
705 2258433 : if (Sim) {
706 2258405 : EvaporativeCoolers::SimEvapCooler(state, CompName, CompIndex);
707 : }
708 2258433 : } break;
709 3190 : case SimAirServingZones::CompType::ZoneVRFasAirLoopEquip: { // ZoneHVAC:TerminalUnit:VariableRefrigerantFlow
710 3190 : if (Sim) {
711 3188 : int ControlledZoneNum = 0;
712 3188 : int constexpr OAUnitNum = 0;
713 3188 : Real64 constexpr OAUCoilOutTemp = 0.0;
714 3188 : bool constexpr ZoneEquipment = false;
715 3188 : Real64 sysOut = 0.0;
716 3188 : Real64 latOut = 0.0;
717 3188 : HVACVariableRefrigerantFlow::SimulateVRF(state,
718 : CompName,
719 : FirstHVACIteration,
720 : ControlledZoneNum,
721 : CompIndex,
722 : HeatingActive,
723 : CoolingActive,
724 : OAUnitNum,
725 : OAUCoilOutTemp,
726 : ZoneEquipment,
727 : sysOut,
728 : latOut);
729 : } else {
730 2 : HVACVariableRefrigerantFlow::isVRFCoilPresent(state, CompName, OACoolingCoil, OAHeatingCoil);
731 : }
732 3190 : } break;
733 0 : default:
734 0 : ShowFatalError(state, format("Invalid Outside Air Component={}", CompType));
735 : }
736 34902364 : }
737 :
738 60004604 : void SimOAMixer(EnergyPlusData &state, std::string const &CompName, int &CompIndex)
739 : {
740 :
741 : // SUBROUTINE INFORMATION:
742 : // AUTHOR Fred Buhl
743 : // DATE WRITTEN Oct 1998
744 :
745 : // PURPOSE OF THIS SUBROUTINE
746 : // Simulate an Outside Air Mixer component
747 :
748 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
749 : int OAMixerNum;
750 :
751 60004604 : if (state.dataMixedAir->GetOAMixerInputFlag) {
752 0 : GetOAMixerInputs(state);
753 0 : state.dataMixedAir->GetOAMixerInputFlag = false;
754 : }
755 :
756 60004604 : if (CompIndex == 0) {
757 1214 : OAMixerNum = Util::FindItemInList(CompName, state.dataMixedAir->OAMixer);
758 1214 : CompIndex = OAMixerNum;
759 1214 : if (OAMixerNum == 0) {
760 0 : ShowFatalError(state, format("SimOAMixer: OutdoorAir:Mixer not found={}", CompName));
761 : }
762 : } else {
763 60003390 : OAMixerNum = CompIndex;
764 : }
765 :
766 60004604 : auto &mixer = state.dataMixedAir->OAMixer(OAMixerNum);
767 :
768 60004604 : mixer.InitOAMixer(state);
769 :
770 60004604 : mixer.CalcOAMixer(state);
771 :
772 60004604 : mixer.UpdateOAMixer(state);
773 60004604 : }
774 :
775 27596359 : void SimOAController(EnergyPlusData &state, std::string const &CtrlName, int &CtrlIndex, bool const FirstHVACIteration, int const AirLoopNum)
776 : {
777 :
778 : // SUBROUTINE INFORMATION:
779 : // AUTHOR Fred Buhl
780 : // DATE WRITTEN Oct 1998
781 :
782 : // PURPOSE OF THIS SUBROUTINE
783 : // Simulate an Outside Air Controller component
784 :
785 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
786 : int OAControllerNum;
787 :
788 27596359 : if ((state.dataMixedAir->GetOAControllerInputFlag) &&
789 : (AirLoopNum > 0)) { // Gets input for object first time Sim routine is called from an airloop
790 413 : GetOAControllerInputs(state);
791 413 : state.dataMixedAir->GetOAControllerInputFlag = false;
792 : }
793 :
794 : // check that the economizer staging operation EconomizerFirst is only used with an sensible load-based controlled AirLoopHVAC:UnitarySystem
795 27596359 : if (AirLoopNum > 0) {
796 27032378 : auto &primaryAirSystems = state.dataAirSystemsData->PrimaryAirSystems(AirLoopNum);
797 27032378 : if (primaryAirSystems.EconomizerStagingCheckFlag == false) {
798 1067 : OAControllerNum = Util::FindItemInList(CtrlName, state.dataMixedAir->OAController);
799 1067 : if (state.dataMixedAir->OAController(OAControllerNum).EconomizerStagingType == HVAC::EconomizerStagingType::EconomizerFirst) {
800 4 : bool sensLoadCtrlUnitarySystemFound = false;
801 8 : for (int BranchNum = 1; BranchNum <= primaryAirSystems.NumBranches; ++BranchNum) {
802 8 : for (int CompNum = 1; CompNum <= primaryAirSystems.Branch(BranchNum).TotalComponents; ++CompNum) {
803 8 : if (primaryAirSystems.Branch(BranchNum).Comp(CompNum).CompType_Num == SimAirServingZones::CompType::UnitarySystemModel) {
804 4 : std::string_view unitarySystemName = primaryAirSystems.Branch(BranchNum).Comp(CompNum).Name;
805 8 : int unitarySystemNum = Util::FindItemInList(
806 4 : unitarySystemName, state.dataUnitarySystems->unitarySys, state.dataUnitarySystems->numUnitarySystems);
807 4 : if (state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_ControlType ==
808 : UnitarySystems::UnitarySys::UnitarySysCtrlType::Load) {
809 4 : if (state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_CoolingCoilType_Num ==
810 3 : HVAC::CoilDX_MultiSpeedCooling ||
811 3 : state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_CoolingCoilType_Num ==
812 7 : HVAC::Coil_CoolingAirToAirVariableSpeed ||
813 2 : state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_CoolingCoilType_Num == HVAC::CoilDX_Cooling) {
814 4 : sensLoadCtrlUnitarySystemFound = true;
815 4 : break;
816 : }
817 : }
818 : }
819 : }
820 : }
821 4 : if (!sensLoadCtrlUnitarySystemFound) {
822 0 : ShowWarningError(
823 : state,
824 0 : format(
825 : "SimOAController: EconomizerFirst was selected in the \"{}\" Controller:OutdoorAir object but the air loop it belongs to "
826 : "does not include an AirLoopHVAC:UnitarySystem with a \"Load\" Control Type input and cooling coil of one of the "
827 : "following types: Coil:Cooling:DX:MultiSpeed,"
828 : " Coil:Cooling:DX:VariableSpeed, or Coil:Cooling:DX. EconomizerFirst will not be enforced.",
829 0 : state.dataMixedAir->OAController(OAControllerNum).Name));
830 : }
831 : }
832 1067 : primaryAirSystems.EconomizerStagingCheckFlag = true;
833 : }
834 : }
835 :
836 27596359 : if (CtrlIndex == 0) {
837 1067 : if (state.dataMixedAir->NumOAControllers > 0) {
838 1067 : OAControllerNum = Util::FindItemInList(CtrlName, state.dataMixedAir->OAController);
839 : } else {
840 0 : OAControllerNum = 0;
841 : }
842 1067 : CtrlIndex = OAControllerNum;
843 1067 : if (OAControllerNum == 0) {
844 0 : ShowFatalError(state, format("SimOAController: Outside Air Controller not found={}", CtrlName));
845 : }
846 : } else {
847 27595292 : OAControllerNum = CtrlIndex;
848 : }
849 :
850 27596359 : InitOAController(state, OAControllerNum, FirstHVACIteration, AirLoopNum);
851 :
852 27596359 : state.dataMixedAir->OAController(OAControllerNum).CalcOAController(state, AirLoopNum, FirstHVACIteration);
853 27596359 : state.dataMixedAir->OAController(OAControllerNum).UpdateOAController(state);
854 27596359 : }
855 :
856 : // Get Input Section of the Module
857 : //******************************************************************************
858 :
859 801 : void GetOutsideAirSysInputs(EnergyPlusData &state)
860 : {
861 :
862 : // SUBROUTINE INFORMATION:
863 : // AUTHOR Fred Buhl
864 : // DATE WRITTEN Oct 1998
865 :
866 : // PURPOSE OF THIS SUBROUTINE
867 : // Input the Outside Air System data and store it in the OutsideAirSys array.
868 :
869 : // METHODOLOGY EMPLOYED:
870 : // Use the Get routines from the InputProcessor module.
871 :
872 : // SUBROUTINE PARAMETER DEFINITIONS:
873 : static constexpr std::string_view RoutineName("GetOutsideAirSysInputs: "); // include trailing blank space
874 :
875 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
876 801 : bool ErrorsFound(false);
877 : int NumNums; // Number of real numbers returned by GetObjectItem
878 : int NumAlphas; // Number of alphanumerics returned by GetObjectItem
879 801 : int TotalArgs(0); // Total number of alpha and numeric arguments (max) for a
880 : int IOStat;
881 801 : Array1D<Real64> NumArray;
882 801 : Array1D_string AlphArray;
883 801 : Array1D_string cAlphaFields; // Alpha field names
884 801 : Array1D_string cNumericFields; // Numeric field names
885 801 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
886 801 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
887 :
888 801 : if (!state.dataMixedAir->GetOASysInputFlag) {
889 0 : return;
890 : }
891 :
892 1602 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
893 801 : state, CurrentModuleObjects[static_cast<int>(CMO::OASystem)], TotalArgs, NumAlphas, NumNums);
894 801 : int MaxNums = NumNums;
895 801 : int MaxAlphas = NumAlphas;
896 1602 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
897 801 : state, CurrentModuleObjects[static_cast<int>(CMO::AirLoopEqList)], TotalArgs, NumAlphas, NumNums);
898 801 : MaxNums = max(MaxNums, NumNums);
899 801 : MaxAlphas = max(MaxAlphas, NumAlphas);
900 1602 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
901 801 : state, CurrentModuleObjects[static_cast<int>(CMO::ControllerList)], TotalArgs, NumAlphas, NumNums);
902 801 : MaxNums = max(MaxNums, NumNums);
903 801 : MaxAlphas = max(MaxAlphas, NumAlphas);
904 :
905 801 : AlphArray.allocate(MaxAlphas);
906 801 : cAlphaFields.allocate(MaxAlphas);
907 801 : NumArray.dimension(MaxNums, 0.0);
908 801 : cNumericFields.allocate(MaxNums);
909 801 : lAlphaBlanks.dimension(MaxAlphas, true);
910 801 : lNumericBlanks.dimension(MaxNums, true);
911 :
912 801 : std::string_view CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::ControllerList)];
913 801 : state.dataMixedAir->NumControllerLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
914 :
915 801 : state.dataMixedAir->ControllerLists.allocate(state.dataMixedAir->NumControllerLists);
916 :
917 2315 : for (int Item = 1; Item <= state.dataMixedAir->NumControllerLists; ++Item) {
918 :
919 : // create a reference for convenience
920 1514 : auto &thisControllerList(state.dataMixedAir->ControllerLists(Item));
921 1514 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
922 : CurrentModuleObject,
923 : Item,
924 : AlphArray,
925 : NumAlphas,
926 : NumArray,
927 : NumNums,
928 : IOStat,
929 : lNumericBlanks,
930 : lAlphaBlanks,
931 : cAlphaFields,
932 : cNumericFields);
933 1514 : thisControllerList.Name = AlphArray(1); // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
934 1514 : thisControllerList.NumControllers = (NumAlphas - 1) / 2;
935 1514 : thisControllerList.ControllerType.dimension(thisControllerList.NumControllers, ControllerKind::Invalid);
936 1514 : thisControllerList.ControllerName.allocate(thisControllerList.NumControllers);
937 1514 : int AlphaNum = 2;
938 3488 : for (int CompNum = 1; CompNum <= thisControllerList.NumControllers; ++CompNum) {
939 : // Json will catch any object types that are not the correct key choice of Controller:OutdoorAir or Controller:WaterCoil
940 3948 : thisControllerList.ControllerType(CompNum) =
941 1974 : static_cast<ControllerKind>(getEnumValue(ControllerKindNamesUC, Util::makeUPPER(AlphArray(AlphaNum))));
942 1974 : thisControllerList.ControllerName(CompNum) = AlphArray(AlphaNum + 1);
943 : // loop over all previous controller lists to check if this controllers is also present on previous controllers
944 9833 : for (int previousListNum = 1; previousListNum < Item; ++previousListNum) {
945 : // loop over each of the controllers listed for this list
946 7859 : auto &previousList(state.dataMixedAir->ControllerLists(previousListNum));
947 18706 : for (int PreviousListControllerNum = 1; PreviousListControllerNum <= previousList.NumControllers; ++PreviousListControllerNum) {
948 19034 : if ((previousList.ControllerType(PreviousListControllerNum) == thisControllerList.ControllerType(CompNum)) &&
949 8187 : (previousList.ControllerName(PreviousListControllerNum) == thisControllerList.ControllerName(CompNum))) {
950 0 : ShowSevereError(state, format("Controller instance repeated in multiple {} objects", CurrentModuleObject));
951 0 : ShowContinueError(state, format("Found in {} = {}", CurrentModuleObject, thisControllerList.Name));
952 0 : ShowContinueError(state, format("Also found in {} = {}", CurrentModuleObject, previousList.Name));
953 0 : ErrorsFound = true;
954 : }
955 : }
956 : }
957 1974 : AlphaNum += 2;
958 : }
959 : }
960 :
961 801 : CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::OASystem)];
962 :
963 801 : state.dataAirLoop->NumOASystems = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
964 :
965 801 : state.dataAirLoop->OutsideAirSys.allocate(state.dataAirLoop->NumOASystems);
966 801 : state.dataSize->OASysEqSizing.allocate(state.dataAirLoop->NumOASystems);
967 801 : state.dataMixedAir->ControllerListUniqueNames.reserve(static_cast<unsigned>(state.dataAirLoop->NumOASystems));
968 801 : state.dataMixedAir->MyOneTimeErrorFlag.dimension(state.dataAirLoop->NumOASystems, true);
969 801 : state.dataMixedAir->MyOneTimeCheckUnitarySysFlag.dimension(state.dataAirLoop->NumOASystems, true);
970 801 : state.dataMixedAir->initOASysFlag.dimension(state.dataAirLoop->NumOASystems, true);
971 :
972 1874 : for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
973 1073 : auto &OASys = state.dataAirLoop->OutsideAirSys(OASysNum);
974 1073 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
975 : CurrentModuleObject,
976 : OASysNum,
977 : AlphArray,
978 : NumAlphas,
979 : NumArray,
980 : NumNums,
981 : IOStat,
982 : lNumericBlanks,
983 : lAlphaBlanks,
984 : cAlphaFields,
985 : cNumericFields);
986 1073 : OASys.Name = AlphArray(1); // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
987 1073 : if (!AlphArray(2).empty()) {
988 2146 : GlobalNames::IntraObjUniquenessCheck(
989 1073 : state, AlphArray(2), CurrentModuleObject, cAlphaFields(2), state.dataMixedAir->ControllerListUniqueNames, ErrorsFound);
990 : }
991 1073 : OASys.ControllerListName = AlphArray(2);
992 1073 : OASys.ComponentListName = AlphArray(3);
993 :
994 5365 : BranchNodeConnections::TestCompSet(state, CurrentModuleObject, OASys.Name, "UNDEFINED", "UNDEFINED", "Air Nodes");
995 :
996 1073 : if (!lAlphaBlanks(3)) {
997 2146 : int ListNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(
998 1073 : state, CurrentModuleObjects[static_cast<int>(CMO::AirLoopEqList)], OASys.ComponentListName);
999 1073 : if (ListNum > 0) {
1000 2146 : state.dataInputProcessing->inputProcessor->getObjectItem(
1001 1073 : state, CurrentModuleObjects[static_cast<int>(CMO::AirLoopEqList)], ListNum, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
1002 1073 : int NumInList = (NumAlphas - 1) / 2;
1003 1073 : OASys.NumComponents = NumInList;
1004 1073 : OASys.ComponentName.allocate(NumInList);
1005 1073 : OASys.ComponentType.allocate(NumInList);
1006 1073 : OASys.ComponentTypeEnum.dimension(NumInList, SimAirServingZones::CompType::Invalid);
1007 1073 : OASys.ComponentIndex.dimension(NumInList, 0);
1008 1073 : OASys.InletNodeNum.dimension(NumInList, 0);
1009 1073 : OASys.OutletNodeNum.dimension(NumInList, 0);
1010 1073 : OASys.compPointer.resize(NumInList + 1, nullptr);
1011 2283 : for (int InListNum = 1; InListNum <= NumInList; ++InListNum) {
1012 1210 : OASys.ComponentName(InListNum) = AlphArray(InListNum * 2 + 1);
1013 1210 : OASys.ComponentType(InListNum) = AlphArray(InListNum * 2);
1014 :
1015 : // Add equipment to component sets array
1016 2420 : BranchNodeConnections::SetUpCompSets(state,
1017 : CurrentModuleObject,
1018 : OASys.Name,
1019 1210 : OASys.ComponentType(InListNum),
1020 1210 : OASys.ComponentName(InListNum),
1021 : "UNDEFINED",
1022 : "UNDEFINED");
1023 : }
1024 : } else {
1025 0 : ShowSevereError(
1026 : state,
1027 0 : format("{} = \"{}\" invalid {}=\"{}\" not found.", CurrentModuleObject, OASys.Name, cAlphaFields(3), OASys.ComponentListName));
1028 0 : ErrorsFound = true;
1029 : }
1030 : } else {
1031 0 : ShowSevereError(state, format("{} = \"{}\" invalid {} is blank and must be entered.", CurrentModuleObject, OASys.Name, cAlphaFields(3)));
1032 0 : ErrorsFound = true;
1033 : }
1034 :
1035 1073 : int ListNum = 0;
1036 1073 : int NumSimpControllers = 0; // number of Controller:Simple objects in an OA System
1037 1073 : if (!lAlphaBlanks(2)) {
1038 2146 : ListNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(
1039 1073 : state, CurrentModuleObjects[static_cast<int>(CMO::ControllerList)], OASys.ControllerListName);
1040 1073 : if (ListNum > 0) {
1041 2146 : state.dataInputProcessing->inputProcessor->getObjectItem(
1042 1073 : state, CurrentModuleObjects[static_cast<int>(CMO::ControllerList)], ListNum, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
1043 1073 : int NumInList = (NumAlphas - 1) / 2;
1044 1073 : OASys.NumControllers = NumInList;
1045 1073 : OASys.ControllerName.allocate(NumInList);
1046 1073 : OASys.ControllerType.allocate(NumInList);
1047 1073 : OASys.controllerTypeEnum.dimension(NumInList, DataAirLoop::ControllerKind::Invalid);
1048 1073 : OASys.ControllerIndex.dimension(NumInList, 0);
1049 2205 : for (int InListNum = 1; InListNum <= NumInList; ++InListNum) {
1050 1132 : OASys.ControllerName(InListNum) = AlphArray(InListNum * 2 + 1);
1051 1132 : OASys.ControllerType(InListNum) = AlphArray(InListNum * 2);
1052 2264 : OASys.controllerTypeEnum(InListNum) =
1053 1132 : static_cast<DataAirLoop::ControllerKind>(getEnumValue(ControllerKindNamesUC, OASys.ControllerType(InListNum)));
1054 : // only count Controller:OutdoorAir types as valid simple controllers
1055 1132 : if (OASys.controllerTypeEnum(InListNum) != DataAirLoop::ControllerKind::OutdoorAir) {
1056 60 : ++NumSimpControllers;
1057 : }
1058 : }
1059 : } else {
1060 0 : ShowSevereError(state,
1061 0 : format("{} = \"{}\" invalid {}=\"{}\" not found.", CurrentModuleObject, AlphArray(1), cAlphaFields(2), AlphArray(2)));
1062 0 : ErrorsFound = true;
1063 : }
1064 : } else {
1065 0 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:DedicatedOutdoorAirSystem") == 0) {
1066 0 : ShowSevereError(state,
1067 0 : format("{} = \"{}\" invalid {} is blank and must be entered.", CurrentModuleObject, AlphArray(1), cAlphaFields(2)));
1068 0 : ErrorsFound = true;
1069 : } else {
1070 0 : ShowWarningError(state,
1071 0 : format("{} = \"{}\": blank {} must be used with AirLoopHVAC:DedicatedOutdoorAirSystem.",
1072 : CurrentModuleObject,
1073 : AlphArray(1),
1074 : cAlphaFields(2)));
1075 : }
1076 : }
1077 1073 : OASys.ControllerListNum = ListNum;
1078 1073 : OASys.NumSimpleControllers = NumSimpControllers;
1079 : }
1080 :
1081 1874 : for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
1082 1073 : auto &OASys = state.dataAirLoop->OutsideAirSys(OASysNum);
1083 2283 : for (int CompNum = 1; CompNum <= OASys.NumComponents; ++CompNum) {
1084 1210 : OASys.ComponentTypeEnum(CompNum) = static_cast<SimAirServingZones::CompType>(getEnumValue(CompTypeNamesUC, OASys.ComponentType(CompNum)));
1085 1210 : if (OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::Fan_System_Object) {
1086 : // construct fan object
1087 1 : OASys.ComponentIndex(CompNum) = Fans::GetFanIndex(state, OASys.ComponentName(CompNum));
1088 1209 : } else if (OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::CoilSystemWater ||
1089 2417 : OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::UnitarySystemModel ||
1090 1208 : OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::DXSystem) {
1091 2 : OASys.ComponentIndex(CompNum) = CompNum;
1092 1207 : } else if (OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::Invalid) {
1093 40 : std::string const thisComp = OASys.ComponentType(CompNum);
1094 40 : if (thisComp == "HEATEXCHANGER:AIRTOAIR:SENSIBLEANDLATENT" || thisComp == "HEATEXCHANGER:DESICCANT:BALANCEDFLOW") {
1095 24 : OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::HeatXchngr;
1096 16 : } else if (thisComp == "DEHUMIDIFIER:DESICCANT:SYSTEM") {
1097 2 : OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::Desiccant;
1098 28 : } else if (thisComp == "EVAPORATIVECOOLER:INDIRECT:CELDEKPAD" || thisComp == "EVAPORATIVECOOLER:INDIRECT:WETCOIL" ||
1099 28 : thisComp == "EVAPORATIVECOOLER:INDIRECT:RESEARCHSPECIAL" || thisComp == "EVAPORATIVECOOLER:DIRECT:RESEARCHSPECIAL") {
1100 14 : OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::EvapCooler;
1101 0 : } else if (thisComp == "HUMIDIFIER:STEAM:GAS") {
1102 0 : OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::Humidifier;
1103 : } else {
1104 0 : ShowSevereError(
1105 : state,
1106 0 : format("{} = \"{}\" invalid Outside Air Component=\"{}\".", CurrentModuleObject, AlphArray(1), OASys.ComponentType(CompNum)));
1107 0 : ErrorsFound = true;
1108 : }
1109 40 : }
1110 : }
1111 :
1112 : // loop through the controllers in the controller list for OA system and save the pointer to the OA controller index
1113 1075 : for (int OAControllerNum = 1; OAControllerNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumControllers; ++OAControllerNum) {
1114 1074 : if (state.dataAirLoop->OutsideAirSys(OASysNum).controllerTypeEnum(OAControllerNum) == DataAirLoop::ControllerKind::OutdoorAir) {
1115 1072 : state.dataAirLoop->OutsideAirSys(OASysNum).OAControllerName =
1116 2144 : state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName(OAControllerNum);
1117 1072 : break;
1118 : }
1119 : }
1120 : }
1121 :
1122 801 : if (ErrorsFound) {
1123 0 : ShowFatalError(state, format("{}Errors found in getting {}.", RoutineName, CurrentModuleObject));
1124 : }
1125 :
1126 801 : AlphArray.deallocate();
1127 801 : cAlphaFields.deallocate();
1128 801 : NumArray.deallocate();
1129 801 : cNumericFields.deallocate();
1130 801 : lAlphaBlanks.deallocate();
1131 801 : lNumericBlanks.deallocate();
1132 :
1133 801 : state.dataMixedAir->GetOASysInputFlag = false;
1134 :
1135 : // once GetOASysInputFlag is set to false other calls to objects can occur without worry that GetOutsideAirSysInputs will be called again
1136 : // now get the pointer for UnitarySystem - doing this earlier can cause recursion which trips IntraObjUniquenessCheck warnings
1137 1874 : for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
1138 2283 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents; ++CompNum) {
1139 1210 : if (state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::CoilSystemWater ||
1140 2419 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::UnitarySystemModel ||
1141 1209 : state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::DXSystem) {
1142 2 : state.dataAirLoop->OutsideAirSys(OASysNum).compPointer[CompNum] = UnitarySystems::UnitarySys::factory(
1143 2 : state, HVAC::UnitarySysType::Unitary_AnyCoilType, state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum), false, 0);
1144 : }
1145 : }
1146 : }
1147 801 : }
1148 :
1149 413 : void GetOAControllerInputs(EnergyPlusData &state)
1150 : {
1151 :
1152 : // SUBROUTINE INFORMATION:
1153 : // AUTHOR Fred Buhl
1154 : // DATE WRITTEN Oct 1998
1155 : // MODIFIED Shirey/Raustad FSEC, June 2003, Jan 2004
1156 : // Mangesh Basarkar, 06/2011: Getting zone OA specifications from Design Specification Object
1157 : // Tianzhen Hong, 3/2012: getting zone air distribution effectiveness and secondary recirculation
1158 : // from DesignSpecification:ZoneAirDistribution objects
1159 :
1160 : // PURPOSE OF THIS SUBROUTINE
1161 : // Input the OAController data and store it in the OAController array.
1162 : // Input the Ventilation:Mechanical data and store it in the VentilationMechanical array.
1163 : // Condense Ventilation:Mechanical data array to include only unique zones specified for each instance of this object.
1164 :
1165 : // METHODOLOGY EMPLOYED:
1166 : // Use the Get routines from the InputProcessor module.
1167 :
1168 : // SUBROUTINE PARAMETER DEFINITIONS:
1169 : static constexpr std::string_view RoutineName("GetOAControllerInputs: "); // include trailing blank space
1170 : static constexpr std::string_view routineName = "GetOAControllerInputs"; // include trailing blank space
1171 :
1172 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1173 : int NumArg; // Number of arguments from GetObjectDefMaxArgs call
1174 : int NumNums; // Number of real numbers returned by GetObjectItem
1175 : int NumAlphas; // Number of alphanumerics returned by GetObjectItem
1176 : int IOStat; // Status of GetObjectItem call
1177 413 : Array1D<Real64> NumArray;
1178 413 : Array1D_string AlphArray;
1179 413 : std::string_view CurrentModuleObject; // Object type for getting and messages
1180 413 : Array1D_string cAlphaFields; // Alpha field names
1181 413 : Array1D_string cNumericFields; // Numeric field names
1182 413 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
1183 413 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
1184 413 : bool ErrorsFound(false); // Flag identifying errors found during get input
1185 :
1186 : // First, call other get input routines in this module to make sure data is filled during this routine.
1187 413 : if (state.dataMixedAir->GetOASysInputFlag) { // Gets input for object first time Sim routine is called
1188 0 : GetOutsideAirSysInputs(state);
1189 0 : state.dataMixedAir->GetOASysInputFlag = false;
1190 : }
1191 413 : if (state.dataMixedAir->GetOAMixerInputFlag) { // Gets input for object first time Sim routine is called
1192 0 : GetOAMixerInputs(state);
1193 0 : state.dataMixedAir->GetOAMixerInputFlag = false;
1194 : }
1195 :
1196 413 : FaultsManager::CheckAndReadFaults(state);
1197 :
1198 826 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
1199 413 : state, CurrentModuleObjects[static_cast<int>(CMO::OAController)], NumArg, NumAlphas, NumNums);
1200 413 : int MaxAlphas = NumAlphas;
1201 413 : int MaxNums = NumNums;
1202 826 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
1203 413 : state, CurrentModuleObjects[static_cast<int>(CMO::ERVController)], NumArg, NumAlphas, NumNums);
1204 413 : MaxAlphas = max(MaxAlphas, NumAlphas);
1205 413 : MaxNums = max(MaxNums, NumNums);
1206 826 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
1207 413 : state, CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)], NumArg, NumAlphas, NumNums);
1208 413 : MaxAlphas = max(MaxAlphas, NumAlphas);
1209 413 : MaxNums = max(MaxNums, NumNums);
1210 :
1211 413 : AlphArray.allocate(MaxAlphas);
1212 413 : NumArray.dimension(MaxNums, 0.0);
1213 413 : lAlphaBlanks.dimension(MaxAlphas, true);
1214 413 : lNumericBlanks.dimension(MaxNums, true);
1215 413 : cAlphaFields.allocate(MaxAlphas);
1216 413 : cNumericFields.allocate(MaxNums);
1217 :
1218 : // Count OAcontrollers and ERVcontrollers and allocate arrays
1219 413 : AllocateOAControllers(state);
1220 :
1221 : // If there are ERV controllers, they have been filled before now NumOAControllers includes the count of NumERVControllers
1222 413 : if (state.dataMixedAir->NumOAControllers > state.dataMixedAir->NumERVControllers) {
1223 413 : CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::OAController)];
1224 413 : int currentOAControllerNum = 0;
1225 1480 : for (int OutAirNum = state.dataMixedAir->NumERVControllers + 1; OutAirNum <= state.dataMixedAir->NumOAControllers; ++OutAirNum) {
1226 1067 : ++currentOAControllerNum;
1227 1067 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1228 : CurrentModuleObject,
1229 : currentOAControllerNum,
1230 : AlphArray,
1231 : NumAlphas,
1232 : NumArray,
1233 : NumNums,
1234 : IOStat,
1235 : lNumericBlanks,
1236 : lAlphaBlanks,
1237 : cAlphaFields,
1238 : cNumericFields);
1239 2134 : GlobalNames::VerifyUniqueInterObjectName(
1240 1067 : state, state.dataMixedAir->OAControllerUniqueNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
1241 :
1242 1067 : ProcessOAControllerInputs(state,
1243 : CurrentModuleObject,
1244 : OutAirNum,
1245 : AlphArray,
1246 : NumAlphas,
1247 : NumArray,
1248 : NumNums,
1249 : lNumericBlanks,
1250 : lAlphaBlanks,
1251 : cAlphaFields,
1252 : cNumericFields,
1253 : ErrorsFound);
1254 :
1255 : // add applicable faults identifier to avoid string comparison at each time step
1256 : // loop through each fault for each OA controller and determine economizer faults
1257 1087 : for (int i = 1; i <= state.dataFaultsMgr->NumFaultyEconomizer; ++i) {
1258 20 : if (state.dataFaultsMgr->FaultsEconomizer(i).ControllerTypeEnum != iController_AirEconomizer) {
1259 0 : continue;
1260 : }
1261 20 : if (Util::SameString(state.dataMixedAir->OAController(OutAirNum).Name, state.dataFaultsMgr->FaultsEconomizer(i).ControllerName)) {
1262 5 : state.dataFaultsMgr->FaultsEconomizer(i).ControllerID = OutAirNum;
1263 5 : ++state.dataMixedAir->OAController(OutAirNum).NumFaultyEconomizer;
1264 : }
1265 : }
1266 : // loop through each fault for each OA controller to determine faulty counts
1267 1067 : state.dataMixedAir->OAController(OutAirNum).EconmizerFaultNum.allocate(state.dataMixedAir->OAController(OutAirNum).NumFaultyEconomizer);
1268 1067 : if (state.dataMixedAir->OAController(OutAirNum).NumFaultyEconomizer > 0) {
1269 12 : for (int j = 0, i = 1; i <= state.dataFaultsMgr->NumFaultyEconomizer; ++i) {
1270 10 : if (state.dataFaultsMgr->FaultsEconomizer(i).ControllerTypeEnum != iController_AirEconomizer) {
1271 0 : continue;
1272 : }
1273 10 : if (Util::SameString(state.dataMixedAir->OAController(OutAirNum).Name, state.dataFaultsMgr->FaultsEconomizer(i).ControllerName)) {
1274 5 : state.dataMixedAir->OAController(OutAirNum).EconmizerFaultNum(++j) = i;
1275 : }
1276 : }
1277 : }
1278 : } // LOOP FOR OutAirNum
1279 :
1280 413 : if (ErrorsFound) {
1281 0 : AlphArray.deallocate();
1282 0 : NumArray.deallocate();
1283 0 : lNumericBlanks.deallocate();
1284 0 : lAlphaBlanks.deallocate();
1285 0 : cAlphaFields.deallocate();
1286 0 : cNumericFields.deallocate();
1287 0 : ShowFatalError(state, format("{}Errors found in getting {} inputs.", RoutineName, CurrentModuleObject));
1288 : }
1289 : }
1290 :
1291 413 : state.dataMixedAir->GetOAControllerInputFlag = false;
1292 :
1293 : // Process Controller:MechanicalVentilation objects
1294 413 : CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)];
1295 413 : state.dataMixedAir->NumVentMechControllers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
1296 413 : if (state.dataMixedAir->NumVentMechControllers > 0) {
1297 26 : state.dataMixedAir->VentilationMechanical.allocate(state.dataMixedAir->NumVentMechControllers);
1298 80 : for (int VentMechNum = 1; VentMechNum <= state.dataMixedAir->NumVentMechControllers; ++VentMechNum) {
1299 54 : auto &thisVentilationMechanical(state.dataMixedAir->VentilationMechanical(VentMechNum));
1300 54 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1301 : CurrentModuleObject,
1302 : VentMechNum,
1303 : AlphArray,
1304 : NumAlphas,
1305 : NumArray,
1306 : NumNums,
1307 : IOStat,
1308 : lNumericBlanks,
1309 : lAlphaBlanks,
1310 : cAlphaFields,
1311 : cNumericFields);
1312 :
1313 54 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, AlphArray(1)};
1314 :
1315 54 : int MechVentZoneCount = 0;
1316 :
1317 54 : int NumGroups = (NumAlphas + NumNums - 5) / 3; // Number of extensible input groups of the VentilationMechanical object
1318 54 : if (mod((NumAlphas + NumNums - 5), 3) != 0) {
1319 0 : ++NumGroups;
1320 : }
1321 54 : thisVentilationMechanical.Name = AlphArray(1); // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
1322 :
1323 54 : if (lAlphaBlanks(2)) {
1324 3 : thisVentilationMechanical.availSched = Sched::GetScheduleAlwaysOn(state);
1325 51 : } else if ((thisVentilationMechanical.availSched = Sched::GetSchedule(state, AlphArray(2))) == nullptr) {
1326 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(2), AlphArray(2));
1327 0 : ErrorsFound = true;
1328 : }
1329 :
1330 : // Adding new flag for DCV
1331 54 : if (Util::SameString(AlphArray(3), "Yes")) {
1332 51 : thisVentilationMechanical.DCVFlag = true;
1333 3 : } else if (Util::SameString(AlphArray(3), "No") || lAlphaBlanks(3)) {
1334 3 : thisVentilationMechanical.DCVFlag = false;
1335 : } else {
1336 0 : ShowSevereError(state,
1337 0 : format("{}=\"{}\" invalid value {}=\"{}\".", CurrentModuleObject, AlphArray(1), cAlphaFields(3), AlphArray(3)));
1338 0 : ShowContinueError(state, "...Valid values are \"Yes\" or \"No\".");
1339 0 : ErrorsFound = true;
1340 : }
1341 :
1342 : // System outdoor air method
1343 54 : thisVentilationMechanical.SystemOAMethod = static_cast<DataSizing::SysOAMethod>(getEnumValue(SOAMNamesUC, Util::makeUPPER(AlphArray(4))));
1344 :
1345 54 : if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQP ||
1346 53 : thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
1347 52 : thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
1348 51 : thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate ||
1349 50 : thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQPCOM) {
1350 4 : if (!state.dataContaminantBalance->Contaminant.CO2Simulation) {
1351 0 : ShowSevereError(
1352 : state,
1353 0 : format(
1354 : "{}=\"{}\" valid {}=\"{}\" requires CO2 simulation.", CurrentModuleObject, AlphArray(1), cAlphaFields(2), AlphArray(2)));
1355 0 : ShowContinueError(state, "The choice must be Yes for the field Carbon Dioxide Concentration in ZoneAirContaminantBalance");
1356 0 : ErrorsFound = true;
1357 : }
1358 : }
1359 :
1360 54 : if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQPGC ||
1361 53 : thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQPCOM) {
1362 1 : if (!state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1363 0 : ShowSevereError(state,
1364 0 : format("{}=\"{}\" valid {}=\"{}\" requires generic contaminant simulation.",
1365 : CurrentModuleObject,
1366 : AlphArray(1),
1367 : cAlphaFields(2),
1368 : AlphArray(2)));
1369 0 : ShowContinueError(state, "The choice must be Yes for the field Generic Contaminant Concentration in ZoneAirContaminantBalance");
1370 0 : ErrorsFound = true;
1371 : }
1372 : }
1373 :
1374 54 : if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::Invalid) { // If specified incorrectly, show errors
1375 0 : thisVentilationMechanical.SystemOAMethod = DataSizing::SysOAMethod::ZoneSum;
1376 0 : ShowWarningError(state,
1377 0 : format("{}=\"{}\" incorrect specification for {}, the ZoneSum method will be used.",
1378 : CurrentModuleObject,
1379 : AlphArray(1),
1380 : cAlphaFields(4)));
1381 : // ErrorsFound=.TRUE.
1382 : }
1383 :
1384 : // Zone maximum outdoor air fraction
1385 54 : thisVentilationMechanical.ZoneMaxOAFraction = NumArray(1);
1386 :
1387 54 : state.dataMixedAir->VentMechZoneOrListName.allocate(NumGroups);
1388 54 : state.dataMixedAir->DesignSpecOAObjName.allocate(NumGroups);
1389 54 : state.dataMixedAir->DesignSpecOAObjIndex.dimension(NumGroups, 0);
1390 54 : state.dataMixedAir->DesignSpecZoneADObjName.allocate(NumGroups);
1391 54 : state.dataMixedAir->DesignSpecZoneADObjIndex.dimension(NumGroups, 0);
1392 :
1393 : // First time through find the total number of zones requiring mechanical ventilation
1394 : // May include duplicate zones. Will check for duplicate zones further down in this subroutine.
1395 452 : for (int groupNum = 1; groupNum <= NumGroups; ++groupNum) {
1396 398 : state.dataMixedAir->VentMechZoneOrListName(groupNum) = AlphArray((groupNum - 1) * 3 + 5);
1397 :
1398 : // Getting OA details from design specification OA object
1399 398 : if (!lAlphaBlanks((groupNum - 1) * 3 + 6)) {
1400 398 : state.dataMixedAir->DesignSpecOAObjName(groupNum) = AlphArray((groupNum - 1) * 3 + 6);
1401 398 : int ObjIndex = Util::FindItemInList(state.dataMixedAir->DesignSpecOAObjName(groupNum), state.dataSize->OARequirements);
1402 398 : state.dataMixedAir->DesignSpecOAObjIndex(groupNum) = ObjIndex;
1403 :
1404 398 : if (ObjIndex == 0) {
1405 0 : ShowSevereError(state, format("{}{}=\"{}\", invalid", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
1406 0 : ShowContinueError(state,
1407 0 : format("... not found {}=\"{}\".",
1408 0 : cAlphaFields((groupNum - 1) * 3 + 6),
1409 0 : state.dataMixedAir->DesignSpecOAObjName(groupNum)));
1410 0 : ErrorsFound = true;
1411 : }
1412 : }
1413 :
1414 : // Get zone air distribution details from design specification Zone Air Distribution object
1415 398 : if (!lAlphaBlanks((groupNum - 1) * 3 + 7)) {
1416 398 : state.dataMixedAir->DesignSpecZoneADObjName(groupNum) = AlphArray((groupNum - 1) * 3 + 7);
1417 398 : int ObjIndex = Util::FindItemInList(state.dataMixedAir->DesignSpecZoneADObjName(groupNum), state.dataSize->ZoneAirDistribution);
1418 398 : state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum) = ObjIndex;
1419 :
1420 398 : if (ObjIndex == 0) {
1421 : // Cannot find the design specification Zone Air Distribution object
1422 0 : ShowSevereError(state, format("{}{}=\"{}\", invalid", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
1423 0 : ShowContinueError(state,
1424 0 : format("... not found {}=\"{}\".",
1425 0 : cAlphaFields((groupNum - 1) * 3 + 7),
1426 0 : state.dataMixedAir->DesignSpecZoneADObjName(groupNum)));
1427 0 : ErrorsFound = true;
1428 : }
1429 : }
1430 :
1431 398 : int ZoneNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->Zone);
1432 398 : if (ZoneNum > 0) {
1433 391 : ++MechVentZoneCount;
1434 : } else {
1435 7 : int ZoneListNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->ZoneList);
1436 7 : if (ZoneListNum > 0) {
1437 7 : MechVentZoneCount += state.dataHeatBal->ZoneList(ZoneListNum).NumOfZones;
1438 : } else {
1439 0 : ShowWarningError(
1440 : state,
1441 0 : format("{}=\"{}\" invalid {} not found.", CurrentModuleObject, AlphArray(1), cAlphaFields((groupNum - 1) * 3 + 5)));
1442 0 : ShowContinueError(
1443 : state,
1444 0 : format("Missing {} = {}", cAlphaFields((groupNum - 1) * 3 + 5), state.dataMixedAir->VentMechZoneOrListName(groupNum)));
1445 0 : ErrorsFound = true;
1446 : }
1447 : }
1448 : }
1449 :
1450 54 : thisVentilationMechanical.NumofVentMechZones = MechVentZoneCount;
1451 :
1452 : // Now allocate and store unique zone and associated ventilation rate information
1453 54 : thisVentilationMechanical.VentMechZone.allocate(MechVentZoneCount);
1454 :
1455 54 : MechVentZoneCount = 0;
1456 :
1457 : // Loop through zone names and list of zone names, remove duplicate zones, and store designspec names and indexes
1458 452 : for (int groupNum = 1; groupNum <= NumGroups; ++groupNum) {
1459 398 : int ZoneNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->Zone);
1460 398 : if (ZoneNum > 0) {
1461 391 : if (std::any_of(thisVentilationMechanical.VentMechZone.begin(),
1462 782 : thisVentilationMechanical.VentMechZone.end(),
1463 9045 : [ZoneNum](auto const &vmZone) { return vmZone.zoneNum == ZoneNum; })) {
1464 : // Disregard duplicate zone names, show warning and do not store data for this zone
1465 0 : ShowWarningError(state,
1466 0 : format("Zone name = {} for {} object = {}",
1467 0 : state.dataMixedAir->VentMechZoneOrListName(groupNum),
1468 : CurrentModuleObject,
1469 0 : thisVentilationMechanical.Name));
1470 0 : ShowContinueError(state, "is specified more than once. The first ventilation values specified for this zone will be used");
1471 0 : ShowContinueError(state, "and the rest will be ignored. Simulation will continue..");
1472 : } else {
1473 : // Store unique zone names
1474 391 : ++MechVentZoneCount;
1475 391 : auto &thisMechVentZone = thisVentilationMechanical.VentMechZone(MechVentZoneCount);
1476 391 : thisMechVentZone.zoneNum = ZoneNum;
1477 391 : thisMechVentZone.name = state.dataHeatBal->Zone(ZoneNum).Name;
1478 :
1479 : // Populating new temp array to hold design spec OA object for each zone
1480 391 : if (state.dataMixedAir->DesignSpecOAObjIndex(groupNum) > 0) {
1481 391 : thisMechVentZone.ZoneDesignSpecOAObjName = state.dataMixedAir->DesignSpecOAObjName(groupNum);
1482 391 : thisMechVentZone.ZoneDesignSpecOAObjIndex = state.dataMixedAir->DesignSpecOAObjIndex(groupNum);
1483 : } else {
1484 0 : if (state.dataGlobal->DoZoneSizing) {
1485 0 : int ObjIndex = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum),
1486 0 : state.dataSize->ZoneSizingInput,
1487 : &ZoneSizingInputData::ZoneName);
1488 0 : if (ObjIndex > 0) {
1489 0 : thisMechVentZone.ZoneDesignSpecOAObjName = state.dataSize->ZoneSizingInput(ObjIndex).DesignSpecOAObjName;
1490 0 : thisMechVentZone.ZoneDesignSpecOAObjIndex = state.dataSize->ZoneSizingInput(ObjIndex).ZoneDesignSpecOAIndex;
1491 : }
1492 : }
1493 : }
1494 : // Zone Air Distribution inputs
1495 391 : if (state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum) > 0) {
1496 : // new DCV inputs
1497 391 : thisMechVentZone.ZoneDesignSpecADObjName = state.dataMixedAir->DesignSpecZoneADObjName(groupNum);
1498 391 : thisMechVentZone.ZoneDesignSpecADObjIndex = state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum);
1499 : } else {
1500 0 : if (state.dataGlobal->DoZoneSizing) {
1501 0 : int ObjIndex = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum),
1502 0 : state.dataSize->ZoneSizingInput,
1503 : &ZoneSizingInputData::ZoneName);
1504 0 : if (ObjIndex > 0) {
1505 0 : thisMechVentZone.ZoneDesignSpecADObjName = state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistEffObjName;
1506 0 : thisMechVentZone.ZoneDesignSpecADObjIndex = state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistributionIndex;
1507 : }
1508 : }
1509 : }
1510 : }
1511 : } else {
1512 : // Not a zone name, must be a zone list
1513 7 : int ZoneListNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->ZoneList);
1514 7 : if (ZoneListNum > 0) {
1515 21 : for (int ScanZoneListNum = 1; ScanZoneListNum <= state.dataHeatBal->ZoneList(ZoneListNum).NumOfZones; ++ScanZoneListNum) {
1516 : // check to make sure zone name is unique (not listed more than once)...
1517 14 : int zoneNum2 = state.dataHeatBal->ZoneList(ZoneListNum).Zone(ScanZoneListNum);
1518 14 : if (std::any_of(thisVentilationMechanical.VentMechZone.begin(),
1519 28 : thisVentilationMechanical.VentMechZone.end(),
1520 42 : [zoneNum2](auto const &vmZone) { return vmZone.zoneNum == zoneNum2; })) {
1521 : // Disregard duplicate zone names, show warning and do not store data for this zone
1522 0 : ShowWarningError(state,
1523 0 : format("Zone name = {} in ZoneList = {} for {} object = {}",
1524 0 : state.dataHeatBal->Zone(zoneNum2).Name,
1525 0 : state.dataMixedAir->VentMechZoneOrListName(groupNum),
1526 : CurrentModuleObject,
1527 0 : thisVentilationMechanical.Name));
1528 0 : ShowContinueError(state, "is a duplicate. The first ventilation values specified for this zone will be used ");
1529 0 : ShowContinueError(state, "and the rest will be ignored. The simulation will continue...");
1530 : } else {
1531 : // Store data for each zone name from zone list (duplicate zone names accounted for in
1532 : // HeatBalanceManager)
1533 14 : ++MechVentZoneCount;
1534 14 : auto &thisMechVentZone = thisVentilationMechanical.VentMechZone(MechVentZoneCount);
1535 14 : thisMechVentZone.zoneNum = zoneNum2;
1536 14 : thisMechVentZone.name = state.dataHeatBal->Zone(zoneNum2).Name;
1537 : // Populating new temp array to hold design spec OA object for each zone
1538 14 : if (state.dataMixedAir->DesignSpecOAObjIndex(groupNum) > 0) {
1539 14 : thisMechVentZone.ZoneDesignSpecOAObjName = state.dataMixedAir->DesignSpecOAObjName(groupNum);
1540 14 : thisMechVentZone.ZoneDesignSpecOAObjIndex = state.dataMixedAir->DesignSpecOAObjIndex(groupNum);
1541 : } else {
1542 0 : if (state.dataGlobal->DoZoneSizing) {
1543 0 : int ObjIndex = Util::FindItemInList(
1544 0 : state.dataHeatBal->Zone(zoneNum2).Name, state.dataSize->ZoneSizingInput, &ZoneSizingInputData::ZoneName);
1545 0 : if (ObjIndex > 0) {
1546 0 : thisMechVentZone.ZoneDesignSpecOAObjName = state.dataSize->ZoneSizingInput(ObjIndex).DesignSpecOAObjName;
1547 0 : thisMechVentZone.ZoneDesignSpecOAObjIndex =
1548 0 : state.dataSize->ZoneSizingInput(ObjIndex).ZoneDesignSpecOAIndex;
1549 : }
1550 : }
1551 : }
1552 :
1553 14 : if (state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum) > 0) {
1554 : // new DCV inputs
1555 14 : thisMechVentZone.ZoneDesignSpecADObjName = state.dataMixedAir->DesignSpecZoneADObjName(groupNum);
1556 14 : thisMechVentZone.ZoneDesignSpecADObjIndex = state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum);
1557 : } else {
1558 0 : if (state.dataGlobal->DoZoneSizing) {
1559 0 : int ObjIndex = Util::FindItemInList(
1560 0 : state.dataHeatBal->Zone(zoneNum2).Name, state.dataSize->ZoneSizingInput, &ZoneSizingInputData::ZoneName);
1561 0 : if (ObjIndex > 0) {
1562 : thisMechVentZone.ZoneDesignSpecADObjName =
1563 0 : state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistEffObjName;
1564 0 : thisMechVentZone.ZoneDesignSpecADObjIndex =
1565 0 : state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistributionIndex;
1566 : }
1567 : }
1568 : }
1569 : }
1570 : }
1571 : }
1572 : }
1573 : }
1574 :
1575 54 : if (MechVentZoneCount <= 0) {
1576 0 : ShowSevereError(state,
1577 0 : format("{}{}=\"{}\", invalid input. At least one Zone or ZoneList Name must be entered.",
1578 : RoutineName,
1579 : CurrentModuleObject,
1580 0 : thisVentilationMechanical.Name));
1581 0 : ErrorsFound = true;
1582 : }
1583 :
1584 : // Overwrite previous number of zones with number that does not include duplicates
1585 54 : thisVentilationMechanical.NumofVentMechZones = MechVentZoneCount;
1586 :
1587 : // Loop over zones and fill OA and AD specs, if none were found, use defaults
1588 459 : for (int ventMechZoneNum = 1; ventMechZoneNum <= MechVentZoneCount; ++ventMechZoneNum) {
1589 405 : auto &thisVentMechZone = thisVentilationMechanical.VentMechZone(ventMechZoneNum);
1590 405 : int zoneOAReqObjIndex = thisVentMechZone.ZoneDesignSpecOAObjIndex;
1591 405 : if (zoneOAReqObjIndex > 0) {
1592 405 : auto const &curOARequirements(state.dataSize->OARequirements(zoneOAReqObjIndex));
1593 405 : thisVentMechZone.ZoneOAAreaRate = curOARequirements.OAFlowPerArea;
1594 405 : thisVentMechZone.ZoneOAPeopleRate = curOARequirements.OAFlowPerPerson;
1595 405 : thisVentMechZone.ZoneOAFlowRate = curOARequirements.OAFlowPerZone;
1596 405 : thisVentMechZone.ZoneOAACHRate = curOARequirements.OAFlowACH;
1597 405 : thisVentMechZone.ZoneOAFlowMethod = curOARequirements.OAFlowMethod;
1598 405 : thisVentMechZone.zoneOASched = curOARequirements.oaFlowFracSched;
1599 405 : thisVentMechZone.oaPropCtlMinRateSched = curOARequirements.oaPropCtlMinRateSched;
1600 405 : if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
1601 3 : if (thisVentMechZone.ZoneOAPeopleRate == 0.0 && thisVentMechZone.ZoneOAAreaRate == 0.0) {
1602 0 : ShowSevereError(
1603 : state,
1604 0 : format("{}{}=\"{}\", invalid input with System Outdoor Air Method = ProportionalControlBasedOnDesignOARate.",
1605 : RoutineName,
1606 : CurrentModuleObject,
1607 0 : thisVentilationMechanical.Name));
1608 0 : ShowContinueError(state,
1609 : " The values of Outdoor Air Flow per Person and Outdoor Air Flow per Zone Floor Area in the same "
1610 : "object can not be zero.");
1611 0 : ErrorsFound = true;
1612 : }
1613 : }
1614 : } else { // use defaults
1615 0 : thisVentMechZone.ZoneOAAreaRate = 0.0;
1616 : // since this is case with no DesSpcOA object, cannot determine the method and default would be Flow/Person which should
1617 : // default to this flow rate
1618 0 : thisVentMechZone.ZoneOAPeopleRate = 0.00944;
1619 0 : thisVentMechZone.ZoneOAFlowRate = 0.0;
1620 0 : thisVentMechZone.ZoneOAACHRate = 0.0;
1621 0 : thisVentMechZone.ZoneOAFlowMethod = OAFlowCalcMethod::PerPerson;
1622 0 : thisVentMechZone.zoneOASched =
1623 0 : Sched::GetScheduleAlwaysOn(state); // defaults to constant-1.0. TODO: what is this really suppoed to be?
1624 0 : ShowWarningError(state, format("{}{}=\"{}", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
1625 0 : ShowContinueError(
1626 0 : state, format("Cannot locate a matching DesignSpecification:OutdoorAir object for Zone=\"{}\".", thisVentMechZone.name));
1627 0 : ShowContinueError(state, "Using default OA of 0.00944 m3/s-person and 0.0 m3/s-m2.");
1628 : }
1629 405 : int zoneAirDistObjIndex = thisVentMechZone.ZoneDesignSpecADObjIndex;
1630 405 : if (zoneAirDistObjIndex > 0) {
1631 405 : auto const &curZoneAirDistribution(state.dataSize->ZoneAirDistribution(zoneAirDistObjIndex));
1632 405 : thisVentMechZone.ZoneADEffCooling = curZoneAirDistribution.ZoneADEffCooling;
1633 405 : thisVentMechZone.ZoneADEffHeating = curZoneAirDistribution.ZoneADEffHeating;
1634 405 : thisVentMechZone.zoneADEffSched = curZoneAirDistribution.zoneADEffSched;
1635 405 : thisVentMechZone.ZoneSecondaryRecirculation = curZoneAirDistribution.ZoneSecondaryRecirculation;
1636 : } else { // use defaults
1637 0 : thisVentMechZone.ZoneADEffCooling = 1.0;
1638 0 : thisVentMechZone.ZoneADEffHeating = 1.0;
1639 0 : thisVentMechZone.ZoneSecondaryRecirculation = 0.0;
1640 0 : ShowWarningError(state, format("{}{}=\"{}\"", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
1641 0 : ShowContinueError(
1642 : state,
1643 0 : format("Cannot locate a matching DesignSpecification:ZoneAirDistribution object for Zone=\"{}\".", thisVentMechZone.name));
1644 0 : ShowContinueError(state, "Using default zone air distribution effectiveness of 1.0 for heating and cooling.");
1645 : }
1646 : }
1647 54 : state.dataMixedAir->VentMechZoneOrListName.deallocate();
1648 54 : state.dataMixedAir->DesignSpecOAObjName.deallocate();
1649 54 : state.dataMixedAir->DesignSpecOAObjIndex.deallocate();
1650 54 : state.dataMixedAir->DesignSpecZoneADObjName.deallocate();
1651 54 : state.dataMixedAir->DesignSpecZoneADObjIndex.deallocate();
1652 : }
1653 :
1654 80 : for (int VentMechNum = 1; VentMechNum <= state.dataMixedAir->NumVentMechControllers; ++VentMechNum) {
1655 54 : auto &thisVentilationMechanical(state.dataMixedAir->VentilationMechanical(VentMechNum));
1656 459 : for (int jZone = 1; jZone <= thisVentilationMechanical.NumofVentMechZones; ++jZone) {
1657 405 : auto &thisVentMechZone = thisVentilationMechanical.VentMechZone(jZone);
1658 405 : if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc) {
1659 3 : if (thisVentMechZone.ZoneOAACHRate > 0.0 || thisVentMechZone.ZoneOAFlowRate > 0.0) {
1660 0 : ShowWarningError(state,
1661 0 : format("{}=\"{}\", inappropriate outdoor air method", CurrentModuleObject, thisVentilationMechanical.Name));
1662 0 : ShowContinueError(state,
1663 0 : format("Inappropriate method for Design Specification Outdoor Air Object Name=\"{}\".",
1664 0 : thisVentMechZone.ZoneDesignSpecOAObjName));
1665 0 : ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
1666 0 : ShowContinueError(state,
1667 : "Since System Outdoor Air Method= ProportionalControlBasedOnOccupancySchedule\", AirChanges/Hour or "
1668 : "Flow/Zone outdoor air methods are not valid. Simulation continues.... ");
1669 : }
1670 : }
1671 405 : if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
1672 3 : if (thisVentMechZone.ZoneOAACHRate > 0.0 || thisVentMechZone.ZoneOAFlowRate > 0.0) {
1673 0 : ShowWarningError(state,
1674 0 : format("{}=\"{}\", inappropriate outdoor air method", CurrentModuleObject, thisVentilationMechanical.Name));
1675 0 : ShowContinueError(state,
1676 0 : format("Inappropriate method for Design Specification Outdoor Air Object Name=\"{}\".",
1677 0 : thisVentMechZone.ZoneDesignSpecOAObjName));
1678 0 : ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
1679 0 : ShowContinueError(state,
1680 : "Since System Outdoor Air Method= ProportionalControlBasedOnDesignOccupancy\", AirChanges/Hour or "
1681 : "Flow/Zone outdoor air methods are not valid. Simulation continues.... ");
1682 : }
1683 : }
1684 :
1685 : // Error check to see if a single duct air terminal is assigned to a zone that has zone secondary recirculation
1686 405 : if (thisVentMechZone.ZoneSecondaryRecirculation > 0.0) {
1687 5 : int ZoneNum = thisVentMechZone.zoneNum;
1688 5 : if (ZoneNum > 0) {
1689 5 : int EquipListIndex = state.dataZoneEquip->ZoneEquipConfig(ZoneNum).EquipListIndex;
1690 5 : if (EquipListIndex > 0) {
1691 15 : for (int EquipListNum = 1; EquipListNum <= state.dataZoneEquip->NumOfZoneEquipLists; ++EquipListNum) {
1692 15 : if (EquipListNum == EquipListIndex) {
1693 5 : for (int EquipNum = 1; EquipNum <= state.dataZoneEquip->ZoneEquipList(EquipListNum).NumOfEquipTypes; ++EquipNum) {
1694 5 : if (Util::SameString(state.dataZoneEquip->ZoneEquipList(EquipListNum).EquipTypeName(EquipNum),
1695 : "ZONEHVAC:AIRDISTRIBUTIONUNIT")) {
1696 15 : for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
1697 15 : if (Util::SameString(state.dataZoneEquip->ZoneEquipList(EquipListNum).EquipName(EquipNum),
1698 15 : state.dataDefineEquipment->AirDistUnit(ADUNum).Name)) {
1699 5 : if ((state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1700 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctVAVReheat) ||
1701 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1702 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolNoReheat) ||
1703 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1704 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolReheat) ||
1705 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1706 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctVAVNoReheat) ||
1707 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1708 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctVAVReheatVSFan) ||
1709 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1710 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctCBVAVReheat) ||
1711 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1712 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctCBVAVNoReheat) ||
1713 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1714 1 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolCooledBeam) ||
1715 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1716 6 : DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolFourPipeBeam) ||
1717 1 : (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
1718 : DataDefineEquip::ZnAirLoopEquipType::DualDuctVAVOutdoorAir)) {
1719 8 : ShowWarningError(state,
1720 8 : format("{}=\"{}\", inappropriate use of Zone secondary recirculation",
1721 : CurrentModuleObject,
1722 4 : thisVentilationMechanical.Name));
1723 8 : ShowContinueError(state,
1724 : "A zone secondary recirculation fraction is specified for zone served by ");
1725 8 : ShowContinueError(state,
1726 8 : format("...terminal unit \"{}\" , that indicates a single path system",
1727 4 : state.dataDefineEquipment->AirDistUnit(ADUNum).Name));
1728 4 : ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
1729 8 : ShowContinueError(state, "...The zone secondary recirculation for that zone was set to 0.0");
1730 4 : thisVentMechZone.ZoneSecondaryRecirculation = 0.0;
1731 : }
1732 5 : goto EquipLoop_exit;
1733 : }
1734 : }
1735 : }
1736 : }
1737 : }
1738 : }
1739 5 : EquipLoop_exit:;
1740 : }
1741 : }
1742 : }
1743 405 : if (thisVentMechZone.ZoneDesignSpecOAObjName.empty()) {
1744 0 : ShowSevereError(
1745 : state,
1746 0 : format("{}=\"{}\", Design Specification Outdoor Air Object Name blank", CurrentModuleObject, thisVentilationMechanical.Name));
1747 0 : ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
1748 0 : ShowContinueError(state, "This field either needs to be filled in in this object or Sizing:Zone object.");
1749 0 : ShowContinueError(state, "For this run, default values for these fields will be used.");
1750 : }
1751 405 : if (thisVentMechZone.ZoneOAPeopleRate <= 0.0 && thisVentilationMechanical.DCVFlag) {
1752 2 : ShowWarningError(state, format("{}=\"{}\", Zone OA/person rate", CurrentModuleObject, thisVentilationMechanical.Name));
1753 2 : ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
1754 4 : ShowContinueError(state,
1755 4 : format("Zone outside air per person rate not set in Design Specification Outdoor Air Object=\"{}\".",
1756 2 : thisVentMechZone.ZoneDesignSpecOAObjName));
1757 : }
1758 :
1759 405 : if (thisVentMechZone.ZoneOAAreaRate < 0.0) {
1760 0 : ShowSevereError(state,
1761 0 : format("{}=\"{}\", invalid Outdoor Air flow per area", CurrentModuleObject, thisVentilationMechanical.Name));
1762 0 : ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
1763 0 : ShowContinueError(state,
1764 0 : format("invalid Outdoor Air flow per area specified in object=\"{}\". Value must be >= 0.0.",
1765 0 : thisVentMechZone.ZoneDesignSpecOAObjName));
1766 0 : ErrorsFound = true;
1767 : }
1768 405 : if (thisVentMechZone.ZoneOAPeopleRate < 0.0) {
1769 0 : ShowSevereError(state,
1770 0 : format("{}=\"{}\", invalid Outdoor Air flow per person", CurrentModuleObject, thisVentilationMechanical.Name));
1771 0 : ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
1772 0 : ShowContinueError(state,
1773 0 : format("invalid Outdoor Air flow per person specified in object \"{}\". Value must be >= 0.0.",
1774 0 : thisVentMechZone.ZoneDesignSpecOAObjName));
1775 0 : ErrorsFound = true;
1776 : }
1777 : }
1778 : }
1779 :
1780 : // Link OA controller object with mechanical ventilation object
1781 115 : for (int OAControllerNum = 1; OAControllerNum <= state.dataMixedAir->NumOAControllers; ++OAControllerNum) {
1782 89 : state.dataMixedAir->OAController(OAControllerNum).VentMechObjectNum = Util::FindItemInList(
1783 89 : state.dataMixedAir->OAController(OAControllerNum).VentilationMechanicalName, state.dataMixedAir->VentilationMechanical);
1784 124 : if (state.dataMixedAir->OAController(OAControllerNum).VentMechObjectNum == 0 &&
1785 35 : !state.dataMixedAir->OAController(OAControllerNum).VentilationMechanicalName.empty()) {
1786 0 : ShowSevereError(state,
1787 0 : format("{}=\"{}\", non-match to Controller:OutdoorAir",
1788 : CurrentModuleObject,
1789 0 : state.dataMixedAir->OAController(OAControllerNum).VentilationMechanicalName));
1790 0 : ShowContinueError(
1791 0 : state, format("Invalid specified in Controller:OutdoorAir object = {}", state.dataMixedAir->OAController(OAControllerNum).Name));
1792 0 : ShowContinueError(state,
1793 0 : format("{} object name must match the {} object name specified in Controller:OutdoorAir.",
1794 : CurrentModuleObject,
1795 : CurrentModuleObject));
1796 0 : ErrorsFound = true;
1797 : }
1798 : }
1799 :
1800 : // write to .eio file
1801 : static constexpr std::string_view Format_700(
1802 : "!<Controller:MechanicalVentilation>,Name,Availability Schedule Name,Demand Controlled Ventilation "
1803 : "{Yes/No},System Outdoor Air Method,Zone Maximum Outdoor Air Fraction,Number of Zones,Zone Name,DSOA "
1804 : "Name,DSZAD Name");
1805 26 : print(state.files.eio, "{}\n", Format_700);
1806 80 : for (auto const &ventMech : state.dataMixedAir->VentilationMechanical) {
1807 54 : print(state.files.eio, " Controller:MechanicalVentilation,{},{},", ventMech.Name, ventMech.availSched ? ventMech.availSched->Name : "");
1808 :
1809 54 : print(state.files.eio, format("{},", yesNoNames[(int)ventMech.DCVFlag]));
1810 :
1811 54 : if (ventMech.SystemOAMethod != DataSizing::SysOAMethod::Invalid) {
1812 54 : print(state.files.eio, printSysOAMethod[(int)ventMech.SystemOAMethod]);
1813 : } else {
1814 0 : print(state.files.eio, "Invalid/Unknown,");
1815 : }
1816 :
1817 54 : print(state.files.eio, "{:.2R},", ventMech.ZoneMaxOAFraction);
1818 54 : print(state.files.eio, "{},", ventMech.NumofVentMechZones);
1819 :
1820 459 : for (int jZone = 1; jZone <= ventMech.NumofVentMechZones; ++jZone) {
1821 405 : auto &thisVentMechZone = ventMech.VentMechZone(jZone);
1822 405 : if (jZone < ventMech.NumofVentMechZones) {
1823 351 : print(state.files.eio,
1824 : "{},{},{},",
1825 351 : state.dataHeatBal->Zone(thisVentMechZone.zoneNum).Name,
1826 351 : thisVentMechZone.ZoneDesignSpecOAObjName,
1827 351 : thisVentMechZone.ZoneDesignSpecADObjName);
1828 : } else {
1829 54 : print(state.files.eio,
1830 : "{},{},{}\n",
1831 54 : state.dataHeatBal->Zone(thisVentMechZone.zoneNum).Name,
1832 54 : thisVentMechZone.ZoneDesignSpecOAObjName,
1833 54 : thisVentMechZone.ZoneDesignSpecADObjName);
1834 : }
1835 : }
1836 26 : }
1837 :
1838 : } // Number of Mechanical Ventilation Objects > 0
1839 :
1840 413 : AlphArray.deallocate();
1841 413 : NumArray.deallocate();
1842 413 : lNumericBlanks.deallocate();
1843 413 : lAlphaBlanks.deallocate();
1844 413 : cAlphaFields.deallocate();
1845 413 : cNumericFields.deallocate();
1846 :
1847 413 : if (ErrorsFound) {
1848 0 : ShowFatalError(state, format("{}Errors found when getting {} inputs.", RoutineName, CurrentModuleObject));
1849 : }
1850 413 : }
1851 :
1852 416 : void AllocateOAControllers(EnergyPlusData &state)
1853 : {
1854 : // PURPOSE OF THIS SUBROUTINE:
1855 : // Allocate the OA controller arrays which are shared by Controller:OutdoorAir and ZoneHVAC:EnergyRecoveryVentilator:Controller
1856 :
1857 416 : if (state.dataMixedAir->AllocateOAControllersFlag) {
1858 828 : state.dataMixedAir->NumOAControllers =
1859 414 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObjects[static_cast<int>(CMO::OAController)]);
1860 828 : state.dataMixedAir->NumERVControllers =
1861 414 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObjects[static_cast<int>(CMO::ERVController)]);
1862 414 : state.dataMixedAir->NumOAControllers += state.dataMixedAir->NumERVControllers;
1863 414 : state.dataMixedAir->OAController.allocate(state.dataMixedAir->NumOAControllers);
1864 414 : state.dataMixedAir->OAControllerUniqueNames.reserve(static_cast<unsigned>(state.dataMixedAir->NumOAControllers));
1865 414 : state.dataMixedAir->AllocateOAControllersFlag = false;
1866 : }
1867 416 : }
1868 :
1869 456 : void GetOAMixerInputs(EnergyPlusData &state)
1870 : {
1871 :
1872 : // SUBROUTINE INFORMATION:
1873 : // AUTHOR Fred Buhl
1874 : // DATE WRITTEN Oct 1998
1875 :
1876 : // PURPOSE OF THIS SUBROUTINE
1877 : // Input the OAMixer data and store it in the OAMixer array.
1878 :
1879 : // METHODOLOGY EMPLOYED:
1880 : // Use the Get routines from the InputProcessor module.
1881 :
1882 : // SUBROUTINE PARAMETER DEFINITIONS:
1883 : static constexpr std::string_view RoutineName("GetOAMixerInputs: "); // include trailing blank space
1884 :
1885 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1886 :
1887 : int NumNums; // Number of REAL(r64) numbers returned by GetObjectItem
1888 : int NumAlphas; // Number of alphanumerics returned by GetObjectItem
1889 : int NumArg; // Number of arguments from GetObjectDefMaxArgs call
1890 456 : Array1D<Real64> NumArray; // array that holds numeric input values
1891 456 : Array1D_string AlphArray; // array that holds alpha input values
1892 456 : Array1D_string cAlphaFields; // Alpha field names
1893 456 : Array1D_string cNumericFields; // Numeric field names
1894 456 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
1895 456 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
1896 456 : bool ErrorsFound(false);
1897 :
1898 456 : if (!state.dataMixedAir->GetOAMixerInputFlag) {
1899 0 : return;
1900 : }
1901 :
1902 912 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
1903 456 : state, CurrentModuleObjects[static_cast<int>(CMO::OAMixer)], NumArg, NumAlphas, NumNums);
1904 :
1905 456 : AlphArray.allocate(NumAlphas);
1906 456 : NumArray.dimension(NumNums, 0.0);
1907 456 : lNumericBlanks.dimension(NumNums, true);
1908 456 : lAlphaBlanks.dimension(NumAlphas, true);
1909 456 : cAlphaFields.allocate(NumAlphas);
1910 456 : cNumericFields.allocate(NumNums);
1911 :
1912 456 : std::string_view const CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::OAMixer)];
1913 :
1914 456 : state.dataMixedAir->NumOAMixers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
1915 :
1916 456 : if (state.dataMixedAir->NumOAMixers > 0) {
1917 :
1918 456 : state.dataMixedAir->OAMixer.allocate(state.dataMixedAir->NumOAMixers);
1919 : int IOStat;
1920 :
1921 1975 : for (int OutAirNum = 1; OutAirNum <= state.dataMixedAir->NumOAMixers; ++OutAirNum) {
1922 1519 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1923 : CurrentModuleObject,
1924 : OutAirNum,
1925 : AlphArray,
1926 : NumAlphas,
1927 : NumArray,
1928 : NumNums,
1929 : IOStat,
1930 : lNumericBlanks,
1931 : lAlphaBlanks,
1932 : cAlphaFields,
1933 : cNumericFields);
1934 : // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
1935 1519 : state.dataMixedAir->OAMixer(OutAirNum).Name = AlphArray(1);
1936 3038 : state.dataMixedAir->OAMixer(OutAirNum).MixNode = NodeInputManager::GetOnlySingleNode(state,
1937 1519 : AlphArray(2),
1938 : ErrorsFound,
1939 : DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
1940 1519 : AlphArray(1),
1941 : DataLoopNode::NodeFluidType::Air,
1942 : DataLoopNode::ConnectionType::Outlet,
1943 : NodeInputManager::CompFluidStream::Primary,
1944 : ObjectIsNotParent);
1945 : // Set connection type to 'Inlet', because this is not necessarily directly from
1946 : // outside air. Outside Air Inlet Node List will set the connection to outside air
1947 1519 : state.dataMixedAir->OAMixer(OutAirNum).InletNode =
1948 3038 : NodeInputManager::GetOnlySingleNode(state,
1949 1519 : AlphArray(3),
1950 : ErrorsFound,
1951 : DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
1952 1519 : AlphArray(1),
1953 : DataLoopNode::NodeFluidType::Air,
1954 : DataLoopNode::ConnectionType::Inlet,
1955 : NodeInputManager::CompFluidStream::Primary,
1956 : ObjectIsNotParent);
1957 3038 : state.dataMixedAir->OAMixer(OutAirNum).RelNode = NodeInputManager::GetOnlySingleNode(state,
1958 1519 : AlphArray(4),
1959 : ErrorsFound,
1960 : DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
1961 1519 : AlphArray(1),
1962 : DataLoopNode::NodeFluidType::Air,
1963 : DataLoopNode::ConnectionType::ReliefAir,
1964 : NodeInputManager::CompFluidStream::Primary,
1965 : ObjectIsNotParent);
1966 3038 : state.dataMixedAir->OAMixer(OutAirNum).RetNode = NodeInputManager::GetOnlySingleNode(state,
1967 1519 : AlphArray(5),
1968 : ErrorsFound,
1969 : DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
1970 1519 : AlphArray(1),
1971 : DataLoopNode::NodeFluidType::Air,
1972 : DataLoopNode::ConnectionType::Inlet,
1973 : NodeInputManager::CompFluidStream::Primary,
1974 : ObjectIsNotParent);
1975 : // Check for dupes in the four nodes.
1976 1519 : if (state.dataMixedAir->OAMixer(OutAirNum).MixNode == state.dataMixedAir->OAMixer(OutAirNum).InletNode) {
1977 0 : ShowSevereError(state,
1978 0 : format("{} = {} {} = {} duplicates the {}.",
1979 : CurrentModuleObject,
1980 0 : state.dataMixedAir->OAMixer(OutAirNum).Name,
1981 : cAlphaFields(3),
1982 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).InletNode),
1983 : cAlphaFields(2)));
1984 0 : ErrorsFound = true;
1985 1519 : } else if (state.dataMixedAir->OAMixer(OutAirNum).MixNode == state.dataMixedAir->OAMixer(OutAirNum).RelNode) {
1986 0 : ShowSevereError(state,
1987 0 : format("{} = {} {} = {} duplicates the {}.",
1988 : CurrentModuleObject,
1989 0 : state.dataMixedAir->OAMixer(OutAirNum).Name,
1990 : cAlphaFields(4),
1991 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RelNode),
1992 : cAlphaFields(2)));
1993 0 : ErrorsFound = true;
1994 1519 : } else if (state.dataMixedAir->OAMixer(OutAirNum).MixNode == state.dataMixedAir->OAMixer(OutAirNum).RetNode) {
1995 0 : ShowSevereError(state,
1996 0 : format("{} = {} {} = {} duplicates the {}.",
1997 : CurrentModuleObject,
1998 0 : state.dataMixedAir->OAMixer(OutAirNum).Name,
1999 : cAlphaFields(5),
2000 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RetNode),
2001 : cAlphaFields(2)));
2002 0 : ErrorsFound = true;
2003 : }
2004 :
2005 1519 : if (state.dataMixedAir->OAMixer(OutAirNum).InletNode == state.dataMixedAir->OAMixer(OutAirNum).RelNode) {
2006 0 : ShowSevereError(state,
2007 0 : format("{} = {} {} = {} duplicates the {}.",
2008 : CurrentModuleObject,
2009 0 : state.dataMixedAir->OAMixer(OutAirNum).Name,
2010 : cAlphaFields(4),
2011 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RelNode),
2012 : cAlphaFields(3)));
2013 0 : ErrorsFound = true;
2014 1519 : } else if (state.dataMixedAir->OAMixer(OutAirNum).InletNode == state.dataMixedAir->OAMixer(OutAirNum).RetNode) {
2015 0 : ShowSevereError(state,
2016 0 : format("{} = {} {} = {} duplicates the {}.",
2017 : CurrentModuleObject,
2018 0 : state.dataMixedAir->OAMixer(OutAirNum).Name,
2019 : cAlphaFields(5),
2020 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RetNode),
2021 : cAlphaFields(3)));
2022 0 : ErrorsFound = true;
2023 : }
2024 :
2025 1519 : if (state.dataMixedAir->OAMixer(OutAirNum).RelNode == state.dataMixedAir->OAMixer(OutAirNum).RetNode) {
2026 0 : ShowSevereError(state,
2027 0 : format("{} = {} {} = {} duplicates the {}.",
2028 : CurrentModuleObject,
2029 0 : state.dataMixedAir->OAMixer(OutAirNum).Name,
2030 : cAlphaFields(5),
2031 0 : state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RetNode),
2032 : cAlphaFields(4)));
2033 0 : ErrorsFound = true;
2034 : }
2035 4557 : BranchNodeConnections::TestCompSet(
2036 1519 : state, CurrentModuleObject, state.dataMixedAir->OAMixer(OutAirNum).Name, AlphArray(3), AlphArray(2), "Air Nodes");
2037 : }
2038 : }
2039 :
2040 456 : if (ErrorsFound) {
2041 0 : ShowFatalError(state, format("{}Errors found in getting {}", RoutineName, CurrentModuleObject));
2042 : }
2043 :
2044 456 : state.dataMixedAir->GetOAMixerInputFlag = false;
2045 456 : }
2046 :
2047 1067 : void ProcessOAControllerInputs(EnergyPlusData &state,
2048 : std::string_view const CurrentModuleObject,
2049 : int const OutAirNum,
2050 : Array1D_string const &AlphArray,
2051 : int const NumAlphas,
2052 : Array1D<Real64> const &NumArray,
2053 : int const NumNums,
2054 : Array1D_bool const &lNumericBlanks, // Unused
2055 : Array1D_bool const &lAlphaBlanks,
2056 : Array1D_string const &cAlphaFields,
2057 : Array1D_string const &cNumericFields, // Unused
2058 : bool &ErrorsFound // If errors found in input
2059 : )
2060 : {
2061 :
2062 : // SUBROUTINE INFORMATION:
2063 : // AUTHOR Fred Buhl
2064 : // DATE WRITTEN Oct 1998
2065 : // MODIFIED Shirey/Raustad FSEC, June 2003, Jan 2004
2066 : // Mangesh Basarkar, 06/2011: Getting zone OA specifications from Design Specification Object
2067 : // Tianzhen Hong, 3/2012: getting zone air distribution effectiveness and secondary recirculation
2068 : // from DesignSpecification:ZoneAirDistribution objects
2069 : // RE-ENGINEERED MJW: Split out processing controller:outdoorair input to facilitate unit testing, Feb 2015
2070 :
2071 : // PURPOSE OF THIS SUBROUTINE
2072 : // Input the OAController data and store it in the OAController array.
2073 :
2074 : // SUBROUTINE PARAMETER DEFINITIONS:
2075 : static constexpr std::string_view RoutineName("GetOAControllerInputs: "); // include trailing blank space
2076 : static constexpr std::string_view routineName = "GetOAControllerInputs";
2077 :
2078 1067 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, AlphArray(1)};
2079 :
2080 1067 : state.dataMixedAir->OAController(OutAirNum).Name = AlphArray(1);
2081 1067 : state.dataMixedAir->OAController(OutAirNum).ControllerType = MixedAirControllerType::ControllerOutsideAir;
2082 1067 : state.dataMixedAir->OAController(OutAirNum).MaxOA = NumArray(2);
2083 1067 : state.dataMixedAir->OAController(OutAirNum).MinOA = NumArray(1);
2084 1067 : state.dataMixedAir->OAController(OutAirNum).MixNode =
2085 2134 : NodeInputManager::GetOnlySingleNode(state,
2086 1067 : AlphArray(4),
2087 : ErrorsFound,
2088 : DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
2089 1067 : AlphArray(1),
2090 : DataLoopNode::NodeFluidType::Air,
2091 : DataLoopNode::ConnectionType::Sensor,
2092 : NodeInputManager::CompFluidStream::Primary,
2093 : ObjectIsNotParent);
2094 2134 : state.dataMixedAir->OAController(OutAirNum).OANode = NodeInputManager::GetOnlySingleNode(state,
2095 1067 : AlphArray(5),
2096 : ErrorsFound,
2097 : DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
2098 1067 : AlphArray(1),
2099 : DataLoopNode::NodeFluidType::Air,
2100 : DataLoopNode::ConnectionType::Actuator,
2101 : NodeInputManager::CompFluidStream::Primary,
2102 : ObjectIsNotParent);
2103 1067 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, state.dataMixedAir->OAController(OutAirNum).OANode)) {
2104 0 : ShowWarningError(state,
2105 0 : format("{}=\"{}\": {}=\"{}\" is not an OutdoorAir:Node.", CurrentModuleObject, AlphArray(1), cAlphaFields(5), AlphArray(5)));
2106 0 : ShowContinueError(state, "Confirm that this is the intended source for the outdoor air stream.");
2107 : }
2108 1067 : if (Util::SameString(AlphArray(6), "NoEconomizer")) {
2109 531 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::NoEconomizer;
2110 536 : } else if (Util::SameString(AlphArray(6), "FixedDryBulb")) {
2111 130 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::FixedDryBulb;
2112 406 : } else if (Util::SameString(AlphArray(6), "FixedEnthalpy")) {
2113 0 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::FixedEnthalpy;
2114 406 : } else if (Util::SameString(AlphArray(6), "FixedDewPointAndDryBulb")) {
2115 0 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::FixedDewPointAndDryBulb;
2116 406 : } else if (Util::SameString(AlphArray(6), "DifferentialDryBulb")) {
2117 374 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::DifferentialDryBulb;
2118 32 : } else if (Util::SameString(AlphArray(6), "DifferentialEnthalpy")) {
2119 25 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::DifferentialEnthalpy;
2120 7 : } else if (Util::SameString(AlphArray(6), "DifferentialDryBulbAndEnthalpy")) {
2121 0 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::DifferentialDryBulbAndEnthalpy;
2122 7 : } else if (Util::SameString(AlphArray(6), "ElectronicEnthalpy")) {
2123 7 : state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::ElectronicEnthalpy;
2124 : } else {
2125 0 : ShowSevereError(state, format("{}=\"{}\" invalid {}=\"{}\" value.", CurrentModuleObject, AlphArray(1), cAlphaFields(6), AlphArray(6)));
2126 0 : ErrorsFound = true;
2127 : }
2128 : // Bypass choice - Added by Amit for new feature implementation
2129 1067 : if (Util::SameString(AlphArray(7), "ModulateFlow")) {
2130 1050 : state.dataMixedAir->OAController(OutAirNum).EconBypass = false;
2131 17 : } else if (Util::SameString(AlphArray(7), "MinimumFlowWithBypass")) {
2132 17 : state.dataMixedAir->OAController(OutAirNum).EconBypass = true;
2133 : } else {
2134 0 : ShowSevereError(state, format("{}=\"{}\" invalid {}=\"{}\" value.", CurrentModuleObject, AlphArray(1), cAlphaFields(7), AlphArray(7)));
2135 0 : ErrorsFound = true;
2136 : }
2137 :
2138 1067 : if (Util::SameString(AlphArray(9), "NoLockout")) {
2139 825 : state.dataMixedAir->OAController(OutAirNum).Lockout = LockoutType::NoLockoutPossible;
2140 242 : } else if (Util::SameString(AlphArray(9), "LockoutWithHeating")) {
2141 201 : state.dataMixedAir->OAController(OutAirNum).Lockout = LockoutType::LockoutWithHeatingPossible;
2142 41 : } else if (Util::SameString(AlphArray(9), "LockoutWithCompressor")) {
2143 41 : state.dataMixedAir->OAController(OutAirNum).Lockout = LockoutType::LockoutWithCompressorPossible;
2144 : } else {
2145 0 : ShowSevereError(state, format("{}=\"{}\" invalid {}=\"{}\" value.", CurrentModuleObject, AlphArray(1), cAlphaFields(9), AlphArray(9)));
2146 0 : ErrorsFound = true;
2147 : }
2148 1067 : if (Util::SameString(AlphArray(10), "FixedMinimum")) {
2149 1024 : state.dataMixedAir->OAController(OutAirNum).FixedMin = true;
2150 : } else {
2151 43 : state.dataMixedAir->OAController(OutAirNum).FixedMin = false;
2152 : }
2153 1067 : if (lNumericBlanks(3)) {
2154 297 : state.dataMixedAir->OAController(OutAirNum).TempLim = HVAC::BlankNumeric;
2155 : } else {
2156 770 : state.dataMixedAir->OAController(OutAirNum).TempLim = NumArray(3);
2157 : }
2158 :
2159 1067 : if (lNumericBlanks(4)) {
2160 651 : state.dataMixedAir->OAController(OutAirNum).EnthLim = HVAC::BlankNumeric;
2161 : } else {
2162 416 : state.dataMixedAir->OAController(OutAirNum).EnthLim = NumArray(4);
2163 : }
2164 1067 : if (lNumericBlanks(5)) {
2165 1052 : state.dataMixedAir->OAController(OutAirNum).DPTempLim = HVAC::BlankNumeric;
2166 : } else {
2167 15 : state.dataMixedAir->OAController(OutAirNum).DPTempLim = NumArray(5);
2168 : }
2169 :
2170 1067 : if (lNumericBlanks(6)) {
2171 370 : state.dataMixedAir->OAController(OutAirNum).TempLowLim = HVAC::BlankNumeric;
2172 : } else {
2173 697 : state.dataMixedAir->OAController(OutAirNum).TempLowLim = NumArray(6);
2174 : }
2175 :
2176 1067 : if (!lAlphaBlanks(8)) {
2177 7 : state.dataMixedAir->OAController(OutAirNum).EnthalpyCurvePtr = Curve::GetCurveIndex(state, AlphArray(8)); // convert curve name to number
2178 7 : if (state.dataMixedAir->OAController(OutAirNum).EnthalpyCurvePtr == 0) {
2179 0 : ShowSevereError(state,
2180 0 : format("{}=\"{}\" invalid {}=\"{}\" not found.", CurrentModuleObject, AlphArray(1), cAlphaFields(8), AlphArray(8)));
2181 0 : ErrorsFound = true;
2182 : } else {
2183 : // Verify Curve Object, only legal types are Quadratic and Cubic
2184 28 : ErrorsFound |= Curve::CheckCurveDims(state,
2185 7 : state.dataMixedAir->OAController(OutAirNum).EnthalpyCurvePtr, // Curve index
2186 : {1}, // Valid dimensions
2187 : RoutineName, // Routine name
2188 : CurrentModuleObject, // Object Type
2189 7 : state.dataMixedAir->OAController(OutAirNum).Name, // Object Name
2190 7 : cAlphaFields(8)); // Field Name
2191 : }
2192 : }
2193 :
2194 1067 : state.dataMixedAir->OAController(OutAirNum).RelNode =
2195 2134 : NodeInputManager::GetOnlySingleNode(state,
2196 1067 : AlphArray(2),
2197 : ErrorsFound,
2198 : DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
2199 1067 : AlphArray(1),
2200 : DataLoopNode::NodeFluidType::Air,
2201 : DataLoopNode::ConnectionType::Actuator,
2202 : NodeInputManager::CompFluidStream::Primary,
2203 : ObjectIsNotParent);
2204 1067 : state.dataMixedAir->OAController(OutAirNum).RetNode =
2205 2134 : NodeInputManager::GetOnlySingleNode(state,
2206 1067 : AlphArray(3),
2207 : ErrorsFound,
2208 : DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
2209 1067 : AlphArray(1),
2210 : DataLoopNode::NodeFluidType::Air,
2211 : DataLoopNode::ConnectionType::Sensor,
2212 : NodeInputManager::CompFluidStream::Primary,
2213 : ObjectIsNotParent);
2214 :
2215 1067 : if (lAlphaBlanks(11)) {
2216 994 : } else if ((state.dataMixedAir->OAController(OutAirNum).minOASched = Sched::GetSchedule(state, AlphArray(11))) == nullptr) {
2217 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(11), AlphArray(11));
2218 0 : ErrorsFound = true;
2219 : }
2220 :
2221 : // Changed by Amit for new feature implementation
2222 1067 : if (lAlphaBlanks(12)) {
2223 45 : } else if ((state.dataMixedAir->OAController(OutAirNum).minOAflowSched = Sched::GetSchedule(state, AlphArray(12))) == nullptr) {
2224 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(12), AlphArray(12));
2225 0 : ErrorsFound = true;
2226 : }
2227 :
2228 1067 : if (lAlphaBlanks(13)) {
2229 59 : } else if ((state.dataMixedAir->OAController(OutAirNum).maxOAflowSched = Sched::GetSchedule(state, AlphArray(13))) == nullptr) {
2230 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(13), AlphArray(13));
2231 0 : ErrorsFound = true;
2232 : }
2233 1067 : state.dataMixedAir->OAController(OutAirNum).VentilationMechanicalName = AlphArray(14);
2234 :
2235 : // Check for a time of day economizer control schedule
2236 1067 : state.dataMixedAir->OAController(OutAirNum).economizerOASched = Sched::GetSchedule(state, AlphArray(15));
2237 :
2238 : // High humidity control option can be used with any economizer flag
2239 1067 : if (Util::SameString(AlphArray(16), "Yes")) {
2240 :
2241 6 : state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum = Util::FindItemInList(AlphArray(17), state.dataHeatBal->Zone);
2242 :
2243 : // Get the node number for the zone with the humidistat
2244 6 : if (state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum > 0) {
2245 6 : bool AirNodeFound = false;
2246 6 : bool AirLoopFound = false;
2247 6 : bool OASysFound = false;
2248 25 : for (int ControlledZoneNum = 1; ControlledZoneNum <= state.dataGlobal->NumOfZones; ++ControlledZoneNum) {
2249 19 : if (ControlledZoneNum != state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum) {
2250 13 : continue;
2251 : }
2252 : // Find the controlled zone number for the specified humidistat location
2253 6 : state.dataMixedAir->OAController(OutAirNum).NodeNumofHumidistatZone =
2254 6 : state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneNode;
2255 : // Determine which OA System uses this OA Controller
2256 6 : int OASysIndex = 0;
2257 6 : for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
2258 6 : for (int OAControllerNum = 1; OAControllerNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumControllers; ++OAControllerNum) {
2259 12 : if (!Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerType(OAControllerNum), CurrentModuleObject) ||
2260 6 : !Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName(OAControllerNum),
2261 6 : state.dataMixedAir->OAController(OutAirNum).Name)) {
2262 0 : continue;
2263 : }
2264 6 : OASysIndex = OASysNum;
2265 6 : OASysFound = true;
2266 6 : break;
2267 : }
2268 6 : if (OASysFound) {
2269 6 : break;
2270 : }
2271 : }
2272 : // Determine if controller is on air loop served by the humidistat location specified
2273 12 : for (int zoneInNode = 1; zoneInNode <= state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumInletNodes; ++zoneInNode) {
2274 6 : int AirLoopNumber = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).InletNodeAirLoopNum(zoneInNode);
2275 6 : if (AirLoopNumber > 0 && OASysIndex > 0) {
2276 6 : for (int BranchNum = 1; BranchNum <= state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).NumBranches; ++BranchNum) {
2277 6 : for (int CompNum = 1;
2278 6 : CompNum <= state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).Branch(BranchNum).TotalComponents;
2279 : ++CompNum) {
2280 6 : if (!Util::SameString(state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).Branch(BranchNum).Comp(CompNum).Name,
2281 18 : state.dataAirLoop->OutsideAirSys(OASysIndex).Name) ||
2282 18 : !Util::SameString(
2283 6 : state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).Branch(BranchNum).Comp(CompNum).TypeOf,
2284 : "AirLoopHVAC:OutdoorAirSystem")) {
2285 0 : continue;
2286 : }
2287 6 : AirLoopFound = true;
2288 6 : break;
2289 : }
2290 6 : if (AirLoopFound) {
2291 6 : break;
2292 : }
2293 : }
2294 6 : for (int HStatZoneNum = 1; HStatZoneNum <= state.dataZoneCtrls->NumHumidityControlZones; ++HStatZoneNum) {
2295 6 : if (state.dataZoneCtrls->HumidityControlZone(HStatZoneNum).ActualZoneNum !=
2296 6 : state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum) {
2297 0 : continue;
2298 : }
2299 6 : AirNodeFound = true;
2300 6 : break;
2301 : }
2302 6 : } else {
2303 0 : if (OASysIndex == 0) {
2304 0 : ShowSevereError(
2305 : state,
2306 0 : format("Did not find an AirLoopHVAC:OutdoorAirSystem for {} = \"{}\"",
2307 0 : MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
2308 0 : state.dataMixedAir->OAController(OutAirNum).Name));
2309 0 : ErrorsFound = true;
2310 : }
2311 : }
2312 : }
2313 : }
2314 6 : if (!AirNodeFound) {
2315 0 : ShowSevereError(state,
2316 0 : format("Did not find Air Node (Zone with Humidistat), {} = \"{}\"",
2317 0 : MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
2318 0 : state.dataMixedAir->OAController(OutAirNum).Name));
2319 0 : ShowContinueError(state, format("Specified {} = {}", cAlphaFields(17), AlphArray(17)));
2320 0 : ShowContinueError(state,
2321 : "Both a ZoneHVAC:EquipmentConnections object and a ZoneControl:Humidistat object must be specified for this zone.");
2322 0 : ErrorsFound = true;
2323 : }
2324 6 : if (!AirLoopFound) {
2325 0 : ShowSevereError(state,
2326 0 : format("Did not find correct Primary Air Loop for {} = \"{}\"",
2327 0 : MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
2328 0 : state.dataMixedAir->OAController(OutAirNum).Name));
2329 0 : ShowContinueError(state, format("{} = {} is not served by this Primary Air Loop equipment.", cAlphaFields(17), AlphArray(17)));
2330 0 : ErrorsFound = true;
2331 : }
2332 : } else {
2333 0 : ShowSevereError(state,
2334 0 : format("Did not find Air Node (Zone with Humidistat), {} = \"{}\"",
2335 0 : MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
2336 0 : state.dataMixedAir->OAController(OutAirNum).Name));
2337 0 : ShowContinueError(state, format("Specified {} = {}", cAlphaFields(17), AlphArray(17)));
2338 0 : ShowContinueError(state,
2339 : "Both a ZoneHVAC:EquipmentConnections object and a ZoneControl:Humidistat object must be specified for this zone.");
2340 0 : ErrorsFound = true;
2341 : }
2342 :
2343 6 : state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio = NumArray(7);
2344 6 : if (state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio <= 0.0 && NumNums > 6) {
2345 0 : ShowWarningError(state, format("{} \"{}\"", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
2346 0 : ShowContinueError(state, format(" {} must be greater than 0.", cNumericFields(7)));
2347 0 : ShowContinueError(state, format(" {} is reset to 1 and the simulation continues.", cNumericFields(7)));
2348 0 : state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio = 1.0;
2349 : }
2350 :
2351 6 : if (Util::SameString(AlphArray(16), "Yes") && state.dataMixedAir->OAController(OutAirNum).FixedMin) {
2352 6 : if (state.dataMixedAir->OAController(OutAirNum).MaxOA > 0.0 && state.dataMixedAir->OAController(OutAirNum).MinOA != AutoSize) {
2353 5 : Real64 OAFlowRatio = state.dataMixedAir->OAController(OutAirNum).MinOA / state.dataMixedAir->OAController(OutAirNum).MaxOA;
2354 5 : if (state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio < OAFlowRatio) {
2355 0 : ShowWarningError(state, format("{} \"{}\"", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
2356 0 : ShowContinueError(state, "... A fixed minimum outside air flow rate and high humidity control have been specified.");
2357 0 : ShowContinueError(
2358 : state,
2359 0 : format("... The {} is less than the ratio of the outside air controllers minimum to maximum outside air flow rate.",
2360 : cNumericFields(7)));
2361 0 : ShowContinueError(
2362 0 : state, format("... Controller {} = {:.4T} m3/s.", cNumericFields(1), state.dataMixedAir->OAController(OutAirNum).MinOA));
2363 0 : ShowContinueError(
2364 0 : state, format("... Controller {} = {:.4T} m3/s.", cNumericFields(2), state.dataMixedAir->OAController(OutAirNum).MaxOA));
2365 0 : ShowContinueError(state, format("... Controller minimum to maximum flow ratio = {:.4T}.", OAFlowRatio));
2366 0 : ShowContinueError(state,
2367 0 : format("... {} = {:.4T}.", cNumericFields(7), state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio));
2368 : }
2369 : }
2370 : }
2371 :
2372 6 : if (NumAlphas >= 18) {
2373 6 : if (Util::SameString(AlphArray(18), "Yes")) {
2374 6 : state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = false;
2375 0 : } else if (Util::SameString(AlphArray(18), "No")) {
2376 0 : state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = true;
2377 : } else {
2378 0 : ShowSevereError(state,
2379 0 : format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
2380 0 : ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(18), AlphArray(18)));
2381 0 : ErrorsFound = true;
2382 : }
2383 : } else {
2384 0 : if (state.dataMixedAir->OAController(OutAirNum).Econo == EconoOp::NoEconomizer) {
2385 0 : state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = true;
2386 : } else {
2387 0 : state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = false;
2388 0 : ShowWarningError(state,
2389 0 : format("{} \"{}\", missing field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
2390 0 : ShowContinueError(state, format("...{} will default to Yes when {}= \"Yes\"", cAlphaFields(18), cAlphaFields(16)));
2391 : }
2392 : }
2393 :
2394 1061 : } else if (Util::SameString(AlphArray(16), "No") || lAlphaBlanks(16)) {
2395 1061 : if (NumAlphas >= 18) {
2396 132 : if (!Util::SameString(AlphArray(18), "Yes") && !Util::SameString(AlphArray(18), "No")) {
2397 0 : ShowSevereError(state,
2398 0 : format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
2399 0 : ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(18), AlphArray(18)));
2400 0 : ErrorsFound = true;
2401 : }
2402 : }
2403 : } else { // Invalid field 16
2404 0 : ShowSevereError(state, format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
2405 0 : ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(16), AlphArray(16)));
2406 0 : ErrorsFound = true;
2407 0 : if (NumAlphas >= 18) {
2408 0 : if (!Util::SameString(AlphArray(18), "Yes") && !Util::SameString(AlphArray(18), "No")) {
2409 0 : ShowSevereError(state,
2410 0 : format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
2411 0 : ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(18), AlphArray(18)));
2412 0 : ErrorsFound = true;
2413 : }
2414 : }
2415 : }
2416 :
2417 1067 : if (NumAlphas > 18) {
2418 131 : if (!lAlphaBlanks(19)) {
2419 127 : if (Util::SameString(AlphArray(19), "BypassWhenWithinEconomizerLimits")) {
2420 104 : state.dataMixedAir->OAController(OutAirNum).HeatRecoveryBypassControlType = HVAC::BypassWhenWithinEconomizerLimits;
2421 23 : } else if (Util::SameString(AlphArray(19), "BypassWhenOAFlowGreaterThanMinimum")) {
2422 23 : state.dataMixedAir->OAController(OutAirNum).HeatRecoveryBypassControlType = HVAC::BypassWhenOAFlowGreaterThanMinimum;
2423 : } else {
2424 0 : ShowWarningError(state, format("{}=\"{}\" invalid {}=\"{}\".", CurrentModuleObject, AlphArray(1), cAlphaFields(19), AlphArray(19)));
2425 0 : ShowContinueError(state, "...assuming \"BypassWhenWithinEconomizerLimits\" and the simulation continues.");
2426 0 : state.dataMixedAir->OAController(OutAirNum).HeatRecoveryBypassControlType = HVAC::BypassWhenWithinEconomizerLimits;
2427 : }
2428 : }
2429 : }
2430 :
2431 1067 : if (NumAlphas > 19) {
2432 4 : if (!lAlphaBlanks(20)) {
2433 4 : if (Util::SameString(AlphArray(20), "EconomizerFirst")) {
2434 4 : state.dataMixedAir->OAController(OutAirNum).EconomizerStagingType = HVAC::EconomizerStagingType::EconomizerFirst;
2435 : } else {
2436 0 : state.dataMixedAir->OAController(OutAirNum).EconomizerStagingType = HVAC::EconomizerStagingType::InterlockedWithMechanicalCooling;
2437 : }
2438 : }
2439 : }
2440 :
2441 1067 : if (Util::SameString(AlphArray(16), "Yes") && state.dataMixedAir->OAController(OutAirNum).Econo == EconoOp::NoEconomizer) {
2442 0 : ShowWarningError(state,
2443 0 : format("{} \"{}\"",
2444 0 : MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
2445 0 : state.dataMixedAir->OAController(OutAirNum).Name));
2446 0 : ShowContinueError(state, format("...Economizer operation must be enabled when {} is set to YES.", cAlphaFields(16)));
2447 0 : ShowContinueError(state, "...The high humidity control option will be disabled and the simulation continues.");
2448 : }
2449 :
2450 1067 : state.dataMixedAir->OAController(OutAirNum).MixedAirSPMNum =
2451 1067 : SetPointManager::GetMixedAirNumWithCoilFreezingCheck(state, state.dataMixedAir->OAController(OutAirNum).MixNode);
2452 1067 : }
2453 :
2454 : // End of Get Input subroutines for the Module
2455 : //******************************************************************************
2456 :
2457 : // Beginning Initialization Section of the Module
2458 : //******************************************************************************
2459 :
2460 27043260 : void InitOutsideAirSys(EnergyPlusData &state, int const(OASysNum), int const AirLoopNum)
2461 : {
2462 :
2463 : // SUBROUTINE INFORMATION:
2464 : // AUTHOR Fred Buhl
2465 : // DATE WRITTEN Oct 1998
2466 :
2467 : // PURPOSE OF THIS SUBROUTINE
2468 : // Initialize the OutsideAirSys data structure
2469 :
2470 27043260 : if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum > -1) {
2471 10882 : return;
2472 : }
2473 :
2474 27032378 : if (state.dataMixedAir->initOASysFlag(OASysNum)) {
2475 1067 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum = OASysNum;
2476 1067 : state.dataMixedAir->initOASysFlag(OASysNum) = false;
2477 : }
2478 : }
2479 :
2480 27596359 : void InitOAController(EnergyPlusData &state, int const OAControllerNum, bool const FirstHVACIteration, int const AirLoopNum)
2481 : {
2482 :
2483 : // SUBROUTINE INFORMATION:
2484 : // AUTHOR Fred Buhl
2485 : // DATE WRITTEN Oct 1998
2486 : // MODIFIED Shirey/Raustad FSEC, June/Aug 2003, Feb 2004
2487 : // Tianzhen Hong, Feb 2009 for DCV
2488 : // Tianzhen Hong, Aug 2013 for economizer faults
2489 :
2490 : // PURPOSE OF THIS SUBROUTINE
2491 : // Initialize the OAController data structure with input node data
2492 :
2493 27596359 : bool ErrorsFound = false;
2494 :
2495 27596359 : auto &thisOAController(state.dataMixedAir->OAController(OAControllerNum));
2496 :
2497 27596359 : if (state.dataMixedAir->InitOAControllerOneTimeFlag) {
2498 414 : state.dataMixedAir->OAControllerMyOneTimeFlag.dimension(state.dataMixedAir->NumOAControllers, true);
2499 414 : state.dataMixedAir->OAControllerMyEnvrnFlag.dimension(state.dataMixedAir->NumOAControllers, true);
2500 414 : state.dataMixedAir->OAControllerMySizeFlag.dimension(state.dataMixedAir->NumOAControllers, true);
2501 414 : state.dataMixedAir->MechVentCheckFlag.dimension(state.dataMixedAir->NumOAControllers, true);
2502 414 : state.dataMixedAir->InitOAControllerSetPointCheckFlag.dimension(state.dataMixedAir->NumOAControllers, true);
2503 414 : state.dataMixedAir->InitOAControllerOneTimeFlag = false;
2504 : }
2505 27596359 : if (state.dataMixedAir->OAControllerMyOneTimeFlag(OAControllerNum)) {
2506 : // Determine Inlet node index for OAController, not a user input for controller, but is obtained from OutsideAirSys and OAMixer
2507 1171 : switch (thisOAController.ControllerType) {
2508 1067 : case MixedAirControllerType::ControllerOutsideAir: {
2509 1067 : int thisOASys = 0;
2510 6389 : for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
2511 : // find which OAsys has this controller
2512 6389 : int found = Util::FindItemInList(thisOAController.Name,
2513 6389 : state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName,
2514 6389 : isize(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName));
2515 6389 : if (found != 0) {
2516 1067 : thisOASys = OASysNum;
2517 1067 : state.dataAirLoop->OutsideAirSys(thisOASys).OAControllerIndex = GetOAController(state, thisOAController.Name);
2518 1067 : break; // we found it
2519 : }
2520 : }
2521 1067 : if (thisOASys == 0) {
2522 0 : ShowSevereError(state, format("InitOAController: Did not find OAController=\"{}\".", thisOAController.Name));
2523 0 : ShowContinueError(state, "in list of valid OA Controllers.");
2524 0 : ErrorsFound = true;
2525 : }
2526 1067 : int thisNumForMixer = Util::FindItem(CurrentModuleObjects[static_cast<int>(CMO::OAMixer)],
2527 1067 : state.dataAirLoop->OutsideAirSys(thisOASys).ComponentType,
2528 1067 : isize(state.dataAirLoop->OutsideAirSys(thisOASys).ComponentType));
2529 1067 : if (thisNumForMixer != 0) {
2530 1067 : std::string_view const equipName = state.dataAirLoop->OutsideAirSys(thisOASys).ComponentName(thisNumForMixer);
2531 1067 : int thisMixerIndex = Util::FindItemInList(equipName, state.dataMixedAir->OAMixer);
2532 1067 : if (thisMixerIndex != 0) {
2533 1067 : thisOAController.InletNode = state.dataMixedAir->OAMixer(thisMixerIndex).InletNode;
2534 : } else {
2535 0 : ShowSevereError(state, format("InitOAController: Did not find OAMixer=\"{}\".", equipName));
2536 0 : ShowContinueError(state, "in list of valid OA Mixers.");
2537 0 : ErrorsFound = true;
2538 : }
2539 : } else {
2540 0 : ShowSevereError(state, "InitOAController: Did not find OutdoorAir:Mixer Component=\"OutdoorAir:Mixer\".");
2541 0 : ShowContinueError(state, "in list of valid OA Components.");
2542 0 : ErrorsFound = true;
2543 : }
2544 :
2545 1067 : if (thisOAController.InletNode == 0) { // throw an error
2546 0 : ShowSevereError(
2547 : state,
2548 0 : format("InitOAController: Failed to find proper inlet node for OutdoorAir:Mixer and Controller = {}", thisOAController.Name));
2549 0 : ErrorsFound = true;
2550 : }
2551 1067 : } break;
2552 104 : case MixedAirControllerType::ControllerStandAloneERV: {
2553 : // set the inlet node to also equal the OA node because this is a special controller for economizing stand alone ERV
2554 : // with the assumption that equipment is bypassed....
2555 104 : thisOAController.InletNode = thisOAController.OANode;
2556 104 : } break;
2557 0 : default: {
2558 0 : ShowSevereError(state,
2559 0 : format("InitOAController: Failed to find ControllerType: {}",
2560 0 : MixedAirControllerTypeNames[static_cast<int>(thisOAController.ControllerType)]));
2561 0 : ErrorsFound = true;
2562 0 : } break;
2563 : }
2564 :
2565 1171 : state.dataMixedAir->OAControllerMyOneTimeFlag(OAControllerNum) = false;
2566 : }
2567 :
2568 55192616 : if (!state.dataGlobal->SysSizingCalc && state.dataMixedAir->InitOAControllerSetPointCheckFlag(OAControllerNum) &&
2569 55192616 : state.dataHVACGlobal->DoSetPointTest && !FirstHVACIteration) {
2570 1171 : int MixedAirNode = thisOAController.MixNode;
2571 1171 : if (MixedAirNode > 0) {
2572 : // IF (OAController(OAControllerNum)%Econo == 1 .AND. .NOT. AirLoopControlInfo(AirLoopNum)%CyclingFan) THEN
2573 1067 : if (thisOAController.Econo > EconoOp::NoEconomizer && state.dataAirLoop->AirLoopControlInfo(AirLoopNum).AnyContFan) {
2574 501 : if (state.dataLoopNodes->Node(MixedAirNode).TempSetPoint == SensedNodeFlagValue) {
2575 16 : if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
2576 0 : ShowSevereError(state, format("MixedAir: Missing temperature setpoint for economizer controller {}", thisOAController.Name));
2577 0 : ShowContinueError(state, format("Node Referenced (by Controller)={}", state.dataLoopNodes->NodeID(MixedAirNode)));
2578 0 : ShowContinueError(
2579 : state, " use a Setpoint Manager with Control Variable = \"Temperature\" to establish a setpoint at the mixed air node.");
2580 0 : state.dataHVACGlobal->SetPointErrorFlag = true;
2581 : } else {
2582 : // add call to check node in EMS
2583 16 : EMSManager::CheckIfNodeSetPointManagedByEMS(
2584 16 : state, MixedAirNode, HVAC::CtrlVarType::Temp, state.dataHVACGlobal->SetPointErrorFlag);
2585 16 : if (state.dataHVACGlobal->SetPointErrorFlag) {
2586 0 : ShowSevereError(state,
2587 0 : format("MixedAir: Missing temperature setpoint for economizer controller {}", thisOAController.Name));
2588 0 : ShowContinueError(state, format("Node Referenced (by Controller)={}", state.dataLoopNodes->NodeID(MixedAirNode)));
2589 0 : ShowContinueError(state,
2590 : " use a Setpoint Manager with Control Variable = \"Temperature\" to establish a setpoint at the "
2591 : "mixed air node.");
2592 0 : ShowContinueError(state, "Or add EMS Actuator to provide temperature setpoint at this node");
2593 : }
2594 : }
2595 : }
2596 : }
2597 : }
2598 :
2599 1171 : state.dataMixedAir->InitOAControllerSetPointCheckFlag(OAControllerNum) = false;
2600 : }
2601 :
2602 27596359 : if (!state.dataGlobal->SysSizingCalc && state.dataMixedAir->OAControllerMySizeFlag(OAControllerNum)) {
2603 1171 : thisOAController.SizeOAController(state);
2604 1171 : if (AirLoopNum > 0) {
2605 1067 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OACtrlNum = OAControllerNum;
2606 1067 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OACtrlName = thisOAController.Name;
2607 1067 : if (thisOAController.Lockout == LockoutType::LockoutWithHeatingPossible) {
2608 201 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithHeating = true;
2609 201 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithCompressor = false;
2610 201 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanNotLockoutEcono = false;
2611 866 : } else if (thisOAController.Lockout == LockoutType::LockoutWithCompressorPossible) {
2612 41 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithHeating = false;
2613 41 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithCompressor = true;
2614 41 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanNotLockoutEcono = false;
2615 : } else {
2616 825 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithHeating = false;
2617 825 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithCompressor = false;
2618 825 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanNotLockoutEcono = true;
2619 : }
2620 : }
2621 1171 : if ((thisOAController.MaxOA - thisOAController.MinOA) < -HVAC::SmallAirVolFlow) {
2622 0 : ShowSevereError(state, format("For Controller:OutdoorAir: {}", thisOAController.Name));
2623 0 : ShowContinueError(state,
2624 0 : format(" maximum outdoor air flow rate ({:.4R}) < minimum outdoor air flow rate ({:.4R})",
2625 0 : thisOAController.MaxOA,
2626 0 : thisOAController.MinOA));
2627 0 : ShowContinueError(state,
2628 : " To set the minimum outside air flow rate use the \"Design (minimum) outdoor air flow rate\" field in the "
2629 : "Sizing:System object");
2630 0 : ErrorsFound = true;
2631 : }
2632 :
2633 1171 : if (AirLoopNum > 0) {
2634 1067 : Real64 DesSupplyVolFlowRate = state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply / state.dataEnvrn->StdRhoAir;
2635 1067 : if ((thisOAController.MinOA - DesSupplyVolFlowRate) > 0.0001) {
2636 0 : ShowWarningError(state,
2637 0 : format("InitOAController: Minimum Outdoor Air Flow Rate for Controller:OutdoorAir={} is greater than Design Supply "
2638 : "Air Flow Rate for AirLoopHVAC={}.",
2639 0 : thisOAController.Name,
2640 0 : state.dataAirSystemsData->PrimaryAirSystems(AirLoopNum).Name));
2641 0 : ShowContinueError(state,
2642 0 : format("...Minimum Outdoor Air Flow Rate={:.6R} will be reset to loop Design Supply Air Flow Rate={:.6R}",
2643 0 : thisOAController.MinOA,
2644 : DesSupplyVolFlowRate));
2645 0 : thisOAController.MinOA = DesSupplyVolFlowRate;
2646 1067 : } else if ((thisOAController.MinOA - DesSupplyVolFlowRate) > 0.0) {
2647 : // If difference is tiny, reset silently
2648 0 : thisOAController.MinOA = DesSupplyVolFlowRate;
2649 : }
2650 1067 : if ((thisOAController.MaxOA - DesSupplyVolFlowRate) > 0.0001) {
2651 0 : ShowWarningError(state,
2652 0 : format("InitOAController: Maximum Outdoor Air Flow Rate for Controller:OutdoorAir={} is greater than Design Supply "
2653 : "Air Flow Rate for AirLoopHVAC={}.",
2654 0 : thisOAController.Name,
2655 0 : state.dataAirSystemsData->PrimaryAirSystems(AirLoopNum).Name));
2656 0 : ShowContinueError(state,
2657 0 : format("...Maximum Outdoor Air Flow Rate={:.6R} will be reset to loop Design Supply Air Flow Rate={:.6R}",
2658 0 : thisOAController.MaxOA,
2659 : DesSupplyVolFlowRate));
2660 0 : thisOAController.MaxOA = DesSupplyVolFlowRate;
2661 1067 : } else if ((thisOAController.MaxOA - DesSupplyVolFlowRate) > 0.0) {
2662 : // If difference is tiny, reset silently
2663 23 : thisOAController.MaxOA = DesSupplyVolFlowRate;
2664 : }
2665 :
2666 : // Check if system has a Sizing:System object and a sizing run has been done
2667 1067 : bool SizingDesRunThisAirSys = false;
2668 1067 : CheckThisAirSystemForSizing(state, AirLoopNum, SizingDesRunThisAirSys);
2669 :
2670 : // Get design outdoor air flow rate
2671 1067 : if (SizingDesRunThisAirSys && thisOAController.VentMechObjectNum > 0) {
2672 47 : state.dataMixedAir->VentilationMechanical(thisOAController.VentMechObjectNum).SysDesOA =
2673 47 : state.dataSize->FinalSysSizing(AirLoopNum).DesOutAirVolFlow;
2674 : }
2675 : }
2676 :
2677 1171 : state.dataMixedAir->OAControllerMySizeFlag(OAControllerNum) = false;
2678 : }
2679 :
2680 27596359 : if (state.dataGlobal->BeginEnvrnFlag && state.dataMixedAir->OAControllerMyEnvrnFlag(OAControllerNum)) {
2681 7451 : Real64 RhoAirStdInit = state.dataEnvrn->StdRhoAir;
2682 7451 : thisOAController.MinOAMassFlowRate = thisOAController.MinOA * RhoAirStdInit;
2683 7451 : thisOAController.MaxOAMassFlowRate = thisOAController.MaxOA * RhoAirStdInit;
2684 7451 : state.dataMixedAir->OAControllerMyEnvrnFlag(OAControllerNum) = false;
2685 7451 : state.dataLoopNodes->Node(thisOAController.OANode).MassFlowRateMax = thisOAController.MaxOAMassFlowRate;
2686 :
2687 : // predefined reporting
2688 7451 : if (thisOAController.Econo > EconoOp::NoEconomizer) {
2689 3637 : std::string_view const equipName = thisOAController.Name;
2690 : // 90.1 descriptor for economizer controls. Changed by Amit for New Feature implementation
2691 3637 : if (thisOAController.Econo == EconoOp::DifferentialEnthalpy) {
2692 166 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "DifferentialEnthalpy");
2693 3471 : } else if (thisOAController.Econo == EconoOp::DifferentialDryBulb) {
2694 2653 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "DifferentialDryBulb");
2695 818 : } else if (thisOAController.Econo == EconoOp::FixedEnthalpy) {
2696 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "FixedEnthalpy");
2697 818 : } else if (thisOAController.Econo == EconoOp::FixedDryBulb) {
2698 771 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "FixedDryBulb");
2699 : } else {
2700 47 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "Other");
2701 : }
2702 :
2703 3637 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoMinOA, equipName, thisOAController.MinOA);
2704 3637 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoMaxOA, equipName, thisOAController.MaxOA);
2705 : // EnergyPlus input echos for economizer controls. Changed by Amit for new feature implementation
2706 3637 : if (thisOAController.Econo == EconoOp::DifferentialDryBulb) {
2707 2653 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "Yes");
2708 : } else {
2709 984 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "No");
2710 : }
2711 3637 : if (thisOAController.Econo == EconoOp::DifferentialEnthalpy) {
2712 166 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "Yes");
2713 : } else {
2714 3471 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "No");
2715 : }
2716 3637 : if (thisOAController.Econo == EconoOp::FixedDryBulb) {
2717 771 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, thisOAController.TempLim);
2718 : } else {
2719 2866 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "-");
2720 : }
2721 3637 : if (thisOAController.Econo == EconoOp::FixedEnthalpy) {
2722 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, thisOAController.EnthLim);
2723 : } else {
2724 3637 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "-");
2725 : }
2726 : }
2727 : }
2728 :
2729 27596359 : if (!state.dataGlobal->BeginEnvrnFlag) {
2730 27507092 : state.dataMixedAir->OAControllerMyEnvrnFlag(OAControllerNum) = true;
2731 : }
2732 :
2733 27596359 : if (state.dataMixedAir->MechVentCheckFlag(OAControllerNum)) {
2734 : // Make these checks only once at the beginning of the simulation
2735 :
2736 : // Make sure all air loop zones and air loop zones with people objects are covered by mechanical ventilation
2737 : // Issue a warning only if the zone is not accounted for in the associated mechanical ventilation object
2738 1171 : if (thisOAController.VentMechObjectNum > 0) {
2739 54 : auto &vent_mech(state.dataMixedAir->VentilationMechanical(thisOAController.VentMechObjectNum));
2740 :
2741 : // Make sure all zones with mechanical ventilation are on the correct air loop
2742 54 : int TempMechVentArrayCounter = 0;
2743 459 : for (int NumMechVentZone = 1; NumMechVentZone <= vent_mech.NumofVentMechZones; ++NumMechVentZone) {
2744 405 : auto &thisMechVentZone = vent_mech.VentMechZone(NumMechVentZone);
2745 405 : int ZoneNum = thisMechVentZone.zoneNum;
2746 405 : auto const &zone(state.dataHeatBal->Zone(ZoneNum));
2747 405 : bool FoundZone = false;
2748 :
2749 4746 : for (int AirLoopZoneInfoZoneNum = 1; AirLoopZoneInfoZoneNum <= state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).NumZones;
2750 : ++AirLoopZoneInfoZoneNum) {
2751 4746 : int NumZone = state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).ActualZoneNumber(AirLoopZoneInfoZoneNum);
2752 4746 : if (ZoneNum == NumZone) {
2753 405 : FoundZone = true;
2754 405 : ++TempMechVentArrayCounter;
2755 405 : if (TempMechVentArrayCounter < NumMechVentZone) { // Copy to lower index
2756 0 : auto &tempMechVentZone = vent_mech.VentMechZone(TempMechVentArrayCounter);
2757 0 : tempMechVentZone.zoneNum = thisMechVentZone.zoneNum;
2758 0 : tempMechVentZone.ZoneOAAreaRate = thisMechVentZone.ZoneOAAreaRate;
2759 0 : tempMechVentZone.ZoneOAPeopleRate = thisMechVentZone.ZoneOAPeopleRate;
2760 0 : tempMechVentZone.ZoneOAFlowRate = thisMechVentZone.ZoneOAFlowRate;
2761 0 : tempMechVentZone.ZoneOAACHRate = thisMechVentZone.ZoneOAACHRate;
2762 0 : tempMechVentZone.ZoneOAFlowMethod = thisMechVentZone.ZoneOAFlowMethod;
2763 0 : tempMechVentZone.zoneOASched = thisMechVentZone.zoneOASched;
2764 0 : tempMechVentZone.ZoneDesignSpecOAObjIndex = thisMechVentZone.ZoneDesignSpecOAObjIndex;
2765 0 : tempMechVentZone.ZoneDesignSpecOAObjName = thisMechVentZone.ZoneDesignSpecOAObjName;
2766 :
2767 : // new DCV
2768 0 : tempMechVentZone.ZoneADEffCooling = thisMechVentZone.ZoneADEffCooling;
2769 0 : tempMechVentZone.ZoneADEffHeating = thisMechVentZone.ZoneADEffHeating;
2770 0 : tempMechVentZone.zoneADEffSched = thisMechVentZone.zoneADEffSched;
2771 : }
2772 :
2773 : // Sum outside air per unit floor area for each mechanical ventilation object only once per simulation
2774 405 : vent_mech.TotAreaOAFlow += zone.FloorArea * zone.Multiplier * zone.ListMultiplier * thisMechVentZone.ZoneOAAreaRate;
2775 405 : vent_mech.TotZoneOAFlow += zone.Multiplier * zone.ListMultiplier * thisMechVentZone.ZoneOAFlowRate;
2776 405 : vent_mech.TotZoneOAACH += zone.Multiplier * zone.ListMultiplier * (thisMechVentZone.ZoneOAACHRate * zone.Volume / 3600.0);
2777 405 : break;
2778 : }
2779 : }
2780 405 : if (!FoundZone) {
2781 0 : ShowWarningError(state,
2782 0 : format("Zone name = {} in {} object name = {} is not on the same air loop as Controller:OutdoorAir = {}",
2783 0 : zone.Name,
2784 0 : CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
2785 0 : thisOAController.VentilationMechanicalName,
2786 0 : thisOAController.Name));
2787 0 : ShowContinueError(state, "This zone will not be used and the simulation will continue...");
2788 : }
2789 : }
2790 :
2791 : // Shrink final arrays to conserve environment space
2792 54 : if (TempMechVentArrayCounter < vent_mech.NumofVentMechZones) {
2793 0 : vent_mech.VentMechZone.resize(TempMechVentArrayCounter);
2794 0 : vent_mech.NumofVentMechZones = TempMechVentArrayCounter;
2795 : }
2796 :
2797 : // predefined report
2798 459 : for (int jZone = 1; jZone <= vent_mech.NumofVentMechZones; ++jZone) {
2799 405 : auto &thisMechVentZone = vent_mech.VentMechZone(jZone);
2800 405 : std::string_view const zoneName = state.dataHeatBal->Zone(thisMechVentZone.zoneNum).Name;
2801 405 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVventMechName, zoneName, vent_mech.Name);
2802 405 : OutputReportPredefined::PreDefTableEntry(
2803 405 : state, state.dataOutRptPredefined->pdchDCVperPerson, zoneName, thisMechVentZone.ZoneOAPeopleRate, 6);
2804 405 : OutputReportPredefined::PreDefTableEntry(
2805 405 : state, state.dataOutRptPredefined->pdchDCVperArea, zoneName, thisMechVentZone.ZoneOAAreaRate, 6);
2806 405 : OutputReportPredefined::PreDefTableEntry(
2807 405 : state, state.dataOutRptPredefined->pdchDCVperZone, zoneName, thisMechVentZone.ZoneOAFlowRate, 6);
2808 405 : OutputReportPredefined::PreDefTableEntry(
2809 405 : state, state.dataOutRptPredefined->pdchDCVperACH, zoneName, thisMechVentZone.ZoneOAACHRate, 6);
2810 405 : OutputReportPredefined::PreDefTableEntry(state,
2811 405 : state.dataOutRptPredefined->pdchDCVMethod,
2812 : zoneName,
2813 405 : OAFlowCalcMethodNames[static_cast<int>(thisMechVentZone.ZoneOAFlowMethod)]);
2814 405 : OutputReportPredefined::PreDefTableEntry(
2815 405 : state, state.dataOutRptPredefined->pdchDCVType, zoneName, SysOAMethodNames[static_cast<int>(vent_mech.SystemOAMethod)]);
2816 405 : if (thisMechVentZone.zoneOASched != nullptr) {
2817 405 : OutputReportPredefined::PreDefTableEntry(
2818 405 : state, state.dataOutRptPredefined->pdchDCVOASchName, zoneName, thisMechVentZone.zoneOASched->Name);
2819 : } else {
2820 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVOASchName, zoneName, "");
2821 : }
2822 :
2823 : // added for new DCV inputs
2824 405 : if (thisMechVentZone.zoneADEffSched != nullptr) {
2825 6 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVZoneADEffCooling, zoneName, "");
2826 6 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVZoneADEffHeating, zoneName, "");
2827 6 : OutputReportPredefined::PreDefTableEntry(
2828 6 : state, state.dataOutRptPredefined->pdchDCVZoneADEffSchName, zoneName, thisMechVentZone.zoneADEffSched->Name);
2829 : } else {
2830 399 : OutputReportPredefined::PreDefTableEntry(
2831 399 : state, state.dataOutRptPredefined->pdchDCVZoneADEffCooling, zoneName, thisMechVentZone.ZoneADEffCooling, 2);
2832 399 : OutputReportPredefined::PreDefTableEntry(
2833 399 : state, state.dataOutRptPredefined->pdchDCVZoneADEffHeating, zoneName, thisMechVentZone.ZoneADEffHeating, 2);
2834 399 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVZoneADEffSchName, zoneName, "");
2835 : }
2836 : }
2837 :
2838 : // Fill People index lists if needed
2839 54 : if (vent_mech.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
2840 4 : for (int peopleNum = 1; peopleNum <= state.dataHeatBal->TotPeople; ++peopleNum) {
2841 6 : for (auto &thisMechVentZone : vent_mech.VentMechZone) {
2842 6 : if (state.dataHeatBal->People(peopleNum).ZonePtr == thisMechVentZone.zoneNum) {
2843 3 : thisMechVentZone.peopleIndexes.push_back(peopleNum);
2844 3 : break;
2845 : }
2846 3 : }
2847 : }
2848 : }
2849 :
2850 : // Check to see if any zones on an air loop are not accounted for by a mechanical ventilation object
2851 459 : for (int AirLoopZoneInfoZoneNum = 1; AirLoopZoneInfoZoneNum <= state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).NumZones;
2852 : ++AirLoopZoneInfoZoneNum) {
2853 405 : int NumZone = state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).ActualZoneNumber(AirLoopZoneInfoZoneNum);
2854 405 : bool FoundAreaZone = false;
2855 405 : bool FoundPeopleZone = false;
2856 4746 : for (int NumMechVentZone = 1; NumMechVentZone <= vent_mech.NumofVentMechZones; ++NumMechVentZone) {
2857 4746 : auto const &thisMechVentZone = vent_mech.VentMechZone(NumMechVentZone);
2858 4746 : int ZoneNum = thisMechVentZone.zoneNum;
2859 4746 : if (ZoneNum == NumZone) {
2860 405 : FoundAreaZone = true;
2861 405 : if (thisMechVentZone.ZoneOAPeopleRate > 0.0) {
2862 339 : FoundPeopleZone = true;
2863 : }
2864 405 : break;
2865 : }
2866 : }
2867 405 : if (!FoundAreaZone) {
2868 0 : ShowWarningError(state,
2869 0 : format("Zone name = {} is not accounted for by {} object name = {}",
2870 0 : state.dataHeatBal->Zone(NumZone).Name,
2871 0 : CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
2872 0 : thisOAController.VentilationMechanicalName));
2873 0 : ShowContinueError(state, "Ventilation per unit floor area has not been specified for this zone, which is connected to");
2874 0 : ShowContinueError(
2875 0 : state, format("the air loop served by Controller:OutdoorAir = {}. Simulation will continue...", thisOAController.Name));
2876 : }
2877 405 : if (!FoundPeopleZone) {
2878 : // Loop through people objects to see if this zone has a people object and only then show a warning
2879 3337 : for (int PeopleNum = 1; PeopleNum <= state.dataHeatBal->TotPeople; ++PeopleNum) {
2880 3271 : if (state.dataHeatBal->People(PeopleNum).ZonePtr == NumZone) {
2881 62 : if (!FoundAreaZone) {
2882 0 : ShowWarningError(state,
2883 0 : format("PEOPLE object for zone = {} is not accounted for by {} object name = {}",
2884 0 : state.dataHeatBal->Zone(NumZone).Name,
2885 0 : CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
2886 0 : thisOAController.VentilationMechanicalName));
2887 0 : ShowContinueError(
2888 : state,
2889 0 : format(
2890 : "A \"PEOPLE\" object has been specified in the idf for this zone, but it is not included in this {} Object.",
2891 0 : CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)]));
2892 0 : ShowContinueError(state,
2893 0 : format("Check {} object. Simulation will continue.",
2894 0 : CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)]));
2895 : }
2896 : }
2897 : }
2898 : } else { // People > 0, check to make sure there is a people statement in the zone
2899 339 : FoundAreaZone = false;
2900 7253 : for (int PeopleNum = 1; PeopleNum <= state.dataHeatBal->TotPeople; ++PeopleNum) {
2901 7209 : if (state.dataHeatBal->People(PeopleNum).ZonePtr != NumZone) {
2902 6914 : continue;
2903 : }
2904 295 : FoundAreaZone = true;
2905 295 : break;
2906 : }
2907 339 : if (!FoundAreaZone) {
2908 88 : ShowWarningError(state,
2909 88 : format("{} = \"{}\", Zone=\"{}\".",
2910 44 : CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
2911 44 : thisOAController.VentilationMechanicalName,
2912 44 : state.dataHeatBal->Zone(NumZone).Name));
2913 88 : ShowContinueError(state,
2914 : "No \"PEOPLE\" object has been specified in the idf for this zone, but the ventilation rate is > 0 in "
2915 : "this Controller:MechanicalVentilation Object.");
2916 132 : ShowContinueError(state, "Check ventilation rate in Controller:MechanicalVentilation object. Simulation will continue.");
2917 : }
2918 : }
2919 : }
2920 : }
2921 :
2922 1171 : state.dataMixedAir->MechVentCheckFlag(OAControllerNum) = false;
2923 : }
2924 : //****
2925 :
2926 : // Perform a one time initialization of AirloopHVAC OA System report variables
2927 : // If AirloopHVAC objects are used, NumPrimaryAirSys > 0 and the initialization proceeds and then sets
2928 : // SetUpAirLoopHVACVariables to .FALSE. so this is never done again and only the first IF is checked
2929 : // each time through Init. If for some reason the primary air system have not yet been read in, this
2930 : // code waits for the air loop data to be available before performing the report variable initialization.
2931 : // If AirloopHVAC objects are not used, NumPrimaryAirSys is always equal to 0 and only these
2932 : // two IF statements are checked each time through Init (e.g., if StandAloneERV controllers are used
2933 : // without AirloopHVAC objects).
2934 27596359 : if (state.dataMixedAir->InitOAControllerSetUpAirLoopHVACVariables) {
2935 7787 : if (AirLoopNum > 0) {
2936 : // Added code to report (TH, 10/20/2008):
2937 : // air economizer status (1 = on, 0 = off or does not exist), and actual and minimum outside air fraction (0 to 1)
2938 413 : std::string airloopName; // Temporary equipment name
2939 1582 : for (int OAControllerLoop = 1; OAControllerLoop <= state.dataMixedAir->NumOAControllers; ++OAControllerLoop) {
2940 1169 : auto &loopOAController(state.dataMixedAir->OAController(OAControllerLoop));
2941 :
2942 : // Find the outside air system that has the OA controller
2943 1169 : if (loopOAController.ControllerType == MixedAirControllerType::ControllerStandAloneERV) {
2944 102 : continue; // ERV controller not on airloop
2945 : }
2946 1067 : bool OASysFound = false;
2947 1067 : int thisOASys = 0;
2948 6389 : for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
2949 11713 : for (int OAControllerLoop2 = 1; OAControllerLoop2 <= state.dataAirLoop->OutsideAirSys(OASysNum).NumControllers;
2950 : ++OAControllerLoop2) {
2951 6391 : if (Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName(OAControllerLoop2), loopOAController.Name)) {
2952 1067 : thisOASys = OASysNum;
2953 1067 : OASysFound = true;
2954 1067 : break;
2955 : }
2956 : }
2957 6389 : if (OASysFound) {
2958 1067 : break;
2959 : }
2960 : }
2961 :
2962 1067 : int airLoopNum = 0;
2963 1067 : bool AirLoopFound = false;
2964 1067 : if (thisOASys <= 0) {
2965 : // Check outside air system name
2966 0 : ShowWarningError(state, format("Cannot find the AirLoopHVAC:OutdoorAirSystem for the OA Controller: {}", loopOAController.Name));
2967 : } else {
2968 : // Find the primary air loop that has the outside air system
2969 6389 : for (int thisAirLoop = 1; thisAirLoop <= state.dataHVACGlobal->NumPrimaryAirSys; ++thisAirLoop) {
2970 11714 : for (int BranchNum = 1; BranchNum <= state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).NumBranches; ++BranchNum) {
2971 19505 : for (int CompNum = 1;
2972 19505 : CompNum <= state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).Branch(BranchNum).TotalComponents;
2973 : ++CompNum) {
2974 14180 : if (!Util::SameString(state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).Branch(BranchNum).Comp(CompNum).Name,
2975 29427 : state.dataAirLoop->OutsideAirSys(thisOASys).Name) ||
2976 15247 : !Util::SameString(state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).Branch(BranchNum).Comp(CompNum).TypeOf,
2977 : "AirLoopHVAC:OutdoorAirSystem")) {
2978 13113 : continue;
2979 : }
2980 1067 : AirLoopFound = true;
2981 1067 : airLoopNum = thisAirLoop;
2982 1067 : break;
2983 : }
2984 6392 : if (AirLoopFound) {
2985 1067 : break;
2986 : }
2987 : }
2988 6389 : if (AirLoopFound) {
2989 1067 : break;
2990 : }
2991 : }
2992 : }
2993 : // Check primary air loop name
2994 1067 : if (AirLoopFound && airLoopNum > 0) {
2995 1067 : airloopName = state.dataAirSystemsData->PrimaryAirSystems(airLoopNum).Name; // OutsideAirSys(OASysIndex)%Name
2996 : } else {
2997 0 : ShowWarningError(state, format("Cannot find the primary air loop for the OA Controller: {}", loopOAController.Name));
2998 0 : airloopName = "AirLoop not found";
2999 : }
3000 :
3001 : // Note use of OAControllerLoop here to keep DO Loop index separate from InitOAController local variable
3002 : // CurrentModuleObject='AirLoopHVAC'
3003 1067 : SetupOutputVariable(state,
3004 : "Air System Outdoor Air Economizer Status",
3005 : Constant::Units::None,
3006 1067 : loopOAController.EconomizerStatus,
3007 : OutputProcessor::TimeStepType::System,
3008 : OutputProcessor::StoreType::Average,
3009 : airloopName);
3010 :
3011 1067 : SetupOutputVariable(state,
3012 : "Air System Outdoor Air Heat Recovery Bypass Status",
3013 : Constant::Units::None,
3014 1067 : loopOAController.HeatRecoveryBypassStatus,
3015 : OutputProcessor::TimeStepType::System,
3016 : OutputProcessor::StoreType::Average,
3017 : airloopName);
3018 :
3019 1067 : SetupOutputVariable(state,
3020 : "Air System Outdoor Air Heat Recovery Bypass Heating Coil Activity Status",
3021 : Constant::Units::None,
3022 1067 : loopOAController.HRHeatingCoilActive,
3023 : OutputProcessor::TimeStepType::System,
3024 : OutputProcessor::StoreType::Average,
3025 : airloopName);
3026 2134 : SetupOutputVariable(state,
3027 : "Air System Outdoor Air Heat Recovery Bypass Minimum Outdoor Air Mixed Air Temperature",
3028 : Constant::Units::C,
3029 1067 : loopOAController.MixedAirTempAtMinOAFlow,
3030 : OutputProcessor::TimeStepType::System,
3031 : OutputProcessor::StoreType::Average,
3032 : airloopName);
3033 :
3034 1067 : SetupOutputVariable(state,
3035 : "Air System Outdoor Air High Humidity Control Status",
3036 : Constant::Units::None,
3037 1067 : loopOAController.HighHumCtrlStatus,
3038 : OutputProcessor::TimeStepType::System,
3039 : OutputProcessor::StoreType::Average,
3040 : airloopName);
3041 :
3042 1067 : SetupOutputVariable(state,
3043 : "Air System Outdoor Air Limiting Factor",
3044 : Constant::Units::None,
3045 1067 : loopOAController.OALimitingFactorReport,
3046 : OutputProcessor::TimeStepType::System,
3047 : OutputProcessor::StoreType::Average,
3048 : airloopName);
3049 :
3050 2134 : SetupOutputVariable(state,
3051 : "Air System Outdoor Air Flow Fraction",
3052 : Constant::Units::None,
3053 1067 : loopOAController.OAFractionRpt,
3054 : OutputProcessor::TimeStepType::System,
3055 : OutputProcessor::StoreType::Average,
3056 : airloopName);
3057 :
3058 2134 : SetupOutputVariable(state,
3059 : "Air System Outdoor Air Minimum Flow Fraction",
3060 : Constant::Units::None,
3061 1067 : loopOAController.MinOAFracLimit,
3062 : OutputProcessor::TimeStepType::System,
3063 : OutputProcessor::StoreType::Average,
3064 : airloopName);
3065 :
3066 2134 : SetupOutputVariable(state,
3067 : "Air System Outdoor Air Mass Flow Rate",
3068 : Constant::Units::kg_s,
3069 1067 : loopOAController.OAMassFlow,
3070 : OutputProcessor::TimeStepType::System,
3071 : OutputProcessor::StoreType::Average,
3072 : airloopName);
3073 :
3074 2134 : SetupOutputVariable(state,
3075 : "Air System Mixed Air Mass Flow Rate",
3076 : Constant::Units::kg_s,
3077 1067 : loopOAController.MixMassFlow,
3078 : OutputProcessor::TimeStepType::System,
3079 : OutputProcessor::StoreType::Average,
3080 : airloopName);
3081 :
3082 2134 : SetupOutputVariable(state,
3083 : "Air System Relief Air Heat Transfer Rate",
3084 : Constant::Units::W,
3085 1067 : loopOAController.RelTotalLossRate,
3086 : OutputProcessor::TimeStepType::System,
3087 : OutputProcessor::StoreType::Average,
3088 : airloopName);
3089 :
3090 2134 : SetupOutputVariable(state,
3091 : "Air System Relief Air Sensible Heat Transfer Rate",
3092 : Constant::Units::W,
3093 1067 : loopOAController.RelSensiLossRate,
3094 : OutputProcessor::TimeStepType::System,
3095 : OutputProcessor::StoreType::Average,
3096 : airloopName);
3097 :
3098 2134 : SetupOutputVariable(state,
3099 : "Air System Relief Air Latent Heat Transfer Rate",
3100 : Constant::Units::W,
3101 1067 : loopOAController.RelLatentLossRate,
3102 : OutputProcessor::TimeStepType::System,
3103 : OutputProcessor::StoreType::Average,
3104 : airloopName);
3105 :
3106 1067 : if (loopOAController.MixedAirSPMNum > 0) {
3107 2 : SetupOutputVariable(state,
3108 : "Air System Outdoor Air Maximum Flow Fraction",
3109 : Constant::Units::None,
3110 1 : loopOAController.MaxOAFracBySetPoint,
3111 : OutputProcessor::TimeStepType::System,
3112 : OutputProcessor::StoreType::Average,
3113 : airloopName);
3114 : }
3115 :
3116 1067 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
3117 335 : SetupEMSInternalVariable(
3118 335 : state, "Outdoor Air Controller Maximum Mass Flow Rate", loopOAController.Name, "[kg/s]", loopOAController.MaxOAMassFlowRate);
3119 335 : SetupEMSInternalVariable(
3120 335 : state, "Outdoor Air Controller Minimum Mass Flow Rate", loopOAController.Name, "[kg/s]", loopOAController.MinOAMassFlowRate);
3121 335 : SetupEMSActuator(state,
3122 : "Outdoor Air Controller",
3123 : loopOAController.Name,
3124 : "Air Mass Flow Rate",
3125 : "[kg/s]",
3126 335 : loopOAController.EMSOverrideOARate,
3127 335 : loopOAController.EMSOARateValue);
3128 : }
3129 :
3130 1067 : if (loopOAController.VentMechObjectNum > 0 && airLoopNum > 0) {
3131 108 : SetupOutputVariable(state,
3132 : "Air System Outdoor Air Mechanical Ventilation Requested Mass Flow Rate",
3133 : Constant::Units::kg_s,
3134 54 : loopOAController.MechVentOAMassFlowRequest,
3135 : OutputProcessor::TimeStepType::System,
3136 : OutputProcessor::StoreType::Average,
3137 : airloopName);
3138 54 : if (!state.dataMixedAir->VentilationMechanical(loopOAController.VentMechObjectNum).DCVFlag) {
3139 3 : state.dataAirLoop->AirLoopControlInfo(airLoopNum).AirLoopDCVFlag = false;
3140 : }
3141 : }
3142 : }
3143 :
3144 413 : state.dataMixedAir->InitOAControllerSetUpAirLoopHVACVariables = false;
3145 413 : }
3146 : }
3147 :
3148 : // Each time step
3149 27596359 : if (FirstHVACIteration) {
3150 : // Mixed air setpoint. Set by a setpoint manager.
3151 9547982 : if (thisOAController.ControllerType == MixedAirControllerType::ControllerOutsideAir) {
3152 9402032 : if (state.dataLoopNodes->Node(thisOAController.MixNode).TempSetPoint > SensedNodeFlagValue) {
3153 8614788 : thisOAController.MixSetTemp = state.dataLoopNodes->Node(thisOAController.MixNode).TempSetPoint;
3154 : } else {
3155 787244 : thisOAController.MixSetTemp = thisOAController.TempLowLim;
3156 : }
3157 :
3158 9402032 : Real64 TotalPeopleOAFlow = 0.0;
3159 9402032 : if (thisOAController.VentMechObjectNum != 0) {
3160 350046 : auto &vent_mech(state.dataMixedAir->VentilationMechanical(thisOAController.VentMechObjectNum));
3161 3536980 : for (int ZoneIndex = 1; ZoneIndex <= vent_mech.NumofVentMechZones; ++ZoneIndex) {
3162 3186934 : auto &thisVentMechZone = vent_mech.VentMechZone(ZoneIndex);
3163 3186934 : int ZoneNum = thisVentMechZone.zoneNum;
3164 :
3165 : // ZoneIntGain(ZoneNum)%NOFOCC is the number of occupants of a zone at each time step, already counting the occupant schedule
3166 3186934 : OAFlowCalcMethod OAFlowMethod = thisVentMechZone.ZoneOAFlowMethod;
3167 3186934 : if (OAFlowMethod == OAFlowCalcMethod::PerPerson || OAFlowMethod == OAFlowCalcMethod::Sum ||
3168 : OAFlowMethod == OAFlowCalcMethod::Max) {
3169 3186934 : TotalPeopleOAFlow += state.dataHeatBal->ZoneIntGain(ZoneNum).NOFOCC * state.dataHeatBal->Zone(ZoneNum).Multiplier *
3170 6373868 : state.dataHeatBal->Zone(ZoneNum).ListMultiplier * thisVentMechZone.ZoneOAPeopleRate *
3171 3186934 : thisVentMechZone.zoneOASched->getCurrentVal();
3172 : }
3173 : }
3174 350046 : vent_mech.TotPeopleOAFlow = TotalPeopleOAFlow;
3175 : }
3176 : } else {
3177 : // Stand Alone ERV does not require a temperature setpoint schedule, make setpoint equal to lower economizer limit
3178 145950 : thisOAController.MixSetTemp = thisOAController.TempLowLim;
3179 : }
3180 : }
3181 :
3182 : // Each iteration
3183 :
3184 27596359 : if (thisOAController.ControllerType == MixedAirControllerType::ControllerOutsideAir) {
3185 : // zone exhaust mass flow is saved in AirLoopFlow%ZoneExhaust
3186 : // the zone exhaust mass flow that is said to be balanced by simple air flows is saved in AirLoopFlow%ZoneExhaustBalanced
3187 27032378 : if (AirLoopNum > 0) {
3188 27032378 : thisOAController.ExhMassFlow =
3189 27032378 : max(0.0, state.dataAirLoop->AirLoopFlow(AirLoopNum).SupFlow - state.dataAirLoop->AirLoopFlow(AirLoopNum).SysRetFlow);
3190 27032378 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).ZoneExhMassFlow = thisOAController.ExhMassFlow;
3191 27032378 : if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).LoopFlowRateSet && !FirstHVACIteration) {
3192 : // if flow rate has been specified by a manager, set it to the specified value
3193 300983 : thisOAController.MixMassFlow =
3194 300983 : state.dataAirLoop->AirLoopFlow(AirLoopNum).ReqSupplyFrac * state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply;
3195 : // state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate = thisOAController.MixMassFlow - thisOAController.ExhMassFlow;
3196 : } else {
3197 26731395 : thisOAController.MixMassFlow = state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate + thisOAController.ExhMassFlow;
3198 :
3199 : // The following was commented out after discussion on PR 7382, it can be reopened for discussion anytime
3200 : // found this equation results in flow that exceeds the design flow rate when there is exhaust flow rate is greater than
3201 : // the design supply air flow rate. Capped the mixed air flow rate at design supply air flow rate, issue #77379
3202 : // thisOAController.MixMassFlow = Node(thisOAController.RetNode).MassFlowRate + thisOAController.ExhMassFlow;
3203 : // thisOAController.MixMassFlow =
3204 : // min(Node(thisOAController.RetNode).MassFlowRate + thisOAController.ExhMassFlow, AirLoopFlow(AirLoopNum).DesSupply);
3205 : }
3206 : } else {
3207 0 : thisOAController.ExhMassFlow = 0.0;
3208 0 : thisOAController.MixMassFlow = state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate;
3209 : }
3210 27032378 : if (state.dataLoopNodes->Node(thisOAController.MixNode).MassFlowRateMaxAvail <= 0.0) {
3211 1804558 : thisOAController.MixMassFlow = 0.0;
3212 : }
3213 : } else {
3214 : // Mixed and exhaust flow rates are passed through to model CONTROLLER:STAND ALONE ERV in SimOAController
3215 563981 : thisOAController.OAMassFlow = thisOAController.MaxOAMassFlowRate;
3216 563981 : thisOAController.MixMassFlow = thisOAController.MaxOAMassFlowRate;
3217 563981 : thisOAController.ExhMassFlow = state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate;
3218 : }
3219 27596359 : thisOAController.ExhMassFlow = max(thisOAController.ExhMassFlow, 0.0);
3220 :
3221 : // Outside air values
3222 27596359 : thisOAController.OATemp = state.dataLoopNodes->Node(thisOAController.OANode).Temp;
3223 27596359 : thisOAController.OAEnth = state.dataLoopNodes->Node(thisOAController.OANode).Enthalpy;
3224 27596359 : thisOAController.OAPress = state.dataLoopNodes->Node(thisOAController.OANode).Press;
3225 27596359 : thisOAController.OAHumRat = state.dataLoopNodes->Node(thisOAController.OANode).HumRat;
3226 :
3227 : // Inlet air values (on OA input side)
3228 27596359 : thisOAController.InletTemp = state.dataLoopNodes->Node(thisOAController.InletNode).Temp;
3229 27596359 : thisOAController.InletEnth = state.dataLoopNodes->Node(thisOAController.InletNode).Enthalpy;
3230 27596359 : thisOAController.InletPress = state.dataLoopNodes->Node(thisOAController.InletNode).Press;
3231 27596359 : thisOAController.InletHumRat = state.dataLoopNodes->Node(thisOAController.InletNode).HumRat;
3232 :
3233 : // Return air values
3234 27596359 : thisOAController.RetTemp = state.dataLoopNodes->Node(thisOAController.RetNode).Temp;
3235 27596359 : thisOAController.RetEnth = state.dataLoopNodes->Node(thisOAController.RetNode).Enthalpy;
3236 :
3237 : // Check sensors faults for the air economizer
3238 27596359 : EconoOp iEco = thisOAController.Econo;
3239 27596359 : if (state.dataFaultsMgr->AnyFaultsInModel && (iEco != EconoOp::NoEconomizer)) {
3240 1745265 : for (int i = 1; i <= thisOAController.NumFaultyEconomizer; ++i) {
3241 241121 : int j = thisOAController.EconmizerFaultNum(i);
3242 241121 : Real64 rSchVal = 0.0;
3243 241121 : if (state.dataFaultsMgr->FaultsEconomizer(j).availSched->getCurrentVal() > 0.0) {
3244 241121 : rSchVal = 1.0;
3245 241121 : if (state.dataFaultsMgr->FaultsEconomizer(j).severitySched != nullptr) {
3246 241121 : rSchVal = state.dataFaultsMgr->FaultsEconomizer(j).severitySched->getCurrentVal();
3247 : }
3248 : } else {
3249 0 : continue; // no fault
3250 : }
3251 :
3252 241121 : Real64 rOffset = rSchVal * state.dataFaultsMgr->FaultsEconomizer(j).Offset;
3253 :
3254 241121 : if (std::abs(rOffset) < 0.000000001) {
3255 8360 : continue;
3256 : }
3257 :
3258 : // ECONOMIZER - outdoor air dry-bulb temperature sensor offset
3259 232761 : switch (iEco) {
3260 232761 : case EconoOp::FixedDryBulb:
3261 : case EconoOp::DifferentialDryBulb:
3262 : case EconoOp::FixedDewPointAndDryBulb:
3263 : case EconoOp::ElectronicEnthalpy:
3264 : case EconoOp::DifferentialDryBulbAndEnthalpy: {
3265 232761 : if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::TemperatureSensorOffset_OutdoorAir) {
3266 : // FaultModel:TemperatureSensorOffset:OutdoorAir
3267 38685 : thisOAController.OATemp += rOffset;
3268 38685 : thisOAController.InletTemp += rOffset;
3269 : }
3270 232761 : } break;
3271 0 : default:
3272 0 : break;
3273 : }
3274 :
3275 : // ECONOMIZER - outdoor air humidity ratio sensor offset. really needed ???
3276 232761 : switch (iEco) {
3277 0 : case EconoOp::FixedDewPointAndDryBulb:
3278 : case EconoOp::ElectronicEnthalpy: {
3279 0 : if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::HumiditySensorOffset_OutdoorAir) {
3280 : // FaultModel:HumiditySensorOffset:OutdoorAir
3281 0 : thisOAController.OAHumRat += rOffset;
3282 0 : thisOAController.InletHumRat += rOffset;
3283 : }
3284 0 : } break;
3285 232761 : default:
3286 232761 : break;
3287 : }
3288 :
3289 : // ECONOMIZER - outdoor air enthalpy sensor offset
3290 232761 : switch (iEco) {
3291 0 : case EconoOp::FixedEnthalpy:
3292 : case EconoOp::ElectronicEnthalpy:
3293 : case EconoOp::DifferentialDryBulbAndEnthalpy: {
3294 0 : if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::EnthalpySensorOffset_OutdoorAir) {
3295 : // FaultModel:EnthalpySensorOffset:OutdoorAir
3296 0 : thisOAController.OAEnth += rOffset;
3297 0 : thisOAController.InletEnth += rOffset;
3298 : }
3299 0 : } break;
3300 232761 : default:
3301 232761 : break;
3302 : }
3303 :
3304 : // ECONOMIZER - return air dry-bulb temperature sensor offset
3305 232761 : switch (iEco) {
3306 232761 : case EconoOp::DifferentialDryBulb:
3307 : case EconoOp::DifferentialDryBulbAndEnthalpy: {
3308 232761 : if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::TemperatureSensorOffset_ReturnAir) {
3309 : // FaultModel:TemperatureSensorOffset:ReturnAir
3310 49993 : thisOAController.RetTemp += rOffset;
3311 : }
3312 232761 : } break;
3313 0 : default:
3314 0 : break;
3315 : }
3316 :
3317 : // ECONOMIZER - return air enthalpy sensor offset
3318 232761 : switch (iEco) {
3319 0 : case EconoOp::ElectronicEnthalpy:
3320 : case EconoOp::DifferentialDryBulbAndEnthalpy: {
3321 0 : if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::EnthalpySensorOffset_ReturnAir) {
3322 : // FaultModel:EnthalpySensorOffset:ReturnAir
3323 0 : thisOAController.RetEnth += rOffset;
3324 : }
3325 0 : } break;
3326 232761 : default:
3327 232761 : break;
3328 : }
3329 : }
3330 : }
3331 :
3332 27596359 : if (ErrorsFound) {
3333 0 : ShowFatalError(state, format("Error in {}; program terminated", CurrentModuleObjects[static_cast<int>(CMO::OAController)]));
3334 : }
3335 27596359 : } // namespace MixedAir
3336 :
3337 60004604 : void OAMixerProps::InitOAMixer(EnergyPlusData &state)
3338 : {
3339 : // SUBROUTINE INFORMATION:
3340 : // AUTHOR Fred Buhl
3341 : // DATE WRITTEN Oct 1998
3342 :
3343 : // PURPOSE OF THIS SUBROUTINE
3344 : // Initialize the OAMixer data structure with input node data
3345 :
3346 60004604 : int RetNode = this->RetNode;
3347 60004604 : int InletNode = this->InletNode;
3348 60004604 : int RelNode = this->RelNode;
3349 :
3350 : // Return air stream data
3351 60004604 : this->RetTemp = state.dataLoopNodes->Node(RetNode).Temp;
3352 60004604 : this->RetHumRat = state.dataLoopNodes->Node(RetNode).HumRat;
3353 60004604 : this->RetEnthalpy = state.dataLoopNodes->Node(RetNode).Enthalpy;
3354 60004604 : this->RetPressure = state.dataLoopNodes->Node(RetNode).Press;
3355 60004604 : this->RetMassFlowRate = state.dataLoopNodes->Node(RetNode).MassFlowRate;
3356 : // Outside air stream data
3357 60004604 : this->OATemp = state.dataLoopNodes->Node(InletNode).Temp;
3358 60004604 : this->OAHumRat = state.dataLoopNodes->Node(InletNode).HumRat;
3359 60004604 : this->OAEnthalpy = state.dataLoopNodes->Node(InletNode).Enthalpy;
3360 60004604 : this->OAPressure = state.dataLoopNodes->Node(InletNode).Press;
3361 60004604 : this->OAMassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
3362 : // Relief air data
3363 60004604 : this->RelMassFlowRate = state.dataLoopNodes->Node(RelNode).MassFlowRate;
3364 60004604 : }
3365 :
3366 27596359 : void OAControllerProps::CalcOAController(EnergyPlusData &state, int const AirLoopNum, bool const FirstHVACIteration)
3367 : {
3368 :
3369 : // SUBROUTINE INFORMATION:
3370 : // AUTHOR Fred Buhl
3371 : // DATE WRITTEN Oct 1998
3372 : // MODIFIED Shirey/Raustad FSEC, June 2003
3373 : // Tianzhen Hong, Feb 2009 for new DCV
3374 : // Brent Griffith ,EMS override of OA rate
3375 : // Mangesh Basarkar, 06/2011: Modifying outside air calculation based on DCV flag
3376 : // Chandan Sharma, FSEC, 25Aug 2011 - Added ProportionalControl
3377 : // to enhance CO2 based DCV control
3378 : // Tianzhen Hong, March 2012, zone maximum OA fraction - a TRACE feature
3379 : // Tianzhen Hong, March 2012, multi-path VRP based on ASHRAE 62.1-2010
3380 :
3381 : // PURPOSE OF THIS SUBROUTINE
3382 : // Determine the outside air flow
3383 :
3384 : // REFERENCES:
3385 : // DOE-2.1E Supplement pages 3.97 - 3.100
3386 : // BLAST User Reference pages 183 - 186
3387 : // ASHRAE Standard 62.1-2010
3388 :
3389 : // SUBROUTINE PARAMETER DEFINITIONS:
3390 : static constexpr std::string_view RoutineName("CalcOAController: ");
3391 :
3392 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3393 27596359 : Real64 OASignal = 0.0; // Outside air flow rate fraction (0.0 to 1.0)
3394 27596359 : bool AirLoopCyclingFan = false; // Type of air loop fan (TRUE if Fan:OnOff)
3395 27596359 : bool HighHumidityOperationFlag = false; // TRUE if zone humidistat senses a high humidity condition
3396 :
3397 27596359 : if (AirLoopNum > 0) {
3398 27032378 : AirLoopCyclingFan = (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).fanOp == HVAC::FanOp::Cycling);
3399 : } else {
3400 563981 : AirLoopCyclingFan = false;
3401 : }
3402 :
3403 27596359 : this->OALimitingFactor = OALimitFactor::None; // oa controller limiting factor
3404 :
3405 : // Check for no flow
3406 27596359 : if (this->MixMassFlow <= HVAC::SmallMassFlow) {
3407 :
3408 1993256 : this->OAMassFlow = 0.0; // outside air mass flow rate
3409 1993256 : this->RelMassFlow = 0.0; // relief air mass flow rate
3410 1993256 : this->MixMassFlow = 0.0; // mixed air mass flow rate
3411 1993256 : this->MinOAFracLimit = 0.0; // minimum OA fraction limit
3412 :
3413 1993256 : this->EconomizerStatus = 0; // economizer status for reporting
3414 1993256 : this->HeatRecoveryBypassStatus = 0; // HR bypass status for reporting
3415 1993256 : this->HRHeatingCoilActive = 0; // resets report variable
3416 1993256 : this->MixedAirTempAtMinOAFlow = state.dataLoopNodes->Node(this->RetNode).Temp; // track return T
3417 1993256 : this->HighHumCtrlStatus = 0; // high humidity control status for reporting
3418 1993256 : this->OAFractionRpt = 0.0; // actual OA fraction for reporting
3419 :
3420 1993256 : this->EconoActive = false; // DataAirLoop variable (OA Controllers)
3421 1993256 : this->HighHumCtrlActive = false; // DataAirLoop variable (OA Controllers)
3422 :
3423 : // also reset air loop data for use by other routines
3424 1993256 : if (AirLoopNum > 0) {
3425 1993154 : auto &curAirLoopControlInfo(state.dataAirLoop->AirLoopControlInfo(AirLoopNum));
3426 1993154 : auto &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
3427 :
3428 1993154 : curAirLoopControlInfo.EconoActive = false; // DataAirLoop variable (AirloopHVAC)
3429 1993154 : curAirLoopControlInfo.HeatRecoveryBypass = false; // DataAirLoop variable (AirloopHVAC)
3430 1993154 : curAirLoopControlInfo.HighHumCtrlActive = false; // DataAirLoop variable (AirloopHVAC)
3431 1993154 : curAirLoopControlInfo.ResimAirLoopFlag = false; // DataAirLoop variable (AirloopHVAC)
3432 1993154 : curAirLoopFlow.OAFrac = 0.0; // DataAirLoop variable (AirloopHVAC)
3433 1993154 : curAirLoopFlow.OAMinFrac = 0.0; // DataAirLoop variable (AirloopHVAC)
3434 1993154 : curAirLoopFlow.MinOutAir = 0.0;
3435 1993154 : curAirLoopFlow.OAFlow = 0.0;
3436 : }
3437 1993256 : return;
3438 : }
3439 :
3440 25603103 : Real64 OutAirMinFrac = 0.0; // Local variable used to calculate min OA fraction
3441 25603103 : if (AirLoopNum > 0) {
3442 25039224 : auto const &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
3443 25039224 : if (curAirLoopFlow.DesSupply >= HVAC::SmallAirVolFlow) {
3444 25039224 : OutAirMinFrac = this->MinOAMassFlowRate / curAirLoopFlow.DesSupply;
3445 : }
3446 : } else {
3447 563879 : if (this->MaxOA >= HVAC::SmallAirVolFlow) {
3448 563879 : OutAirMinFrac = this->MinOA / this->MaxOA;
3449 : }
3450 : }
3451 25603103 : Real64 MinOASchedVal = 1.0; // value of the minimum outside air schedule
3452 25603103 : if (this->minOASched != nullptr) {
3453 24345318 : MinOASchedVal = this->minOASched->getCurrentVal();
3454 24345318 : MinOASchedVal = min(max(MinOASchedVal, 0.0), 1.0);
3455 24345318 : OutAirMinFrac *= MinOASchedVal;
3456 24345318 : this->OALimitingFactor = OALimitFactor::Limits;
3457 : }
3458 :
3459 : // Get outside air mass flow rate calculated by mechanical ventilation object [kg/s]
3460 25603103 : Real64 MechVentOutsideAirMinFrac = 0.0; // fraction of OA specified by mechanical ventilation object
3461 25603103 : if (AirLoopNum > 0 && this->VentMechObjectNum != 0) {
3462 1261261 : auto const &curAirLoopControlInfo(state.dataAirLoop->AirLoopControlInfo(AirLoopNum));
3463 1261261 : auto const &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
3464 :
3465 : // Get system supply air flow rate
3466 1261261 : Real64 SysSA = 0.0; // System supply air mass flow rate [kg/s]
3467 1261261 : if (curAirLoopControlInfo.LoopFlowRateSet) {
3468 : // if flow rate has been specified by a manager, set it to the specified value
3469 : // DesSupply and SupFlow are mass flow rate in kg/s
3470 0 : SysSA = curAirLoopFlow.ReqSupplyFrac * curAirLoopFlow.DesSupply;
3471 : } else {
3472 1261261 : SysSA = curAirLoopFlow.SupFlow;
3473 : }
3474 :
3475 1261261 : this->MechVentOAMassFlowRequest = state.dataMixedAir->VentilationMechanical(this->VentMechObjectNum).CalcMechVentController(state, SysSA);
3476 1261261 : MechVentOutsideAirMinFrac = this->MechVentOAMassFlowRequest / curAirLoopFlow.DesSupply;
3477 1261261 : if (curAirLoopFlow.FanPLR > 0.0) {
3478 1260276 : MechVentOutsideAirMinFrac *= curAirLoopFlow.FanPLR;
3479 1260276 : this->MechVentOAMassFlowRequest *= curAirLoopFlow.FanPLR;
3480 : }
3481 1261261 : } else {
3482 24341842 : this->MechVentOAMassFlowRequest = 0.0;
3483 : }
3484 : //****** use greater of Mechanical Ventilation Outside Air fraction and OutAirMinFrac
3485 25603103 : if ((MechVentOutsideAirMinFrac > 0.0) && (OutAirMinFrac > MechVentOutsideAirMinFrac)) {
3486 0 : if (!state.dataGlobal->WarmupFlag) {
3487 0 : if (this->CountMechVentFrac == 0) {
3488 0 : ++this->CountMechVentFrac;
3489 0 : ShowWarningError(
3490 : state,
3491 0 : format("{}Minimum OA fraction > Mechanical Ventilation Controller request for Controller:OutdoorAir={}, Min OA fraction is used.",
3492 : RoutineName,
3493 0 : this->Name));
3494 0 : ShowContinueError(state,
3495 : "This may be overriding desired ventilation controls. Check inputs for Minimum Outdoor Air Flow Rate, Minimum "
3496 : "Outdoor Air Schedule Name and Controller:MechanicalVentilation");
3497 0 : ShowContinueErrorTimeStamp(
3498 0 : state, format("Minimum OA fraction = {:.4R}, Mech Vent OA fraction = {:.4R}", OutAirMinFrac, MechVentOutsideAirMinFrac));
3499 : } else {
3500 0 : ShowRecurringWarningErrorAtEnd(state,
3501 0 : "Controller:OutdoorAir=\"" + this->Name +
3502 : "\": Min OA fraction > Mechanical ventilation OA fraction, continues...",
3503 0 : this->IndexMechVentFrac,
3504 : OutAirMinFrac,
3505 : OutAirMinFrac);
3506 : }
3507 : }
3508 : }
3509 25603103 : if (MechVentOutsideAirMinFrac > OutAirMinFrac) {
3510 859913 : OutAirMinFrac = MechVentOutsideAirMinFrac;
3511 859913 : this->OALimitingFactor = OALimitFactor::DCV;
3512 : }
3513 :
3514 25603103 : OutAirMinFrac = min(max(OutAirMinFrac, 0.0), 1.0);
3515 :
3516 : // At this point, OutAirMinFrac is still based on AirLoopFlow.DesSupply
3517 25603103 : if (AirLoopNum > 0) {
3518 25039224 : auto &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
3519 :
3520 25039224 : curAirLoopFlow.MinOutAir = OutAirMinFrac * curAirLoopFlow.DesSupply;
3521 :
3522 : // calculate mixed air temp at min OA flow rate
3523 25039224 : Real64 ReliefMassFlowAtMinOA = max(curAirLoopFlow.MinOutAir - this->ExhMassFlow, 0.0);
3524 25039224 : Real64 RecircMassFlowRateAtMinOAFlow = max(state.dataLoopNodes->Node(this->RetNode).MassFlowRate - ReliefMassFlowAtMinOA, 0.0);
3525 25039224 : if ((RecircMassFlowRateAtMinOAFlow + curAirLoopFlow.MinOutAir) > 0.0) {
3526 25027595 : Real64 RecircTemp = state.dataLoopNodes->Node(this->RetNode).Temp; // return air temp used for custom economizer control calculation
3527 25027595 : this->MixedAirTempAtMinOAFlow =
3528 25027595 : (RecircMassFlowRateAtMinOAFlow * RecircTemp + curAirLoopFlow.MinOutAir * state.dataLoopNodes->Node(this->OANode).Temp) /
3529 25027595 : (RecircMassFlowRateAtMinOAFlow + curAirLoopFlow.MinOutAir);
3530 : } else {
3531 11629 : this->MixedAirTempAtMinOAFlow = state.dataLoopNodes->Node(this->RetNode).Temp;
3532 : }
3533 : }
3534 :
3535 : // Economizer
3536 25603103 : this->CalcOAEconomizer(state, AirLoopNum, OutAirMinFrac, OASignal, HighHumidityOperationFlag, FirstHVACIteration);
3537 :
3538 25603103 : this->OAMassFlow = OASignal * this->MixMassFlow;
3539 :
3540 : // Do not allow OA to be below Ventilation:Mechanical flow rate or above mixed mass flow rate
3541 25603103 : if (AirLoopNum > 0 && VentMechObjectNum != 0) {
3542 1261261 : if (this->MechVentOAMassFlowRequest > this->OAMassFlow) {
3543 582317 : this->OAMassFlow = min(this->MechVentOAMassFlowRequest, this->MixMassFlow);
3544 : }
3545 : }
3546 :
3547 : // Do not allow OA to be below Exh for controller:outside air
3548 25603103 : if (this->ControllerType == MixedAirControllerType::ControllerOutsideAir) {
3549 25039224 : if (this->ExhMassFlow > this->OAMassFlow) {
3550 985548 : this->OAMassFlow = this->ExhMassFlow;
3551 985548 : this->OALimitingFactor = OALimitFactor::Exhaust;
3552 : }
3553 : }
3554 :
3555 : // if fixed minimum, don't let go below min OA
3556 25603103 : if (this->FixedMin) {
3557 : // cycling fans allow "average" min OA to be below minimum
3558 24683294 : if (!AirLoopCyclingFan) {
3559 21910918 : Real64 minOASchedMassFlowRate = this->MinOAMassFlowRate * MinOASchedVal;
3560 21910918 : if (minOASchedMassFlowRate > this->OAMassFlow) {
3561 8031938 : this->OAMassFlow = minOASchedMassFlowRate;
3562 8031938 : this->OALimitingFactor = OALimitFactor::Limits;
3563 : }
3564 : }
3565 : }
3566 :
3567 : // Apply Minimum Fraction of Outdoor Air Schedule
3568 25603103 : if (this->minOAflowSched != nullptr) {
3569 2220879 : Real64 MinOAflowfracVal = this->minOAflowSched->getCurrentVal();
3570 2220879 : MinOAflowfracVal = min(max(MinOAflowfracVal, 0.0), 1.0);
3571 2220879 : OutAirMinFrac = max(MinOAflowfracVal, OutAirMinFrac);
3572 2220879 : Real64 minOAFracMassFlowRate = this->MixMassFlow * MinOAflowfracVal;
3573 2220879 : if (minOAFracMassFlowRate > this->OAMassFlow) {
3574 1473302 : this->OAMassFlow = minOAFracMassFlowRate;
3575 1473302 : this->OALimitingFactor = OALimitFactor::Limits;
3576 : }
3577 : }
3578 :
3579 : // Apply Maximum Fraction of Outdoor Air Schedule
3580 25603103 : Real64 currentMaxOAMassFlowRate = this->MaxOAMassFlowRate;
3581 25603103 : if (this->maxOAflowSched != nullptr) {
3582 1162493 : Real64 MaxOAflowfracVal = this->maxOAflowSched->getCurrentVal();
3583 1162493 : MaxOAflowfracVal = min(max(MaxOAflowfracVal, 0.0), 1.0);
3584 1162493 : currentMaxOAMassFlowRate = min(this->MaxOAMassFlowRate, this->MixMassFlow * MaxOAflowfracVal);
3585 1162493 : OutAirMinFrac = min(MaxOAflowfracVal, OutAirMinFrac);
3586 1162493 : if (currentMaxOAMassFlowRate < this->OAMassFlow) {
3587 39969 : this->OAMassFlow = currentMaxOAMassFlowRate;
3588 39969 : this->OALimitingFactor = OALimitFactor::Limits;
3589 : }
3590 : }
3591 :
3592 : // Don't let the OA flow be > than the max OA limit. OA for high humidity control is allowed to be greater than max OA.
3593 : // Night Ventilation has priority and may override an OASignal > 1 high humidity condition with OASignal = 1
3594 25603103 : if (HighHumidityOperationFlag) {
3595 394 : Real64 maxOAMassFlow = this->MaxOAMassFlowRate * max(1.0, OASignal);
3596 394 : if (maxOAMassFlow < this->OAMassFlow) {
3597 0 : this->OAMassFlow = maxOAMassFlow;
3598 0 : this->OALimitingFactor = OALimitFactor::Limits;
3599 : }
3600 : } else {
3601 25602709 : if (this->MaxOAMassFlowRate < this->OAMassFlow) {
3602 511318 : this->OAMassFlow = this->MaxOAMassFlowRate;
3603 511318 : this->OALimitingFactor = OALimitFactor::Limits;
3604 : }
3605 : }
3606 :
3607 25603103 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && (this->ManageDemand) && (this->OAMassFlow > this->DemandLimitFlowRate)) {
3608 3972 : this->OAMassFlow = this->DemandLimitFlowRate;
3609 3972 : this->OALimitingFactor = OALimitFactor::DemandLimit;
3610 : }
3611 25603103 : if (this->EMSOverrideOARate) {
3612 0 : this->OAMassFlow = this->EMSOARateValue;
3613 0 : this->OALimitingFactor = OALimitFactor::EMS;
3614 : }
3615 :
3616 : // Don't let OA flow be > mixed air flow.
3617 : // Seems if RAB (return air bypass) that this should be don't let OA flow be > design supply flow but that causes other issues
3618 25603103 : if (this->MixMassFlow < this->OAMassFlow) {
3619 436954 : this->OAMassFlow = this->MixMassFlow;
3620 436954 : this->OALimitingFactor = OALimitFactor::MixedAir;
3621 : }
3622 :
3623 : // save the min outside air flow fraction and max outside air mass flow rate
3624 25603103 : if (AirLoopNum > 0) {
3625 25039224 : auto &curAirLoopControlInfo(state.dataAirLoop->AirLoopControlInfo(AirLoopNum));
3626 25039224 : auto &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
3627 :
3628 25039224 : curAirLoopFlow.OAMinFrac = OutAirMinFrac;
3629 25039224 : if (this->FixedMin) {
3630 24683294 : curAirLoopFlow.MinOutAir = min(OutAirMinFrac * curAirLoopFlow.DesSupply, this->MixMassFlow);
3631 : } else {
3632 355930 : curAirLoopFlow.MinOutAir = OutAirMinFrac * this->MixMassFlow;
3633 : }
3634 25039224 : if (this->MixMassFlow > 0.0) {
3635 25039224 : curAirLoopFlow.OAFrac = this->OAMassFlow / this->MixMassFlow;
3636 25039224 : curAirLoopFlow.OAFlow = this->OAMassFlow;
3637 : } else {
3638 0 : curAirLoopFlow.OAFrac = 0.0;
3639 0 : curAirLoopFlow.OAFlow = 0.0;
3640 : }
3641 25039224 : this->MinOAFracLimit = OutAirMinFrac;
3642 25039224 : if (HighHumidityOperationFlag && OASignal > 1.0) {
3643 62 : curAirLoopFlow.MaxOutAir = this->MaxOAMassFlowRate * OASignal;
3644 : } else {
3645 25039162 : curAirLoopFlow.MaxOutAir = currentMaxOAMassFlowRate;
3646 : }
3647 :
3648 : // MJW - Not sure if this is necessary but keeping it for now
3649 25039224 : if (curAirLoopControlInfo.HeatingActiveFlag && curAirLoopControlInfo.EconomizerFlowLocked) {
3650 : // The airloop needs to be simulated again so that the heating coil & HX can be resimulated
3651 838844 : if (curAirLoopControlInfo.HeatRecoveryResimFlag && curAirLoopControlInfo.OASysComponentsSimulated) {
3652 103516 : curAirLoopControlInfo.ResimAirLoopFlag = true;
3653 103516 : curAirLoopControlInfo.HeatRecoveryResimFlag = false;
3654 103516 : curAirLoopControlInfo.HeatRecoveryResimFlag2 = true;
3655 : // on the first iteration, air loop heating coils have not be simulated so HeatingCoilActive=FALSE
3656 : // on the second iteration, the heating coils could have been on, but logic tests here could deactivate heating coil
3657 : // reset heating coil active status and HX since logic tests may turn off heating coil
3658 : // the ResimAirLoopFlag will force another iteration and things should line up on subsequent iterations
3659 103516 : curAirLoopControlInfo.HeatingActiveFlag = false;
3660 103516 : this->HRHeatingCoilActive = 0;
3661 103516 : curAirLoopControlInfo.HeatRecoveryBypass = true;
3662 103516 : this->HeatRecoveryBypassStatus = 1;
3663 735328 : } else if (curAirLoopControlInfo.HeatRecoveryResimFlag2) {
3664 103456 : curAirLoopControlInfo.ResimAirLoopFlag = true;
3665 103456 : curAirLoopControlInfo.HeatRecoveryResimFlag2 = false;
3666 : } else {
3667 631872 : curAirLoopControlInfo.ResimAirLoopFlag = false;
3668 : }
3669 24200380 : } else if (curAirLoopControlInfo.HeatingActiveFlag) {
3670 3042675 : this->HRHeatingCoilActive = 1;
3671 : } else {
3672 21157705 : this->HRHeatingCoilActive = 0;
3673 : }
3674 :
3675 : } // if (AirLoopNum > 0)
3676 :
3677 : // Set the relief air flow rate (must be done last to account for changes in OAMassFlow
3678 25603103 : this->RelMassFlow = max(this->OAMassFlow - this->ExhMassFlow, 0.0);
3679 :
3680 : // Save OA fraction for reporting
3681 25603103 : if (this->MixMassFlow > 0) {
3682 25603103 : this->OAFractionRpt = this->OAMassFlow / this->MixMassFlow;
3683 : } else {
3684 0 : if (this->OAMassFlow > 0) {
3685 0 : this->OAFractionRpt = OASignal;
3686 : } else {
3687 0 : this->OAFractionRpt = 0.0;
3688 : }
3689 : }
3690 25603103 : this->RelTemp = this->RetTemp;
3691 25603103 : this->RelEnth = this->RetEnth;
3692 25603103 : this->RelSensiLossRate =
3693 25603103 : this->RelMassFlow * Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat) * (this->RelTemp - state.dataEnvrn->OutDryBulbTemp);
3694 25603103 : this->RelTotalLossRate = this->RelMassFlow * (this->RelEnth - state.dataEnvrn->OutEnthalpy);
3695 25603103 : this->RelLatentLossRate = this->RelTotalLossRate - this->RelSensiLossRate;
3696 25603103 : this->OALimitingFactorReport = static_cast<int>(OALimitingFactor);
3697 : }
3698 :
3699 1261261 : Real64 VentilationMechanicalProps::CalcMechVentController(EnergyPlusData &state,
3700 : Real64 SysSA // System supply air mass flow rate [kg/s]
3701 : )
3702 : {
3703 : static constexpr std::string_view RoutineName("CalcMechVentController: ");
3704 : static std::string_view const &CurrentModuleObject(CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)]);
3705 :
3706 : // new local variables for DCV
3707 : // Zone OA flow rate based on each calculation method [m3/s]
3708 1261261 : std::array<Real64, static_cast<int>(DataSizing::OAFlowCalcMethod::Num)> ZoneOACalc{0.0};
3709 : Real64 ZoneOABZ; // Zone breathing-zone OA flow rate [m3/s]
3710 : Real64 ZoneOA; // Zone OA flow rate [m3/s]
3711 : Real64 ZoneOAFrac; // Zone OA fraction (as a fraction of actual supply air flow rate)
3712 : Real64 SysOAuc; // System uncorrected OA flow rate
3713 : Real64 SysOA; // System supply OA volume flow rate [m3/s]
3714 : Real64 SysEv; // System ventilation efficiency
3715 : Real64 NodeTemp; // node temperature
3716 : Real64 NodeHumRat; // node humidity ratio
3717 1261261 : Real64 ZoneMaxCO2 = 0.0; // Breathing-zone CO2 concentration
3718 1261261 : Real64 ZoneMinCO2 = 0.0; // Minimum CO2 concentration in zone
3719 1261261 : Real64 ZoneOAMin = 0.0; // Minimum Zone OA flow rate when the zone is unoccupied (i.e. ZoneOAPeople = 0)
3720 1261261 : Real64 ZoneOAMax = 0.0; // Maximum Zone OA flow rate (ZoneOAPeople + ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)])
3721 1261261 : Real64 MechVentOAMassFlow = 0.0;
3722 :
3723 : // Apply mechanical ventilation only when it is available/allowed
3724 1261261 : if (this->availSched->getCurrentVal() > 0) {
3725 968939 : Real64 SysOAMassFlow = 0.0; // System supply OA mass flow rate [kg/s]
3726 968939 : if (this->SystemOAMethod == DataSizing::SysOAMethod::IAQP) {
3727 : // IAQP for CO2 control
3728 24528 : for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
3729 18396 : auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
3730 18396 : int ZoneNum = thisMechVentZone.zoneNum;
3731 18396 : SysOAMassFlow +=
3732 18396 : state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToCO2SP * thisMechVentZone.zoneOASched->getCurrentVal();
3733 : }
3734 6132 : MechVentOAMassFlow = SysOAMassFlow;
3735 962807 : } else if (this->SystemOAMethod == DataSizing::SysOAMethod::IAQPGC) {
3736 : // IAQP for generic contaminant control
3737 28720 : for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
3738 21540 : auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
3739 21540 : int ZoneNum = thisMechVentZone.zoneNum;
3740 21540 : SysOAMassFlow +=
3741 21540 : state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToGCSP * thisMechVentZone.zoneOASched->getCurrentVal();
3742 : }
3743 7180 : MechVentOAMassFlow = SysOAMassFlow;
3744 955627 : } else if (this->SystemOAMethod == DataSizing::SysOAMethod::IAQPCOM) {
3745 : // IAQP for both CO2 and generic contaminant control
3746 0 : SysOAMassFlow = 0.0;
3747 0 : for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
3748 0 : auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
3749 0 : int ZoneNum = thisMechVentZone.zoneNum;
3750 0 : SysOAMassFlow +=
3751 0 : state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToCO2SP * thisMechVentZone.zoneOASched->getCurrentVal();
3752 : }
3753 0 : MechVentOAMassFlow = SysOAMassFlow;
3754 0 : SysOAMassFlow = 0.0;
3755 0 : for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
3756 0 : auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
3757 0 : int ZoneNum = thisMechVentZone.zoneNum;
3758 0 : SysOAMassFlow +=
3759 0 : state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToGCSP * thisMechVentZone.zoneOASched->getCurrentVal();
3760 : }
3761 0 : MechVentOAMassFlow = max(SysOAMassFlow, MechVentOAMassFlow);
3762 : } else {
3763 : // for system OA methods: Zone_Sum, VRP, CO2 methods
3764 : // new code for DCV method complying with the VRP defined in ASHRAE Standard 62.1-2010
3765 :
3766 : // Loop through each zone first to calc uncorrected system OA flow rate
3767 955627 : SysOAuc = 0.0;
3768 955627 : SysOA = 0.0;
3769 9276965 : for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
3770 8321338 : auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
3771 8321338 : int ZoneNum = thisMechVentZone.zoneNum;
3772 8321338 : auto const &curZone(state.dataHeatBal->Zone(ZoneNum));
3773 8321338 : Real64 multiplier = curZone.Multiplier * curZone.ListMultiplier * thisMechVentZone.zoneOASched->getCurrentVal();
3774 :
3775 : // Calc the zone OA flow rate based on the people component
3776 : // ZoneIntGain(ZoneNum)%NOFOCC is the number of occupants of a zone at each time step, already counting the occupant schedule
3777 : // Checking DCV flag before calculating zone OA per person
3778 8321338 : if (this->DCVFlag && this->SystemOAMethod != DataSizing::SysOAMethod::ProportionalControlDesOcc) {
3779 6008678 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] =
3780 6008678 : state.dataHeatBal->ZoneIntGain(ZoneNum).NOFOCC * thisMechVentZone.ZoneOAPeopleRate;
3781 : } else {
3782 2312660 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] = curZone.TotOccupants * thisMechVentZone.ZoneOAPeopleRate;
3783 : }
3784 :
3785 : // Calc the zone OA flow rate based on the floor area component
3786 8321338 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] = curZone.FloorArea * thisMechVentZone.ZoneOAAreaRate;
3787 8321338 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] = thisMechVentZone.ZoneOAFlowRate;
3788 8321338 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)] = (thisMechVentZone.ZoneOAACHRate * curZone.Volume) / 3600.0;
3789 :
3790 : // Calc the breathing-zone OA flow rate
3791 8321338 : int OAIndex = thisMechVentZone.ZoneDesignSpecOAObjIndex; // index to design specification outdoor air objects
3792 8321338 : if (OAIndex > 0) {
3793 8321338 : switch (state.dataSize->OARequirements(OAIndex).OAFlowMethod) {
3794 8163158 : case DataSizing::OAFlowCalcMethod::Sum: {
3795 8163158 : ZoneOABZ = multiplier * (ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] +
3796 8163158 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] +
3797 8163158 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] +
3798 8163158 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)]);
3799 8163158 : } break;
3800 0 : case DataSizing::OAFlowCalcMethod::Max: {
3801 0 : ZoneOABZ = multiplier * max(ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)],
3802 0 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)],
3803 0 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)],
3804 0 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)]);
3805 :
3806 0 : } break;
3807 158180 : default: {
3808 158180 : ZoneOABZ = multiplier * ZoneOACalc[static_cast<int>(state.dataSize->OARequirements(OAIndex).OAFlowMethod)];
3809 158180 : break;
3810 : }
3811 : }
3812 : } else {
3813 0 : ZoneOABZ = multiplier * ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)];
3814 : }
3815 :
3816 8321338 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ZoneSum) {
3817 : // Sum the zone OA flow rates and done
3818 1769412 : SysOA += ZoneOABZ;
3819 : } else {
3820 : // Calc the uncorrected system OA flow rate - VRP and ProportionalControl
3821 6551926 : SysOAuc += ZoneOABZ;
3822 : }
3823 : }
3824 :
3825 : // get system supply air flow rate
3826 955627 : if (this->SystemOAMethod == DataSizing::SysOAMethod::VRP || this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
3827 845644 : this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
3828 839508 : this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate ||
3829 833368 : this->SystemOAMethod == DataSizing::SysOAMethod::VRPL) {
3830 :
3831 : // System supply air flow rate is always greater than or equal the system outdoor air flow rate
3832 787505 : if ((SysSA > 0.0) && (SysSA < (SysOAuc * state.dataEnvrn->StdRhoAir))) {
3833 40065 : SysSA = SysOAuc * state.dataEnvrn->StdRhoAir;
3834 : }
3835 :
3836 : // calc Xs - average outdoor air fraction
3837 787505 : if (SysSA > 0.0) {
3838 776993 : Xs = (SysOAuc * state.dataEnvrn->StdRhoAir) / SysSA;
3839 : } else {
3840 10512 : Xs = 0.0;
3841 : }
3842 :
3843 : // Loop through each zone again
3844 787505 : SysEv = 2.0; // starting with a big fraction
3845 7339431 : for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
3846 6551926 : auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
3847 6551926 : int ZoneNum = thisMechVentZone.zoneNum;
3848 6551926 : int ZoneEquipConfigNum = ZoneNum; // correspondence - 1:1 of ZoneEquipConfig to Zone index
3849 6551926 : Real64 ZoneEz = 0.0; // Zone air distribution effectiveness
3850 :
3851 : // Assign references
3852 6551926 : auto &curZone(state.dataHeatBal->Zone(ZoneNum));
3853 6551926 : auto &curZoneSysEnergyDemand(state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneEquipConfigNum));
3854 6551926 : Real64 multiplier = curZone.Multiplier * curZone.ListMultiplier * thisMechVentZone.zoneOASched->getCurrentVal();
3855 :
3856 : // Calc the zone OA flow rate based on the people component
3857 : // ZoneIntGain(ZoneNum)%NOFOCC is the number of occupants of a zone at each time step, already counting the occupant schedule
3858 : // Checking DCV flag before calculating zone OA per person
3859 6551926 : if (this->DCVFlag && this->SystemOAMethod != DataSizing::SysOAMethod::ProportionalControlDesOcc) {
3860 5598571 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] =
3861 5598571 : state.dataHeatBal->ZoneIntGain(ZoneNum).NOFOCC * multiplier * thisMechVentZone.ZoneOAPeopleRate;
3862 : } else {
3863 953355 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] =
3864 953355 : curZone.TotOccupants * multiplier * thisMechVentZone.ZoneOAPeopleRate;
3865 : }
3866 :
3867 : // Calc the zone OA flow rate based on the floor area component
3868 13103852 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] =
3869 6551926 : curZone.FloorArea * multiplier * thisMechVentZone.ZoneOAAreaRate;
3870 6551926 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] = multiplier * thisMechVentZone.ZoneOAFlowRate;
3871 13103852 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)] =
3872 6551926 : multiplier * (thisMechVentZone.ZoneOAACHRate * curZone.Volume) / 3600.0;
3873 :
3874 : // Calc the breathing-zone OA flow rate
3875 6551926 : int OAIndex = thisMechVentZone.ZoneDesignSpecOAObjIndex;
3876 6551926 : if (OAIndex > 0) {
3877 6551926 : switch (state.dataSize->OARequirements(OAIndex).OAFlowMethod) {
3878 6393746 : case DataSizing::OAFlowCalcMethod::Sum: {
3879 6393746 : ZoneOABZ = ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] +
3880 6393746 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] +
3881 6393746 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] +
3882 6393746 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)];
3883 6393746 : } break;
3884 0 : case DataSizing::OAFlowCalcMethod::Max: {
3885 0 : ZoneOABZ = max(ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)],
3886 0 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)],
3887 0 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)],
3888 0 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)]);
3889 :
3890 0 : } break;
3891 158180 : default: {
3892 158180 : ZoneOABZ = ZoneOACalc[static_cast<int>(state.dataSize->OARequirements(OAIndex).OAFlowMethod)];
3893 158180 : break;
3894 : }
3895 : }
3896 : }
3897 :
3898 : // use the ventilation rate procedure in ASHRAE Standard 62.1-2007
3899 : // Calc the zone supplied OA flow rate counting the zone air distribution effectiveness
3900 : // First check whether the zone air distribution effectiveness schedule exists, if yes uses it;
3901 : // otherwise uses the inputs of zone distribution effectiveness in cooling mode or heating mode
3902 6551926 : if (thisMechVentZone.zoneADEffSched != nullptr) {
3903 : // Get schedule value for the zone air distribution effectiveness
3904 24556 : ZoneEz = thisMechVentZone.zoneADEffSched->getCurrentVal();
3905 : } else {
3906 6527370 : Real64 ZoneLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).TotalOutputRequired;
3907 :
3908 : // Zone in cooling mode
3909 6527370 : if (ZoneLoad < 0.0) {
3910 3173457 : ZoneEz = thisMechVentZone.ZoneADEffCooling;
3911 : }
3912 :
3913 : // Zone in heating mode
3914 6527370 : if (ZoneLoad > 0.0) {
3915 2706597 : ZoneEz = thisMechVentZone.ZoneADEffHeating;
3916 : }
3917 : }
3918 6551926 : if (ZoneEz <= 0.0) {
3919 : // Enforce defaults
3920 647316 : ZoneEz = 1.0;
3921 : }
3922 :
3923 : // Calc zone supply OA flow rate
3924 6551926 : if (this->SystemOAMethod == DataSizing::SysOAMethod::VRP || this->SystemOAMethod == DataSizing::SysOAMethod::VRPL) {
3925 : // the VRP case
3926 6496678 : ZoneOA = ZoneOABZ / ZoneEz;
3927 :
3928 55248 : } else if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
3929 36828 : this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
3930 18420 : this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
3931 : // Check whether "Carbon Dioxide Control Availability Schedule" for ZoneControl:ContaminantController is specified
3932 55248 : if (curZone.zoneContamControllerSched != nullptr) {
3933 : // Check the availability schedule value for ZoneControl:ContaminantController
3934 30688 : Real64 ZoneContamControllerSchedVal = curZone.zoneContamControllerSched->getCurrentVal();
3935 30688 : if (ZoneContamControllerSchedVal > 0.0) {
3936 30688 : ZoneOAMin = ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] / ZoneEz;
3937 30688 : ZoneOAMax = (ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] +
3938 30688 : ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)]) /
3939 : ZoneEz;
3940 30688 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
3941 6140 : ZoneOAMax = ZoneOABZ / ZoneEz;
3942 6140 : if (thisMechVentZone.oaPropCtlMinRateSched != nullptr) {
3943 0 : ZoneOAMin = ZoneOAMax * thisMechVentZone.oaPropCtlMinRateSched->getCurrentVal();
3944 : } else {
3945 6140 : ZoneOAMin = ZoneOAMax;
3946 : }
3947 6140 : if (ZoneOAMax < ZoneOAMin) {
3948 0 : ZoneOAMin = ZoneOAMax;
3949 0 : ++this->OAMaxMinLimitErrorCount;
3950 0 : if (this->OAMaxMinLimitErrorCount < 2) {
3951 0 : ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
3952 0 : ShowContinueError(
3953 : state,
3954 0 : format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOARate, maximum zone "
3955 : "outdoor air rate ({:.4R}), is not greater than minimum zone outdoor air rate ({:.4R}).",
3956 : ZoneOAMax,
3957 : ZoneOAMin));
3958 0 : ShowContinueError(state,
3959 : " The minimum zone outdoor air rate is set to the maximum zone outdoor air rate. "
3960 : "Simulation continues...");
3961 0 : ShowContinueErrorTimeStamp(state, "");
3962 : } else {
3963 0 : ShowRecurringWarningErrorAtEnd(
3964 : state,
3965 0 : format("{} = \"{}\", For System Outdoor Air Method = ProportionalControlBasedOnDesignOARate, maximum "
3966 : "zone outdoor air rate is not greater than minimum zone outdoor air rate. Error continues...",
3967 : CurrentModuleObject,
3968 0 : this->Name),
3969 0 : this->OAMaxMinLimitErrorIndex);
3970 : }
3971 : }
3972 : }
3973 :
3974 30688 : if (ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] > 0.0) {
3975 24136 : if (state.dataContaminantBalance->ZoneCO2GainFromPeople(ZoneNum) > 0.0) {
3976 14320 : if (curZone.zoneMinCO2Sched != nullptr) {
3977 : // Take the schedule value of "Minimum Carbon Dioxide Concentration Schedule Name"
3978 : // in the ZoneControl:ContaminantController
3979 11456 : ZoneMinCO2 = curZone.zoneMinCO2Sched->getCurrentVal();
3980 : } else {
3981 2864 : ZoneMinCO2 = state.dataContaminantBalance->OutdoorCO2;
3982 : }
3983 :
3984 : // Calculate zone maximum target CO2 concentration in PPM
3985 14320 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
3986 : // Accumulate CO2 generation from people at design occupancy and current activity level
3987 8592 : Real64 CO2PeopleGeneration = 0.0;
3988 17184 : for (int const PeopleNum : thisMechVentZone.peopleIndexes) {
3989 8592 : CO2PeopleGeneration += state.dataHeatBal->People(PeopleNum).NumberOfPeople *
3990 17184 : state.dataHeatBal->People(PeopleNum).CO2RateFactor *
3991 8592 : state.dataHeatBal->People(PeopleNum).activityLevelSched->getCurrentVal();
3992 8592 : }
3993 8592 : ZoneMaxCO2 = state.dataContaminantBalance->OutdoorCO2 +
3994 8592 : (CO2PeopleGeneration * curZone.Multiplier * curZone.ListMultiplier * 1.0e6) / ZoneOAMax;
3995 5728 : } else if (curZone.zoneMaxCO2Sched != nullptr) {
3996 2864 : ZoneMaxCO2 = curZone.zoneMaxCO2Sched->getCurrentVal();
3997 : } else {
3998 2864 : ZoneMaxCO2 = state.dataContaminantBalance->OutdoorCO2 +
3999 2864 : (state.dataContaminantBalance->ZoneCO2GainFromPeople(ZoneNum) * curZone.Multiplier *
4000 2864 : curZone.ListMultiplier * 1.0e6) /
4001 : ZoneOAMax;
4002 : }
4003 :
4004 14320 : if (ZoneMaxCO2 <= ZoneMinCO2) {
4005 0 : ++this->CO2MaxMinLimitErrorCount;
4006 0 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc) {
4007 0 : if (this->CO2MaxMinLimitErrorCount < 2) {
4008 0 : ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
4009 0 : ShowContinueError(
4010 : state,
4011 0 : format("For System Outdoor Air Method = ProportionalControlBasedOnOccupancySchedule, "
4012 : "maximum target CO2 concentration ({:.2R}), is not greater than minimum target "
4013 : "CO2 concentration ({:.2R}).",
4014 : ZoneMaxCO2,
4015 : ZoneMinCO2));
4016 0 : ShowContinueError(state,
4017 : "\"ProportionalControlBasedOnOccupancySchedule\" will not be modeled. "
4018 : "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
4019 : "continues...");
4020 0 : ShowContinueErrorTimeStamp(state, "");
4021 : } else {
4022 0 : ShowRecurringWarningErrorAtEnd(state,
4023 0 : format("{} = \"{}\", For System Outdoor Air Method = "
4024 : "ProportionalControlBasedOnOccupancySchedule, maximum "
4025 : "target CO2 concentration is not greater than minimum "
4026 : "target CO2 concentration. Error continues...",
4027 : CurrentModuleObject,
4028 0 : this->Name),
4029 0 : this->CO2MaxMinLimitErrorIndex);
4030 : }
4031 : }
4032 0 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
4033 0 : if (this->CO2MaxMinLimitErrorCount < 2) {
4034 0 : ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
4035 0 : ShowContinueError(
4036 : state,
4037 0 : format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOccupancy, "
4038 : "maximum target CO2 concentration ({:.2R}), is not greater than minimum target "
4039 : "CO2 concentration ({:.2R}).",
4040 : ZoneMaxCO2,
4041 : ZoneMinCO2));
4042 0 : ShowContinueError(state,
4043 : "\"ProportionalControlBasedOnDesignOccupancy\" will not be modeled. "
4044 : "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
4045 : "continues...");
4046 0 : ShowContinueErrorTimeStamp(state, "");
4047 : } else {
4048 0 : ShowRecurringWarningErrorAtEnd(state,
4049 0 : format("{} = \"{}\", For System Outdoor Air Method = "
4050 : "ProportionalControlBasedOnDesignOccupancy, maximum "
4051 : "target CO2 concentration is not greater than minimum "
4052 : "target CO2 concentration. Error continues...",
4053 : CurrentModuleObject,
4054 0 : this->Name),
4055 0 : this->CO2MaxMinLimitErrorIndex);
4056 : }
4057 : }
4058 0 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
4059 0 : if (this->CO2MaxMinLimitErrorCount < 2) {
4060 0 : ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
4061 0 : ShowContinueError(
4062 : state,
4063 0 : format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOARate, maximum "
4064 : "target CO2 concentration ({:.2R}), is not greater than minimum target CO2 "
4065 : "concentration ({:.2R}).",
4066 : ZoneMaxCO2,
4067 : ZoneMinCO2));
4068 0 : ShowContinueError(
4069 : state,
4070 : "\"ProportionalControlBasedOnDesignOARate\" will not be modeled. Default "
4071 : "\"Standard62.1VentilationRateProcedure\" will be modeled. Simulation continues...");
4072 0 : ShowContinueErrorTimeStamp(state, "");
4073 : } else {
4074 0 : ShowRecurringWarningErrorAtEnd(state,
4075 0 : format("{} = \"{}\", For System Outdoor Air Method = "
4076 : "ProportionalControlBasedOnDesignOARate, maximum target "
4077 : "CO2 concentration is not greater than minimum target CO2 "
4078 : "concentration. Error continues...",
4079 : CurrentModuleObject,
4080 0 : this->Name),
4081 0 : this->CO2MaxMinLimitErrorIndex);
4082 : }
4083 : }
4084 :
4085 0 : ZoneOA = ZoneOABZ / ZoneEz;
4086 : } else {
4087 :
4088 14320 : if (state.dataContaminantBalance->ZoneAirCO2(ZoneNum) <= ZoneMinCO2) {
4089 : // Zone air CO2 concentration is less than minimum zone CO2 concentration, set the Zone OA flow
4090 : // rate to minimum Zone OA flow rate when the zone is unoccupied
4091 0 : ZoneOA = ZoneOAMin;
4092 14320 : } else if (state.dataContaminantBalance->ZoneAirCO2(ZoneNum) >= ZoneMaxCO2) {
4093 : // Zone air CO2 concentration is greater than maximum zone CO2 concentration, set the Zone OA flow
4094 : // rate to maximum Zone OA flow rate (i.e.
4095 : // ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] + ZoneOAPeople)
4096 1108 : ZoneOA = ZoneOAMax;
4097 : } else {
4098 : // Zone air CO2 concentration is between maximum and minimum limits of zone CO2 concentration,
4099 : // set Zone OA flow rate by proportionally adjusting between ZoneOAMin and ZoneOAMax
4100 13212 : ZoneOA = ZoneOAMin +
4101 13212 : (ZoneOAMax - ZoneOAMin) * ((state.dataContaminantBalance->ZoneAirCO2(ZoneNum) - ZoneMinCO2) /
4102 13212 : (ZoneMaxCO2 - ZoneMinCO2));
4103 : }
4104 : }
4105 : } else {
4106 9816 : if (state.dataGlobal->DisplayExtraWarnings) {
4107 0 : ++this->CO2GainErrorCount;
4108 0 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc) {
4109 0 : if (this->CO2GainErrorCount < 2) {
4110 0 : ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
4111 0 : ShowContinueError(
4112 : state,
4113 0 : format("For System Outdoor Air Method = ProportionalControlBasedOnOccupancySchedule, CO2 "
4114 : "generation from people is not greater than zero. Occurs in Zone =\"{}\". ",
4115 0 : curZone.Name));
4116 0 : ShowContinueError(state,
4117 : "\"ProportionalControlBasedOnOccupancySchedule\" will not be modeled. "
4118 : "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
4119 : "continues...");
4120 0 : ShowContinueErrorTimeStamp(state, "");
4121 : } else {
4122 0 : ShowRecurringWarningErrorAtEnd(
4123 : state,
4124 0 : format("{} = \"{}\", For System Outdoor Air Method = "
4125 : "ProportionalControlBasedOnOccupancySchedule, "
4126 : "CO2 generation from people is not greater than zero. Error continues...",
4127 : CurrentModuleObject,
4128 0 : this->Name),
4129 0 : this->CO2GainErrorIndex);
4130 : }
4131 : }
4132 0 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
4133 0 : if (this->CO2GainErrorCount < 2) {
4134 0 : ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
4135 0 : ShowContinueError(
4136 : state,
4137 0 : format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOccupancy, CO2 "
4138 : "generation from people is not greater than zero. Occurs in Zone =\"{}\". ",
4139 0 : curZone.Name));
4140 0 : ShowContinueError(state,
4141 : "\"ProportionalControlBasedOnDesignOccupancy\" will not be modeled. "
4142 : "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
4143 : "continues...");
4144 0 : ShowContinueErrorTimeStamp(state, "");
4145 : } else {
4146 0 : ShowRecurringWarningErrorAtEnd(
4147 : state,
4148 0 : format(
4149 : "{} = \"{}\", For System Outdoor Air Method = ProportionalControlBasedOnDesignOccupancy, "
4150 : "CO2 generation from people is not greater than zero. Error continues...",
4151 : CurrentModuleObject,
4152 0 : this->Name),
4153 0 : this->CO2GainErrorIndex);
4154 : }
4155 : }
4156 : }
4157 9816 : ZoneOA = ZoneOABZ / ZoneEz;
4158 : }
4159 : } else {
4160 : // ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] is less than or equal to zero
4161 6552 : ZoneOA = ZoneOABZ / ZoneEz;
4162 : }
4163 : } else {
4164 : // ZoneControl:ContaminantController is scheduled off (not available)
4165 0 : ZoneOA = ZoneOABZ / ZoneEz;
4166 : }
4167 : } else {
4168 : // "Carbon Dioxide Control Availability Schedule" for ZoneControl:ContaminantController not found
4169 24560 : ZoneOA = ZoneOABZ / ZoneEz;
4170 : }
4171 55248 : SysOA = SysOA + ZoneOA;
4172 : }
4173 :
4174 : // Get the zone supply air flow rate
4175 6551926 : Real64 ZoneSA = 0.0; // Zone supply air flow rate
4176 6551926 : Real64 ZonePA = 0.0; // Zone primary air flow rate
4177 6551926 : Ep = 1.0;
4178 6551926 : if (ZoneEquipConfigNum > 0) {
4179 6551926 : auto &curZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigNum);
4180 13103852 : for (int InNodeIndex = 1; InNodeIndex <= curZoneEquipConfig.NumInletNodes; ++InNodeIndex) {
4181 : // Assume primary air is always stored at the AirDistUnitCool (cooling deck if dual duct)
4182 6551926 : int PriNode = curZoneEquipConfig.AirDistUnitCool(InNodeIndex).InNode; // primary node of zone terminal unit
4183 6551926 : Real64 MassFlowRate = 0.0;
4184 6551926 : if (PriNode > 0) {
4185 6551926 : NodeTemp = state.dataLoopNodes->Node(PriNode).Temp;
4186 6551926 : NodeHumRat = state.dataLoopNodes->Node(PriNode).HumRat;
4187 6551926 : MassFlowRate = state.dataLoopNodes->Node(PriNode).MassFlowRate;
4188 : }
4189 : // total primary air to terminal units of the zone
4190 6551926 : if (MassFlowRate > 0.0) {
4191 6499334 : ZonePA +=
4192 6499334 : MassFlowRate / Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, NodeTemp, NodeHumRat);
4193 : }
4194 :
4195 : // or InletNode = ZoneEquipConfig(ZoneEquipConfigNum)%AirDistUnitCool(InNodeIndex)%OutNode
4196 6551926 : int InletNode = curZoneEquipConfig.InletNode(InNodeIndex); // outlet node of zone terminal unit
4197 6551926 : MassFlowRate = 0.0;
4198 6551926 : if (InletNode > 0) {
4199 6551926 : NodeTemp = state.dataLoopNodes->Node(InletNode).Temp;
4200 6551926 : NodeHumRat = state.dataLoopNodes->Node(InletNode).HumRat; // ZoneAirHumRat(ZoneNum)
4201 6551926 : MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
4202 : }
4203 : // total supply air to the zone
4204 6551926 : if (MassFlowRate > 0.0) {
4205 6502874 : ZoneSA +=
4206 6502874 : MassFlowRate / Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, NodeTemp, NodeHumRat);
4207 : }
4208 : }
4209 :
4210 : // calc zone primary air fraction
4211 6551926 : if (ZoneSA > 0.0) {
4212 6502874 : Ep = ZonePA / ZoneSA;
4213 : }
4214 6551926 : if (Ep > 1.0) {
4215 0 : Ep = 1.0;
4216 : }
4217 : }
4218 :
4219 : // Calc the zone OA fraction = Zone OA flow rate / Zone supply air flow rate
4220 6551926 : if (ZoneSA > 0.0) {
4221 6502874 : ZoneOAFrac = ZoneOA / ZoneSA;
4222 : // Zone OA fraction cannot be more than 1
4223 6502874 : if (ZoneOAFrac > 1.0) {
4224 216765 : ZoneOAFrac = 1.0;
4225 : }
4226 : } else {
4227 49052 : ZoneOAFrac = 0.0;
4228 : }
4229 :
4230 : // added for TRACE - zone maximum OA fraction - calculate the adjustment factor for the TU/zone supply air flow
4231 : // only for VRP system OA method
4232 6551926 : curZoneSysEnergyDemand.SupplyAirAdjustFactor = 1.0;
4233 :
4234 6551926 : if (this->SystemOAMethod == DataSizing::SysOAMethod::VRP || this->SystemOAMethod == DataSizing::SysOAMethod::VRPL) {
4235 6496678 : if (ZoneOAFrac > this->ZoneMaxOAFraction) {
4236 0 : if (this->ZoneMaxOAFraction > 0.0) {
4237 0 : curZoneSysEnergyDemand.SupplyAirAdjustFactor = ZoneOAFrac / this->ZoneMaxOAFraction;
4238 : } else {
4239 0 : curZoneSysEnergyDemand.SupplyAirAdjustFactor = 1.0;
4240 : }
4241 :
4242 : // cap zone OA fraction at the maximum specified
4243 0 : ZoneOAFrac = this->ZoneMaxOAFraction;
4244 : }
4245 : }
4246 :
4247 : // Zone air secondary recirculation fraction
4248 6551926 : Er = thisMechVentZone.ZoneSecondaryRecirculation;
4249 6551926 : if (Er > 0.0) {
4250 : // multi-path ventilation system using VRP
4251 18354 : Fa = Ep + (1.0 - Ep) * Er;
4252 18354 : Fb = Ep;
4253 18354 : Fc = 1.0 - (1.0 - ZoneEz) * (1.0 - Er) * (1.0 - Ep);
4254 :
4255 : // Calc zone ventilation efficiency
4256 18354 : if (Fa > 0.0) {
4257 18354 : Evz = 1.0 + Xs * Fb / Fa - ZoneOAFrac * Ep * Fc / Fa;
4258 : } else {
4259 0 : Evz = 1.0;
4260 : }
4261 : } else {
4262 : // single-path ventilation system
4263 6533572 : Evz = 1.0 + Xs - ZoneOAFrac;
4264 : }
4265 :
4266 : // calc system ventilation efficiency = Minimum of zone ventilation efficiency
4267 6551926 : if (Evz < 0.0) {
4268 0 : Evz = 0.0;
4269 : }
4270 6551926 : if (Evz < SysEv) {
4271 1409001 : SysEv = Evz;
4272 : }
4273 :
4274 : } // zone loop
4275 :
4276 : // Calc the system supply OA flow rate counting the system ventilation efficiency
4277 787505 : if (SysEv <= 0.0) {
4278 0 : SysEv = 1.0;
4279 : }
4280 :
4281 : // Calc system outdoor air requirement
4282 787505 : if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
4283 781365 : this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
4284 775229 : this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
4285 18416 : SysOA = SysOA / SysEv;
4286 769089 : } else if (this->SystemOAMethod == DataSizing::SysOAMethod::VRPL && this->SysDesOA > 0.0) {
4287 : // Limit system OA to design OA minimum flow rate, as per ASHRAE Guideline 36-2018 Section 5.16.3.1
4288 : // If no system sizing run is done (i.e. no Sizing:System) the design outdoor air flow rate is not known
4289 665246 : SysOA = min(SysOAuc / SysEv, this->SysDesOA);
4290 : } else {
4291 103843 : SysOA = SysOAuc / SysEv;
4292 : }
4293 : }
4294 :
4295 : // Finally calc the system supply OA mass flow rate
4296 955627 : MechVentOAMassFlow = SysOA * state.dataEnvrn->StdRhoAir;
4297 : }
4298 : }
4299 1261261 : return MechVentOAMassFlow;
4300 : }
4301 :
4302 25603103 : void OAControllerProps::CalcOAEconomizer(EnergyPlusData &state,
4303 : int const AirLoopNum,
4304 : Real64 const OutAirMinFrac,
4305 : Real64 &OASignal,
4306 : bool &HighHumidityOperationFlag,
4307 : bool const FirstHVACIteration)
4308 : {
4309 25603103 : int constexpr MaxIte(500); // Maximum number of iterations
4310 25603103 : Real64 constexpr Acc(0.0001); // Accuracy of result
4311 : bool AirLoopEconoLockout; // Economizer lockout flag
4312 : bool AirLoopNightVent; // Night Ventilation flag for air loop
4313 : bool EconomizerOperationFlag; // TRUE if OA economizer is active
4314 : Real64 EconomizerAirFlowScheduleValue; // value of economizer operation schedule (push-button type control schedule)
4315 : Real64 MaximumOAFracBySetPoint; // The maximum OA fraction due to freezing cooling coil check
4316 : Real64 OutAirSignal; // Used to set OA mass flow rate
4317 : Real64 minOAFrac;
4318 :
4319 25603103 : if (AirLoopNum > 0) {
4320 : // Check lockout with heating for any airloop - will lockout economizer even on airloops without a unitary system
4321 25039224 : if (this->Lockout == LockoutType::LockoutWithHeatingPossible) {
4322 : // For all system types (even ones that don't set AirLoopEconoLockout) lock out economizer if unfavorable for heating
4323 5492561 : if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CheckHeatRecoveryBypassStatus &&
4324 939567 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysComponentsSimulated) {
4325 :
4326 392420 : if (this->MixedAirTempAtMinOAFlow <= state.dataLoopNodes->Node(this->MixNode).TempSetPoint) {
4327 146774 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconomizerFlowLocked = true;
4328 : // this->OAMassFlow = AirLoopFlow( AirLoopNum ).MinOutAir;
4329 : // AirLoopFlow( AirLoopNum ).OAFrac = this->OAMassFlow / this->MixMassFlow;
4330 146774 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconoLockout = true;
4331 : } else {
4332 245646 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconomizerFlowLocked = false;
4333 245646 : this->HRHeatingCoilActive = 0;
4334 : }
4335 392420 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CheckHeatRecoveryBypassStatus = false;
4336 : }
4337 : }
4338 : }
4339 :
4340 25603103 : if (AirLoopNum > 0) {
4341 25039224 : AirLoopEconoLockout = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconoLockout;
4342 25039224 : AirLoopNightVent = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).NightVent;
4343 : } else {
4344 563879 : AirLoopEconoLockout = false;
4345 563879 : AirLoopNightVent = false;
4346 : }
4347 :
4348 : // Define an outside air signal
4349 25603103 : if (this->MixedAirSPMNum > 0) {
4350 10151 : this->CoolCoilFreezeCheck = SetPointManager::GetCoilFreezingCheckFlag(state, this->MixedAirSPMNum);
4351 : } else {
4352 25592952 : this->CoolCoilFreezeCheck = false;
4353 : }
4354 :
4355 25603103 : if (std::abs(this->RetTemp - this->InletTemp) > HVAC::SmallTempDiff) {
4356 25602589 : OutAirSignal = (this->RetTemp - this->MixSetTemp) / (this->RetTemp - this->InletTemp);
4357 25602589 : if (this->CoolCoilFreezeCheck) {
4358 2 : this->MaxOAFracBySetPoint = 0.0;
4359 2 : MaximumOAFracBySetPoint = OutAirSignal;
4360 : }
4361 : } else {
4362 514 : if (this->RetTemp - this->MixSetTemp < 0.0) {
4363 189 : if (this->RetTemp - this->InletTemp >= 0.0) {
4364 124 : OutAirSignal = -1.0;
4365 : } else {
4366 65 : OutAirSignal = 1.0;
4367 : }
4368 : } else {
4369 325 : if (this->RetTemp - this->InletTemp >= 0.0) {
4370 292 : OutAirSignal = 1.0;
4371 : } else {
4372 33 : OutAirSignal = -1.0;
4373 : }
4374 : }
4375 : }
4376 25603103 : OutAirSignal = min(max(OutAirSignal, OutAirMinFrac), 1.0);
4377 :
4378 : // If no economizer, set to minimum and disable economizer and high humidity control
4379 25603103 : if (this->Econo == EconoOp::NoEconomizer) {
4380 8153113 : OutAirSignal = OutAirMinFrac;
4381 8153113 : EconomizerOperationFlag = false;
4382 8153113 : EconomizerAirFlowScheduleValue = 0.0;
4383 8153113 : HighHumidityOperationFlag = false;
4384 17449990 : } else if (this->MaxOA < HVAC::SmallAirVolFlow) {
4385 0 : OutAirSignal = OutAirMinFrac;
4386 0 : EconomizerOperationFlag = false;
4387 0 : EconomizerAirFlowScheduleValue = 0.0;
4388 0 : HighHumidityOperationFlag = false;
4389 17449990 : } else if (AirLoopEconoLockout) {
4390 921405 : OutAirSignal = OutAirMinFrac;
4391 921405 : EconomizerOperationFlag = false;
4392 921405 : EconomizerAirFlowScheduleValue = 0.0;
4393 921405 : HighHumidityOperationFlag = false;
4394 : } else {
4395 : // Changed by Amit for new implementation
4396 : // Otherwise do the limit checks
4397 16528585 : EconomizerOperationFlag = true;
4398 : // Outside air temp greater than mix air setpoint
4399 16528585 : if (this->InletTemp > this->MixSetTemp) {
4400 11781801 : OutAirSignal = 1.0;
4401 : }
4402 : // Return air temp limit
4403 16528585 : if (this->Econo == EconoOp::DifferentialDryBulb) {
4404 11830167 : if (this->InletTemp > this->RetTemp) {
4405 4561305 : OutAirSignal = OutAirMinFrac;
4406 4561305 : EconomizerOperationFlag = false;
4407 : }
4408 11830167 : this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
4409 : }
4410 : // Return air enthalpy limit
4411 16528585 : if (this->Econo == EconoOp::DifferentialEnthalpy) {
4412 1346940 : if (this->InletEnth > this->RetEnth) {
4413 925178 : OutAirSignal = OutAirMinFrac;
4414 925178 : EconomizerOperationFlag = false;
4415 : }
4416 1346940 : this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
4417 : }
4418 : // Outside air temperature limit
4419 16528585 : if (this->Econo == EconoOp::FixedDryBulb) {
4420 3289891 : this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
4421 : }
4422 : // Fixed Enthalpy limit
4423 16528585 : if (this->Econo == EconoOp::FixedEnthalpy) {
4424 0 : this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
4425 : }
4426 : // FIXED DEW POINT AND DRY BULB TEMPERATURE STRATEGY
4427 16528585 : if (this->Econo == EconoOp::FixedDewPointAndDryBulb) {
4428 0 : this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
4429 : }
4430 : // ELECRONIC ENTHALPY, HUMIDITY RATIO CURVE
4431 16528585 : if (this->Econo == EconoOp::ElectronicEnthalpy) {
4432 61587 : this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
4433 : }
4434 : // Differential dry bulb and enthalpy strategy
4435 16528585 : if (this->Econo == EconoOp::DifferentialDryBulbAndEnthalpy) {
4436 0 : if (this->InletTemp > this->RetTemp) {
4437 0 : OutAirSignal = OutAirMinFrac;
4438 0 : EconomizerOperationFlag = false;
4439 : }
4440 0 : if (this->InletEnth > this->RetEnth) {
4441 0 : OutAirSignal = OutAirMinFrac;
4442 0 : EconomizerOperationFlag = false;
4443 : }
4444 0 : this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
4445 : }
4446 :
4447 16528585 : if (this->TempLowLim != HVAC::BlankNumeric && this->OATemp < this->TempLowLim) {
4448 959292 : OutAirSignal = OutAirMinFrac;
4449 959292 : EconomizerOperationFlag = false;
4450 : }
4451 : // Increase air flow for humidity control
4452 : // (HumidistatZoneNum is greater than 0 IF High Humidity Control Flag = YES, checked in GetInput)
4453 16528585 : if (this->HumidistatZoneNum > 0) {
4454 : // IF humidistat senses a moisture load check to see if modifying air flow is appropriate, otherwise disable modified air flow
4455 58289 : if (state.dataZoneEnergyDemand->ZoneSysMoistureDemand(this->HumidistatZoneNum).TotalOutputRequired < 0.0) {
4456 : // IF OAController is not allowed to modify air flow during high outdoor humrat condition, then disable modified air flow
4457 : // if indoor humrat is less than or equal to outdoor humrat
4458 42180 : if (!this->ModifyDuringHighOAMoisture &&
4459 21090 : (state.dataLoopNodes->Node(this->NodeNumofHumidistatZone).HumRat - this->OAHumRat) <= HVAC::SmallHumRatDiff) {
4460 20696 : HighHumidityOperationFlag = false;
4461 : } else {
4462 394 : HighHumidityOperationFlag = true;
4463 : }
4464 : } else {
4465 37199 : HighHumidityOperationFlag = false;
4466 : }
4467 : } else {
4468 16470296 : HighHumidityOperationFlag = false;
4469 : }
4470 :
4471 : // Check time of day economizer schedule, enable economizer if schedule value > 0
4472 16528585 : EconomizerAirFlowScheduleValue = 0.0;
4473 16528585 : if (this->economizerOASched != nullptr) {
4474 61772 : EconomizerAirFlowScheduleValue = this->economizerOASched->getCurrentVal();
4475 61772 : if (EconomizerAirFlowScheduleValue > 0.0) {
4476 848 : EconomizerOperationFlag = true;
4477 848 : OutAirSignal = 1.0;
4478 : }
4479 : }
4480 : }
4481 :
4482 : // OutAirSignal will not give exactly the correct mixed air temperature (equal to the setpoint) since
4483 : // it was calculated using the approximate method of sensible energy balance. Now we have to get the
4484 : // accurate result using a full mass, enthalpy and moisture balance and iteration.
4485 25603103 : if (OutAirSignal > OutAirMinFrac && OutAirSignal < 1.0 && this->MixMassFlow > HVAC::VerySmallMassFlow &&
4486 1746299 : this->ControllerType == MixedAirControllerType::ControllerOutsideAir && !AirLoopNightVent) {
4487 : int SolFla; // Flag of solver
4488 :
4489 1746299 : if (AirLoopNum > 0) {
4490 :
4491 1746299 : if (state.dataAirLoop->OutsideAirSys(state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum).NumComponents == 1) {
4492 : // no need to simulate OA System if only a mixer is used in the OutsideAirSystem
4493 :
4494 6582125 : auto f = [&state, this](Real64 const OASignal) {
4495 6582125 : Real64 const OAMassFlowRate = OASignal * this->MixMassFlow;
4496 6582125 : Real64 const RecircMassFlowRate = max(this->MixMassFlow - OAMassFlowRate, 0.0);
4497 6582125 : Real64 const RecircEnth = state.dataLoopNodes->Node(this->RetNode).Enthalpy;
4498 6582125 : Real64 const RecircHumRat = state.dataLoopNodes->Node(this->RetNode).HumRat;
4499 : Real64 const MixEnth =
4500 6582125 : (RecircMassFlowRate * RecircEnth + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).Enthalpy) / this->MixMassFlow;
4501 : Real64 const MixHumRat =
4502 6582125 : (RecircMassFlowRate * RecircHumRat + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).HumRat) / this->MixMassFlow;
4503 6582125 : Real64 const MixTemp = Psychrometrics::PsyTdbFnHW(MixEnth, MixHumRat);
4504 6582125 : return state.dataLoopNodes->Node(this->MixNode).TempSetPoint - MixTemp;
4505 1734575 : };
4506 :
4507 1734575 : General::SolveRoot(state, Acc, MaxIte, SolFla, OASignal, f, OutAirMinFrac, 1.0);
4508 1734575 : if (SolFla < 0) {
4509 805 : OASignal = OutAirSignal;
4510 : }
4511 :
4512 : } else {
4513 :
4514 : // simulate OA System if equipment exists other than the mixer (e.g., heating/cooling coil, HX, etc.)
4515 :
4516 : // 1 - check min OA flow result
4517 11724 : if (this->FixedMin) {
4518 11724 : state.dataLoopNodes->Node(this->OANode).MassFlowRate =
4519 11724 : min(max(this->ExhMassFlow, OutAirMinFrac * state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply),
4520 11724 : state.dataLoopNodes->Node(this->MixNode).MassFlowRate);
4521 11724 : state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
4522 11724 : max(state.dataLoopNodes->Node(this->OANode).MassFlowRate - this->ExhMassFlow, 0.0);
4523 : // save actual OA flow frac for use as min value for RegulaFalsi call
4524 11724 : minOAFrac = max(OutAirMinFrac, state.dataLoopNodes->Node(this->OANode).MassFlowRate / this->MixMassFlow);
4525 : } else {
4526 0 : state.dataLoopNodes->Node(this->OANode).MassFlowRate =
4527 0 : max(this->ExhMassFlow, OutAirMinFrac * state.dataLoopNodes->Node(this->MixNode).MassFlowRate);
4528 0 : state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
4529 0 : max(state.dataLoopNodes->Node(this->OANode).MassFlowRate - this->ExhMassFlow, 0.0);
4530 : // save actual OA flow frac for use as min value for RegulaFalsi call
4531 0 : minOAFrac = max(OutAirMinFrac, state.dataLoopNodes->Node(this->OANode).MassFlowRate / this->MixMassFlow);
4532 : }
4533 11724 : SimOASysComponents(state, state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum, FirstHVACIteration, AirLoopNum);
4534 11724 : Real64 lowFlowResiduum = state.dataLoopNodes->Node(this->MixNode).TempSetPoint - state.dataLoopNodes->Node(this->MixNode).Temp;
4535 :
4536 : // 2 - check max OA flow result
4537 11724 : state.dataLoopNodes->Node(this->OANode).MassFlowRate = max(this->ExhMassFlow, state.dataLoopNodes->Node(this->MixNode).MassFlowRate);
4538 11724 : state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
4539 11724 : max(state.dataLoopNodes->Node(this->OANode).MassFlowRate - this->ExhMassFlow, 0.0);
4540 11724 : SimOASysComponents(state, state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum, FirstHVACIteration, AirLoopNum);
4541 11724 : Real64 highFlowResiduum = state.dataLoopNodes->Node(this->MixNode).TempSetPoint - state.dataLoopNodes->Node(this->MixNode).Temp;
4542 :
4543 : // 3 - test to ensure RegulaFalsi can find an answer
4544 11724 : if ((sign(lowFlowResiduum) == sign(highFlowResiduum))) {
4545 645 : OASignal = OutAirSignal;
4546 : } else {
4547 : // 4 - find result
4548 :
4549 51304 : auto f = [&state, this, FirstHVACIteration, AirLoopNum](Real64 const OASignal) {
4550 51304 : Real64 const MixMassFlowRate = this->MixMassFlow;
4551 51304 : int const OASysNum = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum;
4552 51304 : Real64 localExhMassFlow = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).ZoneExhMassFlow;
4553 51304 : Real64 const OAMassFlowRate = max(localExhMassFlow, OASignal * MixMassFlowRate);
4554 51304 : state.dataLoopNodes->Node(this->OANode).MassFlowRate = OAMassFlowRate; // set OA node mass flow rate
4555 51304 : state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
4556 51304 : max(OAMassFlowRate - localExhMassFlow, 0.0); // set relief node mass flow rate to maintain mixer continuity calcs
4557 51304 : SimOASysComponents(state, OASysNum, FirstHVACIteration, AirLoopNum);
4558 51304 : return state.dataLoopNodes->Node(this->MixNode).TempSetPoint - state.dataLoopNodes->Node(this->MixNode).Temp;
4559 11079 : };
4560 :
4561 11079 : General::SolveRoot(state, (Acc / 10.0), MaxIte, SolFla, OASignal, f, minOAFrac, 1.0);
4562 11079 : if (SolFla < 0) { // if RegulaFalsi fails to find a solution, returns -1 or -2, set to existing OutAirSignal
4563 270 : OASignal = OutAirSignal;
4564 : }
4565 : }
4566 : }
4567 :
4568 : } else {
4569 :
4570 0 : auto f = [&state, this](Real64 const OASignal) {
4571 0 : Real64 const MixMassFlowRate = this->MixMassFlow;
4572 0 : Real64 OAMassFlowRate = OASignal * MixMassFlowRate;
4573 0 : Real64 RecircMassFlowRate = max(MixMassFlowRate - OAMassFlowRate, 0.0);
4574 0 : Real64 RecircEnth = state.dataLoopNodes->Node(this->RetNode).Enthalpy;
4575 0 : Real64 RecircHumRat = state.dataLoopNodes->Node(this->RetNode).HumRat;
4576 : Real64 MixEnth =
4577 0 : (RecircMassFlowRate * RecircEnth + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).Enthalpy) / MixMassFlowRate;
4578 : Real64 MixHumRat =
4579 0 : (RecircMassFlowRate * RecircHumRat + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).HumRat) / MixMassFlowRate;
4580 0 : Real64 MixTemp = Psychrometrics::PsyTdbFnHW(MixEnth, MixHumRat);
4581 0 : return state.dataLoopNodes->Node(this->MixNode).TempSetPoint - MixTemp;
4582 0 : };
4583 :
4584 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, OASignal, f, OutAirMinFrac, 1.0);
4585 0 : if (SolFla < 0) {
4586 0 : OASignal = OutAirSignal;
4587 : }
4588 : }
4589 :
4590 1746299 : } else {
4591 23856804 : OASignal = OutAirSignal;
4592 : }
4593 :
4594 : // Economizer choice "Bypass" forces minimum OA except when high humidity air flow is active based on indoor RH
4595 25603103 : if (this->EconBypass && EconomizerAirFlowScheduleValue == 0.0) {
4596 697950 : OASignal = OutAirMinFrac;
4597 : }
4598 :
4599 : // Set outdoor air signal based on OA flow ratio if high humidity air flow is enabled
4600 25603103 : if (HighHumidityOperationFlag) {
4601 394 : if (this->MixMassFlow > 0.0) {
4602 : // calculate the actual ratio of outside air to mixed air so the magnitude of OA during high humidity control is correct
4603 394 : OASignal = max(OutAirMinFrac, (this->HighRHOAFlowRatio * this->MaxOAMassFlowRate / this->MixMassFlow));
4604 394 : this->OALimitingFactor = OALimitFactor::HighHum;
4605 : }
4606 : }
4607 :
4608 25603103 : if (this->CoolCoilFreezeCheck) {
4609 2 : MaximumOAFracBySetPoint = min(max(MaximumOAFracBySetPoint, 0.0), 1.0);
4610 2 : this->MaxOAFracBySetPoint = MaximumOAFracBySetPoint;
4611 :
4612 : // This should not be messing with OutAirMinFrac, freeze protection should only limit economizer operation
4613 : // if (MaximumOAFracBySetPoint < OutAirMinFrac) {
4614 : // OutAirMinFrac = MaximumOAFracBySetPoint;
4615 : // if (AirLoopNum > 0) AirLoopFlow(AirLoopNum).MinOutAir = OutAirMinFrac * this->MixMassFlow;
4616 : //}
4617 2 : if (MaximumOAFracBySetPoint < OASignal) {
4618 2 : OASignal = MaximumOAFracBySetPoint;
4619 2 : this->OALimitingFactor = OALimitFactor::Limits;
4620 : }
4621 2 : if (OutAirMinFrac > OASignal) {
4622 2 : OASignal = OutAirMinFrac;
4623 2 : this->OALimitingFactor = OALimitFactor::Limits;
4624 : }
4625 : }
4626 :
4627 25603103 : if (AirLoopNum > 0) {
4628 :
4629 : // Set the air loop economizer and high humidity control flags.
4630 25039224 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconoActive = EconomizerOperationFlag;
4631 25039224 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HighHumCtrlActive = HighHumidityOperationFlag;
4632 25039224 : if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconomizerFlowLocked) {
4633 1228860 : this->OAMassFlow = state.dataAirLoop->AirLoopFlow(AirLoopNum).MinOutAir;
4634 1228860 : state.dataAirLoop->AirLoopFlow(AirLoopNum).OAFrac = this->OAMassFlow / this->MixMassFlow;
4635 1228860 : state.dataAirLoop->AirLoopFlow(AirLoopNum).OAFlow = this->OAMassFlow;
4636 : }
4637 :
4638 : // Check heat exchanger bypass control
4639 25039224 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass = false;
4640 25039224 : this->HeatRecoveryBypassStatus = 0;
4641 25039224 : if (EconomizerOperationFlag) {
4642 5535536 : if (this->HeatRecoveryBypassControlType == HVAC::BypassWhenWithinEconomizerLimits) {
4643 5110431 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass = true;
4644 5110431 : this->HeatRecoveryBypassStatus = 1;
4645 425105 : } else if (this->HeatRecoveryBypassControlType == HVAC::BypassWhenOAFlowGreaterThanMinimum) {
4646 425105 : Real64 OAMassFlowMin = OutAirMinFrac * state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply;
4647 425105 : Real64 OAMassFlowActual = OASignal * this->MixMassFlow;
4648 425105 : Real64 reasonablySmallMassFlow = 1e-6;
4649 425105 : if (OAMassFlowActual > (OAMassFlowMin + reasonablySmallMassFlow)) {
4650 342825 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass = true;
4651 342825 : this->HeatRecoveryBypassStatus = 1;
4652 : }
4653 : }
4654 : }
4655 : }
4656 :
4657 : // Set economizer report variable and status flag
4658 25603103 : if (this->Econo == EconoOp::NoEconomizer) {
4659 : // No economizer
4660 8153113 : this->EconomizerStatus = 0;
4661 8153113 : this->EconoActive = false;
4662 : } else {
4663 : // With economizer.
4664 17449990 : if (EconomizerOperationFlag) {
4665 : // Economizer is enabled
4666 5536384 : this->EconomizerStatus = 1;
4667 5536384 : this->EconoActive = true;
4668 5536384 : if ((OASignal > OutAirMinFrac) && !HighHumidityOperationFlag) {
4669 3799728 : this->OALimitingFactor = OALimitFactor::Economizer;
4670 : }
4671 : } else {
4672 : // Economizer is disabled
4673 11913606 : this->EconomizerStatus = 0;
4674 11913606 : this->EconoActive = false;
4675 : }
4676 : }
4677 :
4678 : // Night ventilation control overrides economizer and high humidity control.
4679 25603103 : if (AirLoopNightVent) {
4680 0 : OASignal = 1.0;
4681 0 : this->OALimitingFactor = OALimitFactor::NightVent;
4682 : }
4683 :
4684 : // Set high humidity control report variable and status flag
4685 25603103 : if (HighHumidityOperationFlag) {
4686 394 : this->HighHumCtrlStatus = 1;
4687 394 : this->HighHumCtrlActive = true;
4688 : } else {
4689 25602709 : this->HighHumCtrlStatus = 0;
4690 25602709 : this->HighHumCtrlActive = false;
4691 : }
4692 25603103 : }
4693 :
4694 60004604 : void OAMixerProps::CalcOAMixer(EnergyPlusData &state)
4695 : {
4696 :
4697 : // SUBROUTINE INFORMATION:
4698 : // AUTHOR Fred Buhl
4699 : // DATE WRITTEN Oct 1998
4700 :
4701 : // PURPOSE OF THIS SUBROUTINE
4702 : // Calculate the mixed air flow and conditions
4703 :
4704 : // Define a recirculation mass flow rate
4705 60004604 : Real64 RecircMassFlowRate = this->RetMassFlowRate - this->RelMassFlowRate;
4706 : // In certain low flow conditions the return air mass flow rate can be below the outside air value established
4707 : // by the user. This check will ensure that this condition does not result in non-physical air properties.
4708 60004604 : if (RecircMassFlowRate < 0.0) {
4709 69258 : RecircMassFlowRate = 0.0;
4710 69258 : this->RelMassFlowRate = this->RetMassFlowRate;
4711 : }
4712 :
4713 : // Pass through the return air conditions to the relief air stream. The return air is "split" to
4714 : // the relief air and the recirculation air.
4715 60004604 : this->RelTemp = this->RetTemp;
4716 60004604 : this->RelHumRat = this->RetHumRat;
4717 60004604 : this->RelEnthalpy = this->RetEnthalpy;
4718 60004604 : this->RelPressure = this->RetPressure;
4719 60004604 : Real64 RecircPressure = this->RetPressure;
4720 60004604 : Real64 RecircEnthalpy = this->RetEnthalpy;
4721 60004604 : Real64 RecircHumRat = this->RetHumRat;
4722 : // The recirculation air and the outside air are mixed to form the mixed air stream
4723 60004604 : this->MixMassFlowRate = this->OAMassFlowRate + RecircMassFlowRate;
4724 : // Check for zero flow
4725 60004604 : if (this->MixMassFlowRate <= HVAC::VerySmallMassFlow) {
4726 6462483 : this->MixEnthalpy = this->RetEnthalpy;
4727 6462483 : this->MixHumRat = this->RetHumRat;
4728 6462483 : this->MixPressure = this->RetPressure;
4729 6462483 : this->MixTemp = this->RetTemp;
4730 6462483 : return;
4731 : }
4732 :
4733 53542121 : this->MixEnthalpy = (RecircMassFlowRate * RecircEnthalpy + this->OAMassFlowRate * this->OAEnthalpy) / this->MixMassFlowRate;
4734 53542121 : this->MixHumRat = (RecircMassFlowRate * RecircHumRat + this->OAMassFlowRate * this->OAHumRat) / this->MixMassFlowRate;
4735 53542121 : this->MixPressure = (RecircMassFlowRate * RecircPressure + this->OAMassFlowRate * this->OAPressure) / this->MixMassFlowRate;
4736 : // Mixed air temperature is calculated from the mixed air enthalpy and humidity ratio.
4737 53542121 : this->MixTemp = Psychrometrics::PsyTdbFnHW(this->MixEnthalpy, this->MixHumRat);
4738 :
4739 : // Check for saturation temperature > dry-bulb temperature and modify temperature at constant enthalpy
4740 53542121 : Real64 T_sat = Psychrometrics::PsyTsatFnHPb(state, this->MixEnthalpy, this->MixPressure);
4741 53542121 : if (this->MixTemp < T_sat) {
4742 1361502 : this->MixTemp = T_sat;
4743 1361502 : this->MixHumRat = Psychrometrics::PsyWFnTdbH(state, T_sat, this->MixEnthalpy);
4744 : }
4745 : }
4746 :
4747 : // End of Calculation/Simulation Section of the Module
4748 : //******************************************************************************
4749 :
4750 : // Beginning Sizing Section of the Module
4751 : //******************************************************************************
4752 :
4753 1171 : void OAControllerProps::SizeOAController(EnergyPlusData &state)
4754 : {
4755 :
4756 : // SUBROUTINE INFORMATION:
4757 : // AUTHOR Fred Buhl
4758 : // DATE WRITTEN September 2001
4759 :
4760 : // PURPOSE OF THIS SUBROUTINE:
4761 : // This subroutine is for sizing OAController Components for which flow rates have not been
4762 : // specified in the input.
4763 :
4764 : // METHODOLOGY EMPLOYED:
4765 : // Obtains flow rates from the zone or system sizing arrays.
4766 :
4767 : // SUBROUTINE PARAMETER DEFINITIONS:
4768 : static std::string_view const &CurrentModuleObject(CurrentModuleObjects[static_cast<int>(CMO::OAController)]);
4769 :
4770 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4771 1171 : bool ErrorsFound = false;
4772 1171 : if (this->MaxOA == AutoSize) {
4773 :
4774 919 : if (state.dataSize->CurSysNum > 0) {
4775 :
4776 919 : switch (this->ControllerType) {
4777 919 : case MixedAirControllerType::ControllerOutsideAir: {
4778 919 : CheckSysSizing(state, CurrentModuleObject, this->Name);
4779 919 : switch (state.dataSize->CurDuctType) {
4780 0 : case HVAC::AirDuctType::Cooling: {
4781 0 : this->MaxOA = state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesCoolVolFlow;
4782 0 : } break;
4783 1 : case HVAC::AirDuctType::Heating: {
4784 1 : this->MaxOA = state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesHeatVolFlow;
4785 1 : } break;
4786 918 : case HVAC::AirDuctType::Main:
4787 : case HVAC::AirDuctType::Other:
4788 : default: {
4789 918 : this->MaxOA = state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesMainVolFlow;
4790 918 : } break;
4791 : }
4792 919 : } break;
4793 0 : case MixedAirControllerType::ControllerStandAloneERV: {
4794 0 : } break;
4795 0 : default:
4796 0 : break;
4797 : }
4798 :
4799 0 : } else if (state.dataSize->CurZoneEqNum > 0) {
4800 :
4801 0 : switch (this->ControllerType) {
4802 0 : case MixedAirControllerType::ControllerOutsideAir: {
4803 0 : CheckZoneSizing(state, CurrentModuleObject, this->Name);
4804 0 : this->MaxOA = max(state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow,
4805 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow);
4806 0 : } break;
4807 0 : case MixedAirControllerType::ControllerStandAloneERV: {
4808 0 : } break;
4809 0 : default:
4810 0 : break;
4811 : }
4812 : }
4813 :
4814 919 : if (this->MaxOA < HVAC::SmallAirVolFlow) {
4815 0 : this->MaxOA = 0.0;
4816 : }
4817 :
4818 919 : BaseSizer::reportSizerOutput(state, CurrentModuleObject, this->Name, "Maximum Outdoor Air Flow Rate [m3/s]", this->MaxOA);
4819 : }
4820 :
4821 1171 : if (this->MinOA == AutoSize) {
4822 :
4823 850 : if (state.dataSize->CurSysNum > 0) {
4824 :
4825 850 : CheckSysSizing(state, CurrentModuleObject, this->Name);
4826 850 : if (state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesOutAirVolFlow >= HVAC::SmallAirVolFlow) {
4827 840 : this->MinOA = min(state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesOutAirVolFlow, this->MaxOA);
4828 : } else {
4829 10 : this->MinOA = 0.0;
4830 : }
4831 : }
4832 :
4833 850 : BaseSizer::reportSizerOutput(state, CurrentModuleObject, this->Name, "Minimum Outdoor Air Flow Rate [m3/s]", this->MinOA);
4834 :
4835 850 : if (this->HumidistatZoneNum > 0 && this->FixedMin) {
4836 1 : if (this->MaxOA > 0.0) {
4837 1 : Real64 OAFlowRatio = this->MinOA / this->MaxOA;
4838 1 : if (this->HighRHOAFlowRatio < OAFlowRatio) {
4839 0 : ShowWarningError(state, format("{} \"{}\"", CurrentModuleObject, this->Name));
4840 0 : ShowContinueError(state, "... A fixed minimum outdoor air flow rate and high humidity control have been specified.");
4841 0 : ShowContinueError(state,
4842 : "... The High Humidity Outdoor Air Flow Ratio is less than the ratio of the outdoor air controllers "
4843 : "minimum to maximum outside air flow rate.");
4844 0 : ShowContinueError(state, format("... Controller minimum flow rate = {:.4T} m3/s.", this->MinOA));
4845 0 : ShowContinueError(state, format("... Controller maximum flow rate = {:.4T} m3/s.", this->MaxOA));
4846 0 : ShowContinueError(state, format("... Controller minimum to maximum flow ratio = {:.4T}.", OAFlowRatio));
4847 0 : ShowContinueError(state, format("... High humidity control flow ratio = {:.4T}.", this->HighRHOAFlowRatio));
4848 : }
4849 : }
4850 : }
4851 : }
4852 : // If there is an outside air system, loop over components in the OA system; pass the design air flow rate
4853 : // to the coil components that don't have design air flow as an input.
4854 1171 : if (state.dataSize->CurOASysNum > 0) {
4855 2269 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(state.dataSize->CurOASysNum).NumComponents; ++CompNum) {
4856 1202 : std::string const &CompType = state.dataAirLoop->OutsideAirSys(state.dataSize->CurOASysNum).ComponentType(CompNum);
4857 1202 : std::string const &CompName = state.dataAirLoop->OutsideAirSys(state.dataSize->CurOASysNum).ComponentName(CompNum);
4858 2374 : if (Util::SameString(CompType, "COIL:COOLING:WATER:DETAILEDGEOMETRY") || Util::SameString(CompType, "COIL:HEATING:WATER") ||
4859 2374 : Util::SameString(CompType, "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED")) {
4860 30 : std::string CoilName;
4861 30 : std::string CoilType;
4862 :
4863 30 : if (Util::SameString(CompType, "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED")) {
4864 0 : CoilName = HVACHXAssistedCoolingCoil::GetHXDXCoilName(state, CompType, CompName, ErrorsFound);
4865 0 : CoilType = HVACHXAssistedCoolingCoil::GetHXCoilType(state, CompType, CompName, ErrorsFound);
4866 : } else {
4867 30 : CoilName = CompName;
4868 30 : CoilType = CompType;
4869 : }
4870 30 : WaterCoils::SetCoilDesFlow(state, CoilType, CoilName, this->MinOA, ErrorsFound);
4871 30 : }
4872 : } // End of component loop
4873 : }
4874 1171 : if (ErrorsFound) {
4875 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
4876 : }
4877 1171 : }
4878 :
4879 : // End of Sizing Section of the Module
4880 : //******************************************************************************
4881 :
4882 : // Beginning Update/Reporting Section of the Module
4883 : //******************************************************************************
4884 :
4885 27596359 : void OAControllerProps::UpdateOAController(EnergyPlusData &state)
4886 : {
4887 :
4888 : // SUBROUTINE INFORMATION:
4889 : // AUTHOR Fred Buhl
4890 : // DATE WRITTEN Oct 1998
4891 : // MODIFIED Shirey/Raustad FSEC, June 2003
4892 :
4893 : // PURPOSE OF THIS SUBROUTINE
4894 : // Move the results of CalcOAController to the affected nodes
4895 :
4896 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4897 27596359 : int OutAirNodeNum = this->OANode;
4898 27596359 : int InletAirNodeNum = this->InletNode;
4899 27596359 : int RelAirNodeNum = this->RelNode;
4900 27596359 : int RetAirNodeNum = this->RetNode;
4901 :
4902 27596359 : if (this->ControllerType == MixedAirControllerType::ControllerOutsideAir) {
4903 : // The outside air controller sets the outside air flow rate and the relief air flow rate
4904 27038038 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && (this->ManageDemand) &&
4905 5660 : (this->OAMassFlow > this->DemandLimitFlowRate)) {
4906 0 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->DemandLimitFlowRate;
4907 0 : state.dataLoopNodes->Node(InletAirNodeNum).MassFlowRate = this->DemandLimitFlowRate;
4908 0 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->DemandLimitFlowRate;
4909 : } else {
4910 27032378 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->OAMassFlow;
4911 27032378 : state.dataLoopNodes->Node(InletAirNodeNum).MassFlowRate = this->OAMassFlow;
4912 27032378 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->OAMassFlow;
4913 : }
4914 27032378 : state.dataLoopNodes->Node(RelAirNodeNum).MassFlowRate = this->RelMassFlow;
4915 : } else {
4916 : // The ERV controller sets the supply and secondary inlet node information for the Stand Alone ERV
4917 : // Currently, the Stand Alone ERV only has constant air flows (supply and exhaust), and these are
4918 : // already set in HVACStandAloneERV.cc (subroutine init). Therefore, these flow assignments below are
4919 : // currently redundant but may be useful in the future as mass flow rates can vary based on the controller signal.
4920 563981 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && (this->ManageDemand) &&
4921 0 : (this->OAMassFlow > this->DemandLimitFlowRate)) {
4922 0 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->DemandLimitFlowRate;
4923 0 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->DemandLimitFlowRate;
4924 : } else {
4925 563981 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->OAMassFlow;
4926 563981 : state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->OAMassFlow;
4927 : }
4928 563981 : state.dataLoopNodes->Node(RetAirNodeNum).MassFlowRate = state.dataLoopNodes->Node(this->RetNode).MassFlowRate;
4929 563981 : state.dataLoopNodes->Node(RetAirNodeNum).MassFlowRateMaxAvail = state.dataLoopNodes->Node(this->RetNode).MassFlowRate;
4930 : }
4931 27596359 : }
4932 :
4933 60004604 : void OAMixerProps::UpdateOAMixer(EnergyPlusData &state) const
4934 : {
4935 :
4936 : // SUBROUTINE INFORMATION:
4937 : // AUTHOR Fred Buhl
4938 : // DATE WRITTEN Oct 1998
4939 :
4940 : // PURPOSE OF THIS SUBROUTINE
4941 : // Move the results of CalcOAMixer to the affected nodes
4942 :
4943 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4944 60004604 : int MixNode = this->MixNode;
4945 60004604 : int RelNode = this->RelNode;
4946 60004604 : int RetNode = this->RetNode;
4947 : // Move mixed air data to the mixed air node
4948 60004604 : state.dataLoopNodes->Node(MixNode).MassFlowRate = this->MixMassFlowRate;
4949 60004604 : state.dataLoopNodes->Node(MixNode).Temp = this->MixTemp;
4950 60004604 : state.dataLoopNodes->Node(MixNode).HumRat = this->MixHumRat;
4951 60004604 : state.dataLoopNodes->Node(MixNode).Enthalpy = this->MixEnthalpy;
4952 60004604 : state.dataLoopNodes->Node(MixNode).Press = this->MixPressure;
4953 60004604 : state.dataLoopNodes->Node(MixNode).MassFlowRateMaxAvail = this->MixMassFlowRate;
4954 : // Move the relief air data to the relief air node
4955 60004604 : state.dataLoopNodes->Node(RelNode).MassFlowRate = this->RelMassFlowRate;
4956 60004604 : state.dataLoopNodes->Node(RelNode).Temp = this->RelTemp;
4957 60004604 : state.dataLoopNodes->Node(RelNode).HumRat = this->RelHumRat;
4958 60004604 : state.dataLoopNodes->Node(RelNode).Enthalpy = this->RelEnthalpy;
4959 60004604 : state.dataLoopNodes->Node(RelNode).Press = this->RelPressure;
4960 60004604 : state.dataLoopNodes->Node(RelNode).MassFlowRateMaxAvail = this->RelMassFlowRate;
4961 :
4962 60004604 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
4963 76057 : state.dataLoopNodes->Node(RelNode).CO2 = state.dataLoopNodes->Node(RetNode).CO2;
4964 76057 : if (this->MixMassFlowRate <= HVAC::VerySmallMassFlow) {
4965 5561 : state.dataLoopNodes->Node(MixNode).CO2 = state.dataLoopNodes->Node(RetNode).CO2;
4966 : } else {
4967 70496 : state.dataLoopNodes->Node(MixNode).CO2 =
4968 70496 : ((state.dataLoopNodes->Node(RetNode).MassFlowRate - state.dataLoopNodes->Node(RelNode).MassFlowRate) *
4969 70496 : state.dataLoopNodes->Node(RetNode).CO2 +
4970 70496 : this->OAMassFlowRate * state.dataContaminantBalance->OutdoorCO2) /
4971 70496 : this->MixMassFlowRate;
4972 : }
4973 : }
4974 :
4975 60004604 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
4976 18150 : state.dataLoopNodes->Node(RelNode).GenContam = state.dataLoopNodes->Node(RetNode).GenContam;
4977 18150 : if (this->MixMassFlowRate <= HVAC::VerySmallMassFlow) {
4978 1957 : state.dataLoopNodes->Node(MixNode).GenContam = state.dataLoopNodes->Node(RetNode).GenContam;
4979 : } else {
4980 16193 : state.dataLoopNodes->Node(MixNode).GenContam =
4981 16193 : ((state.dataLoopNodes->Node(RetNode).MassFlowRate - state.dataLoopNodes->Node(RelNode).MassFlowRate) *
4982 16193 : state.dataLoopNodes->Node(RetNode).GenContam +
4983 16193 : this->OAMassFlowRate * state.dataContaminantBalance->OutdoorGC) /
4984 16193 : this->MixMassFlowRate;
4985 : }
4986 : }
4987 60004604 : }
4988 :
4989 : // End of Sizing Section of the Module
4990 : //******************************************************************************
4991 :
4992 : // Beginning Utility Section of the Module
4993 : //******************************************************************************
4994 :
4995 877 : Array1D_int GetOAMixerNodeNumbers(EnergyPlusData &state,
4996 : std::string const &OAMixerName, // must match OA mixer names for the OA mixer type
4997 : bool &ErrorsFound // set to true if problem
4998 : )
4999 : {
5000 :
5001 : // FUNCTION INFORMATION:
5002 : // AUTHOR Richard Raustad
5003 : // DATE WRITTEN June 2006
5004 :
5005 : // PURPOSE OF THIS FUNCTION:
5006 : // This function looks up the given OA mixer and returns the node numbers. If
5007 : // incorrect OA mixer name is given, ErrorsFound is returned as true
5008 : // as zero.
5009 :
5010 : // Return value
5011 877 : Array1D_int OANodeNumbers(4); // return OA mixer nodes
5012 :
5013 : // Obtains and Allocates OA mixer related parameters from input file
5014 877 : if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
5015 37 : GetOAMixerInputs(state);
5016 37 : state.dataMixedAir->GetOAMixerInputFlag = false;
5017 : }
5018 :
5019 877 : int WhichOAMixer = Util::FindItemInList(OAMixerName, state.dataMixedAir->OAMixer);
5020 877 : if (WhichOAMixer != 0) {
5021 877 : OANodeNumbers(1) = state.dataMixedAir->OAMixer(WhichOAMixer).InletNode;
5022 877 : OANodeNumbers(2) = state.dataMixedAir->OAMixer(WhichOAMixer).RelNode;
5023 877 : OANodeNumbers(3) = state.dataMixedAir->OAMixer(WhichOAMixer).RetNode;
5024 877 : OANodeNumbers(4) = state.dataMixedAir->OAMixer(WhichOAMixer).MixNode;
5025 : }
5026 :
5027 877 : if (WhichOAMixer == 0) {
5028 0 : ShowSevereError(state, format("GetOAMixerNodeNumbers: Could not find OA Mixer = \"{}\"", OAMixerName));
5029 0 : ErrorsFound = true;
5030 0 : OANodeNumbers = 0;
5031 : }
5032 :
5033 877 : return OANodeNumbers;
5034 0 : }
5035 :
5036 30 : int GetNumOAMixers(EnergyPlusData &state)
5037 : {
5038 :
5039 : // FUNCTION INFORMATION:
5040 : // AUTHOR Linda Lawrie
5041 : // DATE WRITTEN October 2006
5042 :
5043 : // PURPOSE OF THIS FUNCTION:
5044 : // After making sure get input is done, the number of OA mixers is returned.
5045 :
5046 30 : if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
5047 0 : GetOAMixerInputs(state);
5048 0 : state.dataMixedAir->GetOAMixerInputFlag = false;
5049 : }
5050 :
5051 30 : return state.dataMixedAir->NumOAMixers;
5052 : }
5053 :
5054 0 : int GetNumOAControllers(EnergyPlusData &state)
5055 : {
5056 :
5057 : // FUNCTION INFORMATION:
5058 : // AUTHOR Linda Lawrie
5059 : // DATE WRITTEN October 2006
5060 :
5061 : // PURPOSE OF THIS FUNCTION:
5062 : // After making sure get input is done, the number of OA Controllers is returned.
5063 :
5064 0 : if (state.dataMixedAir->AllocateOAControllersFlag) {
5065 : // Make sure OAControllers are allocated
5066 0 : AllocateOAControllers(state);
5067 : }
5068 :
5069 0 : return state.dataMixedAir->NumOAControllers;
5070 : }
5071 :
5072 15 : int GetOAMixerReliefNodeNumber(EnergyPlusData &state, int const OAMixerNum) // Which Mixer
5073 : {
5074 :
5075 : // FUNCTION INFORMATION:
5076 : // AUTHOR Linda Lawrie
5077 : // DATE WRITTEN October 2006
5078 :
5079 : // PURPOSE OF THIS FUNCTION:
5080 : // After making sure get input is done, the relief node number of indicated mixer is returned.
5081 :
5082 15 : if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
5083 0 : GetOAMixerInputs(state);
5084 0 : state.dataMixedAir->GetOAMixerInputFlag = false;
5085 : }
5086 :
5087 15 : if (OAMixerNum > state.dataMixedAir->NumOAMixers) {
5088 0 : ShowFatalError(state,
5089 0 : format("GetOAMixerReliefNodeNumber: Requested Mixer #={}, which is > number of OA Mixers={}",
5090 : OAMixerNum,
5091 0 : state.dataMixedAir->NumOAMixers));
5092 : }
5093 :
5094 15 : return state.dataMixedAir->OAMixer(OAMixerNum).RelNode;
5095 : }
5096 :
5097 1072 : int GetOASysControllerListIndex(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
5098 : {
5099 :
5100 : // FUNCTION INFORMATION:
5101 : // AUTHOR Fred Buhl
5102 : // DATE WRITTEN April 2007
5103 :
5104 : // PURPOSE OF THIS FUNCTION:
5105 : // After making sure get input is done, the Controller List index of the indicated OA System is returned.
5106 :
5107 1072 : if (state.dataMixedAir->GetOASysInputFlag) {
5108 0 : GetOutsideAirSysInputs(state);
5109 0 : state.dataMixedAir->GetOASysInputFlag = false;
5110 : }
5111 :
5112 1072 : return state.dataAirLoop->OutsideAirSys(OASysNumber).ControllerListNum;
5113 : }
5114 :
5115 1072 : int GetOASysNumSimpControllers(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
5116 : {
5117 :
5118 : // FUNCTION INFORMATION:
5119 : // AUTHOR Fred Buhl
5120 : // DATE WRITTEN April 2007
5121 :
5122 : // PURPOSE OF THIS FUNCTION:
5123 : // After making sure get input is done, the number of Controller:Simple objects in the OA System is returned.
5124 :
5125 1072 : if (state.dataMixedAir->GetOASysInputFlag) {
5126 0 : GetOutsideAirSysInputs(state);
5127 0 : state.dataMixedAir->GetOASysInputFlag = false;
5128 : }
5129 :
5130 1072 : return state.dataAirLoop->OutsideAirSys(OASysNumber).NumSimpleControllers;
5131 : }
5132 :
5133 1072 : int GetOASysNumHeatingCoils(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
5134 : {
5135 :
5136 : // FUNCTION INFORMATION:
5137 : // AUTHOR Fred Buhl
5138 : // DATE WRITTEN May 2007
5139 :
5140 : // PURPOSE OF THIS FUNCTION:
5141 : // After making sure get input is done, the number of heating coils in the OA System is returned.
5142 :
5143 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
5144 1072 : bool Sim(false);
5145 1072 : bool FirstHVACIteration(false);
5146 1072 : bool OAHeatingCoil(false);
5147 1072 : bool OACoolingCoil(false);
5148 1072 : int AirLoopNum(0);
5149 1072 : bool OAHX(false);
5150 :
5151 1072 : if (state.dataMixedAir->GetOASysInputFlag) {
5152 0 : GetOutsideAirSysInputs(state);
5153 0 : state.dataMixedAir->GetOASysInputFlag = false;
5154 : }
5155 :
5156 1072 : int NumHeatingCoils = 0;
5157 2279 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++CompNum) {
5158 1207 : std::string const &CompType = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentType(CompNum);
5159 1207 : std::string const &CompName = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentName(CompNum);
5160 4828 : SimOAComponent(state,
5161 : CompType,
5162 : CompName,
5163 1207 : state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentTypeEnum(CompNum),
5164 : FirstHVACIteration,
5165 1207 : state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentIndex(CompNum),
5166 : AirLoopNum,
5167 : Sim,
5168 : OASysNumber,
5169 : OAHeatingCoil,
5170 : OACoolingCoil,
5171 : OAHX);
5172 1207 : if (OAHeatingCoil) {
5173 32 : ++NumHeatingCoils;
5174 : }
5175 : }
5176 :
5177 1072 : return NumHeatingCoils;
5178 : }
5179 :
5180 1072 : int GetOASysNumHXs(EnergyPlusData &state, int const OASysNumber)
5181 : {
5182 :
5183 : // FUNCTION INFORMATION:
5184 : // AUTHOR Fred Buhl, Rongpeng Zhang
5185 : // DATE WRITTEN Oct. 2015
5186 :
5187 : // PURPOSE OF THIS FUNCTION:
5188 : // After making sure get input is done, the number of heat recovery exchangers in the OA System is returned.
5189 :
5190 1072 : if (state.dataMixedAir->GetOASysInputFlag) {
5191 0 : GetOutsideAirSysInputs(state);
5192 0 : state.dataMixedAir->GetOASysInputFlag = false;
5193 : }
5194 :
5195 1072 : int NumHX = 0;
5196 :
5197 1072 : auto const &componentType_Num = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentTypeEnum;
5198 2279 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++CompNum) {
5199 1207 : SimAirServingZones::CompType const componentTypeNum = componentType_Num(CompNum);
5200 1207 : if (SimAirServingZones::CompType::HeatXchngr == componentTypeNum || SimAirServingZones::CompType::Desiccant == componentTypeNum) {
5201 34 : ++NumHX;
5202 : }
5203 : }
5204 :
5205 1072 : return NumHX;
5206 : }
5207 :
5208 1072 : int GetOASysNumCoolingCoils(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
5209 : {
5210 :
5211 : // FUNCTION INFORMATION:
5212 : // AUTHOR Fred Buhl
5213 : // DATE WRITTEN May 2007
5214 :
5215 : // PURPOSE OF THIS FUNCTION:
5216 : // After making sure get input is done, the number of cooling coils in the OA System is returned.
5217 :
5218 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
5219 1072 : bool Sim(false);
5220 1072 : bool FirstHVACIteration(false);
5221 1072 : bool OAHeatingCoil(false);
5222 1072 : bool OACoolingCoil(false);
5223 1072 : int AirLoopNum(0);
5224 1072 : bool OAHX(false);
5225 :
5226 1072 : if (state.dataMixedAir->GetOASysInputFlag) {
5227 0 : GetOutsideAirSysInputs(state);
5228 0 : state.dataMixedAir->GetOASysInputFlag = false;
5229 : }
5230 :
5231 1072 : int NumCoolingCoils = 0;
5232 2279 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++CompNum) {
5233 1207 : std::string const &CompType = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentType(CompNum);
5234 1207 : std::string const &CompName = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentName(CompNum);
5235 4828 : SimOAComponent(state,
5236 : CompType,
5237 : CompName,
5238 1207 : state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentTypeEnum(CompNum),
5239 : FirstHVACIteration,
5240 1207 : state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentIndex(CompNum),
5241 : AirLoopNum,
5242 : Sim,
5243 : OASysNumber,
5244 : OAHeatingCoil,
5245 : OACoolingCoil,
5246 : OAHX);
5247 1207 : if (OACoolingCoil) {
5248 32 : ++NumCoolingCoils;
5249 : }
5250 : }
5251 :
5252 1072 : return NumCoolingCoils;
5253 : }
5254 :
5255 1072 : int GetOASystemNumber(EnergyPlusData &state, std::string const &OASysName) // OA Sys Name
5256 : {
5257 :
5258 : // FUNCTION INFORMATION:
5259 : // AUTHOR Linda Lawrie
5260 : // DATE WRITTEN October 2006
5261 :
5262 : // PURPOSE OF THIS FUNCTION:
5263 : // After making sure get input is done, the OA System number of indicated OA System is returned.
5264 :
5265 1072 : if (state.dataMixedAir->GetOASysInputFlag) {
5266 212 : GetOutsideAirSysInputs(state);
5267 212 : state.dataMixedAir->GetOASysInputFlag = false;
5268 : }
5269 :
5270 1072 : return Util::FindItemInList(OASysName, state.dataAirLoop->OutsideAirSys);
5271 : }
5272 :
5273 1072 : int FindOAMixerMatchForOASystem(EnergyPlusData &state, int const OASysNumber) // Which OA System
5274 : {
5275 :
5276 : // FUNCTION INFORMATION:
5277 : // AUTHOR Linda Lawrie
5278 : // DATE WRITTEN October 2006
5279 :
5280 : // PURPOSE OF THIS FUNCTION:
5281 : // After making sure get input is done, the matched mixer number is found.
5282 : // Note -- only the first is looked at for an Outside Air System.
5283 :
5284 1072 : if (state.dataMixedAir->GetOAMixerInputFlag) {
5285 391 : GetOAMixerInputs(state);
5286 391 : state.dataMixedAir->GetOAMixerInputFlag = false;
5287 : }
5288 :
5289 1072 : int OAMixerNumber = 0;
5290 1072 : if (OASysNumber > 0 && OASysNumber <= state.dataAirLoop->NumOASystems) {
5291 1204 : for (int OACompNum = 1; OACompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++OACompNum) {
5292 1204 : if (Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentType(OACompNum), "OUTDOORAIR:MIXER")) {
5293 : OAMixerNumber =
5294 1072 : Util::FindItemInList(state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentName(OACompNum), state.dataMixedAir->OAMixer);
5295 1072 : break;
5296 : }
5297 : }
5298 : }
5299 :
5300 1072 : return OAMixerNumber;
5301 : }
5302 :
5303 600 : int GetOAMixerIndex(EnergyPlusData &state, std::string const &OAMixerName) // Which Mixer
5304 : {
5305 :
5306 : // FUNCTION INFORMATION:
5307 : // AUTHOR Linda Lawrie
5308 : // DATE WRITTEN December 2010
5309 :
5310 : // PURPOSE OF THIS FUNCTION:
5311 : // After making sure get input is done, the mixer index of indicated mixer is returned.
5312 :
5313 600 : if (state.dataMixedAir->GetOAMixerInputFlag) {
5314 26 : GetOAMixerInputs(state);
5315 26 : state.dataMixedAir->GetOAMixerInputFlag = false;
5316 : }
5317 :
5318 600 : int OAMixerIndex = Util::FindItem(OAMixerName, state.dataMixedAir->OAMixer);
5319 :
5320 600 : if (OAMixerIndex == 0) {
5321 0 : ShowSevereError(state, format("GetOAMixerIndex: Could not find OutdoorAir:Mixer, Name=\"{}\"", OAMixerName));
5322 : }
5323 :
5324 600 : return OAMixerIndex;
5325 : }
5326 :
5327 1080 : int GetOAMixerInletNodeNumber(EnergyPlusData &state, int const OAMixerNumber) // Which Mixer
5328 : {
5329 :
5330 : // FUNCTION INFORMATION:
5331 : // AUTHOR Linda Lawrie
5332 : // DATE WRITTEN October 2006
5333 :
5334 : // PURPOSE OF THIS FUNCTION:
5335 : // After making sure get input is done, the mixer inlet node number of indicated mixer is returned.
5336 :
5337 1080 : if (state.dataMixedAir->GetOAMixerInputFlag) {
5338 0 : GetOAMixerInputs(state);
5339 0 : state.dataMixedAir->GetOAMixerInputFlag = false;
5340 : }
5341 :
5342 1080 : int OAMixerInletNodeNumber = 0;
5343 1080 : if (OAMixerNumber > 0 && OAMixerNumber <= state.dataMixedAir->NumOAMixers) {
5344 1080 : OAMixerInletNodeNumber = state.dataMixedAir->OAMixer(OAMixerNumber).InletNode;
5345 : }
5346 :
5347 1080 : return OAMixerInletNodeNumber;
5348 : }
5349 :
5350 40088 : int GetOAMixerReturnNodeNumber(EnergyPlusData &state, int const OAMixerNumber) // Which Mixer
5351 : {
5352 :
5353 : // FUNCTION INFORMATION:
5354 : // AUTHOR Brent Griffith
5355 : // DATE WRITTEN December 2006
5356 :
5357 : // PURPOSE OF THIS FUNCTION:
5358 : // After making sure get input is done, the mixer return node number of indicated mixer is returned.
5359 :
5360 : // METHODOLOGY EMPLOYED:
5361 : // followed Linda Lawrie's GetOAMixerInletNodeNumber
5362 :
5363 40088 : if (state.dataMixedAir->GetOAMixerInputFlag) {
5364 0 : GetOAMixerInputs(state);
5365 0 : state.dataMixedAir->GetOAMixerInputFlag = false;
5366 : }
5367 :
5368 40088 : int OAMixerReturnNodeNumber = 0;
5369 40088 : if (OAMixerNumber > 0 && OAMixerNumber <= state.dataMixedAir->NumOAMixers) {
5370 40088 : OAMixerReturnNodeNumber = state.dataMixedAir->OAMixer(OAMixerNumber).RetNode;
5371 : }
5372 :
5373 40088 : return OAMixerReturnNodeNumber;
5374 : }
5375 :
5376 40088 : int GetOAMixerMixedNodeNumber(EnergyPlusData &state, int const OAMixerNumber) // Which Mixer
5377 : {
5378 :
5379 : // FUNCTION INFORMATION:
5380 : // AUTHOR Brent Griffith
5381 : // DATE WRITTEN December 2006
5382 :
5383 : // PURPOSE OF THIS FUNCTION:
5384 : // After making sure get input is done, the mixer mixed air node number of indicated mixer is returned.
5385 :
5386 40088 : if (state.dataMixedAir->GetOAMixerInputFlag) {
5387 0 : GetOAMixerInputs(state);
5388 0 : state.dataMixedAir->GetOAMixerInputFlag = false;
5389 : }
5390 :
5391 40088 : int OAMixerMixedNodeNumber = 0;
5392 40088 : if (OAMixerNumber > 0 && OAMixerNumber <= state.dataMixedAir->NumOAMixers) {
5393 40088 : OAMixerMixedNodeNumber = state.dataMixedAir->OAMixer(OAMixerNumber).MixNode;
5394 : }
5395 :
5396 40088 : return OAMixerMixedNodeNumber;
5397 : }
5398 :
5399 902 : bool CheckForControllerWaterCoil(EnergyPlusData &state,
5400 : DataAirLoop::ControllerKind ControllerType, // should be passed in as UPPERCASE
5401 : std::string const &ControllerName // should be passed in as UPPERCASE
5402 : )
5403 : {
5404 :
5405 : // FUNCTION INFORMATION:
5406 : // AUTHOR Linda Lawrie
5407 : // DATE WRITTEN May 2009
5408 :
5409 : // PURPOSE OF THIS FUNCTION:
5410 : // This routine checks the controller list for existence of the reference coil.
5411 :
5412 902 : if (state.dataMixedAir->GetOASysInputFlag) {
5413 284 : GetOutsideAirSysInputs(state);
5414 284 : state.dataMixedAir->GetOASysInputFlag = false;
5415 : }
5416 :
5417 902 : int OnControllerList = false;
5418 :
5419 6161 : for (int Num = 1; Num <= state.dataMixedAir->NumControllerLists; ++Num) {
5420 14303 : for (int CompNum = 1; CompNum <= state.dataMixedAir->ControllerLists(Num).NumControllers; ++CompNum) {
5421 :
5422 9946 : if (state.dataMixedAir->ControllerLists(Num).ControllerType(CompNum) != ControllerType) {
5423 2718 : continue;
5424 : }
5425 7228 : if (!Util::SameString(state.dataMixedAir->ControllerLists(Num).ControllerName(CompNum), ControllerName)) {
5426 6326 : continue;
5427 : }
5428 902 : OnControllerList = true;
5429 902 : break;
5430 : }
5431 : }
5432 :
5433 902 : return OnControllerList;
5434 : }
5435 :
5436 800 : void CheckControllerLists(EnergyPlusData &state, bool &ErrFound)
5437 : {
5438 :
5439 : // SUBROUTINE INFORMATION:
5440 : // AUTHOR Linda Lawrie
5441 : // DATE WRITTEN May 2009
5442 :
5443 : // PURPOSE OF THIS SUBROUTINE:
5444 : // This routine checks for a "dangling" controller list (AirLoopHVAC:ControllerList).
5445 : // It must be either found on a AirLoopHVAC or AirLoopHVAC:OutdoorAirSystem.
5446 :
5447 : // SUBROUTINE PARAMETER DEFINITIONS:
5448 2400 : static std::string const CurrentModuleObject("AirLoopHVAC:ControllerList");
5449 2400 : static std::string const AirLoopObject("AirLoopHVAC");
5450 :
5451 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5452 : int NumAlphas;
5453 : int NumNumbers;
5454 : int IOStat;
5455 :
5456 800 : if (state.dataMixedAir->GetOASysInputFlag) {
5457 254 : GetOutsideAirSysInputs(state);
5458 254 : state.dataMixedAir->GetOASysInputFlag = false;
5459 : }
5460 :
5461 800 : int NumControllers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
5462 800 : int NumAirLoop = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, AirLoopObject);
5463 800 : std::string_view AirLoopName = "";
5464 :
5465 2309 : for (int Item = 1; Item <= NumControllers; ++Item) {
5466 :
5467 3018 : state.dataInputProcessing->inputProcessor->getObjectItem(
5468 1509 : state, CurrentModuleObject, Item, state.dataIPShortCut->cAlphaArgs, NumAlphas, state.dataIPShortCut->rNumericArgs, NumNumbers, IOStat);
5469 1509 : std::string const ControllerListName = state.dataIPShortCut->cAlphaArgs(1);
5470 1509 : int Count = 0;
5471 :
5472 : // Check AirLoopHVAC -- brute force, get each AirLoopHVAC
5473 :
5474 14459 : for (int Loop = 1; Loop <= NumAirLoop; ++Loop) {
5475 25900 : state.dataInputProcessing->inputProcessor->getObjectItem(
5476 12950 : state, AirLoopObject, Loop, state.dataIPShortCut->cAlphaArgs, NumAlphas, state.dataIPShortCut->rNumericArgs, NumNumbers, IOStat);
5477 12950 : if (state.dataIPShortCut->cAlphaArgs(2) != ControllerListName) {
5478 12509 : continue;
5479 : }
5480 441 : if (++Count == 1) {
5481 441 : AirLoopName = state.dataIPShortCut->cAlphaArgs(1);
5482 : }
5483 : }
5484 :
5485 : // Now check AirLoopHVAC and AirLoopHVAC:OutdoorAirSystem
5486 1509 : int Found = 0;
5487 1509 : if (state.dataAirLoop->NumOASystems > 0) {
5488 1427 : Found = Util::FindItemInList(ControllerListName, state.dataAirLoop->OutsideAirSys, &OutsideAirSysProps::ControllerListName);
5489 1427 : if (Found > 0) {
5490 1068 : ++Count;
5491 : }
5492 : }
5493 :
5494 1509 : if (Count == 0) {
5495 0 : ShowSevereError(state,
5496 0 : format("{}=\"{}\" is not referenced on a AirLoopHVAC or AirLoopHVAC:OutdoorAirSystem object.",
5497 : CurrentModuleObject,
5498 : ControllerListName));
5499 0 : ErrFound = true;
5500 1509 : } else if (Count > 1) {
5501 0 : ShowSevereError(state,
5502 0 : format("{}=\"{}\" has too many references on AirLoopHVAC or AirLoopHVAC:OutdoorAirSystem objects.",
5503 : CurrentModuleObject,
5504 : ControllerListName));
5505 0 : if (Found > 0) {
5506 0 : ShowContinueError(state, format("...AirLoopHVAC:OutdoorAirSystem=\"{}\".", state.dataAirLoop->OutsideAirSys(Found).Name));
5507 : }
5508 0 : ShowContinueError(state, format("...also on AirLoopHVAC=\"{}\".", AirLoopName));
5509 0 : ErrFound = true;
5510 : }
5511 1509 : }
5512 800 : }
5513 :
5514 104 : void CheckOAControllerName(
5515 : EnergyPlusData &state, std::string &OAControllerName, std::string const &ObjectType, std::string const &FieldName, bool &ErrorsFound)
5516 : {
5517 :
5518 : // SUBROUTINE INFORMATION:
5519 : // AUTHOR Linda Lawrie
5520 : // DATE WRITTEN October 2006
5521 :
5522 : // PURPOSE OF THIS SUBROUTINE:
5523 : // When OA Controller data is gotten from other routines, must check to make sure
5524 : // new name doesn't duplicate. (Essentially a pass through to call Verify Name)
5525 : // Currently, this is only called from HVACStandAlongERV::GetStandaloneERV()
5526 :
5527 104 : if (state.dataMixedAir->AllocateOAControllersFlag) {
5528 : // Make sure OAControllers are allocated
5529 3 : AllocateOAControllers(state);
5530 : }
5531 :
5532 104 : GlobalNames::VerifyUniqueInterObjectName(
5533 104 : state, state.dataMixedAir->OAControllerUniqueNames, OAControllerName, ObjectType, FieldName, ErrorsFound);
5534 104 : }
5535 :
5536 16528585 : void OAControllerProps::Checksetpoints(EnergyPlusData &state,
5537 : Real64 const OutAirMinFrac, // Local variable used to calculate min OA fraction
5538 : Real64 &OutAirSignal, // Used to set OA mass flow rate
5539 : bool &EconomizerOperationFlag // logical used to show economizer status
5540 : )
5541 : {
5542 :
5543 : // SUBROUTINE INFORMATION:
5544 : // AUTHOR Amit bhansali
5545 : // DATE WRITTEN August 2008?
5546 :
5547 : // PURPOSE OF THIS SUBROUTINE:
5548 : // This subroutine checks the setpoints of the upper limits of temperatures, limit enthalpy
5549 : // Limit dew point, Enthalpy curve
5550 :
5551 16528585 : if (this->TempLim != HVAC::BlankNumeric && this->OATemp > this->TempLim) {
5552 6231231 : OutAirSignal = OutAirMinFrac;
5553 6231231 : EconomizerOperationFlag = false;
5554 : }
5555 : // Outside air enthalpy limit
5556 16528585 : if (this->EnthLim != HVAC::BlankNumeric && this->OAEnth > this->EnthLim) {
5557 5580202 : OutAirSignal = OutAirMinFrac;
5558 5580202 : EconomizerOperationFlag = false;
5559 : }
5560 :
5561 16528585 : if (this->DPTempLim != HVAC::BlankNumeric) {
5562 93921 : Real64 OADPTemp = Psychrometrics::PsyTdpFnWPb(state, this->OAHumRat, this->OAPress);
5563 93921 : if (OADPTemp > this->DPTempLim) {
5564 46463 : OutAirSignal = OutAirMinFrac;
5565 46463 : EconomizerOperationFlag = false;
5566 : }
5567 : }
5568 :
5569 16528585 : if (this->EnthalpyCurvePtr > 0) {
5570 65070 : if (this->OAHumRat > Curve::CurveValue(state, this->EnthalpyCurvePtr, this->OATemp)) {
5571 36383 : OutAirSignal = OutAirMinFrac;
5572 36383 : EconomizerOperationFlag = false;
5573 : }
5574 : }
5575 16528585 : }
5576 :
5577 612 : int GetNumOASystems(EnergyPlusData &state)
5578 : {
5579 :
5580 : // FUNCTION INFORMATION:
5581 : // AUTHOR Linda Lawrie
5582 : // DATE WRITTEN November 2010
5583 :
5584 : // PURPOSE OF THIS FUNCTION:
5585 : // Get Number of OA Systems, After making sure get input is done
5586 :
5587 612 : if (state.dataMixedAir->GetOASysInputFlag) {
5588 51 : GetOutsideAirSysInputs(state);
5589 51 : state.dataMixedAir->GetOASysInputFlag = false;
5590 : }
5591 :
5592 612 : return state.dataAirLoop->NumOASystems;
5593 : }
5594 :
5595 1073 : int GetOACompListNumber(EnergyPlusData &state, int const OASysNum) // OA Sys Number
5596 : {
5597 :
5598 : // FUNCTION INFORMATION:
5599 : // AUTHOR Heejin Cho
5600 : // DATE WRITTEN November 2010
5601 :
5602 : // PURPOSE OF THIS FUNCTION:
5603 : // After making sure get input is done, the OA System number of indicated OA System is returned.
5604 :
5605 1073 : if (state.dataMixedAir->GetOASysInputFlag) {
5606 0 : GetOutsideAirSysInputs(state);
5607 0 : state.dataMixedAir->GetOASysInputFlag = false;
5608 : }
5609 :
5610 1073 : return state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents;
5611 : }
5612 :
5613 61 : std::string GetOACompName(EnergyPlusData &state,
5614 : int const OASysNum, // OA Sys Number
5615 : int const InListNum // In-list Number
5616 : )
5617 : {
5618 :
5619 : // FUNCTION INFORMATION:
5620 : // AUTHOR Heejin Cho
5621 : // DATE WRITTEN November 2010
5622 :
5623 : // PURPOSE OF THIS FUNCTION:
5624 : // After making sure get input is done, the number of heating coils in the OA System is returned.
5625 :
5626 61 : if (state.dataMixedAir->GetOASysInputFlag) {
5627 0 : GetOutsideAirSysInputs(state);
5628 0 : state.dataMixedAir->GetOASysInputFlag = false;
5629 : }
5630 :
5631 61 : return state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(InListNum);
5632 : }
5633 :
5634 61 : std::string GetOACompType(EnergyPlusData &state,
5635 : int const OASysNum, // OA Sys Number
5636 : int const InListNum // In-list Number
5637 : )
5638 : {
5639 :
5640 : // FUNCTION INFORMATION:
5641 : // AUTHOR Heejin Cho
5642 : // DATE WRITTEN November 2010
5643 :
5644 : // PURPOSE OF THIS FUNCTION:
5645 : // After making sure get input is done, the number of heating coils in the OA System is returned.
5646 :
5647 61 : if (state.dataMixedAir->GetOASysInputFlag) {
5648 0 : GetOutsideAirSysInputs(state);
5649 0 : state.dataMixedAir->GetOASysInputFlag = false;
5650 : }
5651 :
5652 61 : return state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(InListNum);
5653 : }
5654 :
5655 1210 : SimAirServingZones::CompType GetOACompTypeNum(EnergyPlusData &state,
5656 : int const OASysNum, // OA Sys Number
5657 : int const InListNum // In-list Number
5658 : )
5659 : {
5660 :
5661 : // FUNCTION INFORMATION:
5662 : // AUTHOR Heejin Cho
5663 : // DATE WRITTEN November 2010
5664 :
5665 : // PURPOSE OF THIS FUNCTION:
5666 : // After making sure get input is done, the number of heating coils in the OA System is returned.
5667 :
5668 1210 : if (state.dataMixedAir->GetOASysInputFlag) {
5669 0 : GetOutsideAirSysInputs(state);
5670 0 : state.dataMixedAir->GetOASysInputFlag = false;
5671 : }
5672 :
5673 1210 : return state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(InListNum);
5674 : }
5675 :
5676 4 : int GetOAMixerNumber(EnergyPlusData &state, std::string const &OAMixerName // must match OA mixer names for the OA mixer type
5677 : )
5678 : {
5679 :
5680 : // FUNCTION INFORMATION:
5681 : // AUTHOR Lixing Gu
5682 : // DATE WRITTEN Feb. 2018
5683 :
5684 : // PURPOSE OF THIS FUNCTION:
5685 : // This function looks up the given OA mixer and returns the OAMixer number. If
5686 : // incorrect OA mixer name is given, ErrorsFound is returned as true
5687 :
5688 : // Obtains and Allocates OA mixer related parameters from input file
5689 4 : if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
5690 2 : GetOAMixerInputs(state);
5691 2 : state.dataMixedAir->GetOAMixerInputFlag = false;
5692 : }
5693 :
5694 4 : return Util::FindItemInList(OAMixerName, state.dataMixedAir->OAMixer);
5695 : }
5696 : // End of Utility Section of the Module
5697 : //******************************************************************************
5698 :
5699 : } // namespace EnergyPlus::MixedAir
|