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