Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Fmath.hh>
53 :
54 : // EnergyPlus Headers
55 : #include <AirflowNetwork/Solver.hpp>
56 : #include <EnergyPlus/AirLoopHVACDOAS.hh>
57 : #include <EnergyPlus/Autosizing/SystemAirFlowSizing.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/DataEnvironment.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataLoopNode.hh>
66 : #include <EnergyPlus/DataPrecisionGlobals.hh>
67 : #include <EnergyPlus/DataSizing.hh>
68 : #include <EnergyPlus/DataZoneEquipment.hh>
69 : #include <EnergyPlus/EMSManager.hh>
70 : #include <EnergyPlus/Fans.hh>
71 : #include <EnergyPlus/FaultsManager.hh>
72 : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
73 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
74 : #include <EnergyPlus/NodeInputManager.hh>
75 : #include <EnergyPlus/OutputProcessor.hh>
76 : #include <EnergyPlus/OutputReportPredefined.hh>
77 : #include <EnergyPlus/Psychrometrics.hh>
78 : #include <EnergyPlus/ScheduleManager.hh>
79 : #include <EnergyPlus/UtilityRoutines.hh>
80 :
81 : namespace EnergyPlus::Fans {
82 : // Module containing the fan simulation routines
83 :
84 : // MODULE INFORMATION:
85 : // AUTHOR Richard J. Liesen
86 : // DATE WRITTEN April 1998
87 : // MODIFIED Shirey, May 2001
88 : // Griffith, May 2009, EMS changes
89 : // Craig Wray 22Aug2010 Added Fan Component Model
90 :
91 : // PURPOSE OF THIS MODULE:
92 : // To encapsulate the data and algorithms required to
93 : // manage the Fan System Component
94 :
95 : constexpr std::array<std::string_view, (int)MinFlowFracMethod::Num> minFlowFracMethodNamesUC = {"FRACTION", "FIXEDFLOWRATE"};
96 :
97 173240742 : void FanBase::simulate(EnergyPlusData &state,
98 : [[maybe_unused]] bool const _FirstHVACIteration,
99 : ObjexxFCL::Optional<Real64 const> _speedRatio, // SpeedRatio for Fan:SystemModel
100 :
101 : // = current flow/ max design flow rate. It is not exactly the same as
102 : // the legacy speed ratio that was used with SimulateFanComponents.
103 : ObjexxFCL::Optional<Real64 const> _pressureRise, // Pressure difference to use for DeltaPress, for rating DX coils at a
104 : ObjexxFCL::Optional<Real64 const> _flowFraction, // when used, this directs the fan to set the flow at this flow fraction
105 : // different pressure without entire duct system
106 : ObjexxFCL::Optional<Real64 const> _massFlowRate1, // Mass flow rate in operating mode 1 [kg/s]
107 : ObjexxFCL::Optional<Real64 const> _runTimeFraction1, // Run time fraction in operating mode 1
108 : ObjexxFCL::Optional<Real64 const> _massFlowRate2, // Mass flow rate in operating mode 2 [kg/s]
109 : ObjexxFCL::Optional<Real64 const> _runTimeFraction2, // Run time fraction in opearating mode 2
110 : ObjexxFCL::Optional<Real64 const> _pressureRise2 // Pressure difference for operating mode 2
111 : )
112 : {
113 :
114 : // SUBROUTINE INFORMATION:
115 : // AUTHOR Richard Liesen
116 : // DATE WRITTEN February 1998
117 : // MODIFIED Chandan Sharma, March 2011 - FSEC: Added logic for ZoneHVAC sys avail managers
118 :
119 : // PURPOSE OF THIS SUBROUTINE:
120 : // This subroutine manages Fan component simulation.
121 :
122 : // With the correct FanNum Initialize
123 173240742 : init(state);
124 :
125 : // It looks like the behavior is different for system and
126 : // non-system fans? Non-system fans can simulate without being
127 : // sized first?
128 :
129 : // Calculate the Correct Fan Model with the current FanNum
130 173240742 : if (type != HVAC::FanType::SystemModel) {
131 162659141 : auto *_thisFan = dynamic_cast<FanComponent *>(this);
132 162659141 : assert(_thisFan != nullptr);
133 :
134 162659141 : switch (type) {
135 :
136 12136029 : case HVAC::FanType::Constant: {
137 12136029 : _thisFan->simulateConstant(state);
138 12136029 : } break;
139 25089459 : case HVAC::FanType::VAV: {
140 25089459 : _thisFan->simulateVAV(state, _pressureRise);
141 25089459 : } break;
142 123475996 : case HVAC::FanType::OnOff: {
143 123475996 : _thisFan->simulateOnOff(state, _speedRatio);
144 123475996 : } break;
145 1894988 : case HVAC::FanType::Exhaust: {
146 1894988 : _thisFan->simulateZoneExhaust(state);
147 1894988 : } break;
148 62669 : case HVAC::FanType::ComponentModel: {
149 62669 : _thisFan->simulateComponentModel(state);
150 62669 : } break;
151 0 : default: {
152 0 : } break;
153 : } // switch (type)
154 :
155 : } else { // FanType::SystemModel
156 :
157 10581601 : if (sizingFlag) {
158 347 : return;
159 : }
160 :
161 10581254 : auto *_thisFan = dynamic_cast<FanSystem *>(this);
162 10581254 : assert(_thisFan != nullptr);
163 :
164 10585882 : if (present(_pressureRise) && present(_massFlowRate1) && present(_runTimeFraction1) && present(_massFlowRate2) &&
165 10585882 : present(_runTimeFraction2) && present(_pressureRise2)) {
166 0 : Real64 _flowRatio1 = _massFlowRate1 / maxAirMassFlowRate;
167 0 : Real64 _flowRatio2 = _massFlowRate2 / maxAirMassFlowRate;
168 0 : _thisFan->calcSimpleSystemFan(state, _, _pressureRise, _flowRatio1, _runTimeFraction1, _flowRatio2, _runTimeFraction2, _pressureRise2);
169 21157880 : } else if (!present(_pressureRise) && present(_massFlowRate1) && present(_runTimeFraction1) && present(_massFlowRate2) &&
170 21157880 : present(_runTimeFraction2) && !present(_pressureRise2)) {
171 5850098 : Real64 _flowRatio1 = _massFlowRate1 / maxAirMassFlowRate;
172 5850098 : Real64 _flowRatio2 = _massFlowRate2 / maxAirMassFlowRate;
173 5850098 : _thisFan->calcSimpleSystemFan(state, _flowFraction, _, _flowRatio1, _runTimeFraction1, _flowRatio2, _runTimeFraction2, _);
174 4731156 : } else if (present(_pressureRise) && present(_flowFraction)) {
175 0 : _thisFan->calcSimpleSystemFan(state, _flowFraction, _pressureRise, _, _, _, _, _);
176 4731156 : } else if (present(_pressureRise) && !present(_flowFraction)) {
177 4628 : _thisFan->calcSimpleSystemFan(state, _, _pressureRise, _, _, _, _, _);
178 4726528 : } else if (!present(_pressureRise) && present(_flowFraction)) {
179 1339137 : _thisFan->calcSimpleSystemFan(state, _flowFraction, _, _, _, _, _, _);
180 : } else {
181 3387391 : _thisFan->calcSimpleSystemFan(state, _, _, _, _, _, _, _);
182 : }
183 : }
184 :
185 173240395 : update(state);
186 173240395 : report(state);
187 : }
188 :
189 : // Get Input Section of the Module
190 : //******************************************************************************
191 :
192 601 : void GetFanInput(EnergyPlusData &state)
193 : {
194 :
195 : // SUBROUTINE INFORMATION:
196 : // AUTHOR Richard Liesen
197 : // DATE WRITTEN April 1998
198 : // MODIFIED Shirey, May 2001
199 :
200 : // PURPOSE OF THIS SUBROUTINE:
201 : // Obtains input data for fans and stores it in fan data structures
202 :
203 : static constexpr std::string_view routineName = "GetFanInput";
204 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
205 : int NumAlphas;
206 : int NumNums;
207 : int IOStat;
208 601 : bool ErrorsFound(false); // If errors detected in input
209 601 : Array1D_string cAlphaFieldNames;
210 601 : Array1D_string cNumericFieldNames;
211 601 : Array1D_bool lNumericFieldBlanks;
212 601 : Array1D_bool lAlphaFieldBlanks;
213 601 : Array1D_string cAlphaArgs;
214 601 : Array1D<Real64> rNumericArgs;
215 601 : std::string cCurrentModuleObject;
216 : int NumParams;
217 :
218 601 : auto &ip = state.dataInputProcessing->inputProcessor;
219 601 : auto &df = state.dataFans;
220 :
221 601 : state.dataFans->GetFanInputFlag = false;
222 :
223 601 : int MaxAlphas = 0;
224 601 : int MaxNumbers = 0;
225 601 : int NumSimpFan = ip->getNumObjectsFound(state, "Fan:ConstantVolume");
226 601 : if (NumSimpFan > 0) {
227 207 : ip->getObjectDefMaxArgs(state, "Fan:ConstantVolume", NumParams, NumAlphas, NumNums);
228 207 : MaxAlphas = max(MaxAlphas, NumAlphas);
229 207 : MaxNumbers = max(MaxNumbers, NumNums);
230 : }
231 601 : int NumVarVolFan = ip->getNumObjectsFound(state, "Fan:VariableVolume");
232 601 : if (NumVarVolFan > 0) {
233 262 : ip->getObjectDefMaxArgs(state, "Fan:VariableVolume", NumParams, NumAlphas, NumNums);
234 262 : MaxAlphas = max(MaxAlphas, NumAlphas);
235 262 : MaxNumbers = max(MaxNumbers, NumNums);
236 : }
237 601 : int NumOnOff = ip->getNumObjectsFound(state, "Fan:OnOff");
238 601 : if (NumOnOff > 0) {
239 175 : ip->getObjectDefMaxArgs(state, "Fan:OnOff", NumParams, NumAlphas, NumNums);
240 175 : MaxAlphas = max(MaxAlphas, NumAlphas);
241 175 : MaxNumbers = max(MaxNumbers, NumNums);
242 : }
243 601 : int NumZoneExhFan = ip->getNumObjectsFound(state, "Fan:ZoneExhaust");
244 601 : if (NumZoneExhFan > 0) {
245 46 : ip->getObjectDefMaxArgs(state, "Fan:ZoneExhaust", NumParams, NumAlphas, NumNums);
246 46 : MaxAlphas = max(MaxAlphas, NumAlphas);
247 46 : MaxNumbers = max(MaxNumbers, NumNums);
248 : }
249 601 : state.dataFans->NumNightVentPerf = ip->getNumObjectsFound(state, "FanPerformance:NightVentilation");
250 601 : if (df->NumNightVentPerf > 0) {
251 0 : ip->getObjectDefMaxArgs(state, "FanPerformance:NightVentilation", NumParams, NumAlphas, NumNums);
252 0 : MaxAlphas = max(MaxAlphas, NumAlphas);
253 0 : MaxNumbers = max(MaxNumbers, NumNums);
254 : }
255 :
256 601 : int NumCompModelFan = ip->getNumObjectsFound(state, "Fan:ComponentModel");
257 601 : if (NumCompModelFan > 0) {
258 5 : ip->getObjectDefMaxArgs(state, "Fan:ComponentModel", NumParams, NumAlphas, NumNums);
259 5 : MaxAlphas = max(MaxAlphas, NumAlphas);
260 5 : MaxNumbers = max(MaxNumbers, NumNums);
261 : }
262 :
263 601 : int NumSystemModelFan = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Fan:SystemModel");
264 601 : if (NumSystemModelFan > 0) {
265 68 : ip->getObjectDefMaxArgs(state, "Fan:SystemModel", NumParams, NumAlphas, NumNums);
266 68 : MaxAlphas = max(MaxAlphas, NumAlphas);
267 68 : MaxNumbers = max(MaxNumbers, NumNums);
268 : }
269 :
270 601 : cAlphaArgs.allocate(MaxAlphas);
271 601 : cAlphaFieldNames.allocate(MaxAlphas);
272 601 : lAlphaFieldBlanks.dimension(MaxAlphas, false);
273 601 : cNumericFieldNames.allocate(MaxNumbers);
274 601 : lNumericFieldBlanks.dimension(MaxNumbers, false);
275 601 : rNumericArgs.dimension(MaxNumbers, 0.0);
276 :
277 1130 : for (int SimpFanNum = 1; SimpFanNum <= NumSimpFan; ++SimpFanNum) {
278 529 : cCurrentModuleObject = "Fan:ConstantVolume";
279 529 : ip->getObjectItem(state,
280 : cCurrentModuleObject,
281 : SimpFanNum,
282 : cAlphaArgs,
283 : NumAlphas,
284 : rNumericArgs,
285 : NumNums,
286 : IOStat,
287 : lNumericFieldBlanks,
288 : lAlphaFieldBlanks,
289 : cAlphaFieldNames,
290 : cNumericFieldNames);
291 :
292 529 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
293 :
294 529 : if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
295 0 : ShowSevereDuplicateName(state, eoh);
296 0 : ErrorsFound = true;
297 : }
298 :
299 529 : auto *fan = new FanComponent;
300 529 : fan->Name = cAlphaArgs(1);
301 :
302 529 : df->fans.push_back(fan);
303 529 : df->fanMap.insert_or_assign(cAlphaArgs(1), df->fans.size());
304 :
305 529 : fan->type = HVAC::FanType::Constant;
306 529 : fan->sizingPrefix = cNumericFieldNames(3);
307 :
308 529 : if (lAlphaFieldBlanks(2)) {
309 0 : fan->availSched = Sched::GetScheduleAlwaysOn(state);
310 529 : } else if ((fan->availSched = Sched::GetSchedule(state, cAlphaArgs(2))) == nullptr) {
311 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
312 0 : ErrorsFound = true;
313 : }
314 :
315 529 : fan->totalEff = rNumericArgs(1);
316 529 : fan->deltaPress = rNumericArgs(2);
317 529 : fan->maxAirFlowRate = rNumericArgs(3);
318 529 : if (fan->maxAirFlowRate == 0.0) {
319 0 : ShowWarningError(
320 : state,
321 0 : format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
322 : }
323 529 : fan->maxAirFlowRateIsAutosized = true;
324 529 : fan->motorEff = rNumericArgs(4);
325 529 : fan->motorInAirFrac = rNumericArgs(5);
326 529 : fan->minAirFlowRate = 0.0;
327 :
328 529 : fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
329 529 : cAlphaArgs(3),
330 : ErrorsFound,
331 : DataLoopNode::ConnectionObjectType::FanConstantVolume,
332 529 : cAlphaArgs(1),
333 : DataLoopNode::NodeFluidType::Air,
334 : DataLoopNode::ConnectionType::Inlet,
335 : NodeInputManager::CompFluidStream::Primary,
336 : DataLoopNode::ObjectIsNotParent);
337 529 : fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
338 529 : cAlphaArgs(4),
339 : ErrorsFound,
340 : DataLoopNode::ConnectionObjectType::FanConstantVolume,
341 529 : cAlphaArgs(1),
342 : DataLoopNode::NodeFluidType::Air,
343 : DataLoopNode::ConnectionType::Outlet,
344 : NodeInputManager::CompFluidStream::Primary,
345 : DataLoopNode::ObjectIsNotParent);
346 :
347 1207 : fan->endUseSubcategoryName = (NumAlphas > 4) ? cAlphaArgs(5) : "General";
348 :
349 529 : BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(3), cAlphaArgs(4), "Air Nodes");
350 :
351 529 : if (ErrorsFound) {
352 0 : ShowFatalError(state, format("{}: Errors found in input for fan name = {}. Program terminates.", routineName, fan->Name));
353 : }
354 : } // for (iFanConstant)
355 :
356 1038 : for (int VarVolFanNum = 1; VarVolFanNum <= NumVarVolFan; ++VarVolFanNum) {
357 437 : cCurrentModuleObject = "Fan:VariableVolume";
358 437 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
359 : cCurrentModuleObject,
360 : VarVolFanNum,
361 : cAlphaArgs,
362 : NumAlphas,
363 : rNumericArgs,
364 : NumNums,
365 : IOStat,
366 : lNumericFieldBlanks,
367 : lAlphaFieldBlanks,
368 : cAlphaFieldNames,
369 : cNumericFieldNames);
370 :
371 437 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
372 :
373 437 : if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
374 0 : ShowSevereDuplicateName(state, eoh);
375 0 : ErrorsFound = true;
376 : }
377 :
378 437 : auto *fan = new FanComponent;
379 437 : fan->Name = cAlphaArgs(1);
380 :
381 437 : df->fans.push_back(fan);
382 437 : df->fanMap.insert_or_assign(cAlphaArgs(1), df->fans.size());
383 :
384 437 : fan->type = HVAC::FanType::VAV;
385 437 : fan->sizingPrefix = cNumericFieldNames(3);
386 :
387 437 : if (lAlphaFieldBlanks(2)) {
388 0 : fan->availSched = Sched::GetScheduleAlwaysOn(state);
389 437 : } else if ((fan->availSched = Sched::GetSchedule(state, cAlphaArgs(2))) == nullptr) {
390 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
391 0 : ErrorsFound = true;
392 : }
393 :
394 437 : fan->totalEff = rNumericArgs(1);
395 437 : fan->deltaPress = rNumericArgs(2);
396 437 : fan->maxAirFlowRate = rNumericArgs(3);
397 437 : if (fan->maxAirFlowRate == 0.0) {
398 0 : ShowWarningError(
399 : state,
400 0 : format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
401 : }
402 437 : fan->maxAirFlowRateIsAutosized = true;
403 437 : fan->minAirFracMethod = static_cast<MinFlowFracMethod>(getEnumValue(minFlowFracMethodNamesUC, cAlphaArgs(3)));
404 :
405 437 : fan->minFrac = rNumericArgs(4);
406 437 : fan->fixedMin = rNumericArgs(5);
407 437 : fan->motorEff = rNumericArgs(6);
408 437 : fan->motorInAirFrac = rNumericArgs(7);
409 437 : fan->coeffs[0] = rNumericArgs(8);
410 437 : fan->coeffs[1] = rNumericArgs(9);
411 437 : fan->coeffs[2] = rNumericArgs(10);
412 437 : fan->coeffs[3] = rNumericArgs(11);
413 437 : fan->coeffs[4] = rNumericArgs(12);
414 437 : if (fan->coeffs[0] == 0.0 && fan->coeffs[1] == 0.0 && fan->coeffs[2] == 0.0 && fan->coeffs[3] == 0.0 && fan->coeffs[4] == 0.0) {
415 0 : ShowWarningError(state, "Fan Coefficients are all zero. No Fan power will be reported.");
416 0 : ShowContinueError(state, format("For {}, Fan={}", cCurrentModuleObject, cAlphaArgs(1)));
417 : }
418 437 : fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
419 437 : cAlphaArgs(4),
420 : ErrorsFound,
421 : DataLoopNode::ConnectionObjectType::FanVariableVolume,
422 437 : cAlphaArgs(1),
423 : DataLoopNode::NodeFluidType::Air,
424 : DataLoopNode::ConnectionType::Inlet,
425 : NodeInputManager::CompFluidStream::Primary,
426 : DataLoopNode::ObjectIsNotParent);
427 437 : fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
428 437 : cAlphaArgs(5),
429 : ErrorsFound,
430 : DataLoopNode::ConnectionObjectType::FanVariableVolume,
431 437 : cAlphaArgs(1),
432 : DataLoopNode::NodeFluidType::Air,
433 : DataLoopNode::ConnectionType::Outlet,
434 : NodeInputManager::CompFluidStream::Primary,
435 : DataLoopNode::ObjectIsNotParent);
436 :
437 957 : fan->endUseSubcategoryName = (NumAlphas > 5) ? cAlphaArgs(6) : "General";
438 :
439 437 : BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(4), cAlphaArgs(5), "Air Nodes");
440 :
441 437 : if (ErrorsFound) {
442 0 : ShowFatalError(state, format("{}: Errors found in input for fan name = {}. Program terminates.", routineName, fan->Name));
443 : }
444 : } // for (iFanVAV)
445 :
446 762 : for (int ExhFanNum = 1; ExhFanNum <= NumZoneExhFan; ++ExhFanNum) {
447 161 : cCurrentModuleObject = "Fan:ZoneExhaust";
448 161 : ip->getObjectItem(state,
449 : cCurrentModuleObject,
450 : ExhFanNum,
451 : cAlphaArgs,
452 : NumAlphas,
453 : rNumericArgs,
454 : NumNums,
455 : IOStat,
456 : lNumericFieldBlanks,
457 : lAlphaFieldBlanks,
458 : cAlphaFieldNames,
459 : cNumericFieldNames);
460 :
461 161 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
462 :
463 161 : if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
464 0 : ShowSevereDuplicateName(state, eoh);
465 0 : ErrorsFound = true;
466 : }
467 :
468 161 : auto *fan = new FanComponent;
469 161 : fan->Name = cAlphaArgs(1);
470 :
471 161 : df->fans.push_back(fan);
472 161 : df->fanMap.insert_or_assign(fan->Name, df->fans.size());
473 :
474 161 : fan->type = HVAC::FanType::Exhaust;
475 161 : fan->sizingPrefix = cNumericFieldNames(3);
476 :
477 161 : if (lAlphaFieldBlanks(2)) {
478 1 : fan->availSched = Sched::GetScheduleAlwaysOn(state); // Not an availability schedule, but defaults to constant-1.0
479 160 : } else if ((fan->availSched = Sched::GetSchedule(state, cAlphaArgs(2))) == nullptr) {
480 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
481 0 : ErrorsFound = true;
482 160 : } else if (fan->availSched->hasFractionalVal(state)) {
483 0 : ShowWarningCustom(
484 : state,
485 : eoh,
486 0 : format("{}={} has fracdtional values. Only 0.0 in the schedule value turns the fan off.", cAlphaFieldNames(2), cAlphaArgs(2)));
487 : }
488 :
489 161 : fan->totalEff = rNumericArgs(1);
490 161 : fan->deltaPress = rNumericArgs(2);
491 161 : fan->maxAirFlowRate = rNumericArgs(3);
492 161 : fan->maxAirFlowRateIsAutosized = false;
493 161 : fan->motorEff = 1.0;
494 161 : fan->motorInAirFrac = 1.0;
495 161 : fan->minAirFlowRate = 0.0;
496 161 : fan->rhoAirStdInit = state.dataEnvrn->StdRhoAir;
497 161 : fan->maxAirMassFlowRate = fan->maxAirFlowRate * fan->rhoAirStdInit;
498 :
499 161 : if (fan->maxAirFlowRate == 0.0) {
500 0 : ShowWarningError(
501 : state,
502 0 : format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
503 : }
504 :
505 161 : fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
506 161 : cAlphaArgs(3),
507 : ErrorsFound,
508 : DataLoopNode::ConnectionObjectType::FanZoneExhaust,
509 161 : cAlphaArgs(1),
510 : DataLoopNode::NodeFluidType::Air,
511 : DataLoopNode::ConnectionType::Inlet,
512 : NodeInputManager::CompFluidStream::Primary,
513 : DataLoopNode::ObjectIsNotParent);
514 161 : fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
515 161 : cAlphaArgs(4),
516 : ErrorsFound,
517 : DataLoopNode::ConnectionObjectType::FanZoneExhaust,
518 161 : cAlphaArgs(1),
519 : DataLoopNode::NodeFluidType::Air,
520 : DataLoopNode::ConnectionType::Outlet,
521 : NodeInputManager::CompFluidStream::Primary,
522 : DataLoopNode::ObjectIsNotParent);
523 :
524 322 : fan->endUseSubcategoryName = (NumAlphas > 4 && !lAlphaFieldBlanks(5)) ? cAlphaArgs(5) : "General";
525 :
526 161 : if (NumAlphas <= 5 || lAlphaFieldBlanks(6)) {
527 161 : fan->flowFracSched = Sched::GetScheduleAlwaysOn(state); // Not an availability schedule, but defaults to constant-1.0
528 0 : } else if ((fan->flowFracSched = Sched::GetSchedule(state, cAlphaArgs(6))) == nullptr) {
529 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(6), cAlphaArgs(6));
530 0 : ErrorsFound = true;
531 0 : } else if (!fan->flowFracSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
532 0 : Sched::ShowSevereBadMinMax(state, eoh, cAlphaFieldNames(6), cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 1.0);
533 0 : ErrorsFound = true;
534 : }
535 :
536 161 : if (NumAlphas <= 6 || lAlphaFieldBlanks(7)) {
537 154 : fan->availManagerMode = AvailManagerMode::Coupled;
538 7 : } else if ((fan->availManagerMode = static_cast<AvailManagerMode>(getEnumValue(availManagerModeNamesUC, cAlphaArgs(7)))) ==
539 : AvailManagerMode::Invalid) {
540 0 : ShowSevereInvalidKey(state, eoh, cAlphaFieldNames(7), cAlphaArgs(7));
541 0 : ErrorsFound = true;
542 : }
543 :
544 161 : if (NumAlphas <= 7 || lAlphaFieldBlanks(8)) {
545 161 : fan->minTempLimitSched = nullptr;
546 0 : } else if ((fan->minTempLimitSched = Sched::GetSchedule(state, cAlphaArgs(8))) == nullptr) {
547 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(8), cAlphaArgs(8));
548 0 : ErrorsFound = true;
549 : }
550 :
551 161 : if (NumAlphas <= 8 || lAlphaFieldBlanks(9)) {
552 129 : fan->balancedFractSched = nullptr;
553 32 : } else if (state.dataHeatBal->ZoneAirMassFlow.ZoneFlowAdjustment != DataHeatBalance::AdjustmentType::NoAdjustReturnAndMixing) {
554 : // do not include adjusted for "balanced" exhaust flow in the zone total return calculation
555 0 : ShowWarningError(state,
556 0 : format("{}: {}: invalid {} = {} for {}={}",
557 : routineName,
558 : cCurrentModuleObject,
559 : cAlphaFieldNames(9),
560 : cAlphaArgs(9),
561 : cAlphaFieldNames(1),
562 : cAlphaArgs(1)));
563 0 : ShowContinueError(state, "When zone air mass flow balance is enforced, this input field should be left blank.");
564 0 : ShowContinueError(state, "This schedule will be ignored in the simulation.");
565 0 : fan->balancedFractSched = nullptr;
566 32 : } else if ((fan->balancedFractSched = Sched::GetSchedule(state, cAlphaArgs(9))) == nullptr) {
567 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(9), cAlphaArgs(9));
568 0 : ErrorsFound = true;
569 32 : } else if (!fan->balancedFractSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
570 0 : Sched::ShowSevereBadMinMax(state, eoh, cAlphaFieldNames(9), cAlphaArgs(9), Clusive::In, 0.0, Clusive::In, 1.0);
571 0 : ErrorsFound = true;
572 : }
573 161 : if (ErrorsFound) {
574 0 : ShowFatalError(state, format("{}: Errors found in input for fan name = {}. Program terminates.", routineName, fan->Name));
575 : }
576 : } // for (iFanExhaust)
577 :
578 1616 : for (int OnOffFanNum = 1; OnOffFanNum <= NumOnOff; ++OnOffFanNum) {
579 1015 : cCurrentModuleObject = "Fan:OnOff";
580 1015 : ip->getObjectItem(state,
581 : cCurrentModuleObject,
582 : OnOffFanNum,
583 : cAlphaArgs,
584 : NumAlphas,
585 : rNumericArgs,
586 : NumNums,
587 : IOStat,
588 : lNumericFieldBlanks,
589 : lAlphaFieldBlanks,
590 : cAlphaFieldNames,
591 : cNumericFieldNames);
592 :
593 1015 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
594 :
595 1015 : if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
596 0 : ShowSevereDuplicateName(state, eoh);
597 0 : ErrorsFound = true;
598 : }
599 :
600 1015 : auto *fan = new FanComponent;
601 1015 : fan->Name = cAlphaArgs(1);
602 :
603 1015 : df->fans.push_back(fan);
604 1015 : df->fanMap.insert_or_assign(fan->Name, df->fans.size());
605 :
606 1015 : fan->type = HVAC::FanType::OnOff;
607 1015 : fan->sizingPrefix = cNumericFieldNames(3);
608 :
609 1015 : if (lAlphaFieldBlanks(2)) {
610 0 : fan->availSched = Sched::GetScheduleAlwaysOn(state);
611 1015 : } else if ((fan->availSched = Sched::GetSchedule(state, cAlphaArgs(2))) == nullptr) {
612 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
613 0 : ErrorsFound = true;
614 : }
615 :
616 1015 : fan->totalEff = rNumericArgs(1);
617 1015 : fan->deltaPress = rNumericArgs(2);
618 1015 : fan->maxAirFlowRate = rNumericArgs(3);
619 1015 : if (fan->maxAirFlowRate == 0.0) {
620 0 : ShowWarningError(
621 : state,
622 0 : format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
623 : }
624 1015 : fan->maxAirFlowRateIsAutosized = true;
625 : // the following two structure variables are set here, as well as in InitFan, for the Heat Pump:Water Heater object
626 : // (Standard Rating procedure may be called before BeginEnvirFlag is set to TRUE, if so MaxAirMassFlowRate = 0)
627 1015 : fan->rhoAirStdInit = state.dataEnvrn->StdRhoAir;
628 1015 : fan->maxAirMassFlowRate = fan->maxAirFlowRate * fan->rhoAirStdInit;
629 :
630 1015 : fan->motorEff = rNumericArgs(4);
631 1015 : fan->motorInAirFrac = rNumericArgs(5);
632 1015 : fan->minAirFlowRate = 0.0;
633 :
634 1015 : fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
635 1015 : cAlphaArgs(3),
636 : ErrorsFound,
637 : DataLoopNode::ConnectionObjectType::FanOnOff,
638 1015 : cAlphaArgs(1),
639 : DataLoopNode::NodeFluidType::Air,
640 : DataLoopNode::ConnectionType::Inlet,
641 : NodeInputManager::CompFluidStream::Primary,
642 : DataLoopNode::ObjectIsNotParent);
643 1015 : fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
644 1015 : cAlphaArgs(4),
645 : ErrorsFound,
646 : DataLoopNode::ConnectionObjectType::FanOnOff,
647 1015 : cAlphaArgs(1),
648 : DataLoopNode::NodeFluidType::Air,
649 : DataLoopNode::ConnectionType::Outlet,
650 : NodeInputManager::CompFluidStream::Primary,
651 : DataLoopNode::ObjectIsNotParent);
652 :
653 1015 : if (NumAlphas > 4 && !lAlphaFieldBlanks(5)) {
654 25 : fan->powerRatioAtSpeedRatioCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(5));
655 : }
656 :
657 1015 : if (NumAlphas > 5 && !lAlphaFieldBlanks(6)) {
658 10 : fan->effRatioCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(6));
659 : }
660 :
661 2532 : fan->endUseSubcategoryName = (NumAlphas > 6 && !lAlphaFieldBlanks(7)) ? cAlphaArgs(7) : "General";
662 :
663 1015 : BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(3), cAlphaArgs(4), "Air Nodes");
664 :
665 1015 : if (ErrorsFound) {
666 0 : ShowFatalError(state, format("{}: Errors found in input for fan name = {}. Program terminates.", routineName, fan->Name));
667 : }
668 : } // for (iFanOnOff)
669 :
670 601 : if (state.dataFans->NumNightVentPerf > 0) {
671 0 : df->NightVentPerf.allocate(state.dataFans->NumNightVentPerf);
672 0 : for (auto &e : df->NightVentPerf) {
673 0 : e.FanName.clear();
674 0 : e.FanEff = 0.0;
675 0 : e.DeltaPress = 0.0;
676 0 : e.MaxAirFlowRate = 0.0;
677 0 : e.MotEff = 0.0;
678 0 : e.MotInAirFrac = 0.0;
679 0 : e.MaxAirMassFlowRate = 0.0;
680 : }
681 :
682 : // input the night ventilation performance objects
683 0 : for (int NVPerfNum = 1; NVPerfNum <= state.dataFans->NumNightVentPerf; ++NVPerfNum) {
684 0 : cCurrentModuleObject = "FanPerformance:NightVentilation";
685 0 : ip->getObjectItem(state,
686 : cCurrentModuleObject,
687 : NVPerfNum,
688 : cAlphaArgs,
689 : NumAlphas,
690 : rNumericArgs,
691 : NumNums,
692 : IOStat,
693 : lNumericFieldBlanks,
694 : lAlphaFieldBlanks,
695 : cAlphaFieldNames,
696 : cNumericFieldNames);
697 :
698 0 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
699 :
700 0 : auto &nvp = df->NightVentPerf(NVPerfNum);
701 :
702 0 : nvp.FanName = cAlphaArgs(1);
703 0 : nvp.FanEff = rNumericArgs(1);
704 0 : nvp.DeltaPress = rNumericArgs(2);
705 0 : nvp.MaxAirFlowRate = rNumericArgs(3);
706 0 : nvp.MotEff = rNumericArgs(4);
707 0 : nvp.MotInAirFrac = rNumericArgs(5);
708 :
709 0 : auto found = df->fanMap.find(nvp.FanName);
710 0 : if (found == df->fanMap.end()) {
711 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(1), cAlphaArgs(1));
712 0 : ErrorsFound = true;
713 : } else {
714 0 : auto *fan = dynamic_cast<FanComponent *>(df->fans(found->second));
715 0 : assert(fan != nullptr);
716 0 : fan->nightVentPerfNum = NVPerfNum;
717 : }
718 0 : } // for (iNightVent)
719 : }
720 :
721 606 : for (int CompModelFanNum = 1; CompModelFanNum <= NumCompModelFan; ++CompModelFanNum) {
722 5 : cCurrentModuleObject = "Fan:ComponentModel";
723 5 : ip->getObjectItem(state,
724 : cCurrentModuleObject,
725 : CompModelFanNum,
726 : cAlphaArgs,
727 : NumAlphas,
728 : rNumericArgs,
729 : NumNums,
730 : IOStat,
731 : lNumericFieldBlanks,
732 : lAlphaFieldBlanks,
733 : cAlphaFieldNames,
734 : cNumericFieldNames);
735 :
736 5 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
737 :
738 5 : if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
739 0 : ShowSevereDuplicateName(state, eoh);
740 0 : ErrorsFound = true;
741 : }
742 :
743 5 : auto *fan = new FanComponent;
744 5 : fan->Name = cAlphaArgs(1); // Fan name
745 :
746 5 : df->fans.push_back(fan);
747 5 : df->fanMap.insert_or_assign(fan->Name, df->fans.size());
748 :
749 5 : fan->type = HVAC::FanType::ComponentModel;
750 5 : fan->sizingPrefix = cNumericFieldNames(1);
751 :
752 5 : fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
753 5 : cAlphaArgs(2),
754 : ErrorsFound,
755 : DataLoopNode::ConnectionObjectType::FanComponentModel,
756 5 : cAlphaArgs(1),
757 : DataLoopNode::NodeFluidType::Air,
758 : DataLoopNode::ConnectionType::Inlet,
759 : NodeInputManager::CompFluidStream::Primary,
760 : DataLoopNode::ObjectIsNotParent); // Air inlet node name
761 10 : fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
762 5 : cAlphaArgs(3),
763 : ErrorsFound,
764 : DataLoopNode::ConnectionObjectType::FanComponentModel,
765 5 : cAlphaArgs(1),
766 : DataLoopNode::NodeFluidType::Air,
767 : DataLoopNode::ConnectionType::Outlet,
768 : NodeInputManager::CompFluidStream::Primary,
769 : DataLoopNode::ObjectIsNotParent); // Air outlet node name
770 :
771 5 : BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(2), cAlphaArgs(3), "Air Nodes");
772 :
773 5 : if (lAlphaFieldBlanks(4)) {
774 0 : fan->availSched = Sched::GetScheduleAlwaysOn(state);
775 5 : } else if ((fan->availSched = Sched::GetSchedule(state, cAlphaArgs(4))) == nullptr) {
776 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(4), cAlphaArgs(4));
777 0 : ErrorsFound = true;
778 : }
779 :
780 5 : fan->maxAirFlowRate = rNumericArgs(1);
781 5 : if (fan->maxAirFlowRate == 0.0) {
782 0 : ShowWarningError(
783 : state,
784 0 : format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
785 : }
786 5 : fan->maxAirFlowRateIsAutosized = true;
787 5 : fan->minAirFlowRate = rNumericArgs(2);
788 :
789 5 : fan->sizingFactor = rNumericArgs(3); // Fan max airflow sizing factor [-]
790 5 : fan->wheelDia = rNumericArgs(4); // Fan wheel outer diameter [m]
791 5 : fan->outletArea = rNumericArgs(5); // Fan outlet area [m2]
792 5 : fan->maxEff = rNumericArgs(6); // Fan maximum static efficiency [-]
793 5 : fan->eulerMaxEff = rNumericArgs(7); // Euler number at Fan maximum static efficiency [-]
794 5 : fan->maxDimFlow = rNumericArgs(8); // Fan maximum dimensionless airflow [-]
795 5 : fan->pulleyDiaRatio = rNumericArgs(9); // Motor/fan pulley diameter ratio [-]
796 5 : fan->beltMaxTorque = rNumericArgs(10); // Belt maximum torque [N-m, autosizable]
797 5 : fan->beltSizingFactor = rNumericArgs(11); // Belt sizing factor [-]
798 5 : fan->beltTorqueTrans = rNumericArgs(12); // Belt fractional torque transition Region 1-2 [-]
799 5 : fan->motorMaxSpeed = rNumericArgs(13); // Motor maximum speed [rpm]
800 5 : fan->motorMaxOutPower = rNumericArgs(14); // Motor maximum output power [W, autosizable]
801 5 : fan->motorSizingFactor = rNumericArgs(15); // Motor sizing factor [-]
802 5 : fan->motorInAirFrac = rNumericArgs(16); // Fraction of fan and motor losses to airstream [-]
803 :
804 5 : fan->vfdEffType = static_cast<VFDEffType>(getEnumValue(vfdEffTypeNamesUC, cAlphaArgs(5))); // VFD efficiency type [Speed or Power
805 :
806 5 : fan->vfdMaxOutPower = rNumericArgs(17); // VFD maximum output power [W, autosizable]
807 5 : fan->vfdSizingFactor = rNumericArgs(18); // VFD sizing factor [-]
808 :
809 : // Do we need to do error checking on these curve names?
810 5 : if (lAlphaFieldBlanks(6)) {
811 0 : fan->pressRiseCurveNum = 0;
812 5 : } else if ((fan->pressRiseCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(6))) == 0) { // Fan pressure rise curve
813 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(6), cAlphaArgs(6));
814 0 : ErrorsFound = true;
815 : }
816 :
817 5 : fan->pressResetCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(7)); // Duct static pressure reset curve
818 5 : fan->plTotalEffNormCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(8)); // Fan part-load eff (normal) curve
819 5 : fan->plTotalEffStallCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(9)); // Fan part-load eff (stall) curve
820 5 : fan->dimFlowNormCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(10)); // Fan dim airflow (normal) curve
821 5 : fan->dimFlowStallCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(11)); // Fan dim airflow (stall) curve
822 5 : fan->beltMaxEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(12)); // Belt max eff curve
823 5 : fan->plBeltEffReg1CurveNum = Curve::GetCurveIndex(state, cAlphaArgs(13)); // Belt part-load eff Region 1 curve
824 5 : fan->plBeltEffReg2CurveNum = Curve::GetCurveIndex(state, cAlphaArgs(14)); // Belt part-load eff Region 2 curve
825 5 : fan->plBeltEffReg3CurveNum = Curve::GetCurveIndex(state, cAlphaArgs(15)); // Belt part-load eff Region 3 curve
826 5 : fan->motorMaxEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(16)); // Motor max eff curve
827 5 : fan->plMotorEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(17)); // Motor part-load eff curve
828 5 : fan->vfdEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(18)); // VFD eff curve
829 :
830 10 : fan->endUseSubcategoryName = (NumAlphas > 18) ? cAlphaArgs(19) : "General";
831 :
832 5 : if (ErrorsFound) {
833 0 : ShowFatalError(state, format("{}: Errors found in input for fan name = {}. Program terminates.", routineName, fan->Name));
834 : }
835 : } // end Number of Component Model FAN Loop
836 :
837 832 : for (int SystemFanNum = 1; SystemFanNum <= NumSystemModelFan; ++SystemFanNum) {
838 231 : cCurrentModuleObject = "Fan:SystemModel";
839 :
840 231 : ip->getObjectItem(state,
841 : cCurrentModuleObject,
842 : SystemFanNum,
843 : cAlphaArgs,
844 : NumAlphas,
845 : rNumericArgs,
846 : NumNums,
847 : IOStat,
848 : lNumericFieldBlanks,
849 : lAlphaFieldBlanks,
850 : cAlphaFieldNames,
851 : cNumericFieldNames);
852 :
853 231 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
854 :
855 231 : if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
856 0 : ShowSevereDuplicateName(state, eoh);
857 0 : ErrorsFound = true;
858 : }
859 :
860 231 : auto *fan = new FanSystem;
861 231 : fan->Name = cAlphaArgs(1);
862 :
863 231 : df->fans.push_back(fan);
864 231 : df->fanMap.insert_or_assign(fan->Name, df->fans.size());
865 :
866 231 : fan->type = HVAC::FanType::SystemModel;
867 :
868 231 : if (lAlphaFieldBlanks(2)) {
869 4 : fan->availSched = Sched::GetScheduleAlwaysOn(state); // nullptr
870 227 : } else if ((fan->availSched = Sched::GetSchedule(state, cAlphaArgs(2))) == nullptr) {
871 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
872 0 : ErrorsFound = true;
873 : }
874 :
875 231 : fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
876 231 : cAlphaArgs(3),
877 : ErrorsFound,
878 : DataLoopNode::ConnectionObjectType::FanSystemModel,
879 231 : cAlphaArgs(1),
880 : DataLoopNode::NodeFluidType::Air,
881 : DataLoopNode::ConnectionType::Inlet,
882 : NodeInputManager::CompFluidStream::Primary,
883 : DataLoopNode::ObjectIsNotParent);
884 462 : fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
885 231 : cAlphaArgs(4),
886 : ErrorsFound,
887 : DataLoopNode::ConnectionObjectType::FanSystemModel,
888 231 : cAlphaArgs(1),
889 : DataLoopNode::NodeFluidType::Air,
890 : DataLoopNode::ConnectionType::Outlet,
891 : NodeInputManager::CompFluidStream::Primary,
892 : DataLoopNode::ObjectIsNotParent);
893 :
894 231 : BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(3), cAlphaArgs(4), "Air Nodes");
895 :
896 231 : fan->maxAirFlowRate = rNumericArgs(1);
897 231 : if (fan->maxAirFlowRate == DataSizing::AutoSize) {
898 183 : fan->maxAirFlowRateIsAutosized = true;
899 : }
900 :
901 231 : if (lAlphaFieldBlanks(5)) {
902 0 : fan->speedControl = SpeedControl::Discrete;
903 : } else {
904 231 : fan->speedControl = static_cast<SpeedControl>(getEnumValue(speedControlNamesUC, cAlphaArgs(5)));
905 : }
906 :
907 231 : fan->minPowerFlowFrac = rNumericArgs(2);
908 231 : fan->deltaPress = rNumericArgs(3);
909 231 : if (fan->deltaPress <= 0.0) {
910 0 : ShowSevereError(state, format("{}: {} zero or negative, invalid entry in {}", routineName, cCurrentModuleObject, cNumericFieldNames(3)));
911 0 : ErrorsFound = true;
912 : }
913 231 : fan->motorEff = rNumericArgs(4);
914 231 : fan->motorInAirFrac = rNumericArgs(5);
915 231 : fan->designElecPower = rNumericArgs(6);
916 231 : if (fan->designElecPower == DataSizing::AutoSize) {
917 230 : fan->designElecPowerWasAutosized = true;
918 : }
919 231 : if (fan->designElecPowerWasAutosized) {
920 230 : if (lAlphaFieldBlanks(6)) {
921 0 : fan->powerSizingMethod = PowerSizing::PerFlowPerPressure;
922 : } else {
923 230 : fan->powerSizingMethod = static_cast<PowerSizing>(getEnumValue(powerSizingNamesUC, cAlphaArgs(6)));
924 : }
925 230 : fan->elecPowerPerFlowRate = rNumericArgs(7);
926 230 : fan->elecPowerPerFlowRatePerPressure = rNumericArgs(8);
927 230 : fan->totalEff = rNumericArgs(9);
928 : }
929 :
930 231 : if (lAlphaFieldBlanks(7)) {
931 171 : if (fan->speedControl == SpeedControl::Continuous) {
932 0 : ShowWarningError(state, format("{}{}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
933 0 : ShowContinueError(state,
934 0 : format("Continuous speed control requires a fan power curve in {} = {}", cAlphaFieldNames(7), cAlphaArgs(7)));
935 0 : ErrorsFound = true;
936 : }
937 60 : } else if ((fan->powerModFuncFlowFracCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(7))) == 0) {
938 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(7), cAlphaArgs(7));
939 0 : if (fan->speedControl == SpeedControl::Continuous) {
940 0 : ErrorsFound = true;
941 : }
942 : }
943 :
944 231 : fan->nightVentPressureDelta = rNumericArgs(10);
945 231 : fan->nightVentFlowFraction = rNumericArgs(11); // not used
946 :
947 231 : if (lAlphaFieldBlanks(8)) {
948 231 : fan->heatLossDest = HeatLossDest::Outside;
949 0 : } else if ((fan->zoneNum = Util::FindItemInList(cAlphaArgs(8), state.dataHeatBal->Zone)) == 0) {
950 0 : fan->heatLossDest = HeatLossDest::Outside;
951 0 : ShowWarningItemNotFound(state, eoh, cAlphaFieldNames(8), cAlphaArgs(8), "Fan motor heat losses will not be added to a zone");
952 : // continue with simulation but motor losses not sent to a zone.
953 : } else {
954 0 : fan->heatLossDest = HeatLossDest::Zone;
955 : }
956 :
957 231 : fan->zoneRadFract = rNumericArgs(12);
958 231 : if (!lAlphaFieldBlanks(9)) {
959 98 : fan->endUseSubcategoryName = cAlphaArgs(9);
960 : } else {
961 133 : fan->endUseSubcategoryName = "General";
962 : }
963 :
964 231 : if (!lNumericFieldBlanks(13)) {
965 55 : fan->numSpeeds = rNumericArgs(13);
966 : } else {
967 176 : fan->numSpeeds = 1;
968 : }
969 :
970 231 : fan->runtimeFracAtSpeed.resize(fan->numSpeeds, 0.0);
971 231 : if (fan->speedControl == SpeedControl::Discrete && fan->numSpeeds > 1) {
972 : // should have field sets
973 23 : fan->flowFracAtSpeed.resize(fan->numSpeeds, 0.0);
974 23 : fan->powerFracAtSpeed.resize(fan->numSpeeds, 0.0);
975 23 : fan->powerFracInputAtSpeed.resize(fan->numSpeeds, false);
976 23 : if (fan->numSpeeds == ((NumNums - 13) / 2) || fan->numSpeeds == ((NumNums + 1 - 13) / 2)) {
977 72 : for (int loopSet = 0; loopSet < fan->numSpeeds; ++loopSet) {
978 49 : fan->flowFracAtSpeed[loopSet] = rNumericArgs(13 + loopSet * 2 + 1);
979 49 : if (!lNumericFieldBlanks(13 + loopSet * 2 + 2)) {
980 49 : fan->powerFracAtSpeed[loopSet] = rNumericArgs(13 + loopSet * 2 + 2);
981 49 : fan->powerFracInputAtSpeed[loopSet] = true;
982 : } else {
983 0 : fan->powerFracInputAtSpeed[loopSet] = false;
984 : }
985 : }
986 23 : } else {
987 : // field set input does not match number of speeds, throw warning
988 0 : ShowSevereError(state, format("{}: {}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
989 0 : ShowContinueError(state, "Fan with Discrete speed control does not have input for speed data that matches the number of speeds.");
990 0 : ErrorsFound = true;
991 : }
992 : // check that flow fractions are increasing
993 23 : bool increasingOrderError = false;
994 49 : for (int loop = 0; loop < (fan->numSpeeds - 1); ++loop) {
995 26 : if (fan->flowFracAtSpeed[loop] > fan->flowFracAtSpeed[loop + 1]) {
996 0 : increasingOrderError = true;
997 : }
998 : }
999 23 : if (increasingOrderError) {
1000 0 : ShowSevereError(state, format("{}: {}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
1001 0 : ShowContinueError(state,
1002 : "Fan with Discrete speed control and multiple speed levels does not have input with flow fractions arranged in "
1003 : "increasing order.");
1004 0 : ErrorsFound = true;
1005 : }
1006 : }
1007 :
1008 : // check if power curve present when any speeds have no power fraction
1009 231 : if (fan->speedControl == SpeedControl::Discrete && fan->numSpeeds > 1 && fan->powerModFuncFlowFracCurveNum == 0) {
1010 23 : bool foundMissingPowerFraction = false;
1011 72 : for (int loop = 0; loop < fan->numSpeeds; ++loop) {
1012 49 : if (!fan->powerFracInputAtSpeed[loop]) {
1013 0 : foundMissingPowerFraction = true;
1014 : }
1015 : }
1016 23 : if (foundMissingPowerFraction) {
1017 : // field set input does not match number of speeds, throw warning
1018 0 : ShowSevereError(state, format("{}: {}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
1019 0 : ShowContinueError(
1020 : state,
1021 : "Fan with Discrete speed control does not have input for power fraction at all speed levels and does not have a power curve.");
1022 0 : ErrorsFound = true;
1023 : }
1024 : }
1025 :
1026 231 : if (fan->heatLossDest == HeatLossDest::Zone) {
1027 0 : SetupZoneInternalGain(
1028 : state, fan->zoneNum, fan->Name, DataHeatBalance::IntGainType::FanSystemModel, &fan->qdotConvZone, nullptr, &fan->qdotRadZone);
1029 : }
1030 231 : if (ErrorsFound) {
1031 0 : ShowFatalError(state, format("{}: Errors found in input for fan name = {}. Program terminates.", routineName, fan->Name));
1032 : }
1033 :
1034 : } // for (iFanSystemModel)
1035 :
1036 601 : cAlphaArgs.deallocate();
1037 601 : cAlphaFieldNames.deallocate();
1038 601 : lAlphaFieldBlanks.deallocate();
1039 601 : cNumericFieldNames.deallocate();
1040 601 : lNumericFieldBlanks.deallocate();
1041 601 : rNumericArgs.deallocate();
1042 :
1043 : // Check Fans
1044 : // There is a faster way to do this if this gets too slow (doubt it)
1045 2979 : for (auto const *fan1 : df->fans) {
1046 109166 : for (auto const *fan2 : df->fans) {
1047 106788 : if (fan1 == fan2) {
1048 2378 : continue;
1049 : }
1050 :
1051 104410 : if (fan1->inletNodeNum == fan2->inletNodeNum) {
1052 0 : ErrorsFound = true;
1053 0 : ShowSevereError(state, "GetFanInput, duplicate fan inlet node names, must be unique for fans.");
1054 0 : ShowContinueError(state,
1055 0 : format("Fan={}:{} and Fan={}:{}.",
1056 0 : HVAC::fanTypeNames[(int)fan1->type],
1057 0 : fan1->Name,
1058 0 : HVAC::fanTypeNames[(int)fan2->type],
1059 0 : fan2->Name));
1060 0 : ShowContinueError(state, format("Inlet Node Name=\"{}\".", state.dataLoopNodes->NodeID(fan1->inletNodeNum)));
1061 : }
1062 104410 : if (fan1->outletNodeNum == fan2->outletNodeNum) {
1063 0 : ErrorsFound = true;
1064 0 : ShowSevereError(state, "GetFanInput, duplicate fan outlet node names, must be unique for fans.");
1065 0 : ShowContinueError(state,
1066 0 : format("Fan={}:{} and Fan={}:{}.",
1067 0 : HVAC::fanTypeNames[(int)fan1->type],
1068 0 : fan1->Name,
1069 0 : HVAC::fanTypeNames[(int)fan2->type],
1070 0 : fan2->Name));
1071 0 : ShowContinueError(state, format("Outlet Node Name=\"{}\".", state.dataLoopNodes->NodeID(fan1->outletNodeNum)));
1072 : }
1073 : }
1074 : }
1075 :
1076 2979 : for (auto *fan : df->fans) {
1077 : // Setup Report variables for the Fans CurrentModuleObject='Fans'
1078 4756 : SetupOutputVariable(state,
1079 : "Fan Electricity Rate",
1080 : Constant::Units::W,
1081 2378 : fan->totalPower,
1082 : OutputProcessor::TimeStepType::System,
1083 : OutputProcessor::StoreType::Average,
1084 2378 : fan->Name);
1085 4756 : SetupOutputVariable(state,
1086 : "Fan Rise in Air Temperature",
1087 : Constant::Units::deltaC,
1088 2378 : fan->deltaTemp,
1089 : OutputProcessor::TimeStepType::System,
1090 : OutputProcessor::StoreType::Average,
1091 2378 : fan->Name);
1092 4756 : SetupOutputVariable(state,
1093 : "Fan Heat Gain to Air",
1094 : Constant::Units::W,
1095 2378 : fan->powerLossToAir,
1096 : OutputProcessor::TimeStepType::System,
1097 : OutputProcessor::StoreType::Average,
1098 2378 : fan->Name);
1099 4756 : SetupOutputVariable(state,
1100 : "Fan Electricity Energy",
1101 : Constant::Units::J,
1102 2378 : fan->totalEnergy,
1103 : OutputProcessor::TimeStepType::System,
1104 : OutputProcessor::StoreType::Sum,
1105 2378 : fan->Name,
1106 : Constant::eResource::Electricity,
1107 : OutputProcessor::Group::HVAC,
1108 : OutputProcessor::EndUseCat::Fans,
1109 : fan->endUseSubcategoryName);
1110 4756 : SetupOutputVariable(state,
1111 : "Fan Air Mass Flow Rate",
1112 : Constant::Units::kg_s,
1113 2378 : fan->outletAirMassFlowRate,
1114 : OutputProcessor::TimeStepType::System,
1115 : OutputProcessor::StoreType::Average,
1116 2378 : fan->Name);
1117 2378 : if (fan->type == HVAC::FanType::Exhaust) {
1118 161 : auto *fanExhaust = dynamic_cast<FanComponent *>(fan);
1119 161 : assert(fanExhaust != nullptr);
1120 161 : if (fanExhaust->balancedFractSched != nullptr) {
1121 64 : SetupOutputVariable(state,
1122 : "Fan Unbalanced Air Mass Flow Rate",
1123 : Constant::Units::kg_s,
1124 32 : fanExhaust->unbalancedOutletMassFlowRate,
1125 : OutputProcessor::TimeStepType::System,
1126 : OutputProcessor::StoreType::Average,
1127 32 : fanExhaust->Name);
1128 64 : SetupOutputVariable(state,
1129 : "Fan Balanced Air Mass Flow Rate",
1130 : Constant::Units::kg_s,
1131 32 : fanExhaust->balancedOutletMassFlowRate,
1132 : OutputProcessor::TimeStepType::System,
1133 : OutputProcessor::StoreType::Average,
1134 32 : fanExhaust->Name);
1135 : }
1136 : }
1137 :
1138 2378 : if (fan->type == HVAC::FanType::OnOff) {
1139 1015 : auto *fanOnOff = dynamic_cast<FanComponent *>(fan);
1140 1015 : assert(fanOnOff != nullptr);
1141 :
1142 2030 : SetupOutputVariable(state,
1143 : "Fan Runtime Fraction",
1144 : Constant::Units::None,
1145 1015 : fanOnOff->runtimeFrac,
1146 : OutputProcessor::TimeStepType::System,
1147 : OutputProcessor::StoreType::Average,
1148 1015 : fanOnOff->Name);
1149 :
1150 1363 : } else if (fan->type == HVAC::FanType::SystemModel) {
1151 231 : auto *fanSystem = dynamic_cast<FanSystem *>(fan);
1152 231 : assert(fanSystem != nullptr);
1153 :
1154 231 : if (fanSystem->speedControl != SpeedControl::Discrete) {
1155 60 : continue;
1156 : }
1157 :
1158 171 : if (fanSystem->numSpeeds == 1) {
1159 296 : SetupOutputVariable(state,
1160 : "Fan Runtime Fraction",
1161 : Constant::Units::None,
1162 148 : fanSystem->runtimeFracAtSpeed[0],
1163 : OutputProcessor::TimeStepType::System,
1164 : OutputProcessor::StoreType::Average,
1165 148 : fanSystem->Name);
1166 : } else {
1167 72 : for (int speedLoop = 0; speedLoop < fanSystem->numSpeeds; ++speedLoop) {
1168 147 : SetupOutputVariable(state,
1169 98 : format("Fan Runtime Fraction Speed {}", speedLoop + 1),
1170 : Constant::Units::None,
1171 49 : fanSystem->runtimeFracAtSpeed[speedLoop],
1172 : OutputProcessor::TimeStepType::System,
1173 : OutputProcessor::StoreType::Average,
1174 49 : fan->Name);
1175 : }
1176 : }
1177 : }
1178 :
1179 : } // for (fan)
1180 :
1181 : bool anyRan;
1182 601 : EMSManager::ManageEMS(state, EMSManager::EMSCallFrom::ComponentGetInput, anyRan, ObjexxFCL::Optional_int_const());
1183 :
1184 601 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
1185 896 : for (auto *fan : df->fans) {
1186 831 : SetupEMSInternalVariable(state, "Fan Maximum Mass Flow Rate", fan->Name, "[kg/s]", fan->maxAirMassFlowRate);
1187 831 : SetupEMSActuator(state, "Fan", fan->Name, "Fan Air Mass Flow Rate", "[kg/s]", fan->EMSMaxMassFlowOverrideOn, fan->EMSAirMassFlowValue);
1188 831 : SetupEMSInternalVariable(state, "Fan Nominal Pressure Rise", fan->Name, "[Pa]", fan->deltaPress);
1189 831 : SetupEMSActuator(state, "Fan", fan->Name, "Fan Pressure Rise", "[Pa]", fan->EMSPressureOverrideOn, fan->EMSPressureValue);
1190 831 : SetupEMSInternalVariable(state, "Fan Nominal Total Efficiency", fan->Name, "[fraction]", fan->totalEff);
1191 831 : SetupEMSActuator(state, "Fan", fan->Name, "Fan Total Efficiency", "[fraction]", fan->EMSTotalEffOverrideOn, fan->EMSTotalEffValue);
1192 831 : SetupEMSActuator(
1193 831 : state, "Fan", fan->Name, "Fan Autosized Air Flow Rate", "[m3/s]", fan->EMSMaxAirFlowRateOverrideOn, fan->EMSMaxAirFlowRateValue);
1194 : }
1195 : }
1196 601 : } // GetFanInput()
1197 :
1198 : // End of Get Input subroutines for the HB Module
1199 : //******************************************************************************
1200 :
1201 : // Beginning Initialization Section of the Module
1202 : //******************************************************************************
1203 :
1204 162659141 : void FanComponent::init(EnergyPlusData &state)
1205 : {
1206 :
1207 : // SUBROUTINE INFORMATION:
1208 : // AUTHOR Richard J. Liesen
1209 : // DATE WRITTEN February 1998
1210 :
1211 : // PURPOSE OF THIS SUBROUTINE:
1212 : // This subroutine is for initializations of the Fan Components.
1213 :
1214 : // METHODOLOGY EMPLOYED:
1215 : // Uses the status flags to trigger initializations.
1216 162659141 : auto &df = state.dataFans;
1217 :
1218 : // need to check all fans to see if they are on Zone Equipment List or issue warning
1219 162659141 : if (!state.dataFans->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
1220 564 : state.dataFans->ZoneEquipmentListChecked = true;
1221 2793 : for (auto *fan : df->fans) {
1222 2229 : if (fan->type != HVAC::FanType::Exhaust) {
1223 2068 : continue;
1224 : }
1225 161 : if (DataZoneEquipment::CheckZoneEquipmentList(state, HVAC::fanTypeNames[(int)fan->type], fan->Name)) {
1226 161 : continue;
1227 : }
1228 0 : ShowSevereError(state,
1229 0 : format("InitFans: Fan=[{},{}] is not on any ZoneHVAC:EquipmentList. It will not be simulated.",
1230 0 : HVAC::fanTypeNames[(int)fan->type],
1231 0 : fan->Name));
1232 : }
1233 : }
1234 :
1235 162659141 : if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
1236 :
1237 2144 : set_size(state);
1238 : // Set the loop cycling flag
1239 2144 : if (type == HVAC::FanType::OnOff) {
1240 1013 : if (state.dataSize->CurSysNum > 0) {
1241 407 : state.dataAirLoop->AirLoopControlInfo(state.dataSize->CurSysNum).CyclingFan = true;
1242 : }
1243 : }
1244 :
1245 2144 : sizingFlag = false;
1246 : }
1247 :
1248 : // Do the Begin Environment initializations
1249 162659141 : if (state.dataGlobal->BeginEnvrnFlag && envrnFlag) {
1250 :
1251 : // For all Fan inlet nodes convert the Volume flow to a mass flow
1252 13464 : rhoAirStdInit = state.dataEnvrn->StdRhoAir;
1253 :
1254 : // Change the Volume Flow Rates to Mass Flow Rates
1255 :
1256 13464 : maxAirMassFlowRate = maxAirFlowRate * rhoAirStdInit;
1257 13464 : if (minAirFracMethod == MinFlowFracMethod::MinFrac) {
1258 12114 : minAirFlowRate = maxAirFlowRate * minFrac;
1259 12114 : minAirMassFlowRate = minAirFlowRate * rhoAirStdInit;
1260 1350 : } else if (minAirFracMethod == MinFlowFracMethod::FixedMin) {
1261 1350 : minAirFlowRate = fixedMin;
1262 1350 : minAirMassFlowRate = minAirFlowRate * rhoAirStdInit;
1263 : }
1264 13464 : if (nightVentPerfNum > 0) {
1265 0 : df->NightVentPerf(nightVentPerfNum).MaxAirMassFlowRate = df->NightVentPerf(nightVentPerfNum).MaxAirFlowRate * rhoAirStdInit;
1266 : }
1267 :
1268 : // Init the Node Control variables
1269 13464 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMax = maxAirMassFlowRate;
1270 : // According to the IO Ref guide:
1271 : // "Note that this field is only used to calculate the fan power.
1272 : // This field does not enforce the system air flow rate during simulation"
1273 : // Node(OutNode).MassFlowRateMin = fan.MinAirMassFlowRate;
1274 :
1275 : // Initialize all report variables to a known state at beginning of simulation
1276 13464 : totalPower = 0.0;
1277 13464 : deltaTemp = 0.0;
1278 13464 : powerLossToAir = 0.0;
1279 13464 : totalEnergy = 0.0;
1280 :
1281 13464 : envrnFlag = false;
1282 : }
1283 :
1284 162659141 : if (!state.dataGlobal->BeginEnvrnFlag) {
1285 162287510 : envrnFlag = true;
1286 : }
1287 :
1288 : // Do the following initializations (every time step): This should be the info from
1289 : // the previous components outlets or the node data in this section.
1290 :
1291 : // Do a check and make sure that the max and min available(control) flow is
1292 : // between the physical max and min for the Fan while operating.
1293 :
1294 162659141 : auto const &inletNode = state.dataLoopNodes->Node(inletNodeNum);
1295 162659141 : auto const &outletNode = state.dataLoopNodes->Node(outletNodeNum);
1296 :
1297 162659141 : massFlowRateMaxAvail = min(outletNode.MassFlowRateMax, inletNode.MassFlowRateMaxAvail);
1298 162659141 : massFlowRateMinAvail = min(max(outletNode.MassFlowRateMin, inletNode.MassFlowRateMinAvail), inletNode.MassFlowRateMaxAvail);
1299 :
1300 : // Load the node data in this section for the component simulation
1301 : // First need to make sure that the MassFlowRate is between the max and min avail.
1302 162659141 : if (type != HVAC::FanType::Exhaust) {
1303 160764153 : inletAirMassFlowRate = min(inletNode.MassFlowRate, massFlowRateMaxAvail);
1304 160764153 : inletAirMassFlowRate = max(inletAirMassFlowRate, massFlowRateMinAvail);
1305 : } else { // zone exhaust fans
1306 1894988 : massFlowRateMaxAvail = maxAirMassFlowRate;
1307 1894988 : massFlowRateMinAvail = 0.0;
1308 1894988 : if (flowFracSched != nullptr) { // modulate flow
1309 1894988 : inletAirMassFlowRate = massFlowRateMaxAvail * flowFracSched->getCurrentVal();
1310 1894988 : inletAirMassFlowRate = max(0.0, inletAirMassFlowRate);
1311 : } else { // always run at max
1312 0 : inletAirMassFlowRate = massFlowRateMaxAvail;
1313 : }
1314 1894988 : if (EMSMaxMassFlowOverrideOn) {
1315 0 : inletAirMassFlowRate = min(EMSAirMassFlowValue, massFlowRateMaxAvail);
1316 : }
1317 : }
1318 :
1319 : // Then set the other conditions
1320 162659141 : inletAirTemp = inletNode.Temp;
1321 162659141 : inletAirHumRat = inletNode.HumRat;
1322 162659141 : inletAirEnthalpy = inletNode.Enthalpy;
1323 162659141 : }
1324 :
1325 2145 : void FanComponent::set_size(EnergyPlusData &state)
1326 : {
1327 :
1328 : // SUBROUTINE INFORMATION:
1329 : // AUTHOR Fred Buhl
1330 : // DATE WRITTEN September 2001
1331 : // MODIFIED Craig Wray August 2010 - added fan, belt, motor, and VFD component sizing
1332 : // August 2013 Daeho Kang, add component sizing table entries
1333 :
1334 : // PURPOSE OF THIS SUBROUTINE:
1335 : // This subroutine is for sizing fans for which flow rates have not been
1336 : // specified in the input, or when fan component sizes have not been specified
1337 :
1338 : // METHODOLOGY EMPLOYED:
1339 : // Obtains flow rates from the zone or system sizing arrays.
1340 :
1341 : // SUBROUTINE PARAMETER DEFINITIONS:
1342 : static constexpr std::string_view routineName = "FanComponent::set_size()"; // include trailing blank space
1343 :
1344 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1345 2145 : bool _bPRINT = true; // TRUE if sizing is reported to output (eio)
1346 :
1347 2145 : std::string SizingString = sizingPrefix + " [m3/s]";
1348 :
1349 2145 : Real64 _tempFlow = maxAirFlowRate; // autosized flow rate of fan [m3/s]
1350 2145 : state.dataSize->DataAutosizable = maxAirFlowRateIsAutosized;
1351 2145 : state.dataSize->DataEMSOverrideON = EMSMaxAirFlowRateOverrideOn;
1352 2145 : state.dataSize->DataEMSOverride = EMSMaxAirFlowRateValue;
1353 2145 : airLoopNum = state.dataSize->CurSysNum;
1354 :
1355 2145 : bool errorsFound = false;
1356 2145 : SystemAirFlowSizer sizerSystemAirFlow;
1357 2145 : sizerSystemAirFlow.overrideSizingString(SizingString);
1358 2145 : sizerSystemAirFlow.initializeWithinEP(state, HVAC::fanTypeNames[(int)type], Name, _bPRINT, routineName);
1359 2145 : maxAirFlowRate = sizerSystemAirFlow.size(state, _tempFlow, errorsFound);
1360 :
1361 2145 : state.dataSize->DataAutosizable = true;
1362 2145 : state.dataSize->DataEMSOverrideON = false;
1363 2145 : state.dataSize->DataEMSOverride = 0.0;
1364 :
1365 2145 : Real64 _volFlow = maxAirFlowRate; // Maximum volumetric airflow through fan [m3/s at standard conditions]
1366 2145 : if (type == HVAC::FanType::ComponentModel) {
1367 : // Get air density at standard conditions and get mass airflow through fan
1368 : // From WeatherManager:
1369 : // StdBaroPress=(101.325d0*(1.0d0-2.25577d-05*WeatherFileElevation)**5.2559d0)*1000.d0
1370 : // StdRhoAir=PsyRhoAirFnPbTdbW(StdBaroPress,20,0)
1371 : // From PsychRoutines:
1372 : // w=MAX(dw,1.0d-5)
1373 : // rhoair = pb/(287.d0*(tdb+Constant::Kelvin())*(1.0d0+1.6077687d0*w))
1374 5 : Real64 _rhoAir = state.dataEnvrn->StdRhoAir;
1375 :
1376 : // Adjust max fan volumetric airflow using fan sizing factor
1377 5 : _volFlow *= sizingFactor; //[m3/s at standard conditions]
1378 :
1379 : // Calculate max fan static pressure rise using max fan volumetric flow, std air density, air-handling system characteristics,
1380 : // and Sherman-Wray system curve model (assumes static pressure surrounding air distribution system is zero)
1381 5 : Real64 _ductStaticPress = Curve::CurveValue(state, pressResetCurveNum, _volFlow); // Duct static pressure setpoint [Pa]
1382 5 : Real64 _deltaPressTot = Curve::CurveValue(state, pressRiseCurveNum, _volFlow, _ductStaticPress); // Max fan total pressure rise [Pa]
1383 5 : Real64 _outletVelPress = 0.5 * _rhoAir * pow_2(_volFlow / outletArea); // Max fan outlet velocity pressure [Pa]
1384 : // Outlet velocity pressure cannot exceed total pressure rise
1385 5 : _outletVelPress = min(_outletVelPress, _deltaPressTot);
1386 5 : deltaPress = _deltaPressTot - _outletVelPress; // Max fan static pressure rise [Pa]
1387 :
1388 : // Calculate max fan air power using volumetric flow abd corresponding fan static pressure rise
1389 5 : airPower = _volFlow * deltaPress; //[W]
1390 :
1391 : // Calculate fan wheel efficiency at max fan volumetric flow and corresponding fan static pressure rise,
1392 : // using fan characteristics and Wray dimensionless fan static efficiency model
1393 5 : Real64 _eulerNum = (deltaPress * pow_4(wheelDia)) / (_rhoAir * pow_2(_volFlow)); //[-]
1394 5 : Real64 _normalizedEulerNum = std::log10(_eulerNum / eulerMaxEff);
1395 5 : wheelEff = (_normalizedEulerNum <= 0.0) ? Curve::CurveValue(state, plTotalEffNormCurveNum, _normalizedEulerNum)
1396 1 : : Curve::CurveValue(state, plTotalEffStallCurveNum, _normalizedEulerNum);
1397 :
1398 5 : wheelEff = max(wheelEff * maxEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
1399 :
1400 : // Calculate max fan shaft power using fan air power and fan efficiency
1401 : // at max fan static pressure rise and max fan volumetric flow
1402 5 : shaftPower = (airPower / wheelEff); //[W]
1403 5 : shaftPowerMax = shaftPower; //[W]
1404 :
1405 : // Calculate fan shaft speed, motor speed, and fan torque using Wray dimensionless fan airflow model
1406 5 : Real64 _dimFlow = (_normalizedEulerNum <= 0.0) ? // Fan dimensionless airflow [-]
1407 4 : Curve::CurveValue(state, dimFlowNormCurveNum, _normalizedEulerNum)
1408 1 : : Curve::CurveValue(state, dimFlowStallCurveNum, _normalizedEulerNum); //[-]
1409 :
1410 5 : Real64 _speedRadS = _volFlow / (_dimFlow * maxDimFlow * pow_3(wheelDia)); //[rad/s]
1411 5 : fanSpeed = _speedRadS * 9.549296586; //[rpm, conversion factor is 30/PI]
1412 :
1413 5 : if (pulleyDiaRatio == DataSizing::AutoSize) {
1414 : // WRITE(*,*) 'Autosizing pulley drive ratio'
1415 5 : pulleyDiaRatio = fanSpeed / motorMaxSpeed; //[-]
1416 : }
1417 :
1418 : // For direct-drive, should have PulleyDiaRatio = 1
1419 5 : Real64 _motorSpeed = fanSpeed / pulleyDiaRatio; //[rpm]
1420 :
1421 : // Check for inconsistent drive ratio and motor speed, and report design fan speed with warning
1422 5 : if (_motorSpeed > (motorMaxSpeed + 1.e-5)) {
1423 0 : ShowWarningError(state,
1424 0 : format("Drive ratio for {}: {} is too low at design conditions -- check motor speed and drive ratio inputs",
1425 0 : HVAC::fanTypeNames[(int)type],
1426 0 : Name));
1427 0 : ShowContinueError(state, format("...Design fan speed [rev/min]: {:.2R}", fanSpeed));
1428 : }
1429 :
1430 5 : fanTorque = shaftPower / _speedRadS; //[N-m]
1431 :
1432 5 : if (beltMaxTorque == DataSizing::AutoSize) {
1433 : // WRITE(*,*) 'Autosizing fan belt'
1434 5 : beltMaxTorque = fanTorque; //[N-m]
1435 : }
1436 : // Adjust max belt torque using belt sizing factor
1437 5 : beltMaxTorque *= beltSizingFactor; //[N-m]
1438 :
1439 : // Check for undersized belt and report design size with warning
1440 5 : if (fanTorque > (beltMaxTorque + 1.e-5)) {
1441 0 : ShowWarningError(state,
1442 0 : format("Belt for {}: {} is undersized at design conditions -- check belt inputs", HVAC::fanTypeNames[(int)type], Name));
1443 0 : ShowContinueError(state, format("...Design belt output torque (without oversizing) [Nm]: {:.2R}", fanTorque));
1444 : }
1445 :
1446 : // Calculate belt max efficiency using correlations and coefficients based on AMCA data
1447 : // Direct-drive is represented using curve coefficients such that "belt" max eff and PL eff = 1.0
1448 5 : Real64 _XbeltMax = std::log(shaftPowerMax / 746.0); // Natural log of belt output power in hp
1449 5 : beltMaxEff = (beltMaxEffCurveNum != 0) ? std::exp(Curve::CurveValue(state, beltMaxEffCurveNum, _XbeltMax)) : 1.0;
1450 :
1451 : // Calculate belt part-load drive efficiency and input power using correlations and coefficients based on ACEEE data
1452 5 : Real64 _torqueRatio = fanTorque / beltMaxTorque; //[-]
1453 : Real64 _plBeltEff; // Belt normalized (part-load) efficiency [-]
1454 5 : if ((_torqueRatio <= beltTorqueTrans) && (plBeltEffReg1CurveNum != 0)) {
1455 0 : _plBeltEff = Curve::CurveValue(state, plBeltEffReg1CurveNum, _torqueRatio); //[-]
1456 5 : } else if ((_torqueRatio > beltTorqueTrans) && (_torqueRatio <= 1.0) && (plBeltEffReg2CurveNum != 0)) {
1457 5 : _plBeltEff = Curve::CurveValue(state, plBeltEffReg2CurveNum, _torqueRatio); //[-]
1458 0 : } else if ((_torqueRatio > 1.0) && (plBeltEffReg3CurveNum != 0)) {
1459 0 : _plBeltEff = Curve::CurveValue(state, plBeltEffReg3CurveNum, _torqueRatio); //[-]
1460 : } else {
1461 0 : _plBeltEff = 1.0; // Direct drive or no curve specified - use constant efficiency
1462 : }
1463 5 : beltEff = beltMaxEff * _plBeltEff; //[-]
1464 5 : beltEff = max(beltEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
1465 5 : beltInputPower = shaftPower / beltEff; //[W]
1466 :
1467 5 : if (motorMaxOutPower == DataSizing::AutoSize) {
1468 : // WRITE(*,*) 'Autosizing fan motor'
1469 5 : motorMaxOutPower = beltInputPower;
1470 : }
1471 : // Adjust max motor output power using motor sizing factor
1472 5 : motorMaxOutPower *= motorSizingFactor; //[W]
1473 :
1474 : // Check for undersized motor and report design size with warning
1475 5 : if (beltInputPower > (motorMaxOutPower + 1.e-5)) {
1476 0 : ShowWarningError(
1477 0 : state, format("Motor for {}: {} is undersized at design conditions -- check motor inputs", HVAC::fanTypeNames[(int)type], Name));
1478 0 : ShowContinueError(state, format("...Design motor output power (without oversizing) [W]: {:.2R}", beltInputPower));
1479 : }
1480 :
1481 : // Calculate motor max efficiency using correlations and coefficients based on MotorMaster+ data
1482 5 : Real64 _XmotorMax = std::log(motorMaxOutPower / 746.0); // Natural log of motor output power in hp
1483 5 : motorMaxEff = (motorMaxEffCurveNum != 0) ? Curve::CurveValue(state, motorMaxEffCurveNum, _XmotorMax) : 1.0;
1484 :
1485 : // Calculate motor part-load efficiency and input power using correlations and coefficients based on MotorMaster+ data
1486 5 : Real64 _motorOutPowerRatio = beltInputPower / motorMaxOutPower; //[-]
1487 : // Motor normalized (part-load) efficiency [-]
1488 5 : Real64 _plMotorEff = (plMotorEffCurveNum != 0) ? Curve::CurveValue(state, plMotorEffCurveNum, _motorOutPowerRatio) : 1.0;
1489 :
1490 5 : motorEff = max(motorMaxEff * _plMotorEff, 0.01);
1491 :
1492 : // Calculate motor input power using belt input power and motor efficiency
1493 5 : motorInputPower = beltInputPower / motorEff; //[W]
1494 :
1495 : // Calculate max VFD efficiency and input power using correlations and coefficients based on VFD type
1496 5 : if ((vfdEffType == VFDEffType::Speed) && (vfdEffCurveNum != 0)) {
1497 0 : Real64 _vfdSpeedRatio = _motorSpeed / motorMaxSpeed; // Ratio of motor speed to motor max speed [-]
1498 0 : vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdSpeedRatio);
1499 5 : } else if ((vfdEffType == VFDEffType::Power) && (vfdEffCurveNum != 0)) {
1500 5 : if (vfdMaxOutPower == DataSizing::AutoSize) {
1501 : // WRITE(*,*) 'Autosizing fan VFD'
1502 5 : vfdMaxOutPower = motorInputPower;
1503 : }
1504 : // Adjust max VFD output power using VFD sizing factor
1505 5 : vfdMaxOutPower *= vfdSizingFactor; //[W]
1506 :
1507 : // Check for undersized VFD and report design size with warning
1508 5 : if (motorInputPower > (vfdMaxOutPower + 1.e-5)) {
1509 0 : ShowWarningError(
1510 0 : state, format("VFD for {}: {} is undersized at design conditions -- check VFD inputs", HVAC::fanTypeNames[(int)type], Name));
1511 0 : ShowContinueError(state, format("...Design VFD output power (without oversizing) [W]: {:.2R}", motorInputPower));
1512 : }
1513 :
1514 5 : Real64 _vfdOutPowerRatio = motorInputPower / vfdMaxOutPower; // Ratio of VFD output power to max VFD output power [-]
1515 5 : vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdOutPowerRatio);
1516 5 : } else {
1517 : // No curve specified - use constant efficiency
1518 0 : vfdMaxOutPower = 0.0;
1519 0 : vfdEff = 0.97;
1520 : }
1521 :
1522 5 : vfdEff = max(vfdEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
1523 :
1524 : // Calculate VFD "rated" input power using motor input power and VFD efficiency
1525 5 : Real64 _ratedPower = motorInputPower / vfdEff; //[W]
1526 :
1527 : // Calculate combined fan system efficiency: includes fan, belt, motor, and VFD
1528 : // Equivalent to fan%FanAirPower / fan%FanPower
1529 5 : totalEff = wheelEff * beltEff * motorEff * vfdEff;
1530 :
1531 : // Report fan, belt, motor, and VFD characteristics at design condition to .eio file
1532 5 : std::string_view fanTypeName = HVAC::fanTypeNames[(int)type];
1533 :
1534 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Airflow [m3/s]", _volFlow);
1535 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Static Pressure Rise [Pa]", deltaPress);
1536 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Shaft Power [W]", shaftPower);
1537 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Motor Output Power [W]", motorMaxOutPower);
1538 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design VFD Output Power [W]", vfdMaxOutPower);
1539 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Rated Power [W]", _ratedPower);
1540 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Drive Ratio []", pulleyDiaRatio);
1541 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Belt Output Torque [Nm]", beltMaxTorque);
1542 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Efficiency []", wheelEff);
1543 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Maximum Belt Efficiency []", beltMaxEff);
1544 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Belt Efficiency []", beltEff);
1545 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Maximum Motor Efficiency []", motorMaxEff);
1546 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Motor Efficiency []", motorEff);
1547 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design VFD Efficiency []", vfdEff);
1548 5 : BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Combined Efficiency []", totalEff);
1549 : } // End fan component sizing
1550 :
1551 : // Rearrange order to match table and use FanVolFlow to calculate RatedPower
1552 : // ALSO generates values if Component Model fan, for which DeltaPress and FanEff vary with flow
1553 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanType, Name, HVAC::fanTypeNames[(int)type]);
1554 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanTotEff, Name, totalEff);
1555 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanDeltaP, Name, deltaPress);
1556 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanVolFlow, Name, _volFlow);
1557 2145 : Real64 _ratedPower = _volFlow * deltaPress / totalEff; // total fan power
1558 2145 : BaseSizer::reportSizerOutput(state, HVAC::fanTypeNames[(int)type], Name, "Design Electric Power Consumption [W]", _ratedPower);
1559 2145 : if (type != HVAC::FanType::ComponentModel) {
1560 2140 : designPointFEI = FanSystem::report_fei(state, _volFlow, _ratedPower, deltaPress);
1561 : }
1562 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwr, Name, _ratedPower);
1563 2145 : if (_volFlow != 0.0) {
1564 2136 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwrPerFlow, Name, _ratedPower / _volFlow);
1565 : }
1566 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorIn, Name, motorInAirFrac);
1567 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEndUse, Name, endUseSubcategoryName);
1568 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEnergyIndex, Name, designPointFEI);
1569 :
1570 : // Std 229 Fans (Fans.cc)
1571 4290 : OutputReportPredefined::PreDefTableEntry(
1572 2145 : state, state.dataOutRptPredefined->pdchFanPurpose, Name, "N/A"); // fan.FanType); // purpose? not the same
1573 4290 : OutputReportPredefined::PreDefTableEntry(state,
1574 2145 : state.dataOutRptPredefined->pdchFanAutosized,
1575 : Name,
1576 2145 : maxAirFlowRateIsAutosized ? "Yes" : "No"); // autosizable vs. autosized equivalent?
1577 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorEff, Name, motorEff);
1578 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorHeatToZoneFrac, Name, 0.0);
1579 2145 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorHeatZone, Name, "N/A");
1580 2145 : if (airLoopNum == 0) {
1581 969 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, "N/A");
1582 1176 : } else if (airLoopNum <= state.dataHVACGlobal->NumPrimaryAirSys) {
1583 2352 : OutputReportPredefined::PreDefTableEntry(
1584 2352 : state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, state.dataAirSystemsData->PrimaryAirSystems(airLoopNum).Name);
1585 : } else {
1586 0 : OutputReportPredefined::PreDefTableEntry(
1587 : state,
1588 0 : state.dataOutRptPredefined->pdchFanAirLoopName,
1589 : Name,
1590 0 : state.dataAirLoopHVACDOAS->airloopDOAS[airLoopNum - state.dataHVACGlobal->NumPrimaryAirSys - 1].Name);
1591 : }
1592 :
1593 2145 : if (nightVentPerfNum > 0) {
1594 0 : if (state.dataFans->NightVentPerf(nightVentPerfNum).MaxAirFlowRate == DataSizing::AutoSize) {
1595 0 : state.dataFans->NightVentPerf(nightVentPerfNum).MaxAirFlowRate = maxAirFlowRate;
1596 : }
1597 : }
1598 :
1599 : // Now that sizing is done, do check if the design point of fan is covered in the fault Fan Curve
1600 2145 : if (faultyFilterFlag) {
1601 1 : auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
1602 :
1603 : // Check fault availability schedules
1604 1 : if (!fault.CheckFaultyAirFilterFanCurve(state)) {
1605 0 : ShowSevereError(state, format("FaultModel:Fouling:AirFilter = \"{}\"", fault.Name));
1606 0 : ShowContinueError(state,
1607 0 : format("Invalid Fan Curve Name = \"{}\" does not cover ", state.dataCurveManager->curves(fault.fanCurveNum)->Name));
1608 0 : ShowContinueError(state, format("the operational point of Fan {}", Name));
1609 0 : ShowFatalError(state, format("SizeFan: Invalid FaultModel:Fouling:AirFilter={}", fault.Name));
1610 : }
1611 : }
1612 2145 : } // FanComponent::set_size()
1613 :
1614 12136029 : void FanComponent::simulateConstant(EnergyPlusData &state)
1615 : {
1616 :
1617 : // SUBROUTINE INFORMATION:
1618 : // AUTHOR Unknown
1619 : // DATE WRITTEN Unknown
1620 : // MODIFIED Brent Griffith, May 2009, added EMS override
1621 : // Rongpeng Zhang, April 2015, added faulty fan operations due to fouling air filters
1622 :
1623 : // PURPOSE OF THIS SUBROUTINE:
1624 : // This subroutine simulates the simple constant volume fan.
1625 :
1626 : // METHODOLOGY EMPLOYED:
1627 : // Converts design pressure rise and efficiency into fan power and temperature rise
1628 : // Constant fan pressure rise is assumed.
1629 :
1630 : // REFERENCES:
1631 : // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
1632 :
1633 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1634 : Real64 _deltaPress; // [N/m2]
1635 : Real64 _totalEff;
1636 : Real64 _motorInAirFrac;
1637 : Real64 _motorEff;
1638 : Real64 _shaftPower; // power delivered to fan shaft
1639 :
1640 12136029 : if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
1641 0 : auto const &nightVentPerf = state.dataFans->NightVentPerf(nightVentPerfNum);
1642 0 : _deltaPress = nightVentPerf.DeltaPress;
1643 0 : _totalEff = nightVentPerf.FanEff;
1644 0 : _motorEff = nightVentPerf.MotEff;
1645 0 : _motorInAirFrac = nightVentPerf.MotInAirFrac;
1646 : } else {
1647 12136029 : _deltaPress = deltaPress;
1648 12136029 : _totalEff = totalEff;
1649 12136029 : _motorEff = motorEff;
1650 12136029 : _motorInAirFrac = motorInAirFrac;
1651 : }
1652 :
1653 : // For a Constant Volume Simple Fan the Max Flow Rate is the Flow Rate for the fan
1654 12136029 : Real64 _rhoAir = rhoAirStdInit;
1655 12136029 : Real64 _massFlow = inletAirMassFlowRate;
1656 :
1657 : // Faulty fan operations
1658 : // Update MassFlow & DeltaPress if there are fouling air filters corresponding to the fan
1659 12136029 : if (faultyFilterFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) && (!state.dataGlobal->KickOffSimulation)) {
1660 0 : auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
1661 : // Check fault availability schedules
1662 0 : if (fault.availSched->getCurrentVal() > 0.0) {
1663 : // Decrease of the Fan Design Volume Flow Rate [m3/sec]
1664 0 : Real64 _fanDesignFlowRateDec = CalFaultyFanAirFlowReduction(
1665 0 : state, Name, maxAirFlowRate, deltaPress, (fault.pressFracSched->getCurrentVal() - 1) * deltaPress, fault.fanCurveNum);
1666 :
1667 : // Update MassFlow & DeltaPress of the fan
1668 0 : _massFlow = min(_massFlow, maxAirMassFlowRate - _fanDesignFlowRateDec * _rhoAir);
1669 0 : _deltaPress = fault.pressFracSched->getCurrentVal() * deltaPress;
1670 : }
1671 : }
1672 :
1673 : // EMS overwrite MassFlow, DeltaPress, and FanEff
1674 12136029 : if (EMSMaxMassFlowOverrideOn) {
1675 0 : _massFlow = EMSAirMassFlowValue;
1676 : }
1677 12136029 : if (EMSPressureOverrideOn) {
1678 14258 : _deltaPress = EMSPressureValue;
1679 : }
1680 12136029 : if (EMSTotalEffOverrideOn) {
1681 0 : _totalEff = EMSTotalEffValue;
1682 : }
1683 :
1684 12136029 : _massFlow = min(_massFlow, maxAirMassFlowRate);
1685 12136029 : _massFlow = max(_massFlow, minAirMassFlowRate);
1686 :
1687 : // Determine the Fan Schedule for the Time step
1688 12136029 : if ((availSched->getCurrentVal() > 0.0 || state.dataHVACGlobal->TurnFansOn) && !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0) {
1689 : // Fan is operating
1690 10142380 : totalPower = max(0.0, _massFlow * _deltaPress / (_totalEff * _rhoAir)); // total fan power
1691 10142380 : _shaftPower = _motorEff * totalPower; // power delivered to shaft
1692 10142380 : powerLossToAir = _shaftPower + (totalPower - _shaftPower) * _motorInAirFrac;
1693 10142380 : outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
1694 : // This fan does not change the moisture or Mass Flow across the component
1695 10142380 : outletAirHumRat = inletAirHumRat;
1696 10142380 : outletAirMassFlowRate = _massFlow;
1697 10142380 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
1698 :
1699 : } else {
1700 : // Fan is off and not operating no power consumed and mass flow rate.
1701 1993649 : totalPower = 0.0;
1702 1993649 : powerLossToAir = 0.0;
1703 1993649 : outletAirMassFlowRate = 0.0;
1704 1993649 : outletAirHumRat = inletAirHumRat;
1705 1993649 : outletAirEnthalpy = inletAirEnthalpy;
1706 1993649 : outletAirTemp = inletAirTemp;
1707 : // Set the Control Flow variables to 0.0 flow when OFF.
1708 1993649 : massFlowRateMaxAvail = 0.0;
1709 1993649 : massFlowRateMinAvail = 0.0;
1710 : }
1711 12136029 : } // FanComponent::simulateConstant
1712 :
1713 25089459 : void FanComponent::simulateVAV(EnergyPlusData &state, ObjexxFCL::Optional<Real64 const> _pressureRise)
1714 : {
1715 :
1716 : // SUBROUTINE INFORMATION:
1717 : // AUTHOR Unknown
1718 : // DATE WRITTEN Unknown
1719 : // MODIFIED Phil Haves
1720 : // Brent Griffith, May 2009 for EMS
1721 : // Rongpeng Zhang, April 2015, added faulty fan operations due to fouling air filters
1722 :
1723 : // PURPOSE OF THIS SUBROUTINE:
1724 : // This subroutine simulates the simple variable volume fan.
1725 :
1726 : // METHODOLOGY EMPLOYED:
1727 : // Converts design pressure rise and efficiency into fan power and temperature rise
1728 : // Constant fan pressure rise is assumed.
1729 : // Uses curves of fan power fraction vs. fan part load to determine fan power at
1730 : // off design conditions.
1731 :
1732 : // REFERENCES:
1733 : // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
1734 :
1735 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1736 : Real64 _deltaPress; // [N/m2 = Pa]
1737 : Real64 _totalEff; // Total fan efficiency - combined efficiency of fan, drive train,
1738 : Real64 _maxAirMassFlowRate;
1739 : Real64 _motorInAirFrac;
1740 : Real64 _motorEff;
1741 : Real64 _partLoadFrac;
1742 :
1743 : // Simple Variable Volume Fan - default values from DOE-2
1744 : // Type of Fan Coeff1 Coeff2 Coeff3 Coeff4 Coeff5
1745 : // INLET VANE DAMPERS 0.35071223 0.30850535 -0.54137364 0.87198823 0.000
1746 : // DISCHARGE DAMPERS 0.37073425 0.97250253 -0.34240761 0.000 0.000
1747 : // VARIABLE SPEED MOTOR 0.0015302446 0.0052080574 1.1086242 -0.11635563 0.000
1748 :
1749 25089459 : if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
1750 0 : auto const &nightVentPerf = state.dataFans->NightVentPerf(nightVentPerfNum);
1751 0 : _deltaPress = nightVentPerf.DeltaPress;
1752 0 : _totalEff = nightVentPerf.FanEff;
1753 0 : _motorEff = nightVentPerf.MotEff;
1754 0 : _motorInAirFrac = nightVentPerf.MotInAirFrac;
1755 0 : _maxAirMassFlowRate = nightVentPerf.MaxAirMassFlowRate;
1756 : } else {
1757 25089459 : if (present(_pressureRise)) {
1758 28609 : _deltaPress = _pressureRise;
1759 : } else {
1760 25060850 : _deltaPress = deltaPress;
1761 : }
1762 25089459 : _totalEff = totalEff;
1763 25089459 : _motorEff = motorEff;
1764 25089459 : _motorInAirFrac = motorInAirFrac;
1765 25089459 : _maxAirMassFlowRate = maxAirMassFlowRate;
1766 : }
1767 :
1768 25089459 : Real64 _rhoAir = rhoAirStdInit;
1769 25089459 : Real64 _massFlow = inletAirMassFlowRate;
1770 25089459 : Real64 _maxAirFlowRate = maxAirFlowRate;
1771 :
1772 : // Faulty fan operations
1773 : // Update MassFlow & DeltaPress if there are fouling air filters corresponding to the fan
1774 25092027 : if (faultyFilterFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) && (!state.dataGlobal->KickOffSimulation) &&
1775 2568 : (!EMSMaxMassFlowOverrideOn)) {
1776 :
1777 2568 : auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
1778 : // Check fault availability schedules
1779 2568 : if (fault.availSched->getCurrentVal() > 0.0) {
1780 : Real64 _fanDesignFlowRateDec = // Decrease of the Fan Design Volume Flow Rate [m3/sec]
1781 7704 : CalFaultyFanAirFlowReduction(
1782 2568 : state, Name, maxAirFlowRate, deltaPress, (fault.pressFracSched->getCurrentVal() - 1) * deltaPress, fault.fanCurveNum);
1783 :
1784 : // Update MassFlow & DeltaPress of the fan
1785 2568 : _maxAirFlowRate = maxAirFlowRate - _fanDesignFlowRateDec;
1786 2568 : _maxAirMassFlowRate = maxAirMassFlowRate - _fanDesignFlowRateDec * _rhoAir;
1787 2568 : _deltaPress = fault.pressFracSched->getCurrentVal() * deltaPress;
1788 : }
1789 : }
1790 :
1791 : // EMS overwrite MassFlow, DeltaPress, and FanEff
1792 25089459 : if (EMSPressureOverrideOn) {
1793 0 : _deltaPress = EMSPressureValue;
1794 : }
1795 25089459 : if (EMSTotalEffOverrideOn) {
1796 0 : _totalEff = EMSTotalEffValue;
1797 : }
1798 25089459 : if (EMSMaxMassFlowOverrideOn) {
1799 0 : _massFlow = EMSAirMassFlowValue;
1800 : }
1801 :
1802 25089459 : _massFlow = min(_massFlow, _maxAirMassFlowRate);
1803 :
1804 : // Determine the Fan Schedule for the Time step
1805 25089459 : if ((availSched->getCurrentVal() > 0.0 || state.dataHVACGlobal->TurnFansOn) && !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0) {
1806 : // Fan is operating - calculate power loss and enthalpy rise
1807 : // fan%FanPower = PartLoadFrac*FullMassFlow*DeltaPress/(FanEff*RhoAir) ! total fan power
1808 : // Calculate and check limits on fraction of system flow
1809 : // unused0909 MaxFlowFrac = 1.0
1810 : // MinFlowFrac is calculated from the ration of the volume flows and is non-dimensional
1811 23027523 : Real64 _minFlowFrac = minAirFlowRate / _maxAirFlowRate;
1812 : // The actual flow fraction is calculated from MassFlow and the MaxVolumeFlow * AirDensity
1813 23027523 : Real64 _flowFracActual = _massFlow / _maxAirMassFlowRate;
1814 :
1815 : // Calculate the part Load Fraction (PH 7/13/03)
1816 :
1817 23027523 : Real64 _flowFracForPower = max(_minFlowFrac, min(_flowFracActual, 1.0)); // limit flow fraction to allowed range
1818 23027523 : if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
1819 0 : _partLoadFrac = 1.0;
1820 : } else {
1821 23027523 : _partLoadFrac = coeffs[0] + coeffs[1] * _flowFracForPower + coeffs[2] * pow_2(_flowFracForPower) + coeffs[3] * pow_3(_flowFracForPower) +
1822 23027523 : coeffs[4] * pow_4(_flowFracForPower);
1823 : }
1824 :
1825 23027523 : totalPower = max(0.0, _partLoadFrac * _maxAirMassFlowRate * _deltaPress / (_totalEff * _rhoAir)); // total fan power (PH 7/13/03)
1826 :
1827 23027523 : Real64 _shaftPower = _motorEff * totalPower; // power delivered to shaft
1828 23027523 : powerLossToAir = _shaftPower + (totalPower - _shaftPower) * _motorInAirFrac;
1829 23027523 : outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
1830 : // This fan does not change the moisture or Mass Flow across the component
1831 23027523 : outletAirHumRat = inletAirHumRat;
1832 23027523 : outletAirMassFlowRate = _massFlow;
1833 23027523 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
1834 :
1835 : // KHL/FB, 2/10/2011. NFP implemented as CR 8338.
1836 : // When fan air flow is less than 10%, the fan power curve is linearized between the 10% to 0% to
1837 : // avoid the unrealistic high temperature rise across the fan.
1838 : // TH, 2/15/2011
1839 : // This change caused diffs for VAV systems when fan runs at less than 10% flow conditions.
1840 : // A potential way to improve is to check the temperature rise across the fan first,
1841 : // if it is too high (say > 20C) then applies the code.
1842 23027523 : Real64 _deltaTAcrossFan = outletAirTemp - inletAirTemp;
1843 23027523 : if (_deltaTAcrossFan > 20.0) {
1844 : // added to address the fan heat issue during low air flow conditions
1845 : Real64 _fanPoweratLowMinimum; // Fan Power at Low Minimum Airflow [W]
1846 : Real64 _partLoadFracatLowMin;
1847 0 : Real64 _minFlowFracLimitFanHeat = 0.10;
1848 0 : if (_flowFracForPower < _minFlowFracLimitFanHeat) {
1849 0 : _partLoadFracatLowMin = coeffs[0] + coeffs[1] * _minFlowFracLimitFanHeat + coeffs[2] * pow_2(_minFlowFracLimitFanHeat) +
1850 0 : coeffs[3] * pow_3(_minFlowFracLimitFanHeat) + coeffs[4] * pow_4(_minFlowFracLimitFanHeat);
1851 0 : _fanPoweratLowMinimum = _partLoadFracatLowMin * _maxAirMassFlowRate * _deltaPress / (_totalEff * _rhoAir);
1852 0 : totalPower = max(0.0, _flowFracForPower * _fanPoweratLowMinimum / _minFlowFracLimitFanHeat);
1853 0 : } else if (_flowFracActual < _minFlowFracLimitFanHeat) {
1854 0 : _partLoadFracatLowMin = coeffs[0] + coeffs[1] * _minFlowFracLimitFanHeat + coeffs[2] * pow_2(_minFlowFracLimitFanHeat) +
1855 0 : coeffs[3] * pow_3(_minFlowFracLimitFanHeat) + coeffs[4] * pow_4(_minFlowFracLimitFanHeat);
1856 0 : _fanPoweratLowMinimum = _partLoadFracatLowMin * _maxAirMassFlowRate * _deltaPress / (_totalEff * _rhoAir);
1857 0 : totalPower = max(0.0, _flowFracActual * _fanPoweratLowMinimum / _minFlowFracLimitFanHeat);
1858 : }
1859 0 : _shaftPower = _motorEff * totalPower; // power delivered to shaft
1860 0 : powerLossToAir = _shaftPower + (totalPower - _shaftPower) * _motorInAirFrac;
1861 0 : outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
1862 : // This fan does not change the moisture or Mass Flow across the component
1863 0 : outletAirHumRat = inletAirHumRat;
1864 0 : outletAirMassFlowRate = _massFlow;
1865 0 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
1866 : }
1867 :
1868 : } else {
1869 : // Fan is off and not operating no power consumed and mass flow rate.
1870 2061936 : totalPower = 0.0;
1871 2061936 : powerLossToAir = 0.0;
1872 2061936 : outletAirMassFlowRate = 0.0;
1873 2061936 : outletAirHumRat = inletAirHumRat;
1874 2061936 : outletAirEnthalpy = inletAirEnthalpy;
1875 2061936 : outletAirTemp = inletAirTemp;
1876 : // Set the Control Flow variables to 0.0 flow when OFF.
1877 2061936 : massFlowRateMaxAvail = 0.0;
1878 2061936 : massFlowRateMinAvail = 0.0;
1879 : }
1880 25089459 : } // FanComponent::SimVAV()
1881 :
1882 123475996 : void FanComponent::simulateOnOff(EnergyPlusData &state, ObjexxFCL::Optional<Real64 const> _speedRatio)
1883 : {
1884 :
1885 : // SUBROUTINE INFORMATION:
1886 : // AUTHOR Unknown
1887 : // DATE WRITTEN Unknown
1888 : // MODIFIED Shirey, May 2001
1889 : // R. Raustad - FSEC, Jan 2009 - added SpeedRatio for multi-speed fans
1890 : // Brent Griffith, May 2009 for EMS
1891 : // Rongpeng Zhang, April 2015, added faulty fan operations due to fouling air filters
1892 :
1893 : // PURPOSE OF THIS SUBROUTINE:
1894 : // This subroutine simulates the simple on/off fan.
1895 :
1896 : // METHODOLOGY EMPLOYED:
1897 : // Converts design pressure rise and efficiency into fan power and temperature rise
1898 : // Constant fan pressure rise is assumed.
1899 : // Uses curves of fan power fraction vs. fan part load to determine fan power at
1900 : // off design conditions.
1901 : // Same as simple (constant volume) fan, except added part-load curve input
1902 :
1903 : // REFERENCES:
1904 : // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
1905 :
1906 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1907 : Real64 _effRatioAtSpeedRatio; // Efficiency ratio at current speed ratio (Curve object)
1908 :
1909 123475996 : Real64 _massFlow = inletAirMassFlowRate;
1910 123475996 : Real64 _maxAirMassFlowRate = maxAirMassFlowRate;
1911 123475996 : Real64 _deltaPress = deltaPress; // [N/m2]
1912 123475996 : Real64 _totalEff = totalEff;
1913 123475996 : Real64 _rhoAir = rhoAirStdInit;
1914 :
1915 : // Faulty fan operations
1916 : // Update MassFlow & DeltaPress if there are fouling air filters corresponding to the fan
1917 123475996 : if (faultyFilterFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) && (!state.dataGlobal->KickOffSimulation) &&
1918 0 : (!EMSMaxMassFlowOverrideOn)) {
1919 :
1920 0 : auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
1921 :
1922 : // Check fault availability schedules
1923 0 : if (fault.availSched->getCurrentVal() > 0.0) {
1924 : Real64 _fanDesignFlowRateDec = // Decrease of the Fan Design Volume Flow Rate [m3/sec]
1925 0 : CalFaultyFanAirFlowReduction(
1926 0 : state, Name, maxAirFlowRate, deltaPress, (fault.pressFracSched->getCurrentVal() - 1) * deltaPress, fault.fanCurveNum);
1927 :
1928 : // Update MassFlow & DeltaPress of the fan
1929 0 : _maxAirMassFlowRate = maxAirMassFlowRate - _fanDesignFlowRateDec * _rhoAir;
1930 0 : _deltaPress = fault.pressFracSched->getCurrentVal() * deltaPress;
1931 : }
1932 : }
1933 :
1934 : // EMS overwrite MassFlow, DeltaPress, and FanEff
1935 123475996 : if (EMSMaxMassFlowOverrideOn) {
1936 0 : _massFlow = EMSAirMassFlowValue;
1937 : }
1938 123475996 : if (EMSPressureOverrideOn) {
1939 0 : _deltaPress = EMSPressureValue;
1940 : }
1941 123475996 : if (EMSTotalEffOverrideOn) {
1942 0 : _totalEff = EMSTotalEffValue;
1943 : }
1944 :
1945 123475996 : _massFlow = min(_massFlow, _maxAirMassFlowRate);
1946 123475996 : _massFlow = max(_massFlow, minAirMassFlowRate);
1947 123475996 : runtimeFrac = 0.0;
1948 :
1949 : // Determine the Fan Schedule for the Time step
1950 229465085 : if ((availSched->getCurrentVal() > 0.0 || state.dataHVACGlobal->TurnFansOn) && !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0 &&
1951 105989089 : maxAirMassFlowRate > 0.0) {
1952 : // The actual flow fraction is calculated from MassFlow and the MaxVolumeFlow * AirDensity
1953 105989089 : Real64 _flowFrac = _massFlow / _maxAirMassFlowRate;
1954 :
1955 : // Calculate the part load ratio, can't be greater than 1
1956 105989089 : Real64 _partLoadRatio = min(1.0, _flowFrac);
1957 : // Fan is operating
1958 105989089 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction <= 0.0) {
1959 0 : ShowRecurringWarningErrorAtEnd(state, "Fan:OnOff, OnOffFanPartLoadFraction <= 0.0, Reset to 1.0", state.dataFans->ErrCount);
1960 0 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // avoid divide by zero or negative PLF
1961 : }
1962 :
1963 105989089 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction < 0.7) {
1964 0 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 0.7; // a warning message is already issued from the DX coils or gas heating coil
1965 : }
1966 :
1967 : // Keep fan runtime fraction between 0.0 and 1.0, and RTF >= PLR
1968 105989089 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0) {
1969 90929399 : runtimeFrac = _partLoadRatio;
1970 : } else {
1971 15059690 : runtimeFrac = max(0.0, min(1.0, _partLoadRatio / state.dataHVACGlobal->OnOffFanPartLoadFraction));
1972 : }
1973 : // The fan speed ratio (passed from parent) determines the fan power according to fan laws
1974 105989089 : if (present(_speedRatio)) {
1975 : // fan%FanPower = MassFlow*DeltaPress/(FanEff*RhoAir*OnOffFanPartLoadFraction)! total fan power
1976 104331253 : totalPower = max(0.0, _maxAirMassFlowRate * runtimeFrac * _deltaPress / (_totalEff * _rhoAir));
1977 :
1978 : // Do not modify fan power calculation unless fan power vs speed ratio curve is used.
1979 104331253 : if (powerRatioAtSpeedRatioCurveNum > 0) {
1980 :
1981 : // adjust RTF to be in line with speed ratio (i.e., MaxAirMassFlowRate is not MAX when SpeedRatio /= 1)
1982 : // PLR = Mdot/MAXFlow => Mdot/(MAXFlow * SpeedRatio), RTF = PLR/PLF => PLR/SpeedRatio/PLF = RTF / SpeedRatio
1983 200468 : if (_speedRatio > 0.0) {
1984 200456 : runtimeFrac = min(1.0, runtimeFrac / _speedRatio);
1985 : }
1986 :
1987 200468 : Real64 _speedRaisedToPower = Curve::CurveValue(state, powerRatioAtSpeedRatioCurveNum, _speedRatio);
1988 200468 : if (_speedRaisedToPower < 0.0) {
1989 0 : if (oneTimePowerRatioCheck && !state.dataGlobal->WarmupFlag) {
1990 0 : ShowSevereError(state, format("{} = {}\"", HVAC::fanTypeNames[(int)type], Name));
1991 0 : ShowContinueError(state, "Error in Fan Power Ratio curve. Curve output less than 0.0.");
1992 0 : ShowContinueError(state, format("Curve output = {:.5T}, fan speed ratio = {:.5T}", _speedRaisedToPower, _speedRatio));
1993 0 : ShowContinueError(state, "Check curve coefficients to ensure proper power ratio as a function of fan speed ratio.");
1994 0 : ShowContinueError(state, "Resetting Fan Power Ratio curve output to 0.0 and the simulation continues.");
1995 0 : ShowContinueErrorTimeStamp(state, "Occurrence info:");
1996 0 : oneTimePowerRatioCheck = false;
1997 : }
1998 0 : _speedRaisedToPower = 0.0;
1999 : }
2000 200468 : if (effRatioCurveNum > 0 && !state.dataGlobal->WarmupFlag) {
2001 30475 : _effRatioAtSpeedRatio = Curve::CurveValue(state, effRatioCurveNum, _speedRatio);
2002 30475 : if (_effRatioAtSpeedRatio < 0.01) {
2003 0 : if (oneTimeEffRatioCheck && !state.dataGlobal->WarmupFlag) {
2004 0 : ShowSevereError(state, format("{} = {}\"", HVAC::fanTypeNames[(int)type], Name));
2005 0 : ShowContinueError(state, "Error in Fan Efficiency Ratio curve. Curve output less than 0.01.");
2006 0 : ShowContinueError(state, format("Curve output = {:.5T}, fan speed ratio = {:.5T}", _effRatioAtSpeedRatio, _speedRatio));
2007 0 : ShowContinueError(state, "Check curve coefficients to ensure proper efficiency ratio as a function of fan speed ratio.");
2008 0 : ShowContinueError(state, "Resetting Fan Efficiency Ratio curve output to 0.01 and the simulation continues.");
2009 0 : ShowContinueErrorTimeStamp(state, "Occurrence info:");
2010 0 : oneTimeEffRatioCheck = false;
2011 : }
2012 0 : _effRatioAtSpeedRatio = 0.01;
2013 : }
2014 : } else {
2015 169993 : _effRatioAtSpeedRatio = 1.0;
2016 : }
2017 200468 : totalPower *= _speedRaisedToPower / _effRatioAtSpeedRatio;
2018 : }
2019 : } else {
2020 1657836 : totalPower = max(0.0, _maxAirMassFlowRate * runtimeFrac * _deltaPress / (_totalEff * _rhoAir)); // total fan power
2021 : }
2022 :
2023 : // OnOffFanPartLoadFraction is passed via DataHVACGlobals from the cooling or heating coil that is
2024 : // requesting the fan to operate in cycling fan/cycling coil mode
2025 105989089 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // reset to 1 in case other on/off fan is called without a part load curve
2026 105989089 : Real64 _shaftPower = motorEff * totalPower; // power delivered to shaft
2027 105989089 : powerLossToAir = _shaftPower + (totalPower - _shaftPower) * motorInAirFrac;
2028 105989089 : outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
2029 : // This fan does not change the moisture or Mass Flow across the component
2030 105989089 : outletAirHumRat = inletAirHumRat;
2031 105989089 : outletAirMassFlowRate = _massFlow;
2032 : // fan%OutletAirTemp = Tin + PowerLossToAir/(MassFlow*PsyCpAirFnW(Win,Tin))
2033 105989089 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
2034 : } else {
2035 : // Fan is off and not operating no power consumed and mass flow rate.
2036 17486907 : totalPower = 0.0;
2037 17486907 : powerLossToAir = 0.0;
2038 17486907 : outletAirMassFlowRate = 0.0;
2039 17486907 : outletAirHumRat = inletAirHumRat;
2040 17486907 : outletAirEnthalpy = inletAirEnthalpy;
2041 17486907 : outletAirTemp = inletAirTemp;
2042 : // Set the Control Flow variables to 0.0 flow when OFF.
2043 17486907 : massFlowRateMaxAvail = 0.0;
2044 17486907 : massFlowRateMinAvail = 0.0;
2045 : }
2046 123475996 : } // FanComponent::simulateOnOff()
2047 :
2048 1894988 : void FanComponent::simulateZoneExhaust(EnergyPlusData &state)
2049 : {
2050 :
2051 : // SUBROUTINE INFORMATION:
2052 : // AUTHOR Fred Buhl
2053 : // DATE WRITTEN Jan 2000
2054 : // MODIFIED Brent Griffith, May 2009 for EMS
2055 : // Brent Griffith, Feb 2013 controls upgrade
2056 :
2057 : // PURPOSE OF THIS SUBROUTINE:
2058 : // This subroutine simulates the Zone Exhaust Fan
2059 :
2060 : // METHODOLOGY EMPLOYED:
2061 : // Converts design pressure rise and efficiency into fan power and temperature rise
2062 : // Constant fan pressure rise is assumed.
2063 :
2064 : // REFERENCES:
2065 : // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
2066 :
2067 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2068 1894988 : bool _fanIsRunning = false; // There seems to be a missing else case below unless false is assumed
2069 :
2070 1894988 : Real64 _deltaPress = deltaPress; // [N/m2]
2071 1894988 : if (EMSPressureOverrideOn) {
2072 0 : _deltaPress = EMSPressureValue;
2073 : }
2074 :
2075 1894988 : Real64 _totalEff = totalEff;
2076 1894988 : if (EMSTotalEffOverrideOn) {
2077 0 : _totalEff = EMSTotalEffValue;
2078 : }
2079 :
2080 : // For a Constant Volume Simple Fan the Max Flow Rate is the Flow Rate for the fan
2081 1894988 : Real64 _Tin = inletAirTemp;
2082 1894988 : Real64 _rhoAir = rhoAirStdInit;
2083 1894988 : Real64 _massFlow = inletAirMassFlowRate;
2084 :
2085 : // When the AvailManagerMode == ExhaustFanCoupledToAvailManagers then the
2086 : // Exhaust Fan is interlocked with air loop availability via global TurnFansOn and TurnFansOff variables.
2087 : // There is now the option to control if user wants to decouple air loop operation and exhaust fan operation
2088 : // (zone air mass balance issues).
2089 :
2090 : // apply controls to determine if operating
2091 1894988 : if (availManagerMode == AvailManagerMode::Coupled) {
2092 1791116 : if (((availSched->getCurrentVal() > 0.0) || state.dataHVACGlobal->TurnFansOn) && !state.dataHVACGlobal->TurnFansOff &&
2093 : _massFlow > 0.0) { // available
2094 1535828 : _fanIsRunning = (minTempLimitSched != nullptr) ? (_Tin >= minTempLimitSched->getCurrentVal()) : true;
2095 : } else {
2096 255288 : _fanIsRunning = false;
2097 : }
2098 :
2099 103872 : } else if (availManagerMode == AvailManagerMode::Decoupled) {
2100 103872 : if (availSched->getCurrentVal() > 0.0 && _massFlow > 0.0) {
2101 99828 : _fanIsRunning = (minTempLimitSched != nullptr) ? (_Tin >= minTempLimitSched->getCurrentVal()) : true;
2102 : } else {
2103 4044 : _fanIsRunning = false;
2104 : }
2105 : }
2106 :
2107 1894988 : if (_fanIsRunning) {
2108 : // Fan is operating
2109 1635656 : totalPower = max(0.0, _massFlow * _deltaPress / (_totalEff * _rhoAir)); // total fan power
2110 1635656 : powerLossToAir = totalPower;
2111 1635656 : outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
2112 : // This fan does not change the moisture or Mass Flow across the component
2113 1635656 : outletAirHumRat = inletAirHumRat;
2114 1635656 : outletAirMassFlowRate = _massFlow;
2115 1635656 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
2116 :
2117 : } else {
2118 : // Fan is off and not operating no power consumed and mass flow rate.
2119 259332 : totalPower = 0.0;
2120 259332 : powerLossToAir = 0.0;
2121 259332 : outletAirMassFlowRate = 0.0;
2122 259332 : outletAirHumRat = inletAirHumRat;
2123 259332 : outletAirEnthalpy = inletAirEnthalpy;
2124 259332 : outletAirTemp = inletAirTemp;
2125 : // Set the Control Flow variables to 0.0 flow when OFF.
2126 259332 : massFlowRateMaxAvail = 0.0;
2127 259332 : massFlowRateMinAvail = 0.0;
2128 259332 : inletAirMassFlowRate = 0.0;
2129 : }
2130 1894988 : } // FanComponent::SimulateZoneExhaust()
2131 :
2132 62669 : void FanComponent::simulateComponentModel(EnergyPlusData &state)
2133 : {
2134 :
2135 : // SUBROUTINE INFORMATION:
2136 : // AUTHOR Craig Wray, LBNL
2137 : // DATE WRITTEN Feb 2010
2138 :
2139 : // PURPOSE OF THIS SUBROUTINE:
2140 : // This subroutine simulates the component model fan.
2141 :
2142 : // METHODOLOGY EMPLOYED:
2143 : // Calculate fan volumetric flow and corresponding fan static pressure rise,
2144 : // using air-handling system characteristics and Sherman-Wray system curve model
2145 : // Calculate fan air power using volumetric flow and fan static pressure rise
2146 : // Calculate fan wheel efficiency using fan volumetric flow, fan static pressure rise,
2147 : // fan characteristics, and Wray dimensionless fan static efficiency model
2148 : // Calculate fan shaft power using fan air power and fan static efficiency
2149 : // Calculate fan shaft speed and torque using Wray dimensionless fan airflow model
2150 : // Calculate belt part-load efficiency using correlations and coefficients based on ACEEE data
2151 : // Calculate belt input power using fan shaft power and belt efficiency
2152 : // Calculate motor part-load efficiency using correlations and coefficients based on MotorMaster+ data
2153 : // Calculate motor input power using belt input power and motor efficiency
2154 : // Calculate VFD efficiency using correlations and coefficients based on DOE data
2155 : // Calculate VFD input power using motor input power and VFD efficiency
2156 : // Calculate combined efficiency of fan, belt, motor, and VFD
2157 : // Calculate air temperature rise due to fan (and belt+motor if in airstream) power entering air-handler airflow
2158 : // Calculate output node conditions
2159 :
2160 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2161 : Real64 _maxAirMassFlowRate; // Fan Max mass airflow [kg/s]
2162 : Real64 _motorInAirFrac; // Fraction of fan power input to airstream
2163 :
2164 : // Local variables
2165 : Real64 _dimFlow; // Fan dimensionless airflow [-]
2166 : Real64 _beltPLEff; // Belt normalized (part-load) efficiency [-]
2167 : Real64 _motorPLEff; // Motor normalized (part-load) efficiency [-]
2168 62669 : Real64 _vfdSpeedRatio(0.0); // Ratio of motor speed to motor max speed [-]
2169 62669 : Real64 _vfdOutPowerRatio(0.0); // Ratio of VFD output power to max VFD output power [-]
2170 :
2171 62669 : if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
2172 0 : _motorInAirFrac = state.dataFans->NightVentPerf(nightVentPerfNum).MotInAirFrac;
2173 0 : _maxAirMassFlowRate = state.dataFans->NightVentPerf(nightVentPerfNum).MaxAirMassFlowRate;
2174 : } else {
2175 62669 : _motorInAirFrac = motorInAirFrac;
2176 62669 : _maxAirMassFlowRate = maxAirMassFlowRate;
2177 : }
2178 :
2179 : // Get air density at standard conditions and get mass airflow through fan
2180 : // From WeatherManager:
2181 : // StdBaroPress=(101.325d0*(1.0d0-2.25577d-05*WeatherFileElevation)**5.2559d0)*1000.d0
2182 : // StdRhoAir=PsyRhoAirFnPbTdbW(StdBaroPress,20,0)
2183 : // From PsychRoutines:
2184 : // w=MAX(dw,1.0d-5)
2185 : // rhoair = pb/(287.d0*(tdb+Constant::Kelvin())*(1.0d0+1.6077687d0*w))
2186 62669 : Real64 _rhoAir = rhoAirStdInit;
2187 62669 : Real64 _massFlow = min(inletAirMassFlowRate, maxAirMassFlowRate);
2188 :
2189 : // IF (fan%EMSMaxMassFlowOverrideOn) MassFlow = fan%EMSAirMassFlowValue
2190 :
2191 : // Determine the Fan Schedule for the Time step
2192 62669 : if ((availSched->getCurrentVal() > 0.0 || state.dataHVACGlobal->TurnFansOn) && !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0) {
2193 : // Fan is operating - calculate fan pressure rise, component efficiencies and power, and also air enthalpy rise
2194 :
2195 : // Calculate fan static pressure rise using fan volumetric flow, std air density, air-handling system characteristics,
2196 : // and Sherman-Wray system curve model (assumes static pressure surrounding air distribution system is zero)
2197 48123 : Real64 _volFlow = _massFlow / _rhoAir; //[m3/s at standard conditions]
2198 48123 : Real64 _ductStaticPress = Curve::CurveValue(state, pressResetCurveNum, _volFlow); // Duct static pressure setpoint [Pa]
2199 48123 : Real64 _deltaPressTot = Curve::CurveValue(state, pressRiseCurveNum, _volFlow, _ductStaticPress); // Fan total pressure rise [Pa]
2200 48123 : Real64 _outletVelPress = 0.5 * _rhoAir * pow_2(_volFlow / outletArea); // Fan outlet velocity pressure [Pa]
2201 : // Outlet velocity pressure cannot exceed total pressure rise
2202 48123 : _outletVelPress = min(_outletVelPress, _deltaPressTot);
2203 48123 : deltaPress = _deltaPressTot - _outletVelPress; // Fan static pressure rise [Pa]
2204 :
2205 : // IF (fan%EMSFanPressureOverrideOn) DeltaPress = fan%EMSFanPressureValue
2206 :
2207 : // Calculate fan static air power using volumetric flow and fan static pressure rise
2208 48123 : airPower = _volFlow * deltaPress; //[W]
2209 :
2210 : // Calculate fan wheel efficiency using fan volumetric flow, fan static pressure rise,
2211 : // fan characteristics, and Wray dimensionless fan static efficiency model
2212 48123 : Real64 _eulerNum = (deltaPress * pow_4(wheelDia)) / (_rhoAir * pow_2(_volFlow)); //[-]
2213 48123 : Real64 _normalizedEulerNum = std::log10(_eulerNum / eulerMaxEff);
2214 48123 : if (_normalizedEulerNum <= 0.0) {
2215 14568 : wheelEff = Curve::CurveValue(state, plTotalEffNormCurveNum, _normalizedEulerNum);
2216 : } else {
2217 33555 : wheelEff = Curve::CurveValue(state, plTotalEffStallCurveNum, _normalizedEulerNum);
2218 : }
2219 48123 : wheelEff *= maxEff; // [-]
2220 48123 : wheelEff = max(wheelEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
2221 :
2222 : // Calculate fan shaft power using fan static air power and fan static efficiency
2223 48123 : shaftPower = airPower / wheelEff; //[W]
2224 :
2225 : // Calculate fan shaft speed, fan torque, and motor speed using Wray dimensionless fan airflow model
2226 48123 : if (_normalizedEulerNum <= 0.0) {
2227 14568 : _dimFlow = Curve::CurveValue(state, dimFlowNormCurveNum, _normalizedEulerNum); //[-]
2228 : } else {
2229 33555 : _dimFlow = Curve::CurveValue(state, dimFlowStallCurveNum, _normalizedEulerNum); //[-]
2230 : }
2231 48123 : Real64 _speedRadS = _volFlow / (_dimFlow * maxDimFlow * pow_3(wheelDia)); //[rad/s]
2232 48123 : fanTorque = shaftPower / _speedRadS; //[N-m]
2233 48123 : fanSpeed = _speedRadS * 9.549296586; //[rpm, conversion factor is 30/PI]
2234 48123 : Real64 _motorSpeed = fanSpeed * pulleyDiaRatio; //[rpm]
2235 :
2236 : // Calculate belt part-load drive efficiency using correlations and coefficients based on ACEEE data
2237 : // Direct-drive is represented using curve coefficients such that "belt" max eff and PL eff = 1.0
2238 48123 : Real64 _torqueRatio = fanTorque / beltMaxTorque; //[-]
2239 48123 : if ((_torqueRatio <= beltTorqueTrans) && (plBeltEffReg1CurveNum != 0)) {
2240 3688 : _beltPLEff = Curve::CurveValue(state, plBeltEffReg1CurveNum, _torqueRatio); //[-]
2241 44435 : } else if ((_torqueRatio > beltTorqueTrans) && (_torqueRatio <= 1.0) && (plBeltEffReg2CurveNum != 0)) {
2242 44435 : _beltPLEff = Curve::CurveValue(state, plBeltEffReg2CurveNum, _torqueRatio); //[-]
2243 0 : } else if ((_torqueRatio > 1.0) && (plBeltEffReg3CurveNum != 0)) {
2244 0 : _beltPLEff = Curve::CurveValue(state, plBeltEffReg3CurveNum, _torqueRatio); //[-]
2245 : } else {
2246 0 : _beltPLEff = 1.0; // Direct drive or no curve specified - use constant efficiency
2247 : }
2248 48123 : beltEff = beltMaxEff * _beltPLEff; //[-]
2249 48123 : beltEff = max(beltEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
2250 :
2251 : // Calculate belt input power using fan shaft power and belt efficiency
2252 48123 : beltInputPower = shaftPower / beltEff; //[W]
2253 :
2254 : // Calculate motor part-load efficiency using correlations and coefficients based on MotorMaster+ data
2255 48123 : Real64 _motorOutPowerRatio = beltInputPower / motorMaxOutPower; //[-]
2256 48123 : if (plMotorEffCurveNum != 0) {
2257 48123 : _motorPLEff = Curve::CurveValue(state, plMotorEffCurveNum, _motorOutPowerRatio); //[-]
2258 : } else {
2259 0 : _motorPLEff = 1.0; // No curve specified - use constant efficiency
2260 : }
2261 48123 : motorEff = motorMaxEff * _motorPLEff; //[-]
2262 48123 : motorEff = max(motorEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
2263 :
2264 : // Calculate motor input power using belt input power and motor efficiency
2265 48123 : motorInputPower = beltInputPower / motorEff; //[W]
2266 :
2267 : // Calculate VFD efficiency using correlations and coefficients based on VFD type
2268 48123 : if ((vfdEffType == VFDEffType::Speed) && (vfdEffCurveNum != 0)) {
2269 0 : _vfdSpeedRatio = _motorSpeed / motorMaxSpeed; //[-]
2270 0 : vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdSpeedRatio); //[-]
2271 48123 : } else if ((vfdEffType == VFDEffType::Power) && (vfdEffCurveNum != 0)) {
2272 48123 : _vfdOutPowerRatio = motorInputPower / vfdMaxOutPower; //[-]
2273 48123 : vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdOutPowerRatio); //[-]
2274 : } else {
2275 : // No curve specified - use constant efficiency
2276 0 : vfdMaxOutPower = 0.0;
2277 0 : vfdEff = 0.97;
2278 : }
2279 48123 : vfdEff = max(vfdEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
2280 :
2281 : // Calculate VFD input power using motor input power and VFD efficiency
2282 48123 : vfdInputPower = motorInputPower / vfdEff; //[W]
2283 48123 : totalPower = vfdInputPower; //[W]
2284 :
2285 : // Calculate combined fan system efficiency: includes fan, belt, motor, and VFD
2286 : // Equivalent to fan%FanAirPower / fan%FanPower
2287 48123 : totalEff = wheelEff * beltEff * motorEff * vfdEff;
2288 :
2289 : // IF (fan%EMSFanEffOverrideOn) FanEff = fan%EMSFanEffValue
2290 :
2291 : // Calculate air enthalpy and temperature rise from power entering air stream from fan wheel, belt, and motor
2292 : // Assumes MotInAirFrac applies to belt and motor but NOT to VFD
2293 48123 : powerLossToAir = shaftPower + (motorInputPower - shaftPower) * motorInAirFrac; //[W]
2294 48123 : outletAirEnthalpy = inletAirEnthalpy + (powerLossToAir / _massFlow); //[kJ/kg]
2295 :
2296 : // This fan does not change the moisture or mass flow across the component
2297 48123 : outletAirHumRat = inletAirHumRat; //[-]
2298 48123 : outletAirMassFlowRate = _massFlow; //[kg/s]
2299 48123 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
2300 : } else {
2301 : // Fan is OFF and not operating -- no power consumed and zero mass flow rate
2302 14546 : totalPower = 0.0;
2303 14546 : shaftPower = 0.0;
2304 14546 : powerLossToAir = 0.0;
2305 14546 : outletAirMassFlowRate = 0.0;
2306 14546 : outletAirHumRat = inletAirHumRat;
2307 14546 : outletAirEnthalpy = inletAirEnthalpy;
2308 14546 : outletAirTemp = inletAirTemp;
2309 : // Set the Control Flow variables to 0.0 flow when OFF.
2310 14546 : massFlowRateMaxAvail = 0.0;
2311 14546 : massFlowRateMinAvail = 0.0;
2312 :
2313 14546 : deltaPress = 0.0;
2314 14546 : airPower = 0.0;
2315 14546 : wheelEff = 0.0;
2316 14546 : fanSpeed = 0.0;
2317 14546 : fanTorque = 0.0;
2318 14546 : beltEff = 0.0;
2319 14546 : beltInputPower = 0.0;
2320 14546 : motorEff = 0.0;
2321 14546 : motorInputPower = 0.0;
2322 14546 : vfdEff = 0.0;
2323 14546 : vfdInputPower = 0.0;
2324 14546 : totalEff = 0.0;
2325 : }
2326 62669 : } // FanComponent::simulateComponentModel()
2327 :
2328 162659141 : void FanComponent::update(EnergyPlusData &state)
2329 : {
2330 :
2331 : // SUBROUTINE INFORMATION:
2332 : // AUTHOR Richard Liesen
2333 : // DATE WRITTEN April 1998
2334 : // MODIFIED L. Gu, Feb. 1, 2007, No unbalance airflow when Zone Exhaust Fans are used in the AirflowNetwork
2335 :
2336 : // PURPOSE OF THIS SUBROUTINE:
2337 : // This subroutine updates the fan outlet nodes.
2338 :
2339 : // METHODOLOGY EMPLOYED:
2340 : // Data is moved from the fan data structure to the fan outlet nodes.
2341 :
2342 162659141 : auto &inletNode = state.dataLoopNodes->Node(inletNodeNum);
2343 162659141 : auto &outletNode = state.dataLoopNodes->Node(outletNodeNum);
2344 :
2345 : // Set the outlet air nodes of the fan
2346 162659141 : outletNode.MassFlowRate = outletAirMassFlowRate;
2347 162659141 : outletNode.Temp = outletAirTemp;
2348 162659141 : outletNode.HumRat = outletAirHumRat;
2349 162659141 : outletNode.Enthalpy = outletAirEnthalpy;
2350 : // Set the outlet nodes for properties that just pass through & not used
2351 162659141 : outletNode.Quality = inletNode.Quality;
2352 162659141 : outletNode.Press = inletNode.Press;
2353 :
2354 : // Set the Node Flow Control Variables from the Fan Control Variables
2355 162659141 : outletNode.MassFlowRateMaxAvail = massFlowRateMaxAvail;
2356 162659141 : outletNode.MassFlowRateMinAvail = massFlowRateMinAvail;
2357 :
2358 162659141 : if (type == HVAC::FanType::Exhaust) {
2359 1894988 : inletNode.MassFlowRate = inletAirMassFlowRate;
2360 1894988 : if (state.afn->AirflowNetworkNumOfExhFan == 0) {
2361 1698400 : state.dataHVACGlobal->UnbalExhMassFlow = inletAirMassFlowRate;
2362 1698400 : if (balancedFractSched != nullptr) {
2363 436326 : state.dataHVACGlobal->BalancedExhMassFlow = state.dataHVACGlobal->UnbalExhMassFlow * balancedFractSched->getCurrentVal();
2364 436326 : state.dataHVACGlobal->UnbalExhMassFlow = state.dataHVACGlobal->UnbalExhMassFlow - state.dataHVACGlobal->BalancedExhMassFlow;
2365 : } else {
2366 1262074 : state.dataHVACGlobal->BalancedExhMassFlow = 0.0;
2367 : }
2368 : } else {
2369 196588 : state.dataHVACGlobal->UnbalExhMassFlow = 0.0;
2370 196588 : state.dataHVACGlobal->BalancedExhMassFlow = 0.0;
2371 : }
2372 1894988 : unbalancedOutletMassFlowRate = state.dataHVACGlobal->UnbalExhMassFlow;
2373 1894988 : balancedOutletMassFlowRate = state.dataHVACGlobal->BalancedExhMassFlow;
2374 : }
2375 :
2376 162659141 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
2377 909320 : outletNode.CO2 = inletNode.CO2;
2378 : }
2379 :
2380 162659141 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
2381 237340 : outletNode.GenContam = inletNode.GenContam;
2382 : }
2383 162659141 : }
2384 :
2385 162659141 : void FanComponent::report(EnergyPlusData &state)
2386 : {
2387 :
2388 : // SUBROUTINE INFORMATION:
2389 : // AUTHOR Richard Liesen
2390 : // DATE WRITTEN April 1998
2391 :
2392 : // PURPOSE OF THIS SUBROUTINE:
2393 : // This subroutine updates the report variables for the fans.
2394 :
2395 162659141 : totalEnergy = totalPower * state.dataHVACGlobal->TimeStepSysSec;
2396 162659141 : deltaTemp = outletAirTemp - inletAirTemp;
2397 :
2398 162659141 : if (isAFNFan && (airLoopNum > 0)) {
2399 2346561 : if (type == HVAC::FanType::OnOff) {
2400 2052652 : state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = runtimeFrac;
2401 : }
2402 : }
2403 162659141 : } // FanComponent::report()
2404 :
2405 5630 : int GetFanIndex(EnergyPlusData &state, std::string const &FanName)
2406 : {
2407 :
2408 : // SUBROUTINE INFORMATION:
2409 : // AUTHOR Linda Lawrie
2410 : // DATE WRITTEN June 2004
2411 :
2412 : // PURPOSE OF THIS SUBROUTINE:
2413 : // This subroutine sets an index for a given fan -- issues error message if that fan
2414 : // is not legal fan.
2415 :
2416 5630 : if (state.dataFans->GetFanInputFlag) { // First time subroutine has been entered
2417 601 : GetFanInput(state);
2418 601 : state.dataFans->GetFanInputFlag = false;
2419 : }
2420 :
2421 5630 : auto found = state.dataFans->fanMap.find(FanName);
2422 11260 : return (found == state.dataFans->fanMap.end()) ? 0 : found->second;
2423 5630 : } // GetFanIndex()
2424 :
2425 2568 : Real64 CalFaultyFanAirFlowReduction(EnergyPlusData &state,
2426 : std::string const &FanName, // name of the fan
2427 : Real64 const FanDesignAirFlowRate, // Fan Design Volume Flow Rate [m3/sec]
2428 : Real64 const FanDesignDeltaPress, // Fan Design Delta Pressure [Pa]
2429 : Real64 const FanFaultyDeltaPressInc, // Increase of Fan Delta Pressure in the Faulty Case [Pa]
2430 : int const FanCurvePtr // Fan Curve Index
2431 : )
2432 : {
2433 :
2434 : // SUBROUTINE INFORMATION:
2435 : // AUTHOR Rongpeng Zhang
2436 : // DATE WRITTEN Apr. 2015
2437 :
2438 : // PURPOSE OF THIS SUBROUTINE:
2439 : // Calculate the decrease of the fan air flow rate, given the fan curve
2440 : // and the increase of fan pressure rise due to fouling air filters
2441 :
2442 : // Check whether the fan curve covers the design operational point of the fan
2443 2568 : Real64 FanCalDeltaPress = Curve::CurveValue(state, FanCurvePtr, FanDesignAirFlowRate); // [Pa]
2444 2568 : if ((FanCalDeltaPress < 0.9 * FanDesignDeltaPress) || (FanCalDeltaPress > 1.1 * FanDesignDeltaPress)) {
2445 0 : ShowWarningError(state, format("The design operational point of the fan {} does not fall ", FanName));
2446 0 : ShowContinueError(state, "on the fan curve provided in the FaultModel:Fouling:AirFilter object. ");
2447 0 : return 0.0;
2448 : }
2449 :
2450 : // Calculate the Fan Volume Flow Rate in the Faulty Case
2451 2568 : Real64 FanFaultyAirFlowRate = FanDesignAirFlowRate; // Fan Volume Flow Rate in the Faulty Case [m3/sec]
2452 2568 : Real64 FanCalDeltaPresstemp = Curve::CurveValue(state, FanCurvePtr, FanFaultyAirFlowRate); // Calculated Fan Delta Pressure for temp use [Pa]
2453 2568 : FanCalDeltaPress = FanCalDeltaPresstemp;
2454 :
2455 816624 : while (FanCalDeltaPress < (FanDesignDeltaPress + FanFaultyDeltaPressInc)) {
2456 814056 : FanFaultyAirFlowRate = FanFaultyAirFlowRate - 0.005;
2457 814056 : FanCalDeltaPresstemp = Curve::CurveValue(state, FanCurvePtr, FanFaultyAirFlowRate);
2458 :
2459 814056 : if ((FanCalDeltaPresstemp <= FanCalDeltaPress) || (FanFaultyAirFlowRate <= state.dataCurveManager->curves(FanCurvePtr)->inputLimits[0].min)) {
2460 : // The new operational point of the fan go beyond the fan selection range
2461 0 : ShowWarningError(state, format("The operational point of the fan {} may go beyond the fan selection ", FanName));
2462 0 : ShowContinueError(state, "range in the faulty fouling air filter cases");
2463 0 : break;
2464 : }
2465 :
2466 814056 : FanCalDeltaPress = FanCalDeltaPresstemp;
2467 : }
2468 :
2469 2568 : return FanDesignAirFlowRate - FanFaultyAirFlowRate;
2470 : }
2471 :
2472 758 : Real64 FanComponent::getDesignHeatGain(EnergyPlusData &state,
2473 : Real64 const _volFlow // fan volumetric flow rate [m3/s]
2474 : )
2475 : {
2476 : // FUNCTION INFORMATION:
2477 : // AUTHOR Fred Buhl
2478 : // DATE WRITTEN August 2014
2479 :
2480 : // PURPOSE OF THIS FUNCTION:
2481 : // This function calculates and returns the design fan heat gain from the fan input data
2482 :
2483 : // METHODOLOGY EMPLOYED:
2484 : // Simple fan: Qdot,tot = (Vdot*deltaP)/Eff,tot
2485 : // Qdot,air = Eff,mot*Qdot,tot + (Qdot,tot - Eff,mot*Qdot,tot)*Frac,mot-in-airstream
2486 :
2487 758 : if (type != HVAC::FanType::ComponentModel) {
2488 758 : Real64 _deltaP = deltaPress; // fan design pressure rise [N/m2]
2489 758 : Real64 _totalEff = totalEff; // fan design total efficiency
2490 758 : Real64 _motorEff = motorEff; // fan design motor efficiency
2491 758 : Real64 _motorInAirFrac = motorInAirFrac;
2492 758 : Real64 _powerTot = (_volFlow * _deltaP) / _totalEff;
2493 758 : return _motorEff * _powerTot + (_powerTot - _motorEff * _powerTot) * _motorInAirFrac;
2494 : } else {
2495 0 : if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
2496 0 : set_size(state);
2497 0 : sizingFlag = false;
2498 : }
2499 0 : return shaftPower + (motorInputPower - shaftPower) * motorInAirFrac;
2500 : }
2501 : } // FanComponent::getDesignHeatGain()
2502 :
2503 14068 : void FanComponent::getInputsForDesignHeatGain(EnergyPlusData &state,
2504 : Real64 &_deltaP,
2505 : Real64 &_motEff,
2506 : Real64 &_totEff,
2507 : Real64 &_motInAirFrac,
2508 : Real64 &_fanShaftPow,
2509 : Real64 &_motInPower,
2510 : bool &_fanCompModel)
2511 : {
2512 14068 : if (type != HVAC::FanType::ComponentModel) {
2513 14057 : _deltaP = deltaPress;
2514 14057 : _motEff = motorEff;
2515 14057 : _totEff = totalEff;
2516 14057 : _motInAirFrac = motorInAirFrac;
2517 14057 : _fanShaftPow = 0.0;
2518 14057 : _motInPower = 0.0;
2519 14057 : _fanCompModel = false;
2520 : } else {
2521 11 : _deltaP = 0.0;
2522 11 : _motEff = 0.0;
2523 11 : _totEff = 0.0;
2524 11 : if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
2525 1 : set_size(state);
2526 1 : sizingFlag = false;
2527 : }
2528 11 : _fanCompModel = true;
2529 11 : _fanShaftPow = shaftPower;
2530 11 : _motInPower = motorInputPower;
2531 11 : _motInAirFrac = motorInAirFrac;
2532 : }
2533 14068 : } // FanComponent::getInputsForDesHeatGain()
2534 :
2535 10581601 : void FanSystem::init(EnergyPlusData &state)
2536 : {
2537 10581601 : if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
2538 73 : set_size(state);
2539 73 : sizingFlag = false;
2540 : }
2541 :
2542 10581601 : auto &outletNode = state.dataLoopNodes->Node(outletNodeNum);
2543 10581601 : auto &inletNode = state.dataLoopNodes->Node(inletNodeNum);
2544 :
2545 10581601 : if (state.dataGlobal->BeginEnvrnFlag && envrnFlag) {
2546 :
2547 : // Currently, fan does not force minimum mass flow, only used for power calculation
2548 : // m_minAirFlowRate = designAirVolFlowRate * m_minPowerFlowFrac;
2549 : // m_minAirMassFlowRate = m_minAirFlowRate * m_rhoAirStdInit;
2550 :
2551 : // Init the Node Control variables
2552 1349 : outletNode.MassFlowRateMax = maxAirMassFlowRate;
2553 : // Currently, fan does not force minimum mass flow, only used for power calculation
2554 : // DataLoopNode::Node( outletNodeNum ).MassFlowRateMin = m_minAirMassFlowRate;
2555 :
2556 : // Initialize all report variables to a known state at beginning of simulation
2557 1349 : totalPower = 0.0;
2558 1349 : deltaTemp = 0.0;
2559 1349 : powerLossToAir = 0.0;
2560 1349 : totalEnergy = 0.0;
2561 2869 : for (int iSpeed = 0; iSpeed < numSpeeds; ++iSpeed) {
2562 1520 : runtimeFracAtSpeed[iSpeed] = 0.0;
2563 : }
2564 1349 : envrnFlag = false;
2565 : }
2566 :
2567 10581601 : if (!state.dataGlobal->BeginEnvrnFlag) {
2568 10539054 : envrnFlag = true;
2569 : }
2570 :
2571 10581601 : massFlowRateMaxAvail = min(outletNode.MassFlowRateMax, inletNode.MassFlowRateMaxAvail);
2572 10581601 : massFlowRateMinAvail = min(max(outletNode.MassFlowRateMin, inletNode.MassFlowRateMinAvail), inletNode.MassFlowRateMaxAvail);
2573 :
2574 : // Load the node data in this section for the component simulation
2575 : // First need to make sure that the MassFlowRate is between the max and min avail.
2576 10581601 : inletAirMassFlowRate = min(inletNode.MassFlowRate, massFlowRateMaxAvail);
2577 10581601 : inletAirMassFlowRate = max(inletAirMassFlowRate, massFlowRateMinAvail);
2578 :
2579 : // Then set the other conditions
2580 10581601 : inletAirTemp = inletNode.Temp;
2581 10581601 : inletAirHumRat = inletNode.HumRat;
2582 10581601 : inletAirEnthalpy = inletNode.Enthalpy;
2583 10581601 : } // FanSystem::init()
2584 :
2585 231 : void FanSystem::set_size(EnergyPlusData &state)
2586 : {
2587 : static constexpr std::string_view routineName = "FanSystem::set_size";
2588 :
2589 231 : Real64 _tempFlow = maxAirFlowRate;
2590 231 : bool _bPRINT = true;
2591 231 : state.dataSize->DataAutosizable = true;
2592 231 : state.dataSize->DataEMSOverrideON = EMSMaxAirFlowRateOverrideOn;
2593 231 : state.dataSize->DataEMSOverride = EMSMaxAirFlowRateValue;
2594 231 : airLoopNum = state.dataSize->CurSysNum;
2595 :
2596 231 : bool ErrorsFound = false;
2597 231 : SystemAirFlowSizer sizerSystemAirFlow;
2598 231 : sizerSystemAirFlow.initializeWithinEP(state, HVAC::fanTypeNames[(int)type], Name, _bPRINT, routineName);
2599 231 : maxAirFlowRate = sizerSystemAirFlow.size(state, _tempFlow, ErrorsFound);
2600 :
2601 231 : state.dataSize->DataAutosizable = true; // should be false?
2602 231 : state.dataSize->DataEMSOverrideON = false;
2603 231 : state.dataSize->DataEMSOverride = 0.0;
2604 :
2605 231 : if (designElecPowerWasAutosized) {
2606 :
2607 230 : switch (powerSizingMethod) {
2608 0 : case PowerSizing::PerFlow: {
2609 0 : designElecPower = maxAirFlowRate * elecPowerPerFlowRate;
2610 0 : } break;
2611 21 : case PowerSizing::PerFlowPerPressure: {
2612 21 : designElecPower = maxAirFlowRate * deltaPress * elecPowerPerFlowRatePerPressure;
2613 21 : } break;
2614 209 : case PowerSizing::TotalEfficiencyAndPressure: {
2615 209 : designElecPower = maxAirFlowRate * deltaPress / totalEff;
2616 209 : } break;
2617 0 : case PowerSizing::Invalid: {
2618 : // do nothing (no assert?)
2619 0 : break;
2620 : }
2621 0 : default:
2622 0 : assert(false);
2623 :
2624 : } // end switch
2625 :
2626 : // report design power
2627 230 : BaseSizer::reportSizerOutput(state, HVAC::fanTypeNames[(int)type], Name, "Design Electric Power Consumption [W]", designElecPower);
2628 :
2629 : } // end if power was autosized
2630 :
2631 231 : rhoAirStdInit = state.dataEnvrn->StdRhoAir;
2632 231 : maxAirMassFlowRate = maxAirFlowRate * rhoAirStdInit;
2633 :
2634 : // calculate total fan system efficiency at design, else set to 1 to avoid div by zero
2635 231 : totalEff = (designElecPower > 0.0) ? maxAirFlowRate * deltaPress / designElecPower : 1.0;
2636 :
2637 231 : if (speedControl == SpeedControl::Discrete && numSpeeds > 1) { // set up values at speeds
2638 23 : massFlowAtSpeed.resize(numSpeeds, 0.0);
2639 23 : totalEffAtSpeed.resize(numSpeeds, 0.0);
2640 72 : for (int loop = 0; loop < numSpeeds; ++loop) {
2641 49 : massFlowAtSpeed[loop] = maxAirMassFlowRate * flowFracAtSpeed[loop];
2642 49 : if (powerFracInputAtSpeed[loop]) { // use speed power fraction
2643 49 : if (designElecPower > 0.0) {
2644 49 : totalEffAtSpeed[loop] = flowFracAtSpeed[loop] * maxAirFlowRate * deltaPress / (designElecPower * powerFracAtSpeed[loop]);
2645 : } else {
2646 0 : totalEffAtSpeed[loop] = 1.0;
2647 : }
2648 : } else { // use power curve
2649 0 : totalEffAtSpeed[loop] = flowFracAtSpeed[loop] * maxAirFlowRate * deltaPress /
2650 0 : (designElecPower * Curve::CurveValue(state, powerModFuncFlowFracCurveNum, flowFracAtSpeed[loop]));
2651 0 : powerFracAtSpeed[loop] = Curve::CurveValue(state, powerModFuncFlowFracCurveNum, flowFracAtSpeed[loop]);
2652 : }
2653 : }
2654 : }
2655 231 : designPointFEI = report_fei(state, maxAirFlowRate, designElecPower, deltaPress);
2656 :
2657 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanType, Name, HVAC::fanTypeNames[(int)type]);
2658 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanTotEff, Name, totalEff);
2659 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanDeltaP, Name, deltaPress);
2660 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanVolFlow, Name, maxAirFlowRate);
2661 :
2662 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwr, Name, designElecPower);
2663 231 : if (maxAirFlowRate != 0.0) {
2664 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwrPerFlow, Name, designElecPower / maxAirFlowRate);
2665 : }
2666 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorIn, Name, motorInAirFrac);
2667 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEnergyIndex, Name, designPointFEI);
2668 :
2669 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEndUse, Name, endUseSubcategoryName);
2670 :
2671 231 : sizingFlag = false;
2672 :
2673 : // Std 229 Fans (HVACFan.cc)
2674 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPurpose, Name, "N/A"); // m_fanType); // purpose? not the same
2675 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanAutosized, Name, maxAirFlowRateIsAutosized ? "Yes" : "No");
2676 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorEff, Name, motorEff);
2677 231 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorHeatToZoneFrac, Name, 1 - motorInAirFrac);
2678 462 : OutputReportPredefined::PreDefTableEntry(state,
2679 231 : state.dataOutRptPredefined->pdchFanMotorHeatZone,
2680 : Name,
2681 693 : heatLossDest == HeatLossDest::Zone ? state.dataHeatBal->Zone(zoneNum).Name : "N/A");
2682 231 : if (airLoopNum == 0) {
2683 170 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, "N/A");
2684 61 : } else if (airLoopNum <= state.dataHVACGlobal->NumPrimaryAirSys) {
2685 120 : OutputReportPredefined::PreDefTableEntry(
2686 120 : state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, state.dataAirSystemsData->PrimaryAirSystems(airLoopNum).Name);
2687 : } else {
2688 2 : OutputReportPredefined::PreDefTableEntry(
2689 : state,
2690 1 : state.dataOutRptPredefined->pdchFanAirLoopName,
2691 : Name,
2692 1 : state.dataAirLoopHVACDOAS->airloopDOAS[airLoopNum - state.dataHVACGlobal->NumPrimaryAirSys - 1].Name);
2693 : }
2694 231 : }
2695 :
2696 2371 : Real64 FanSystem::report_fei(EnergyPlusData &state, Real64 const _designFlowRate, Real64 const _designElecPower, Real64 const _designDeltaPress)
2697 : {
2698 : // PURPOSE OF THIS SUBROUTINE:
2699 : // Calculate the Fan Energy Index
2700 :
2701 : // REFERENCES:
2702 : // ANSI/AMCA Standard 207-17: Fan System Efficiency and Fan System Input Power Calculation, 2017.
2703 : // AANSI / AMCA Standard 208 - 18: Calculation of the Fan Energy Index, 2018.
2704 :
2705 2371 : Real64 constexpr rhoAirStd = 1.2; // Value from the above referenced standard
2706 2371 : Real64 constexpr tempAirFan = 21.0; // Standard fan inlet temperature in Celsius
2707 2371 : Real64 constexpr hrAirFan = 0.5; // Standard fan inlet humidity ratio (50%)
2708 2371 : Real64 _wAirFan = Psychrometrics::PsyWFnTdbRhPb(state, tempAirFan, hrAirFan, state.dataEnvrn->StdBaroPress);
2709 2371 : Real64 _rhoAirFan = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, tempAirFan, _wAirFan);
2710 :
2711 : // Calculate reference fan shaft power
2712 2371 : Real64 _refFanShaftPower = (_designFlowRate + 0.118) * (_designDeltaPress + 100 * _rhoAirFan / rhoAirStd) / (1000 * 0.66);
2713 :
2714 : // Calculate reference reference fan transmission efficiency
2715 2371 : Real64 _refFanTransEff = 0.96 * pow((_refFanShaftPower / (_refFanShaftPower + 1.64)), 0.05);
2716 :
2717 : // Calculate reference reference fan motor efficiency
2718 2371 : Real64 _refFanMotorOutput = _refFanShaftPower / _refFanTransEff;
2719 :
2720 : Real64 _refFanMotorEff;
2721 2371 : if (_refFanMotorOutput < 185.0) {
2722 2340 : _refFanMotorEff = -0.003812 * pow(std::log10(_refFanMotorOutput), 4) + 0.025834 * pow(std::log10(_refFanMotorOutput), 3) -
2723 2340 : 0.072577 * pow(std::log10(_refFanMotorOutput), 2) + 0.125559 * std::log10(_refFanMotorOutput) + 0.850274;
2724 : } else {
2725 31 : _refFanMotorEff = 0.962;
2726 : }
2727 :
2728 : // Calculate reference reference fan motor controller efficiency
2729 2371 : Real64 _refFanMotorCtrlEff = 1;
2730 :
2731 2371 : Real64 _refFanElecPower = _refFanShaftPower / (_refFanTransEff * _refFanMotorEff * _refFanMotorCtrlEff);
2732 :
2733 2371 : return (_designElecPower > 0.0) ? (_refFanElecPower * 1000 / _designElecPower) : 0.0;
2734 : } // FanSystem::report_fei()
2735 :
2736 10581254 : void FanSystem::calcSimpleSystemFan(
2737 : EnergyPlusData &state,
2738 : ObjexxFCL::Optional<Real64 const> _flowFraction, // Flow fraction for entire timestep (not used if flow ratios are present)
2739 : ObjexxFCL::Optional<Real64 const> _pressureRise, // Pressure difference to use for DeltaPress
2740 : ObjexxFCL::Optional<Real64 const> _flowRatio1, // Flow ratio in operating mode 1
2741 : ObjexxFCL::Optional<Real64 const> _runTimeFrac1, // Run time fraction in operating mode 1
2742 : ObjexxFCL::Optional<Real64 const> _flowRatio2, // Flow ratio in operating mode 2
2743 : ObjexxFCL::Optional<Real64 const> _runTimeFrac2, // Run time fraction in operating mode 2
2744 : ObjexxFCL::Optional<Real64 const> _pressureRise2 // Pressure difference to use for operating mode 2
2745 : )
2746 : {
2747 : Real64 _localFlowFrac;
2748 : Real64 _localTotalEff;
2749 10581254 : std::array<Real64, 2> _localPressureRise = {0.0, 0.0}; // [0] is operating mode 1, [1] is operating mode 2
2750 10581254 : std::array<Real64, 2> _localAirMassFlow = {0.0, 0.0};
2751 10581254 : std::array<Real64, 2> _localFlowRatio = {0.0, 0.0};
2752 10581254 : std::array<Real64, 2> _localRuntimeFrac = {1.0, 1.0};
2753 10581254 : bool _useFlowRatiosAndRunTimeFracs = false;
2754 :
2755 : // Number of operating modes, 1 or 2 ( e.g. heating, ventilating, cooling)
2756 10581254 : int _numModes = (present(_flowRatio2) && present(_runTimeFrac2)) ? 2 : 1;
2757 :
2758 10581254 : if (state.dataHVACGlobal->NightVentOn) {
2759 : // assume if non-zero inputs for night data then this fan is to be used with that data
2760 0 : if (nightVentPressureDelta > 0.0) {
2761 0 : _localPressureRise[0] = nightVentPressureDelta;
2762 0 : _localPressureRise[1] = nightVentPressureDelta;
2763 : }
2764 :
2765 0 : _localFlowFrac = (maxAirMassFlowRate > 0.0) ? inletAirMassFlowRate / maxAirMassFlowRate : 1.0;
2766 :
2767 0 : _localAirMassFlow[0] = inletAirMassFlowRate;
2768 :
2769 : } else { // not in night mode
2770 10581254 : _localPressureRise[0] = present(_pressureRise) ? _pressureRise() : deltaPress;
2771 :
2772 10581254 : _localPressureRise[1] = present(_pressureRise2) ? _pressureRise2() : deltaPress;
2773 :
2774 10581254 : if (present(_flowFraction)) {
2775 1339137 : _localFlowFrac = _flowFraction;
2776 1339137 : _localAirMassFlow[0] = _localFlowFrac * maxAirMassFlowRate;
2777 : } else {
2778 9242117 : if (maxAirMassFlowRate > 0.0) { // protect div by 0
2779 9242117 : _localFlowFrac = inletAirMassFlowRate / maxAirMassFlowRate;
2780 : } else {
2781 0 : _localFlowFrac = 1.0;
2782 : }
2783 9242117 : _localAirMassFlow[0] = inletAirMassFlowRate;
2784 : }
2785 10581254 : if (present(_flowRatio1) && present(_flowRatio2) && present(_runTimeFrac1) && present(_runTimeFrac2)) {
2786 5850098 : _useFlowRatiosAndRunTimeFracs = true;
2787 5850098 : _localRuntimeFrac[0] = _runTimeFrac1;
2788 5850098 : _localRuntimeFrac[1] = _runTimeFrac2;
2789 5850098 : _localFlowRatio[0] = _flowRatio1;
2790 5850098 : _localAirMassFlow[0] = _localFlowRatio[0] * maxAirMassFlowRate * _localRuntimeFrac[0];
2791 5850098 : _localFlowRatio[1] = _flowRatio2;
2792 5850098 : _localAirMassFlow[1] = _localFlowRatio[1] * maxAirMassFlowRate * _localRuntimeFrac[1];
2793 : } else {
2794 4731156 : _localRuntimeFrac[0] = 1.0; // if runTimeFracs are not present, assume single-mode operation
2795 4731156 : _localRuntimeFrac[1] = 0.0; // if runTimeFracs are not present, assume single-mode operation
2796 : }
2797 : }
2798 :
2799 10581254 : Real64 _localFaultMaxAirMassFlow = 0.0;
2800 10581254 : bool _faultActive = false;
2801 10581254 : Real64 _localFaultPressureRise = 0.0;
2802 0 : if (faultyFilterFlag && (state.dataFaultsMgr->NumFaultyAirFilter > 0) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
2803 10581254 : state.dataGlobal->DoWeathSim && (!EMSMaxMassFlowOverrideOn) && (!EMSPressureOverrideOn)) {
2804 0 : auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
2805 0 : if (fault.availSched->getCurrentVal() > 0) {
2806 0 : _faultActive = true;
2807 0 : Real64 _pressFrac = fault.pressFracSched->getCurrentVal();
2808 : Real64 _designFlowRateDec = // Decrease of the Fan Design Volume Flow Rate [m3/sec]
2809 0 : Fans::CalFaultyFanAirFlowReduction(state, Name, maxAirFlowRate, deltaPress, (_pressFrac - 1) * deltaPress, fault.fanCurveNum);
2810 :
2811 0 : _localFaultMaxAirMassFlow = maxAirMassFlowRate - _designFlowRateDec * rhoAirStdInit;
2812 0 : _localFaultPressureRise = _pressFrac * deltaPress;
2813 : }
2814 : }
2815 :
2816 27012606 : for (int mode = 0; mode < _numModes; ++mode) {
2817 : // EMS override MassFlow, DeltaPress, and FanEff
2818 16431352 : if (EMSPressureOverrideOn) {
2819 0 : _localPressureRise[mode] = EMSPressureValue;
2820 : }
2821 16431352 : if (EMSTotalEffOverrideOn) {
2822 0 : _localTotalEff = EMSTotalEffValue;
2823 : }
2824 16431352 : if (EMSMaxMassFlowOverrideOn) {
2825 0 : _localAirMassFlow[mode] = EMSAirMassFlowValue;
2826 : }
2827 :
2828 16431352 : _localAirMassFlow[mode] = min(_localAirMassFlow[mode], maxAirMassFlowRate);
2829 16431352 : if (_faultActive) {
2830 0 : _localAirMassFlow[mode] = min(_localAirMassFlow[mode], _localFaultMaxAirMassFlow);
2831 0 : _localPressureRise[mode] = _localFaultPressureRise;
2832 : }
2833 16431352 : _localFlowFrac = _localAirMassFlow[0] / maxAirMassFlowRate;
2834 16431352 : _localFlowFrac = min(1.0, _localFlowFrac);
2835 :
2836 16431352 : if (_localRuntimeFrac[mode] > 0.0) {
2837 10467428 : _localFlowRatio[mode] = _localAirMassFlow[mode] / (maxAirMassFlowRate * _localRuntimeFrac[mode]);
2838 : }
2839 16431352 : _localFlowRatio[mode] = min(1.0, _localFlowRatio[mode]);
2840 : }
2841 :
2842 : // zero these now, because the may accumulate across multiple operating modes
2843 10581254 : powerLossToAir = 0.0;
2844 10581254 : totalPower = 0.0;
2845 10581254 : outletAirMassFlowRate = 0.0;
2846 10581254 : if (speedControl == SpeedControl::Discrete) {
2847 20100378 : for (int loop = 0; loop < numSpeeds; ++loop) {
2848 11012341 : runtimeFracAtSpeed[loop] = 0.0;
2849 : }
2850 : }
2851 :
2852 20499809 : if ((availSched->getCurrentVal() > 0.0 || state.dataHVACGlobal->TurnFansOn) && !state.dataHVACGlobal->TurnFansOff &&
2853 9918555 : ((_localAirMassFlow[0] + _localAirMassFlow[1]) > 0.0)) {
2854 : // fan is running
2855 :
2856 21932942 : for (int mode = 0; mode < _numModes; ++mode) {
2857 :
2858 : // if no flow for this mode then continue to the next mode
2859 13271408 : if (_localAirMassFlow[mode] == 0.0) {
2860 3647092 : continue;
2861 : }
2862 :
2863 9624316 : switch (speedControl) {
2864 :
2865 8325109 : case SpeedControl::Discrete: {
2866 : //
2867 8325109 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction <= 0.0) {
2868 0 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0;
2869 : }
2870 8325109 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction < 0.7) {
2871 0 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 0.7; // a warning message is already issued from the DX coils or gas heating coil
2872 : }
2873 8325109 : if (_useFlowRatiosAndRunTimeFracs) {
2874 : // Use flow ratios and runtimefractions pass from parent (allows fan to cycle at a specified speed)
2875 4897240 : Real64 _locRuntimeFrac = (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0)
2876 4897240 : ? _localRuntimeFrac[mode]
2877 509208 : : max(0.0, min(1.0, _localRuntimeFrac[mode] / state.dataHVACGlobal->OnOffFanPartLoadFraction));
2878 4897240 : Real64 _locFlowRatio = _localFlowRatio[mode]; // Current mode flow rate / max flow rate
2879 4897240 : Real64 _locLowSpeedRuntimeFrac = 0.0;
2880 4897240 : Real64 _locHiSpeedRuntimeFrac = 0.0;
2881 4897240 : if (numSpeeds == 1) { // CV or OnOff
2882 3194151 : _localTotalEff = totalEff;
2883 3194151 : _locHiSpeedRuntimeFrac = _locRuntimeFrac * _locFlowRatio;
2884 3194151 : runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
2885 3194151 : totalPower +=
2886 3194151 : max(0.0, _locHiSpeedRuntimeFrac * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit));
2887 1703089 : } else if (numSpeeds > 1) { // multi speed
2888 :
2889 : // find which two speed levels bracket flow ratios and calculate runtimefraction at each speed
2890 : // ideally the flow ratios passed in will match one of the fan m_flowFractionAtSpeed but it is not required
2891 1703089 : int _lowSideSpeed = -1;
2892 1703089 : int _hiSideSpeed = -1;
2893 :
2894 1703089 : if (_locFlowRatio <= flowFracAtSpeed[0]) { // on/off at lowest speed
2895 841576 : _hiSideSpeed = 0;
2896 841576 : _locHiSpeedRuntimeFrac = _locFlowRatio * _locRuntimeFrac / flowFracAtSpeed[0];
2897 841576 : runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
2898 : } else {
2899 861513 : _lowSideSpeed = 0; // hush up cppcheck
2900 861513 : _hiSideSpeed = 0; // hush up cppcheck
2901 861513 : for (int loop = 0; loop < numSpeeds - 1; ++loop) {
2902 861513 : if ((flowFracAtSpeed[loop] <= _locFlowRatio) && (_locFlowRatio <= flowFracAtSpeed[loop + 1])) {
2903 861513 : _lowSideSpeed = loop;
2904 861513 : _hiSideSpeed = loop + 1;
2905 861513 : break;
2906 : }
2907 : }
2908 : Real64 _locLowSpeedTimeFrac =
2909 861513 : (flowFracAtSpeed[_hiSideSpeed] - _locFlowRatio) / (flowFracAtSpeed[_hiSideSpeed] - flowFracAtSpeed[_lowSideSpeed]);
2910 861513 : _locLowSpeedRuntimeFrac = _locLowSpeedTimeFrac * _localRuntimeFrac[mode];
2911 861513 : _locHiSpeedRuntimeFrac = (1 - _locLowSpeedTimeFrac) * _localRuntimeFrac[mode];
2912 861513 : runtimeFracAtSpeed[_lowSideSpeed] += _locLowSpeedRuntimeFrac;
2913 861513 : runtimeFracAtSpeed[_hiSideSpeed] += _locHiSpeedRuntimeFrac;
2914 : }
2915 1703089 : if (_lowSideSpeed != -1 && _hiSideSpeed != -1) {
2916 861513 : totalPower += max(0.0,
2917 861513 : _locLowSpeedRuntimeFrac * massFlowAtSpeed[_lowSideSpeed] * _localPressureRise[mode] /
2918 861513 : (totalEffAtSpeed[_lowSideSpeed] * rhoAirStdInit) +
2919 861513 : _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
2920 861513 : (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
2921 841576 : } else if (_lowSideSpeed == -1 && _hiSideSpeed == 0) {
2922 841576 : totalPower += max(0.0,
2923 841576 : _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
2924 841576 : (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
2925 : }
2926 : }
2927 : } else {
2928 : // Use localFlowFraction which is not locked at a particular flow ratio (legacy method for fan:onoff)
2929 3427869 : Real64 _locLowSpeedRuntimeFrac = 0.0;
2930 3427869 : Real64 _locHiSpeedRuntimeFrac = 0.0;
2931 3427869 : Real64 _locRuntimeFrac = (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0)
2932 3427869 : ? _localFlowFrac
2933 9656 : : max(0.0, min(1.0, _localFlowFrac / state.dataHVACGlobal->OnOffFanPartLoadFraction));
2934 :
2935 3427869 : if (numSpeeds == 1) { // CV or OnOff
2936 3203180 : _localTotalEff = totalEff;
2937 3203180 : _locHiSpeedRuntimeFrac = _locRuntimeFrac;
2938 3203180 : runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
2939 3203180 : totalPower +=
2940 3203180 : max(0.0, _locHiSpeedRuntimeFrac * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit));
2941 224689 : } else if (numSpeeds > 1) { // multi speed
2942 :
2943 : // find which two speed levels bracket flow fraction and calculate runtimefraction
2944 224689 : int _lowSideSpeed = -1;
2945 224689 : int _hiSideSpeed = -1;
2946 :
2947 224689 : if (_locRuntimeFrac < flowFracAtSpeed[0]) { // on/off between zero and lowest speed
2948 11823 : _hiSideSpeed = 0;
2949 11823 : _locHiSpeedRuntimeFrac = _locRuntimeFrac / flowFracAtSpeed[0];
2950 11823 : runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
2951 : } else {
2952 212866 : _lowSideSpeed = 0; // hush up cppcheck
2953 212866 : _hiSideSpeed = 0; // hush up cppcheck
2954 219156 : for (int loop = 0; loop < numSpeeds - 1; ++loop) {
2955 219156 : if ((flowFracAtSpeed[loop] <= _locRuntimeFrac) && (_locRuntimeFrac <= flowFracAtSpeed[loop + 1])) {
2956 212866 : _lowSideSpeed = loop;
2957 212866 : _hiSideSpeed = loop + 1;
2958 212866 : break;
2959 : }
2960 : }
2961 212866 : _locLowSpeedRuntimeFrac =
2962 212866 : (flowFracAtSpeed[_hiSideSpeed] - _locRuntimeFrac) / (flowFracAtSpeed[_hiSideSpeed] - flowFracAtSpeed[_lowSideSpeed]);
2963 212866 : _locHiSpeedRuntimeFrac =
2964 212866 : (_locRuntimeFrac - flowFracAtSpeed[_lowSideSpeed]) / (flowFracAtSpeed[_hiSideSpeed] - flowFracAtSpeed[_lowSideSpeed]);
2965 212866 : runtimeFracAtSpeed[_lowSideSpeed] += _locLowSpeedRuntimeFrac;
2966 212866 : runtimeFracAtSpeed[_hiSideSpeed] += _locHiSpeedRuntimeFrac;
2967 : }
2968 224689 : if (_lowSideSpeed != -1 && _hiSideSpeed != -1) {
2969 212866 : totalPower += max(0.0,
2970 212866 : _locLowSpeedRuntimeFrac * massFlowAtSpeed[_lowSideSpeed] * _localPressureRise[mode] /
2971 212866 : (totalEffAtSpeed[_lowSideSpeed] * rhoAirStdInit) +
2972 212866 : _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
2973 212866 : (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
2974 11823 : } else if (_lowSideSpeed == -1 && _hiSideSpeed == 0) {
2975 11823 : totalPower += max(0.0,
2976 11823 : _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
2977 11823 : (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
2978 : }
2979 : }
2980 : }
2981 8325109 : _localTotalEff = totalEff;
2982 8325109 : } break;
2983 :
2984 1299207 : case SpeedControl::Continuous: {
2985 1299207 : _localTotalEff = totalEff;
2986 1299207 : Real64 _locFlowRatio(0.0);
2987 1299207 : Real64 _locRuntimeFrac(0.0);
2988 1299207 : if (_useFlowRatiosAndRunTimeFracs) {
2989 675416 : _locFlowRatio = _localFlowRatio[mode];
2990 675416 : _locRuntimeFrac = _localRuntimeFrac[mode];
2991 : } else {
2992 623791 : _locFlowRatio = _localFlowFrac;
2993 623791 : _locRuntimeFrac = 1.0;
2994 : }
2995 :
2996 1299207 : Real64 _localFlowFracForPower = max(minPowerFlowFrac, _locFlowRatio);
2997 1299207 : Real64 _localPowerFrac = (state.dataHVACGlobal->NightVentOn) ? 1.0 : // not sure why, but legacy fan had this for night ventilation
2998 1299207 : Curve::CurveValue(state, powerModFuncFlowFracCurveNum, _localFlowFracForPower);
2999 : Real64 _localFanPower =
3000 1299207 : max(0.0, _locRuntimeFrac * _localPowerFrac * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit));
3001 1299207 : Real64 _shaftPower = motorEff * _localFanPower;
3002 1299207 : Real64 _localPowerLossToAir = _shaftPower + (_localFanPower - _shaftPower) * motorInAirFrac;
3003 1299207 : outletAirEnthalpy = inletAirEnthalpy + _localPowerLossToAir / _localAirMassFlow[mode]; // this will get revised later
3004 1299207 : outletAirHumRat = inletAirHumRat; // this will get revised later
3005 1299207 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat); // this will get revised later
3006 : // When fan air flow is less than 10%, the fan power curve is linearized between the 10% to 0% to
3007 : // avoid the unrealistic high temperature rise across the fan.
3008 1299207 : Real64 _deltaTAcrossFan = outletAirTemp - inletAirTemp;
3009 1299207 : if (_deltaTAcrossFan > 20.0) {
3010 106 : Real64 _minFlowFracLimitFanHeat = 0.10;
3011 106 : Real64 _powerFracAtLowMin = 0.0;
3012 106 : Real64 _fanPowerAtLowMinimum = 0.0;
3013 106 : if (_localFlowFracForPower < _minFlowFracLimitFanHeat) {
3014 0 : _powerFracAtLowMin = Curve::CurveValue(state, powerModFuncFlowFracCurveNum, _minFlowFracLimitFanHeat);
3015 0 : _fanPowerAtLowMinimum = _powerFracAtLowMin * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit);
3016 0 : _localFanPower = max(0.0, _localFlowFracForPower * _fanPowerAtLowMinimum / _minFlowFracLimitFanHeat);
3017 106 : } else if (_locFlowRatio < _minFlowFracLimitFanHeat) {
3018 106 : _powerFracAtLowMin = Curve::CurveValue(state, powerModFuncFlowFracCurveNum, _minFlowFracLimitFanHeat);
3019 106 : _fanPowerAtLowMinimum = _powerFracAtLowMin * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit);
3020 106 : _localFanPower = max(0.0, _locFlowRatio * _fanPowerAtLowMinimum / _minFlowFracLimitFanHeat);
3021 : }
3022 : }
3023 1299207 : totalPower += _localFanPower;
3024 1299207 : } break;
3025 : // continuous speed control case
3026 0 : case SpeedControl::Invalid: {
3027 : // do nothing
3028 0 : } break;
3029 0 : default:
3030 0 : assert(false);
3031 : } // end switch
3032 9624316 : outletAirMassFlowRate += _localAirMassFlow[mode];
3033 :
3034 : } // end of operating mode loop
3035 :
3036 8661534 : if (outletAirMassFlowRate > 0.0) {
3037 8661534 : Real64 _shaftPower = motorEff * totalPower; // power delivered to shaft
3038 8661534 : powerLossToAir = _shaftPower + (totalPower - _shaftPower) * motorInAirFrac;
3039 8661534 : outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / outletAirMassFlowRate;
3040 : // This fan does not change the moisture or Mass Flow across the component
3041 8661534 : outletAirHumRat = inletAirHumRat;
3042 8661534 : outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
3043 : } else {
3044 0 : totalPower = 0.0;
3045 0 : powerLossToAir = 0.0;
3046 0 : outletAirHumRat = inletAirHumRat;
3047 0 : outletAirEnthalpy = inletAirEnthalpy;
3048 0 : outletAirTemp = inletAirTemp;
3049 0 : massFlowRateMaxAvail = 0.0;
3050 0 : massFlowRateMinAvail = 0.0;
3051 : }
3052 : } else { // fan is off
3053 : // Fan is off and not operating no power consumed and mass flow rate.
3054 1919720 : totalPower = 0.0;
3055 1919720 : powerLossToAir = 0.0;
3056 1919720 : outletAirHumRat = inletAirHumRat;
3057 1919720 : outletAirEnthalpy = inletAirEnthalpy;
3058 1919720 : outletAirTemp = inletAirTemp;
3059 : // Set the Control Flow variables to 0.0 flow when OFF.
3060 1919720 : if (isSecondaryDriver) {
3061 : // sometimes the air is moving with the fan off, eg. AirTerminal:SingleDuct:VAV:Reheat:VariableSpeedFan
3062 71934 : outletAirMassFlowRate = _localAirMassFlow[0] + _localAirMassFlow[1];
3063 71934 : if (outletAirMassFlowRate == 0.0) {
3064 44 : massFlowRateMaxAvail = 0.0;
3065 44 : massFlowRateMinAvail = 0.0;
3066 : }
3067 : } else {
3068 1847786 : outletAirMassFlowRate = 0.0;
3069 1847786 : massFlowRateMaxAvail = 0.0;
3070 1847786 : massFlowRateMinAvail = 0.0;
3071 : }
3072 : }
3073 :
3074 10581254 : if (heatLossDest == HeatLossDest::Zone) {
3075 0 : Real64 _powerLossToZone = totalPower - powerLossToAir;
3076 0 : qdotConvZone = _powerLossToZone * (1.0 - zoneRadFract);
3077 0 : qdotRadZone = _powerLossToZone * zoneRadFract;
3078 : }
3079 10581254 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // reset to 1
3080 10581254 : } // FanSystem::report()
3081 :
3082 10581254 : void FanSystem::update(EnergyPlusData &state) // does not change state of object, only update elsewhere
3083 : {
3084 : // Set the outlet air node of the fan
3085 10581254 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRate = outletAirMassFlowRate;
3086 10581254 : state.dataLoopNodes->Node(outletNodeNum).Temp = outletAirTemp;
3087 10581254 : state.dataLoopNodes->Node(outletNodeNum).HumRat = outletAirHumRat;
3088 10581254 : state.dataLoopNodes->Node(outletNodeNum).Enthalpy = outletAirEnthalpy;
3089 : // Set the outlet nodes for properties that just pass through & not used
3090 10581254 : state.dataLoopNodes->Node(outletNodeNum).Quality = state.dataLoopNodes->Node(inletNodeNum).Quality;
3091 10581254 : state.dataLoopNodes->Node(outletNodeNum).Press = state.dataLoopNodes->Node(inletNodeNum).Press;
3092 :
3093 : // Set the Node Flow Control Variables from the Fan Control Variables
3094 10581254 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMaxAvail = massFlowRateMaxAvail;
3095 10581254 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMinAvail = massFlowRateMinAvail;
3096 :
3097 : // make sure inlet has the same mass flow
3098 10581254 : state.dataLoopNodes->Node(inletNodeNum).MassFlowRate = outletAirMassFlowRate;
3099 :
3100 10581254 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
3101 0 : state.dataLoopNodes->Node(outletNodeNum).CO2 = state.dataLoopNodes->Node(inletNodeNum).CO2;
3102 : }
3103 10581254 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
3104 0 : state.dataLoopNodes->Node(outletNodeNum).GenContam = state.dataLoopNodes->Node(inletNodeNum).GenContam;
3105 : }
3106 :
3107 10581254 : if (isAFNFan && (airLoopNum > 0)) {
3108 107946 : if (speedControl == SpeedControl::Continuous) {
3109 0 : state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = runtimeFracAtSpeed[0];
3110 : } else {
3111 107946 : if (numSpeeds == 1) {
3112 107946 : state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = outletAirMassFlowRate / maxAirMassFlowRate;
3113 0 : } else if (outletAirMassFlowRate <= massFlowAtSpeed[0]) {
3114 0 : state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = outletAirMassFlowRate / massFlowAtSpeed[0];
3115 : } else {
3116 0 : state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = 1.0;
3117 : }
3118 : }
3119 : }
3120 10581254 : } // FanSystem::update()
3121 :
3122 10581254 : void FanSystem::report(EnergyPlusData &state)
3123 : {
3124 10581254 : totalEnergy = totalPower * state.dataHVACGlobal->TimeStepSysSec;
3125 10581254 : deltaTemp = outletAirTemp - inletAirTemp;
3126 10581254 : }
3127 :
3128 0 : Real64 FanSystem::getDesignTemperatureRise(EnergyPlusData &state) const
3129 : {
3130 0 : if (!sizingFlag) {
3131 0 : Real64 _cpAir = Psychrometrics::PsyCpAirFnW(DataPrecisionGlobals::constant_zero);
3132 0 : return (deltaPress / (rhoAirStdInit * _cpAir * totalEff)) * (motorEff + motorInAirFrac * (1.0 - motorEff));
3133 : } else {
3134 : // TODO throw warning, exception, call sizing?
3135 0 : ShowWarningError(state, "FanSystem::getDesignTemperatureRise called before fan sizing completed ");
3136 0 : return 0.0;
3137 : }
3138 : }
3139 :
3140 15 : Real64 FanSystem::getDesignHeatGain(EnergyPlusData &state, Real64 const _volFlow // fan volume flow rate [m3/s]
3141 : )
3142 : {
3143 15 : if (sizingFlag) {
3144 0 : set_size(state);
3145 : }
3146 :
3147 15 : Real64 _fanPowerTot = (_volFlow * deltaPress) / totalEff;
3148 15 : return motorEff * _fanPowerTot + (_fanPowerTot - motorEff * _fanPowerTot) * motorInAirFrac;
3149 : }
3150 :
3151 2007 : void FanSystem::getInputsForDesignHeatGain(EnergyPlusData &state,
3152 : Real64 &_deltaP,
3153 : Real64 &_motEff,
3154 : Real64 &_totEff,
3155 : Real64 &_motInAirFrac,
3156 : Real64 &_shaftPower,
3157 : Real64 &_motInPower,
3158 : bool &_fanComponentModel)
3159 : {
3160 2007 : if (sizingFlag) {
3161 158 : set_size(state);
3162 : }
3163 :
3164 2007 : _deltaP = deltaPress;
3165 2007 : _motEff = motorEff;
3166 2007 : _totEff = totalEff;
3167 2007 : _motInAirFrac = motorInAirFrac;
3168 :
3169 2007 : _shaftPower = 0.0;
3170 2007 : _motInPower = 0.0;
3171 2007 : _fanComponentModel = false;
3172 2007 : }
3173 : } // namespace EnergyPlus::Fans
|