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