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