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 : // ObjexxFCL Headers
49 : #include <ObjexxFCL/Array.functions.hh>
50 : #include <ObjexxFCL/Fmath.hh>
51 :
52 : // EnergyPlus Headers
53 : #include <AirflowNetwork/Solver.hpp>
54 : #include <EnergyPlus/Autosizing/Base.hh>
55 : #include <EnergyPlus/BranchNodeConnections.hh>
56 : #include <EnergyPlus/Data/EnergyPlusData.hh>
57 : #include <EnergyPlus/DataContaminantBalance.hh>
58 : #include <EnergyPlus/DataEnvironment.hh>
59 : #include <EnergyPlus/DataHVACGlobals.hh>
60 : #include <EnergyPlus/DataHeatBalance.hh>
61 : #include <EnergyPlus/DataIPShortCuts.hh>
62 : #include <EnergyPlus/DataLoopNode.hh>
63 : #include <EnergyPlus/DataSizing.hh>
64 : #include <EnergyPlus/DataZoneEquipment.hh>
65 : #include <EnergyPlus/ExhaustAirSystemManager.hh>
66 : #include <EnergyPlus/Fans.hh>
67 : #include <EnergyPlus/GeneralRoutines.hh>
68 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
69 : #include <EnergyPlus/MixerComponent.hh>
70 : #include <EnergyPlus/NodeInputManager.hh>
71 : #include <EnergyPlus/Psychrometrics.hh>
72 : #include <EnergyPlus/ScheduleManager.hh>
73 : #include <EnergyPlus/UtilityRoutines.hh>
74 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
75 :
76 : namespace EnergyPlus {
77 :
78 : namespace ExhaustAirSystemManager {
79 : // Module containing the routines dealing with the AirLoopHVAC:ExhaustSystem
80 :
81 : static constexpr std::array<std::string_view, static_cast<int>(ZoneExhaustControl::FlowControlType::Num)> flowControlTypeNamesUC = {
82 : "SCHEDULED", "FOLLOWSUPPLY"};
83 :
84 6509971 : void SimExhaustAirSystem(EnergyPlusData &state, bool FirstHVACIteration)
85 : {
86 : // Obtains and Allocates Mixer related parameters from input file
87 6509971 : if (state.dataExhAirSystemMrg->GetInputFlag) { // First time subroutine has been entered
88 796 : GetExhaustAirSystemInput(state);
89 796 : state.dataExhAirSystemMrg->GetInputFlag = false;
90 : }
91 :
92 6517377 : for (int ExhaustAirSystemNum = 1; ExhaustAirSystemNum <= state.dataZoneEquip->NumExhaustAirSystems; ++ExhaustAirSystemNum) {
93 7406 : CalcExhaustAirSystem(state, ExhaustAirSystemNum, FirstHVACIteration);
94 : }
95 :
96 : // After this, update the exhaust flows according to zone grouping:
97 6509971 : UpdateZoneExhaustControl(state);
98 6509971 : }
99 :
100 796 : void GetExhaustAirSystemInput(EnergyPlusData &state)
101 : {
102 796 : if (!state.dataExhAirSystemMrg->GetInputFlag) return;
103 : // state.dataExhAirSystemMrg->GetInputFlag = false;
104 :
105 : // Locals
106 796 : bool ErrorsFound = false;
107 :
108 796 : constexpr std::string_view RoutineName("GetExhaustAirSystemInput: ");
109 796 : constexpr std::string_view routineName = "GetExhaustAirSystemInput";
110 796 : std::string const cCurrentModuleObject = "AirLoopHVAC:ExhaustSystem";
111 :
112 796 : auto &ip = state.dataInputProcessing->inputProcessor;
113 796 : auto const instances = ip->epJSON.find(cCurrentModuleObject);
114 796 : if (instances != ip->epJSON.end()) {
115 1 : auto const &objectSchemaProps = ip->getObjectSchemaProps(state, cCurrentModuleObject);
116 1 : auto &instancesValue = instances.value();
117 1 : int numExhaustSystems = instancesValue.size();
118 1 : int exhSysNum = 0;
119 :
120 1 : if (numExhaustSystems > 0) {
121 1 : state.dataZoneEquip->ExhaustAirSystem.allocate(numExhaustSystems);
122 : }
123 :
124 3 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
125 2 : ++exhSysNum;
126 2 : auto const &objectFields = instance.value();
127 2 : auto &thisExhSys = state.dataZoneEquip->ExhaustAirSystem(exhSysNum);
128 2 : thisExhSys.Name = Util::makeUPPER(instance.key());
129 2 : ip->markObjectAsUsed(cCurrentModuleObject, instance.key());
130 :
131 4 : std::string zoneMixerName = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_mixer_name");
132 2 : int zoneMixerIndex = 0;
133 2 : bool zoneMixerErrFound = false;
134 2 : MixerComponent::GetZoneMixerIndex(state, zoneMixerName, zoneMixerIndex, zoneMixerErrFound, thisExhSys.Name);
135 :
136 2 : if (!zoneMixerErrFound) {
137 : // With the correct MixerNum Initialize
138 2 : MixerComponent::InitAirMixer(state, zoneMixerIndex); // Initialize all Mixer related parameters
139 :
140 : // See if need to do the zone mixer's CheckEquipName() function
141 2 : bool IsNotOK = false; // Flag to verify name
142 2 : ValidateComponent(state, "AirLoopHVAC:ZoneMixer", zoneMixerName, IsNotOK, "AirLoopHVAC:ExhaustSystem");
143 2 : if (IsNotOK) {
144 0 : ShowSevereError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhSys.Name));
145 0 : ShowContinueError(state, format("ZoneMixer Name ={} mismatch or not found.", zoneMixerName));
146 0 : ErrorsFound = true;
147 : } else {
148 : // normal conditions
149 : }
150 : } else {
151 0 : ShowSevereError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhSys.Name));
152 0 : ShowContinueError(state, format("Zone Mixer Name ={} not found.", zoneMixerName));
153 0 : ErrorsFound = true;
154 : }
155 2 : thisExhSys.ZoneMixerName = zoneMixerName;
156 2 : thisExhSys.ZoneMixerIndex = zoneMixerIndex;
157 :
158 2 : thisExhSys.centralFanType = static_cast<HVAC::FanType>(
159 2 : getEnumValue(HVAC::fanTypeNamesUC, Util::makeUPPER(ip->getAlphaFieldValue(objectFields, objectSchemaProps, "fan_object_type"))));
160 2 : if (thisExhSys.centralFanType != HVAC::FanType::SystemModel && thisExhSys.centralFanType != HVAC::FanType::ComponentModel) {
161 0 : ShowSevereError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhSys.Name));
162 0 : ShowContinueError(state, format("Fan Type ={} is not supported.", HVAC::fanTypeNames[(int)thisExhSys.centralFanType]));
163 0 : ShowContinueError(state, "It needs to be either a Fan:SystemModel or a Fan:ComponentModel type.");
164 0 : ErrorsFound = true;
165 : }
166 :
167 4 : std::string centralFanName = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "fan_name");
168 :
169 2 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, thisExhSys.Name};
170 2 : int centralFanIndex = Fans::GetFanIndex(state, centralFanName);
171 2 : if (centralFanIndex == 0) {
172 0 : ShowSevereItemNotFound(state, eoh, "fan_name", centralFanName);
173 0 : ErrorsFound = true;
174 : } else {
175 2 : auto *fan = state.dataFans->fans(centralFanIndex);
176 :
177 2 : thisExhSys.AvailScheduleNum = fan->availSchedNum;
178 :
179 6 : BranchNodeConnections::SetUpCompSets(state,
180 : cCurrentModuleObject,
181 : thisExhSys.Name,
182 2 : HVAC::fanTypeNames[(int)thisExhSys.centralFanType],
183 : centralFanName,
184 2 : state.dataLoopNodes->NodeID(fan->inletNodeNum),
185 2 : state.dataLoopNodes->NodeID(fan->outletNodeNum));
186 :
187 4 : SetupOutputVariable(state,
188 : "Central Exhaust Fan Mass Flow Rate",
189 : Constant::Units::kg_s,
190 2 : thisExhSys.centralFan_MassFlowRate,
191 : OutputProcessor::TimeStepType::System,
192 : OutputProcessor::StoreType::Average,
193 2 : thisExhSys.Name);
194 :
195 4 : SetupOutputVariable(state,
196 : "Central Exhaust Fan Volumetric Flow Rate Standard",
197 : Constant::Units::m3_s,
198 2 : thisExhSys.centralFan_VolumeFlowRate_Std,
199 : OutputProcessor::TimeStepType::System,
200 : OutputProcessor::StoreType::Average,
201 2 : thisExhSys.Name);
202 :
203 4 : SetupOutputVariable(state,
204 : "Central Exhaust Fan Volumetric Flow Rate Current",
205 : Constant::Units::m3_s,
206 2 : thisExhSys.centralFan_VolumeFlowRate_Cur,
207 : OutputProcessor::TimeStepType::System,
208 : OutputProcessor::StoreType::Average,
209 2 : thisExhSys.Name);
210 :
211 4 : SetupOutputVariable(state,
212 : "Central Exhaust Fan Power",
213 : Constant::Units::W,
214 2 : thisExhSys.centralFan_Power,
215 : OutputProcessor::TimeStepType::System,
216 : OutputProcessor::StoreType::Average,
217 2 : thisExhSys.Name);
218 :
219 4 : SetupOutputVariable(state,
220 : "Central Exhaust Fan Energy",
221 : Constant::Units::J,
222 2 : thisExhSys.centralFan_Energy,
223 : OutputProcessor::TimeStepType::System,
224 : OutputProcessor::StoreType::Sum,
225 2 : thisExhSys.Name);
226 : }
227 :
228 2 : thisExhSys.CentralFanName = centralFanName;
229 2 : thisExhSys.CentralFanIndex = centralFanIndex;
230 :
231 : // sizing
232 2 : if (thisExhSys.SizingFlag) {
233 2 : SizeExhaustSystem(state, exhSysNum);
234 : }
235 3 : }
236 1 : state.dataZoneEquip->NumExhaustAirSystems = numExhaustSystems;
237 : } else {
238 : // If no exhaust systems are defined, then do something <or nothing>:
239 : }
240 :
241 796 : if (ErrorsFound) {
242 0 : ShowFatalError(state, "Errors found getting AirLoopHVAC:ExhaustSystem. Preceding condition(s) causes termination.");
243 : }
244 796 : }
245 :
246 7406 : void CalcExhaustAirSystem(EnergyPlusData &state, int const ExhaustAirSystemNum, bool FirstHVACIteration)
247 : {
248 7406 : auto &thisExhSys = state.dataZoneEquip->ExhaustAirSystem(ExhaustAirSystemNum);
249 7406 : constexpr std::string_view RoutineName = "CalExhaustAirSystem: ";
250 7406 : constexpr std::string_view cCurrentModuleObject = "AirloopHVAC:ExhaustSystem";
251 7406 : bool ErrorsFound = false;
252 7406 : if (!(state.afn->AirflowNetworkFanActivated && state.afn->distribution_simulated)) {
253 7406 : MixerComponent::SimAirMixer(state, thisExhSys.ZoneMixerName, thisExhSys.ZoneMixerIndex);
254 : } else {
255 : // Give a warning that the current model does not work with AirflowNetwork for now
256 0 : ShowSevereError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhSys.Name));
257 0 : ShowContinueError(state, "AirloopHVAC:ExhaustSystem currently does not work with AirflowNetwork.");
258 0 : ErrorsFound = true;
259 : }
260 :
261 7406 : if (ErrorsFound) {
262 0 : ShowFatalError(state, "Errors found conducting CalcExhasutAirSystem(). Preceding condition(s) causes termination.");
263 : }
264 :
265 7406 : Real64 mixerFlow_Prior = 0.0;
266 7406 : int outletNode_index = state.dataMixerComponent->MixerCond(thisExhSys.ZoneMixerIndex).OutletNode;
267 7406 : mixerFlow_Prior = state.dataLoopNodes->Node(outletNode_index).MassFlowRate;
268 : if (mixerFlow_Prior == 0.0) {
269 : // No flow coming out from the exhaust controls;
270 : // fan should be cut off now;
271 : }
272 :
273 7406 : int outletNode_Num = 0;
274 7406 : Real64 RhoAirCurrent = state.dataEnvrn->StdRhoAir;
275 :
276 7406 : if (thisExhSys.centralFanType == HVAC::FanType::SystemModel) {
277 7406 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0;
278 7406 : state.dataFans->fans(thisExhSys.CentralFanIndex)->simulate(state, false, _, _);
279 :
280 : // Update report variables
281 7406 : outletNode_Num = state.dataFans->fans(thisExhSys.CentralFanIndex)->outletNodeNum;
282 :
283 7406 : thisExhSys.centralFan_MassFlowRate = state.dataLoopNodes->Node(outletNode_Num).MassFlowRate;
284 :
285 7406 : thisExhSys.centralFan_VolumeFlowRate_Std = state.dataLoopNodes->Node(outletNode_Num).MassFlowRate / state.dataEnvrn->StdRhoAir;
286 :
287 14812 : RhoAirCurrent = EnergyPlus::Psychrometrics::PsyRhoAirFnPbTdbW(state,
288 7406 : state.dataEnvrn->OutBaroPress,
289 7406 : state.dataLoopNodes->Node(outletNode_Num).Temp,
290 7406 : state.dataLoopNodes->Node(outletNode_Num).HumRat);
291 7406 : if (RhoAirCurrent <= 0.0) RhoAirCurrent = state.dataEnvrn->StdRhoAir;
292 7406 : thisExhSys.centralFan_VolumeFlowRate_Cur = state.dataLoopNodes->Node(outletNode_Num).MassFlowRate / RhoAirCurrent;
293 :
294 7406 : thisExhSys.centralFan_Power = state.dataFans->fans(thisExhSys.CentralFanIndex)->totalPower;
295 :
296 7406 : thisExhSys.centralFan_Energy = thisExhSys.centralFan_Power * state.dataHVACGlobal->TimeStepSysSec;
297 :
298 0 : } else if (thisExhSys.centralFanType == HVAC::FanType::ComponentModel) {
299 0 : auto *fan = state.dataFans->fans(thisExhSys.CentralFanIndex);
300 0 : fan->simulate(state, FirstHVACIteration);
301 :
302 : // Update output variables
303 :
304 0 : outletNode_Num = fan->outletNodeNum;
305 :
306 0 : thisExhSys.centralFan_MassFlowRate = fan->outletAirMassFlowRate;
307 :
308 0 : thisExhSys.centralFan_VolumeFlowRate_Std = fan->outletAirMassFlowRate / state.dataEnvrn->StdRhoAir;
309 :
310 0 : RhoAirCurrent = EnergyPlus::Psychrometrics::PsyRhoAirFnPbTdbW(state,
311 0 : state.dataEnvrn->OutBaroPress,
312 0 : state.dataLoopNodes->Node(outletNode_Num).Temp,
313 0 : state.dataLoopNodes->Node(outletNode_Num).HumRat);
314 0 : if (RhoAirCurrent <= 0.0) RhoAirCurrent = state.dataEnvrn->StdRhoAir;
315 0 : thisExhSys.centralFan_VolumeFlowRate_Cur = fan->outletAirMassFlowRate / RhoAirCurrent;
316 :
317 0 : thisExhSys.centralFan_Power = fan->totalPower * 1000.0;
318 :
319 0 : thisExhSys.centralFan_Energy = fan->totalEnergy * 1000.0;
320 : }
321 7406 : thisExhSys.exhTotalHVACReliefHeatLoss = state.dataLoopNodes->Node(outletNode_Num).MassFlowRate *
322 7406 : (state.dataLoopNodes->Node(outletNode_Num).Enthalpy - state.dataEnvrn->OutEnthalpy);
323 :
324 7406 : Real64 mixerFlow_Posterior = 0.0;
325 7406 : mixerFlow_Posterior = state.dataLoopNodes->Node(outletNode_index).MassFlowRate;
326 : if (mixerFlow_Posterior < HVAC::SmallMassFlow) {
327 : // fan flow is nearly zero and should be considered off
328 : // but this still can use the ratio
329 : }
330 : if (mixerFlow_Prior < HVAC::SmallMassFlow) {
331 : // this is the case where the fan flow should be resetted to zeros and not run the ratio
332 : }
333 7406 : if ((mixerFlow_Prior - mixerFlow_Posterior > HVAC::SmallMassFlow) || (mixerFlow_Prior - mixerFlow_Posterior < -HVAC::SmallMassFlow)) {
334 : // calculate a ratio
335 0 : Real64 flowRatio = mixerFlow_Posterior / mixerFlow_Prior;
336 0 : if (flowRatio > 1.0) {
337 0 : ShowWarningError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhSys.Name));
338 0 : ShowContinueError(state, "Requested flow rate is lower than the exhasut fan flow rate.");
339 0 : ShowContinueError(state, "Will scale up the requested flow rate to meet fan flow rate.");
340 : }
341 :
342 : // get the mixer inlet node index
343 0 : int zoneMixerIndex = thisExhSys.ZoneMixerIndex;
344 0 : for (int i = 1; i <= state.dataMixerComponent->MixerCond(zoneMixerIndex).NumInletNodes; ++i) {
345 0 : int exhLegIndex = state.dataExhAirSystemMrg->mixerIndexMap[state.dataMixerComponent->MixerCond(zoneMixerIndex).InletNode(i)];
346 0 : CalcZoneHVACExhaustControl(state, exhLegIndex, flowRatio);
347 : }
348 :
349 : // Simulate the mixer again to update the flow
350 0 : MixerComponent::SimAirMixer(state, thisExhSys.ZoneMixerName, thisExhSys.ZoneMixerIndex);
351 :
352 : // if the adjustment matches, then no need to run fan simulation again.
353 : }
354 7406 : }
355 :
356 796 : void GetZoneExhaustControlInput(EnergyPlusData &state)
357 : {
358 : // Process ZoneExhaust Control inputs
359 :
360 : // Locals
361 796 : bool ErrorsFound = false;
362 :
363 : // Use the json helper to process input
364 796 : constexpr std::string_view RoutineName("GetZoneExhaustControlInput: ");
365 796 : std::string const cCurrentModuleObject = "ZoneHVAC:ExhaustControl";
366 796 : auto &ip = state.dataInputProcessing->inputProcessor;
367 796 : auto const instances = ip->epJSON.find(cCurrentModuleObject);
368 796 : if (instances != ip->epJSON.end()) {
369 1 : auto const &objectSchemaProps = ip->getObjectSchemaProps(state, cCurrentModuleObject);
370 1 : auto &instancesValue = instances.value();
371 1 : int numZoneExhaustControls = instancesValue.size();
372 1 : int exhCtrlNum = 0;
373 : int NumAlphas;
374 : int NumNums;
375 :
376 1 : if (numZoneExhaustControls > 0) {
377 1 : state.dataZoneEquip->ZoneExhaustControlSystem.allocate(numZoneExhaustControls);
378 : }
379 :
380 6 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
381 5 : ++exhCtrlNum;
382 5 : auto const &objectFields = instance.value();
383 5 : auto &thisExhCtrl = state.dataZoneEquip->ZoneExhaustControlSystem(exhCtrlNum);
384 5 : thisExhCtrl.Name = Util::makeUPPER(instance.key());
385 5 : ip->markObjectAsUsed(cCurrentModuleObject, instance.key());
386 :
387 10 : std::string availSchName = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "availability_schedule_name");
388 5 : if (availSchName == "") {
389 : // blank
390 5 : thisExhCtrl.AvailScheduleNum = ScheduleManager::ScheduleAlwaysOn;
391 : } else {
392 0 : thisExhCtrl.AvailScheduleNum = ScheduleManager::GetScheduleIndex(state, availSchName);
393 0 : if (thisExhCtrl.AvailScheduleNum == 0) {
394 : // mismatch, reset to always on
395 0 : thisExhCtrl.AvailScheduleNum = ScheduleManager::ScheduleAlwaysOn;
396 0 : ShowWarningError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhCtrl.Name));
397 0 : ShowContinueError(state, format("Avaiability Schedule Name = {} not found.", availSchName));
398 0 : ShowContinueError(state, "Availability Schedule is reset to Always ON.");
399 : }
400 : }
401 :
402 10 : std::string zoneName = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "zone_name");
403 5 : thisExhCtrl.ZoneName = zoneName;
404 5 : int zoneNum = Util::FindItemInList(zoneName, state.dataHeatBal->Zone);
405 5 : thisExhCtrl.ZoneNum = zoneNum;
406 :
407 5 : thisExhCtrl.ControlledZoneNum = Util::FindItemInList(zoneName, state.dataHeatBal->Zone);
408 :
409 : // These two nodes are required inputs:
410 10 : std::string inletNodeName = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "inlet_node_name");
411 5 : int inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
412 : inletNodeName,
413 : ErrorsFound,
414 : DataLoopNode::ConnectionObjectType::ZoneHVACExhaustControl,
415 5 : thisExhCtrl.Name,
416 : DataLoopNode::NodeFluidType::Air,
417 : DataLoopNode::ConnectionType::Inlet,
418 : NodeInputManager::CompFluidStream::Primary,
419 : DataLoopNode::ObjectIsParent);
420 5 : thisExhCtrl.InletNodeNum = inletNodeNum;
421 :
422 10 : std::string outletNodeName = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "outlet_node_name");
423 :
424 5 : int outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
425 : outletNodeName,
426 : ErrorsFound,
427 : DataLoopNode::ConnectionObjectType::ZoneHVACExhaustControl,
428 5 : thisExhCtrl.Name,
429 : DataLoopNode::NodeFluidType::Air,
430 : DataLoopNode::ConnectionType::Outlet,
431 : NodeInputManager::CompFluidStream::Primary,
432 5 : DataLoopNode::ObjectIsParent);
433 5 : thisExhCtrl.OutletNodeNum = outletNodeNum;
434 :
435 5 : if (!state.dataExhAirSystemMrg->mappingDone) {
436 5 : state.dataExhAirSystemMrg->mixerIndexMap.emplace(outletNodeNum, exhCtrlNum);
437 : }
438 :
439 5 : Real64 designExhaustFlowRate = ip->getRealFieldValue(objectFields, objectSchemaProps, "design_exhaust_flow_rate");
440 5 : thisExhCtrl.DesignExhaustFlowRate = designExhaustFlowRate;
441 :
442 10 : std::string flowControlTypeName = Util::makeUPPER(ip->getAlphaFieldValue(objectFields, objectSchemaProps, "flow_control_type"));
443 : // std::string flowControlTypeName = Util::makeUPPER(fields.at("flow_control_type").get<std::string>());
444 5 : thisExhCtrl.FlowControlOption =
445 5 : static_cast<ZoneExhaustControl::FlowControlType>(getEnumValue(flowControlTypeNamesUC, flowControlTypeName));
446 :
447 : std::string exhaustFlowFractionScheduleName =
448 10 : ip->getAlphaFieldValue(objectFields, objectSchemaProps, "exhaust_flow_fraction_schedule_name");
449 : // Schedule matching
450 5 : int exhaustFlowFractionScheduleNum = 0;
451 5 : exhaustFlowFractionScheduleNum = ScheduleManager::GetScheduleIndex(state, exhaustFlowFractionScheduleName);
452 :
453 5 : if (exhaustFlowFractionScheduleNum > 0) {
454 : // normal conditions
455 5 : } else if (exhaustFlowFractionScheduleNum == 0) {
456 : // blank, treat as always available
457 : } else {
458 0 : exhaustFlowFractionScheduleNum = 0;
459 : // a regular warning
460 0 : ShowWarningError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhCtrl.Name));
461 0 : ShowContinueError(state, format("Schedule Name = {} not found.", exhaustFlowFractionScheduleName));
462 : }
463 5 : thisExhCtrl.ExhaustFlowFractionScheduleNum = exhaustFlowFractionScheduleNum;
464 :
465 5 : thisExhCtrl.SupplyNodeOrNodelistName = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "supply_node_or_nodelist_name");
466 :
467 5 : bool NodeListError = false;
468 5 : int NumParams = 0;
469 5 : int NumNodes = 0;
470 :
471 5 : ip->getObjectDefMaxArgs(state, "NodeList", NumParams, NumAlphas, NumNums);
472 5 : thisExhCtrl.SuppNodeNums.dimension(NumParams, 0);
473 5 : NodeInputManager::GetNodeNums(state,
474 5 : thisExhCtrl.SupplyNodeOrNodelistName,
475 : NumNodes,
476 5 : thisExhCtrl.SuppNodeNums,
477 : NodeListError,
478 : DataLoopNode::NodeFluidType::Air,
479 : DataLoopNode::ConnectionObjectType::ZoneHVACExhaustControl, // maybe zone inlets?
480 5 : thisExhCtrl.Name,
481 : DataLoopNode::ConnectionType::Sensor,
482 : NodeInputManager::CompFluidStream::Primary,
483 : DataLoopNode::ObjectIsNotParent); // , // _, // supplyNodeOrNodelistName);
484 :
485 : // Verify these nodes are indeed supply nodes:
486 5 : if (thisExhCtrl.FlowControlOption == ZoneExhaustControl::FlowControlType::FollowSupply) { // FollowSupply
487 0 : bool nodeNotFound = false;
488 0 : for (size_t i = 1; i <= thisExhCtrl.SuppNodeNums.size(); ++i) {
489 0 : CheckForSupplyNode(state, exhCtrlNum, nodeNotFound);
490 0 : if (nodeNotFound) {
491 0 : ShowSevereError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhCtrl.Name));
492 0 : ShowContinueError(state,
493 0 : format("Node or NodeList Name ={}. Must all be supply nodes.", thisExhCtrl.SupplyNodeOrNodelistName));
494 0 : ErrorsFound = true;
495 : }
496 : }
497 : }
498 :
499 : // Deal with design exhaust autosize here;
500 5 : if (thisExhCtrl.DesignExhaustFlowRate == DataSizing::AutoSize) {
501 4 : SizeExhaustControlFlow(state, exhCtrlNum, thisExhCtrl.SuppNodeNums);
502 : }
503 :
504 : std::string minZoneTempLimitScheduleName =
505 10 : ip->getAlphaFieldValue(objectFields, objectSchemaProps, "minimum_zone_temperature_limit_schedule_name");
506 5 : int minZoneTempLimitScheduleNum = 0;
507 5 : minZoneTempLimitScheduleNum = ScheduleManager::GetScheduleIndex(state, minZoneTempLimitScheduleName);
508 :
509 5 : if (minZoneTempLimitScheduleNum > 0) {
510 : // normal conditions
511 5 : } else if (minZoneTempLimitScheduleNum == 0) {
512 : // blank or anything like that, treat as no comparision
513 : } else {
514 0 : minZoneTempLimitScheduleNum = 0;
515 : // a regular warning
516 0 : ShowWarningError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhCtrl.Name));
517 0 : ShowContinueError(state, format("Schedule Name ={} not found.", minZoneTempLimitScheduleName));
518 : }
519 5 : thisExhCtrl.MinZoneTempLimitScheduleNum = minZoneTempLimitScheduleNum;
520 :
521 : std::string minExhFlowFracScheduleName =
522 10 : ip->getAlphaFieldValue(objectFields, objectSchemaProps, "minimum_exhaust_flow_fraction_schedule_name");
523 : // to do so schedule matching
524 5 : int minExhFlowFracScheduleNum = 0;
525 5 : minExhFlowFracScheduleNum = ScheduleManager::GetScheduleIndex(state, minExhFlowFracScheduleName);
526 :
527 5 : if (minExhFlowFracScheduleNum > 0) {
528 : // normal conditions
529 5 : } else if (minExhFlowFracScheduleNum == 0) {
530 : // blank, meaning minimum is zero
531 : } else {
532 0 : minExhFlowFracScheduleNum = 0;
533 : // a regular warning
534 0 : ShowWarningError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhCtrl.Name));
535 0 : ShowContinueError(state, format("Schedule Name ={} not found.", minExhFlowFracScheduleName));
536 : }
537 5 : thisExhCtrl.MinExhFlowFracScheduleNum = minExhFlowFracScheduleNum;
538 :
539 : std::string balancedExhFracScheduleName =
540 10 : ip->getAlphaFieldValue(objectFields, objectSchemaProps, "balanced_exhaust_fraction_schedule_name");
541 : // to do so schedule matching
542 5 : int balancedExhFracScheduleNum = 0;
543 5 : balancedExhFracScheduleNum = ScheduleManager::GetScheduleIndex(state, balancedExhFracScheduleName);
544 :
545 5 : if (balancedExhFracScheduleNum > 0) {
546 : // normal conditions
547 5 : } else if (balancedExhFracScheduleNum == 0) {
548 : // blank, treated as not activated
549 : } else {
550 0 : balancedExhFracScheduleNum = 0;
551 : // a regular warning
552 0 : ShowWarningError(state, format("{}{}={}", RoutineName, cCurrentModuleObject, thisExhCtrl.Name));
553 0 : ShowContinueError(state, format("Schedule Name ={} not found.", balancedExhFracScheduleName));
554 : }
555 :
556 : // Maybe an additional check per IORef:
557 : // This input field must be blank when the zone air flow balance is enforced. If user specifies a schedule and zone air flow balance
558 : // is enforced, then EnergyPlus throws a warning error message, ignores the schedule and simulation continues.
559 :
560 5 : thisExhCtrl.BalancedExhFracScheduleNum = balancedExhFracScheduleNum;
561 6 : }
562 :
563 1 : state.dataZoneEquip->NumZoneExhaustControls = numZoneExhaustControls; // or exhCtrlNum
564 :
565 : // Done with creating a map that contains a table of for each zone to exhasut controls
566 1 : state.dataExhAirSystemMrg->mappingDone = true;
567 : } else {
568 : // If no exhaust systems are defined, then do something <or nothing>:
569 : }
570 :
571 796 : if (ErrorsFound) {
572 0 : ShowFatalError(state, "Errors found getting ZoneHVAC:ExhaustControl. Preceding condition(s) causes termination.");
573 : }
574 796 : }
575 :
576 6509971 : void SimZoneHVACExhaustControls(EnergyPlusData &state)
577 : {
578 6509971 : if (state.dataExhCtrlSystemMrg->GetInputFlag) { // First time subroutine has been entered
579 796 : GetZoneExhaustControlInput(state);
580 796 : state.dataExhCtrlSystemMrg->GetInputFlag = false;
581 : }
582 :
583 6528486 : for (int ExhaustControlNum = 1; ExhaustControlNum <= state.dataZoneEquip->NumZoneExhaustControls; ++ExhaustControlNum) {
584 18515 : CalcZoneHVACExhaustControl(state, ExhaustControlNum);
585 : }
586 :
587 : // report results if needed
588 6509971 : }
589 :
590 18515 : void CalcZoneHVACExhaustControl(EnergyPlusData &state, int const ZoneHVACExhaustControlNum, Real64 const FlowRatio)
591 : {
592 : // Calculate a zonehvac exhaust control system
593 :
594 18515 : auto &thisExhCtrl = state.dataZoneEquip->ZoneExhaustControlSystem(ZoneHVACExhaustControlNum);
595 :
596 18515 : int InletNode = thisExhCtrl.InletNodeNum;
597 18515 : int OutletNode = thisExhCtrl.OutletNodeNum;
598 18515 : auto &thisExhInlet = state.dataLoopNodes->Node(InletNode);
599 18515 : auto &thisExhOutlet = state.dataLoopNodes->Node(OutletNode);
600 : Real64 MassFlow;
601 18515 : Real64 Tin = state.dataZoneTempPredictorCorrector->zoneHeatBalance(thisExhCtrl.ZoneNum).ZT;
602 18515 : Real64 thisExhCtrlAvailScheVal = ScheduleManager::GetCurrentScheduleValue(state, thisExhCtrl.AvailScheduleNum);
603 :
604 18515 : if (FlowRatio >= 0.0) {
605 0 : thisExhCtrl.BalancedFlow *= FlowRatio;
606 0 : thisExhCtrl.UnbalancedFlow *= FlowRatio;
607 :
608 0 : thisExhInlet.MassFlowRate *= FlowRatio;
609 : } else {
610 : // Availability schedule:
611 18515 : if (thisExhCtrlAvailScheVal <= 0.0) {
612 0 : MassFlow = 0.0;
613 0 : thisExhInlet.MassFlowRate = 0.0;
614 : } else {
615 : //
616 : }
617 :
618 18515 : Real64 DesignFlowRate = thisExhCtrl.DesignExhaustFlowRate;
619 18515 : Real64 FlowFrac = 0.0;
620 18515 : if (thisExhCtrl.MinExhFlowFracScheduleNum > 0) {
621 0 : FlowFrac = ScheduleManager::GetCurrentScheduleValue(state, thisExhCtrl.ExhaustFlowFractionScheduleNum);
622 0 : if (FlowFrac < 0.0) {
623 0 : ShowWarningError(
624 0 : state, format("Exhaust Flow Fraction Schedule value is negative for Zone Exhaust Control Named: {};", thisExhCtrl.Name));
625 0 : ShowContinueError(state, "Reset value to zero and continue the simulation.");
626 0 : FlowFrac = 0.0;
627 : }
628 : }
629 :
630 18515 : Real64 MinFlowFrac = 0.0;
631 18515 : if (thisExhCtrl.MinExhFlowFracScheduleNum > 0) {
632 0 : MinFlowFrac = ScheduleManager::GetCurrentScheduleValue(state, thisExhCtrl.MinExhFlowFracScheduleNum);
633 0 : if (MinFlowFrac < 0.0) {
634 0 : ShowWarningError(
635 : state,
636 0 : format("Minimum Exhaust Flow Fraction Schedule value is negative for Zone Exhaust Control Named: {};", thisExhCtrl.Name));
637 0 : ShowContinueError(state, "Reset value to zero and continue the simulation.");
638 0 : MinFlowFrac = 0.0;
639 : }
640 : }
641 :
642 18515 : if (FlowFrac < MinFlowFrac) {
643 0 : FlowFrac = MinFlowFrac;
644 : }
645 :
646 18515 : if (thisExhCtrlAvailScheVal > 0.0) { // available
647 18515 : if (thisExhCtrl.MinZoneTempLimitScheduleNum > 0) {
648 0 : if (Tin >= ScheduleManager::GetCurrentScheduleValue(state, thisExhCtrl.MinZoneTempLimitScheduleNum)) {
649 : } else {
650 0 : FlowFrac = MinFlowFrac;
651 : }
652 : } else {
653 : // flow not changed
654 : }
655 : } else {
656 0 : FlowFrac = 0.0; // directly set flow rate to zero.
657 : }
658 :
659 18515 : if (thisExhCtrl.FlowControlOption == ZoneExhaustControl::FlowControlType::FollowSupply) { // follow-supply
660 0 : Real64 supplyFlowRate = 0.0;
661 0 : int numOfSuppNodes = thisExhCtrl.SuppNodeNums.size();
662 0 : for (int i = 1; i <= numOfSuppNodes; ++i) {
663 0 : supplyFlowRate += state.dataLoopNodes->Node(thisExhCtrl.SuppNodeNums(i)).MassFlowRate;
664 : }
665 0 : MassFlow = supplyFlowRate * FlowFrac;
666 : } else { // Scheduled or Invalid
667 18515 : MassFlow = DesignFlowRate * FlowFrac;
668 : }
669 :
670 18515 : if (thisExhCtrl.BalancedExhFracScheduleNum > 0) {
671 0 : thisExhCtrl.BalancedFlow = // state.dataHVACGlobal->BalancedExhMassFlow =
672 0 : MassFlow * // state.dataHVACGlobal->UnbalExhMassFlow *
673 0 : ScheduleManager::GetCurrentScheduleValue(state, thisExhCtrl.BalancedExhFracScheduleNum);
674 0 : thisExhCtrl.UnbalancedFlow = // state.dataHVACGlobal->UnbalExhMassFlow =
675 0 : MassFlow - // = state.dataHVACGlobal->UnbalExhMassFlow -
676 0 : thisExhCtrl.BalancedFlow; // state.dataHVACGlobal->BalancedExhMassFlow;
677 : } else {
678 18515 : thisExhCtrl.BalancedFlow = 0.0;
679 18515 : thisExhCtrl.UnbalancedFlow = MassFlow;
680 : }
681 :
682 18515 : thisExhInlet.MassFlowRate = MassFlow;
683 : }
684 :
685 18515 : thisExhOutlet.MassFlowRate = thisExhInlet.MassFlowRate;
686 :
687 18515 : thisExhOutlet.Temp = thisExhInlet.Temp;
688 18515 : thisExhOutlet.HumRat = thisExhInlet.HumRat;
689 18515 : thisExhOutlet.Enthalpy = thisExhInlet.Enthalpy;
690 : // Set the outlet nodes for properties that just pass through & not used
691 18515 : thisExhOutlet.Quality = thisExhInlet.Quality;
692 18515 : thisExhOutlet.Press = thisExhInlet.Press;
693 :
694 : // Set the Node Flow Control Variables from the Fan Control Variables
695 18515 : thisExhOutlet.MassFlowRateMax = thisExhInlet.MassFlowRateMax;
696 18515 : thisExhOutlet.MassFlowRateMaxAvail = thisExhInlet.MassFlowRateMaxAvail;
697 18515 : thisExhOutlet.MassFlowRateMinAvail = thisExhInlet.MassFlowRateMinAvail;
698 :
699 : // these might also be useful to pass through
700 18515 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
701 0 : thisExhOutlet.CO2 = thisExhInlet.CO2;
702 : }
703 :
704 18515 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
705 0 : thisExhOutlet.GenContam = thisExhInlet.GenContam;
706 : }
707 18515 : }
708 :
709 2 : void SizeExhaustSystem(EnergyPlusData &state, int const exhSysNum)
710 : {
711 2 : auto &thisExhSys = state.dataZoneEquip->ExhaustAirSystem(exhSysNum);
712 :
713 2 : if (!thisExhSys.SizingFlag) {
714 0 : return;
715 : }
716 :
717 : // mixer outlet sizing:
718 2 : Real64 outletFlowMaxAvail = 0.0;
719 7 : for (int i = 1; i <= state.dataMixerComponent->MixerCond(thisExhSys.ZoneMixerIndex).NumInletNodes; ++i) {
720 5 : int inletNode_index = state.dataMixerComponent->MixerCond(thisExhSys.ZoneMixerIndex).InletNode(i);
721 5 : outletFlowMaxAvail += state.dataLoopNodes->Node(inletNode_index).MassFlowRateMaxAvail;
722 : }
723 :
724 : // mixer outlet considered OutletMassFlowRateMaxAvail?
725 2 : int outletNode_index = state.dataMixerComponent->MixerCond(thisExhSys.ZoneMixerIndex).OutletNode;
726 2 : state.dataLoopNodes->Node(outletNode_index).MassFlowRateMaxAvail = outletFlowMaxAvail;
727 :
728 2 : auto *fan = state.dataFans->fans(thisExhSys.CentralFanIndex);
729 : // then central exhasut fan sizing here:
730 2 : if (thisExhSys.centralFanType == HVAC::FanType::SystemModel) {
731 2 : if (fan->maxAirFlowRate == DataSizing::AutoSize) {
732 0 : fan->maxAirFlowRate = outletFlowMaxAvail / state.dataEnvrn->StdRhoAir;
733 : }
734 2 : BaseSizer::reportSizerOutput(state, "FAN:SYSTEMMODEL", fan->Name, "Design Fan Airflow [m3/s]", fan->maxAirFlowRate);
735 0 : } else if (thisExhSys.centralFanType == HVAC::FanType::ComponentModel) {
736 0 : if (fan->maxAirMassFlowRate == DataSizing::AutoSize) {
737 0 : fan->maxAirMassFlowRate = outletFlowMaxAvail * dynamic_cast<Fans::FanComponent *>(fan)->sizingFactor;
738 : }
739 0 : BaseSizer::reportSizerOutput(state,
740 0 : HVAC::fanTypeNames[(int)fan->type],
741 : fan->Name,
742 : "Design Fan Airflow [m3/s]",
743 0 : fan->maxAirMassFlowRate / state.dataEnvrn->StdRhoAir);
744 : } else {
745 : //
746 : }
747 :
748 : // after evertyhing sized, set the sizing flag to be false
749 2 : thisExhSys.SizingFlag = false;
750 : }
751 :
752 4 : void SizeExhaustControlFlow(EnergyPlusData &state, int const zoneExhCtrlNum, Array1D_int &NodeNums)
753 : {
754 4 : auto &thisExhCtrl = state.dataZoneEquip->ZoneExhaustControlSystem(zoneExhCtrlNum);
755 :
756 4 : Real64 designFlow = 0.0;
757 :
758 4 : if (thisExhCtrl.FlowControlOption == ZoneExhaustControl::FlowControlType::FollowSupply) { // FollowSupply
759 : // size based on supply nodelist flow
760 0 : for (size_t i = 1; i <= NodeNums.size(); ++i) {
761 0 : designFlow += state.dataLoopNodes->Node(NodeNums(i)).MassFlowRateMax;
762 : }
763 : } else { // scheduled etc.
764 : // based on zone OA.
765 4 : designFlow = state.dataSize->FinalZoneSizing(thisExhCtrl.ZoneNum).MinOA;
766 : }
767 :
768 4 : thisExhCtrl.DesignExhaustFlowRate = designFlow;
769 4 : }
770 :
771 6509971 : void UpdateZoneExhaustControl(EnergyPlusData &state)
772 : {
773 6528486 : for (int i = 1; i <= state.dataZoneEquip->NumZoneExhaustControls; ++i) {
774 18515 : int controlledZoneNum = state.dataZoneEquip->ZoneExhaustControlSystem(i).ControlledZoneNum;
775 18515 : state.dataZoneEquip->ZoneEquipConfig(controlledZoneNum).ZoneExh +=
776 18515 : state.dataZoneEquip->ZoneExhaustControlSystem(i).BalancedFlow + state.dataZoneEquip->ZoneExhaustControlSystem(i).UnbalancedFlow;
777 18515 : state.dataZoneEquip->ZoneEquipConfig(controlledZoneNum).ZoneExhBalanced += state.dataZoneEquip->ZoneExhaustControlSystem(i).BalancedFlow;
778 : }
779 6509971 : }
780 :
781 0 : void CheckForSupplyNode(EnergyPlusData &state, int const ExhCtrlNum, bool &NodeNotFound)
782 : {
783 : // Trying to check a node to see if it is truely a supply node
784 : // for a nodelist, need a call loop to check each node in the list
785 :
786 0 : auto &thisExhCtrl = state.dataZoneEquip->ZoneExhaustControlSystem(ExhCtrlNum);
787 :
788 : // check a node is a zone inlet node.
789 0 : std::string_view constexpr RoutineName = "GetExhaustControlInput: ";
790 0 : std::string_view constexpr CurrentModuleObject = "ZoneHVAC:ExhaustControl";
791 :
792 0 : bool ZoneNodeNotFound = true;
793 0 : bool ErrorsFound = false;
794 0 : for (size_t i = 1; i <= thisExhCtrl.SuppNodeNums.size(); ++i) {
795 0 : int supplyNodeNum = thisExhCtrl.SuppNodeNums(i);
796 0 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(thisExhCtrl.ZoneNum).NumInletNodes; ++NodeNum) {
797 0 : if (supplyNodeNum == state.dataZoneEquip->ZoneEquipConfig(thisExhCtrl.ZoneNum).InletNode(NodeNum)) {
798 0 : ZoneNodeNotFound = false;
799 0 : break;
800 : }
801 : }
802 0 : if (ZoneNodeNotFound) {
803 0 : ShowSevereError(state, format("{}{}={}", RoutineName, CurrentModuleObject, thisExhCtrl.Name));
804 0 : ShowContinueError(
805 : state,
806 0 : format("Supply or supply list = \"{}\" contains at least one node that is not a zone inlet node for Zone Name = \"{}\"",
807 0 : thisExhCtrl.SupplyNodeOrNodelistName,
808 0 : thisExhCtrl.ZoneName));
809 0 : ShowContinueError(state, "..Nodes in the supply node or nodelist must be a zone inlet node.");
810 0 : ErrorsFound = true;
811 : }
812 : }
813 :
814 0 : NodeNotFound = ErrorsFound;
815 0 : }
816 :
817 2 : bool ExhaustSystemHasMixer(EnergyPlusData &state, std::string_view CompName) // component (mixer) name
818 : {
819 : // Given a mixer name, this routine determines if that mixer is found on Exhaust Systems.
820 :
821 2 : if (state.dataExhAirSystemMrg->GetInputFlag) {
822 0 : GetExhaustAirSystemInput(state);
823 0 : state.dataExhAirSystemMrg->GetInputFlag = false;
824 : }
825 :
826 : return // ( state.dataZoneEquip->NumExhaustAirSystems > 0) &&
827 2 : (Util::FindItemInList(CompName, state.dataZoneEquip->ExhaustAirSystem, &ExhaustAir::ZoneMixerName) > 0);
828 : }
829 :
830 : } // namespace ExhaustAirSystemManager
831 :
832 : } // namespace EnergyPlus
|