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