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