Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // EnergyPlus Headers
49 : #include <EnergyPlus/Autosizing/SystemAirFlowSizing.hh>
50 : #include <EnergyPlus/BranchNodeConnections.hh>
51 : #include <EnergyPlus/CurveManager.hh>
52 : #include <EnergyPlus/Data/EnergyPlusData.hh>
53 : #include <EnergyPlus/DataContaminantBalance.hh>
54 : #include <EnergyPlus/DataEnvironment.hh>
55 : #include <EnergyPlus/DataHVACGlobals.hh>
56 : #include <EnergyPlus/DataHeatBalance.hh>
57 : #include <EnergyPlus/DataLoopNode.hh>
58 : #include <EnergyPlus/DataPrecisionGlobals.hh>
59 : #include <EnergyPlus/DataSizing.hh>
60 : #include <EnergyPlus/EMSManager.hh>
61 : #include <EnergyPlus/EnergyPlus.hh>
62 : #include <EnergyPlus/Fans.hh>
63 : #include <EnergyPlus/FaultsManager.hh>
64 : #include <EnergyPlus/HVACFan.hh>
65 : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
66 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
67 : #include <EnergyPlus/NodeInputManager.hh>
68 : #include <EnergyPlus/OutputProcessor.hh>
69 : #include <EnergyPlus/OutputReportPredefined.hh>
70 : #include <EnergyPlus/Psychrometrics.hh>
71 : #include <EnergyPlus/ScheduleManager.hh>
72 : #include <ObjexxFCL/Optional.hh>
73 :
74 : namespace EnergyPlus {
75 :
76 : namespace HVACFan {
77 :
78 269 : int getFanObjectVectorIndex( // lookup vector index for fan object name in object array EnergyPlus::HVACFan::fanObjs
79 : EnergyPlusData &state,
80 : std::string const &objectName, // IDF name in input
81 : bool const ErrorCheck)
82 : {
83 269 : int index = -1;
84 269 : bool found = false;
85 907 : for (std::size_t loop = 0; loop < state.dataHVACFan->fanObjs.size(); ++loop) {
86 638 : if (objectName == state.dataHVACFan->fanObjs[loop]->name) {
87 204 : if (!found) {
88 204 : index = loop;
89 204 : found = true;
90 : } else { // found duplicate
91 : // TODO throw warning?
92 0 : index = -1;
93 0 : ShowSevereError(state,
94 0 : "getFanObjectVectorIndex: Found duplicate Fan:SystemModel inputs of name =" + objectName + ". Check inputs");
95 : }
96 : }
97 : }
98 269 : if (!found && ErrorCheck) {
99 0 : ShowSevereError(state, "getFanObjectVectorIndex: did not find Fan:SystemModel name =" + objectName + ". Check inputs");
100 : }
101 269 : return index;
102 : }
103 :
104 351 : bool checkIfFanNameIsAFanSystem( // look up to see if input contains a Fan:SystemModel with the name (for use before object construction
105 : EnergyPlusData &state,
106 : std::string const &objectName)
107 : {
108 :
109 351 : int testNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "Fan:SystemModel", objectName);
110 351 : if (testNum > 0) {
111 99 : return true;
112 : } else {
113 252 : return false;
114 : }
115 : }
116 :
117 7213445 : void FanSystem::simulate(
118 : EnergyPlusData &state,
119 : Optional<Real64 const> flowFraction, // when used, this directs the fan to set the flow at this flow fraction = current flow/ max design flow
120 : // rate. It is not exactly the same as the legacy speed ratio that was used with SimulateFanComponents.
121 : Optional_bool_const zoneCompTurnFansOn, // can be used as turn fans ON signal from ZoneHVAC component
122 : Optional_bool_const zoneCompTurnFansOff, // can be used as turn Fans OFF signal from ZoneHVAC component
123 : Optional<Real64 const>
124 : pressureRise, // Pressure difference to use for DeltaPress, for rating DX coils at a different pressure without entire duct system
125 : Optional<Real64 const> massFlowRate1, // Mass flow rate in operating mode 1 [kg/s]
126 : Optional<Real64 const> runTimeFraction1, // Run time fraction in operating mode 1
127 : Optional<Real64 const> massFlowRate2, // Mass flow rate in operating mode 2 [kg/s]
128 : Optional<Real64 const> runTimeFraction2, // Run time fraction in opearating mode 2
129 : Optional<Real64 const> pressureRise2 // Pressure difference for operating mode 2
130 : )
131 : {
132 :
133 7213445 : m_objTurnFansOn = false;
134 7213445 : m_objTurnFansOff = false;
135 :
136 7213445 : init(state);
137 :
138 7213445 : if (m_objSizingFlag) {
139 227 : return; // can't run calculations until sizing is completed
140 : }
141 :
142 7213218 : if (present(zoneCompTurnFansOn) && present(zoneCompTurnFansOff)) {
143 : // Set module-level logic flags equal to ZoneCompTurnFansOn and ZoneCompTurnFansOff values passed into this routine
144 : // for ZoneHVAC components with system availability managers defined.
145 : // The module-level flags get used in the other subroutines (e.g., SimSimpleFan,SimVariableVolumeFan and SimOnOffFan)
146 2727053 : m_objTurnFansOn = zoneCompTurnFansOn;
147 2727053 : m_objTurnFansOff = zoneCompTurnFansOff;
148 : } else {
149 : // Set module-level logic flags equal to the global LocalTurnFansOn and LocalTurnFansOff variables for all other cases.
150 4486165 : m_objTurnFansOn = state.dataHVACGlobal->TurnFansOn;
151 4486165 : m_objTurnFansOff = state.dataHVACGlobal->TurnFansOff;
152 : }
153 7213218 : if (present(pressureRise) && present(massFlowRate1) && present(runTimeFraction1) && present(massFlowRate2) && present(runTimeFraction2) &&
154 0 : present(pressureRise2)) {
155 0 : Real64 flowRatio1 = massFlowRate1 / m_maxAirMassFlowRate;
156 0 : Real64 flowRatio2 = massFlowRate2 / m_maxAirMassFlowRate;
157 0 : calcSimpleSystemFan(state, _, pressureRise, flowRatio1, runTimeFraction1, flowRatio2, runTimeFraction2, pressureRise2);
158 24643104 : } else if (!present(pressureRise) && present(massFlowRate1) && present(runTimeFraction1) && present(massFlowRate2) &&
159 13229374 : present(runTimeFraction2) && !present(pressureRise2)) {
160 3008078 : Real64 flowRatio1 = massFlowRate1 / m_maxAirMassFlowRate;
161 3008078 : Real64 flowRatio2 = massFlowRate2 / m_maxAirMassFlowRate;
162 3008078 : calcSimpleSystemFan(state, flowFraction, _, flowRatio1, runTimeFraction1, flowRatio2, runTimeFraction2, _);
163 4205140 : } else if (present(pressureRise) && present(flowFraction)) {
164 0 : calcSimpleSystemFan(state, flowFraction, pressureRise, _, _, _, _, _);
165 4205140 : } else if (present(pressureRise) && !present(flowFraction)) {
166 4628 : calcSimpleSystemFan(state, _, pressureRise, _, _, _, _, _);
167 4200512 : } else if (!present(pressureRise) && present(flowFraction)) {
168 936396 : calcSimpleSystemFan(state, flowFraction, _, _, _, _, _, _);
169 : } else {
170 3264116 : calcSimpleSystemFan(state, _, _, _, _, _, _, _);
171 : }
172 :
173 7213218 : update(state);
174 :
175 7213218 : report(state);
176 : }
177 :
178 7213445 : void FanSystem::init(EnergyPlusData &state)
179 : {
180 7213445 : if (!state.dataGlobal->SysSizingCalc && m_objSizingFlag) {
181 54 : set_size(state);
182 54 : m_objSizingFlag = false;
183 : }
184 :
185 7213445 : if (state.dataGlobal->BeginEnvrnFlag && m_objEnvrnFlag) {
186 :
187 : // Currently, fan does not force minimum mass flow, only used for power calculation
188 : // m_minAirFlowRate = designAirVolFlowRate * m_minPowerFlowFrac;
189 : // m_minAirMassFlowRate = m_minAirFlowRate * m_rhoAirStdInit;
190 :
191 : // Init the Node Control variables
192 1033 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMax = m_maxAirMassFlowRate;
193 : // Currently, fan does not force minimum mass flow, only used for power calculation
194 : // DataLoopNode::Node( outletNodeNum ).MassFlowRateMin = m_minAirMassFlowRate;
195 :
196 : // Initialize all report variables to a known state at beginning of simulation
197 1033 : m_fanPower = 0.0;
198 1033 : m_deltaTemp = 0.0;
199 1033 : m_powerLossToAir = 0.0;
200 1033 : m_fanEnergy = 0.0;
201 2221 : for (auto loop = 0; loop < m_numSpeeds; ++loop) {
202 1188 : m_fanRunTimeFractionAtSpeed[loop] = 0.0;
203 : }
204 1033 : m_objEnvrnFlag = false;
205 : }
206 :
207 7213445 : if (!state.dataGlobal->BeginEnvrnFlag) {
208 7178987 : m_objEnvrnFlag = true;
209 : }
210 :
211 7213445 : m_massFlowRateMaxAvail =
212 7213445 : min(state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMax, state.dataLoopNodes->Node(inletNodeNum).MassFlowRateMaxAvail);
213 7213445 : m_massFlowRateMinAvail =
214 7213445 : min(max(state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMin, state.dataLoopNodes->Node(inletNodeNum).MassFlowRateMinAvail),
215 7213445 : state.dataLoopNodes->Node(inletNodeNum).MassFlowRateMaxAvail);
216 :
217 : // Load the node data in this section for the component simulation
218 : // First need to make sure that the MassFlowRate is between the max and min avail.
219 7213445 : m_inletAirMassFlowRate = min(state.dataLoopNodes->Node(inletNodeNum).MassFlowRate, m_massFlowRateMaxAvail);
220 7213445 : m_inletAirMassFlowRate = max(m_inletAirMassFlowRate, m_massFlowRateMinAvail);
221 :
222 : // Then set the other conditions
223 7213445 : m_inletAirTemp = state.dataLoopNodes->Node(inletNodeNum).Temp;
224 7213445 : m_inletAirHumRat = state.dataLoopNodes->Node(inletNodeNum).HumRat;
225 7213445 : m_inletAirEnthalpy = state.dataLoopNodes->Node(inletNodeNum).Enthalpy;
226 7213445 : }
227 :
228 171 : void FanSystem::set_size(EnergyPlusData &state)
229 : {
230 : static constexpr std::string_view routineName = "FanSystem::set_size ";
231 :
232 171 : Real64 tempFlow = designAirVolFlowRate;
233 171 : bool bPRINT = true;
234 171 : state.dataSize->DataAutosizable = true;
235 171 : state.dataSize->DataEMSOverrideON = m_maxAirFlowRateEMSOverrideOn;
236 171 : state.dataSize->DataEMSOverride = m_maxAirFlowRateEMSOverrideValue;
237 :
238 171 : bool errorsFound = false;
239 342 : SystemAirFlowSizer sizerSystemAirFlow;
240 171 : sizerSystemAirFlow.initializeWithinEP(state, m_fanType, name, bPRINT, routineName);
241 171 : designAirVolFlowRate = sizerSystemAirFlow.size(state, tempFlow, errorsFound);
242 :
243 171 : state.dataSize->DataAutosizable = true; // should be false?
244 171 : state.dataSize->DataEMSOverrideON = false;
245 171 : state.dataSize->DataEMSOverride = 0.0;
246 :
247 171 : if (m_designElecPowerWasAutosized) {
248 :
249 170 : switch (m_powerSizingMethod) {
250 :
251 0 : case PowerSizingMethod::PowerPerFlow: {
252 0 : designElecPower = designAirVolFlowRate * m_elecPowerPerFlowRate;
253 0 : break;
254 : }
255 2 : case PowerSizingMethod::PowerPerFlowPerPressure: {
256 2 : designElecPower = designAirVolFlowRate * deltaPress * m_elecPowerPerFlowRatePerPressure;
257 2 : break;
258 : }
259 168 : case PowerSizingMethod::TotalEfficiencyAndPressure: {
260 168 : designElecPower = designAirVolFlowRate * deltaPress / m_fanTotalEff;
261 168 : break;
262 : }
263 0 : case PowerSizingMethod::Invalid: {
264 : // do nothing
265 0 : break;
266 : }
267 0 : default:
268 0 : assert(false);
269 :
270 : } // end switch
271 :
272 : // report design power
273 170 : BaseSizer::reportSizerOutput(state, m_fanType, name, "Design Electric Power Consumption [W]", designElecPower);
274 :
275 : } // end if power was autosized
276 :
277 171 : m_rhoAirStdInit = state.dataEnvrn->StdRhoAir;
278 171 : m_maxAirMassFlowRate = designAirVolFlowRate * m_rhoAirStdInit;
279 :
280 : // calculate total fan system efficiency at design, else set to 1 to avoid div by zero
281 171 : if (designElecPower > 0.0) {
282 171 : m_fanTotalEff = designAirVolFlowRate * deltaPress / designElecPower;
283 : } else {
284 0 : m_fanTotalEff = 1.0;
285 : }
286 :
287 171 : if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1) { // set up values at speeds
288 20 : m_massFlowAtSpeed.resize(m_numSpeeds, 0.0);
289 20 : m_totEfficAtSpeed.resize(m_numSpeeds, 0.0);
290 63 : for (auto loop = 0; loop < m_numSpeeds; ++loop) {
291 43 : m_massFlowAtSpeed[loop] = m_maxAirMassFlowRate * m_flowFractionAtSpeed[loop];
292 43 : if (m_powerFractionInputAtSpeed[loop]) { // use speed power fraction
293 43 : if (designElecPower > 0.0) {
294 43 : m_totEfficAtSpeed[loop] =
295 43 : m_flowFractionAtSpeed[loop] * designAirVolFlowRate * deltaPress / (designElecPower * m_powerFractionAtSpeed[loop]);
296 : } else {
297 0 : m_totEfficAtSpeed[loop] = 1.0;
298 : }
299 : } else { // use power curve
300 0 : m_totEfficAtSpeed[loop] =
301 0 : m_flowFractionAtSpeed[loop] * designAirVolFlowRate * deltaPress /
302 0 : (designElecPower * Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, m_flowFractionAtSpeed[loop]));
303 0 : m_powerFractionAtSpeed[loop] = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, m_flowFractionAtSpeed[loop]);
304 : }
305 : }
306 : }
307 171 : Real64 rhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataLoopNodes->Node(inletNodeNum).Press, m_inletAirTemp, m_inletAirHumRat);
308 171 : m_designPointFEI = report_fei(state, designAirVolFlowRate, designElecPower, deltaPress, rhoAir);
309 :
310 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanType, name, m_fanType);
311 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanTotEff, name, m_fanTotalEff);
312 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanDeltaP, name, deltaPress);
313 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanVolFlow, name, designAirVolFlowRate);
314 :
315 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwr, name, designElecPower);
316 171 : if (designAirVolFlowRate != 0.0) {
317 513 : OutputReportPredefined::PreDefTableEntry(
318 342 : state, state.dataOutRptPredefined->pdchFanPwrPerFlow, name, designElecPower / designAirVolFlowRate);
319 : }
320 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorIn, name, m_motorInAirFrac);
321 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEnergyIndex, name, m_designPointFEI);
322 :
323 171 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEndUse, name, m_endUseSubcategoryName);
324 :
325 171 : m_objSizingFlag = false;
326 171 : }
327 :
328 2110 : Real64 FanSystem::report_fei(
329 : EnergyPlusData &state, Real64 const designFlowRate, Real64 const designElecPower, Real64 const designDeltaPress, Real64 inletRhoAir)
330 : {
331 : // PURPOSE OF THIS SUBROUTINE:
332 : // Calculate the Fan Energy Index
333 :
334 : // REFERENCES:
335 : // ANSI/AMCA Standard 207-17: Fan System Efficiency and Fan System Input Power Calculation, 2017.
336 : // AANSI / AMCA Standard 208 - 18: Calculation of the Fan Energy Index, 2018.
337 :
338 2110 : assert(state.dataEnvrn->StdRhoAir > 0.0);
339 : // Calculate reference fan shaft power
340 2110 : Real64 refFanShaftPower = (designFlowRate + 0.118) * (designDeltaPress + 100 * inletRhoAir / state.dataEnvrn->StdRhoAir) / (1000 * 0.66);
341 :
342 : // Calculate reference reference fan transmission efficiency
343 2110 : Real64 refFanTransEff = 0.96 * pow((refFanShaftPower / (refFanShaftPower + 1.64)), 0.05);
344 :
345 : // Calculate reference reference fan motor efficiency
346 2110 : Real64 refFanMotorOutput = refFanShaftPower / refFanTransEff;
347 :
348 : Real64 refFanMotorEff;
349 2110 : if (refFanMotorOutput < 185.0) {
350 6237 : refFanMotorEff = -0.003812 * pow(std::log10(refFanMotorOutput), 4) + 0.025834 * pow(std::log10(refFanMotorOutput), 3) -
351 4158 : 0.072577 * pow(std::log10(refFanMotorOutput), 2) + 0.125559 * std::log10(refFanMotorOutput) + 0.850274;
352 : } else {
353 31 : refFanMotorEff = 0.962;
354 : }
355 :
356 : // Calculate reference reference fan motor controller efficiency
357 2110 : Real64 refFanMotorCtrlEff = 1;
358 :
359 2110 : Real64 refFanElecPower = refFanShaftPower / (refFanTransEff * refFanMotorEff * refFanMotorCtrlEff);
360 :
361 2110 : if (designElecPower > 0.0) {
362 2086 : return refFanElecPower * 1000 / designElecPower;
363 : } else {
364 24 : return 0.0;
365 : }
366 : }
367 :
368 171 : FanSystem::FanSystem(EnergyPlusData &state, std::string const &objectName)
369 : : availSchedIndex(0), inletNodeNum(0), outletNodeNum(0), designAirVolFlowRate(0.0), speedControl(SpeedControlMethod::NotSet), deltaPress(0.0),
370 : designElecPower(0.0), powerModFuncFlowFractionCurveIndex(0), AirLoopNum(0), AirPathFlag(false), fanIsSecondaryDriver(false),
371 : m_fanType_Num(0), m_designAirVolFlowRateWasAutosized(false), m_minPowerFlowFrac(0.0), m_motorEff(0.0), m_motorInAirFrac(0.0),
372 : m_designElecPowerWasAutosized(false), m_powerSizingMethod(PowerSizingMethod::Invalid), m_elecPowerPerFlowRate(0.0),
373 : m_elecPowerPerFlowRatePerPressure(0.0), m_fanTotalEff(0.0), m_nightVentPressureDelta(0.0), m_nightVentFlowFraction(0.0), m_zoneNum(0),
374 : m_zoneRadFract(0.0), m_heatLossesDestination(ThermalLossDestination::Invalid), m_qdotConvZone(0.0), m_qdotRadZone(0.0), m_numSpeeds(0),
375 : m_inletAirMassFlowRate(0.0), m_outletAirMassFlowRate(0.0), m_maxAirMassFlowRate(0.0), m_inletAirTemp(0.0), m_outletAirTemp(0.0),
376 : m_inletAirHumRat(0.0), m_outletAirHumRat(0.0), m_inletAirEnthalpy(0.0), m_outletAirEnthalpy(0.0), m_objTurnFansOn(false),
377 : m_objTurnFansOff(false), m_objEnvrnFlag(true), m_objSizingFlag(true), m_fanPower(0.0), m_fanEnergy(0.0),
378 : m_maxAirFlowRateEMSOverrideOn(false), m_maxAirFlowRateEMSOverrideValue(0.0), m_eMSFanPressureOverrideOn(false), m_eMSFanPressureValue(0.0),
379 : m_eMSFanEffOverrideOn(false), m_eMSFanEffValue(0.0), m_eMSMaxMassFlowOverrideOn(false), m_eMSAirMassFlowValue(0.0),
380 : m_faultyFilterFlag(false), m_faultyFilterIndex(0),
381 :
382 171 : m_massFlowRateMaxAvail(0.0), m_massFlowRateMinAvail(0.0), m_rhoAirStdInit(0.0), m_designPointFEI(0.0)
383 : // oneTimePowerCurveCheck_( true )
384 : {
385 :
386 : static constexpr std::string_view routineName = "HVACFan constructor ";
387 : int numAlphas; // Number of elements in the alpha array
388 : int numNums; // Number of elements in the numeric array
389 : int numTotFields; // Total number of alpha and numeric fields
390 : int IOStat; // IO Status when calling get input subroutine
391 171 : bool errorsFound = false;
392 342 : std::string locCurrentModuleObject = "Fan:SystemModel";
393 342 : Array1D_string alphaArgs;
394 342 : Array1D_string alphaFieldNames;
395 342 : Array1D_bool isAlphaFieldBlank;
396 342 : Array1D<Real64> numericArgs;
397 342 : Array1D_string numericFieldNames;
398 342 : Array1D_bool isNumericFieldBlank;
399 171 : int objectNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, locCurrentModuleObject, objectName);
400 171 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, locCurrentModuleObject, numTotFields, numAlphas, numNums);
401 171 : if (numAlphas > 0) {
402 171 : alphaArgs.allocate(numAlphas);
403 171 : alphaFieldNames.allocate(numAlphas);
404 171 : isAlphaFieldBlank.allocate(numAlphas);
405 : }
406 171 : if (numNums > 0) {
407 171 : numericArgs.allocate(numNums);
408 171 : numericFieldNames.allocate(numNums);
409 171 : isNumericFieldBlank.allocate(numNums);
410 : }
411 :
412 171 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
413 : locCurrentModuleObject,
414 : objectNum,
415 : alphaArgs,
416 : numAlphas,
417 : numericArgs,
418 : numNums,
419 : IOStat,
420 : isNumericFieldBlank,
421 : isAlphaFieldBlank,
422 : alphaFieldNames,
423 : numericFieldNames);
424 :
425 171 : name = alphaArgs(1);
426 : // TODO how to check for unique names across objects during get input?
427 171 : m_fanType = locCurrentModuleObject;
428 171 : m_fanType_Num = DataHVACGlobals::FanType_SystemModelObject;
429 171 : if (isAlphaFieldBlank(2)) {
430 2 : availSchedIndex = DataGlobalConstants::ScheduleAlwaysOn;
431 : } else {
432 169 : availSchedIndex = ScheduleManager::GetScheduleIndex(state, alphaArgs(2));
433 169 : if (availSchedIndex == 0) {
434 0 : ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
435 0 : ShowContinueError(state, "Invalid " + alphaFieldNames(2) + " = " + alphaArgs(2));
436 0 : errorsFound = true;
437 : }
438 : }
439 171 : inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
440 171 : alphaArgs(3),
441 : errorsFound,
442 : DataLoopNode::ConnectionObjectType::FanSystemModel,
443 171 : alphaArgs(1),
444 : DataLoopNode::NodeFluidType::Air,
445 : DataLoopNode::ConnectionType::Inlet,
446 : NodeInputManager::CompFluidStream::Primary,
447 171 : DataLoopNode::ObjectIsNotParent);
448 171 : outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
449 171 : alphaArgs(4),
450 : errorsFound,
451 : DataLoopNode::ConnectionObjectType::FanSystemModel,
452 171 : alphaArgs(1),
453 : DataLoopNode::NodeFluidType::Air,
454 : DataLoopNode::ConnectionType::Outlet,
455 : NodeInputManager::CompFluidStream::Primary,
456 171 : DataLoopNode::ObjectIsNotParent);
457 :
458 171 : BranchNodeConnections::TestCompSet(state, locCurrentModuleObject, alphaArgs(1), alphaArgs(3), alphaArgs(4), "Air Nodes");
459 :
460 171 : designAirVolFlowRate = numericArgs(1);
461 171 : if (designAirVolFlowRate == DataSizing::AutoSize) {
462 126 : m_designAirVolFlowRateWasAutosized = true;
463 : }
464 :
465 171 : if (isAlphaFieldBlank(5)) {
466 0 : speedControl = SpeedControlMethod::Discrete;
467 171 : } else if (UtilityRoutines::SameString(alphaArgs(5), "Continuous")) {
468 21 : speedControl = SpeedControlMethod::Continuous;
469 150 : } else if (UtilityRoutines::SameString(alphaArgs(5), "Discrete")) {
470 150 : speedControl = SpeedControlMethod::Discrete;
471 : } else {
472 0 : ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
473 0 : ShowContinueError(state, "Invalid " + alphaFieldNames(5) + " = " + alphaArgs(5));
474 0 : errorsFound = true;
475 : }
476 :
477 171 : m_minPowerFlowFrac = numericArgs(2);
478 171 : deltaPress = numericArgs(3);
479 171 : if (deltaPress <= 0.0) {
480 0 : ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + " zero or negative, invalid entry in " + numericFieldNames(3));
481 0 : errorsFound = true;
482 : }
483 171 : m_motorEff = numericArgs(4);
484 171 : m_motorInAirFrac = numericArgs(5);
485 171 : designElecPower = numericArgs(6);
486 171 : if (designElecPower == DataSizing::AutoSize) {
487 170 : m_designElecPowerWasAutosized = true;
488 : }
489 171 : if (m_designElecPowerWasAutosized) {
490 170 : if (isAlphaFieldBlank(6)) {
491 0 : m_powerSizingMethod = PowerSizingMethod::PowerPerFlowPerPressure;
492 170 : } else if (UtilityRoutines::SameString(alphaArgs(6), "PowerPerFlow")) {
493 0 : m_powerSizingMethod = PowerSizingMethod::PowerPerFlow;
494 170 : } else if (UtilityRoutines::SameString(alphaArgs(6), "PowerPerFlowPerPressure")) {
495 2 : m_powerSizingMethod = PowerSizingMethod::PowerPerFlowPerPressure;
496 168 : } else if (UtilityRoutines::SameString(alphaArgs(6), "TotalEfficiencyAndPressure")) {
497 168 : m_powerSizingMethod = PowerSizingMethod::TotalEfficiencyAndPressure;
498 : } else {
499 0 : ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
500 0 : ShowContinueError(state, "Invalid " + alphaFieldNames(6) + " = " + alphaArgs(6));
501 0 : errorsFound = true;
502 : }
503 170 : m_elecPowerPerFlowRate = numericArgs(7);
504 170 : m_elecPowerPerFlowRatePerPressure = numericArgs(8);
505 170 : m_fanTotalEff = numericArgs(9);
506 : }
507 171 : if (!isAlphaFieldBlank(7)) {
508 21 : powerModFuncFlowFractionCurveIndex = Curve::GetCurveIndex(state, alphaArgs(7));
509 21 : if (powerModFuncFlowFractionCurveIndex == 0) {
510 0 : ShowWarningError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
511 0 : ShowContinueError(state, "Invalid " + alphaFieldNames(7) + " = " + alphaArgs(7));
512 0 : ShowContinueError(state, "Curve not found.");
513 0 : if (speedControl == SpeedControlMethod::Continuous) {
514 0 : errorsFound = true;
515 : }
516 : }
517 : } else { // blank
518 150 : if (speedControl == SpeedControlMethod::Continuous) {
519 0 : ShowWarningError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
520 0 : ShowContinueError(state, "Continuous speed control requires a fan power curve in " + alphaFieldNames(7) + " = " + alphaArgs(7));
521 0 : errorsFound = true;
522 : }
523 : }
524 171 : m_nightVentPressureDelta = numericArgs(10);
525 171 : m_nightVentFlowFraction = numericArgs(11); // not used
526 171 : m_zoneNum = UtilityRoutines::FindItemInList(alphaArgs(8), state.dataHeatBal->Zone);
527 171 : if (m_zoneNum > 0) m_heatLossesDestination = ThermalLossDestination::ZoneGains;
528 171 : if (m_zoneNum == 0) {
529 171 : if (isAlphaFieldBlank(8)) {
530 171 : m_heatLossesDestination = ThermalLossDestination::LostToOutside;
531 : } else {
532 0 : m_heatLossesDestination = ThermalLossDestination::LostToOutside;
533 0 : ShowWarningError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
534 0 : ShowContinueError(state, "Invalid " + alphaFieldNames(8) + " = " + alphaArgs(8));
535 0 : ShowContinueError(state, "Zone name not found. Fan motor heat losses will not be added to a zone");
536 : // continue with simulation but motor losses not sent to a zone.
537 : }
538 : }
539 171 : m_zoneRadFract = numericArgs(12);
540 171 : if (!isAlphaFieldBlank(9)) {
541 72 : m_endUseSubcategoryName = alphaArgs(9);
542 : } else {
543 99 : m_endUseSubcategoryName = "General";
544 : }
545 :
546 171 : if (!isNumericFieldBlank(13)) {
547 49 : m_numSpeeds = numericArgs(13);
548 : } else {
549 122 : m_numSpeeds = 1;
550 : }
551 171 : m_fanRunTimeFractionAtSpeed.resize(m_numSpeeds, 0.0);
552 171 : if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1) {
553 : // should have field sets
554 20 : m_flowFractionAtSpeed.resize(m_numSpeeds, 0.0);
555 20 : m_powerFractionAtSpeed.resize(m_numSpeeds, 0.0);
556 20 : m_powerFractionInputAtSpeed.resize(m_numSpeeds, false);
557 20 : if (m_numSpeeds == ((numNums - 13) / 2) || m_numSpeeds == ((numNums + 1 - 13) / 2)) {
558 63 : for (auto loopSet = 0; loopSet < m_numSpeeds; ++loopSet) {
559 43 : m_flowFractionAtSpeed[loopSet] = numericArgs(13 + loopSet * 2 + 1);
560 43 : if (!isNumericFieldBlank(13 + loopSet * 2 + 2)) {
561 43 : m_powerFractionAtSpeed[loopSet] = numericArgs(13 + loopSet * 2 + 2);
562 43 : m_powerFractionInputAtSpeed[loopSet] = true;
563 : } else {
564 0 : m_powerFractionInputAtSpeed[loopSet] = false;
565 : }
566 20 : }
567 : } else {
568 : // field set input does not match number of speeds, throw warning
569 0 : ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
570 0 : ShowContinueError(state, "Fan with Discrete speed control does not have input for speed data that matches the number of speeds.");
571 0 : errorsFound = true;
572 : }
573 : // check that flow fractions are increasing
574 20 : bool increasingOrderError = false;
575 43 : for (auto loop = 0; loop < (m_numSpeeds - 1); ++loop) {
576 23 : if (m_flowFractionAtSpeed[loop] > m_flowFractionAtSpeed[loop + 1]) {
577 0 : increasingOrderError = true;
578 : }
579 : }
580 20 : if (increasingOrderError) {
581 0 : ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
582 0 : ShowContinueError(state,
583 : "Fan with Discrete speed control and multiple speed levels does not have input with flow fractions arranged in "
584 : "increasing order.");
585 0 : errorsFound = true;
586 : }
587 : }
588 :
589 : // check if power curve present when any speeds have no power fraction
590 171 : if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1 && powerModFuncFlowFractionCurveIndex == 0) {
591 20 : bool foundMissingPowerFraction = false;
592 63 : for (auto loop = 0; loop < m_numSpeeds; ++loop) {
593 43 : if (!m_powerFractionInputAtSpeed[loop]) {
594 0 : foundMissingPowerFraction = true;
595 : }
596 : }
597 20 : if (foundMissingPowerFraction) {
598 : // field set input does not match number of speeds, throw warning
599 0 : ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
600 0 : ShowContinueError(
601 : state,
602 : "Fan with Discrete speed control does not have input for power fraction at all speed levels and does not have a power curve.");
603 0 : errorsFound = true;
604 : }
605 : }
606 :
607 171 : if (errorsFound) {
608 0 : ShowFatalError(state, std::string{routineName} + "Errors found in input for fan name = " + name + ". Program terminates.");
609 : }
610 :
611 342 : SetupOutputVariable(state,
612 : "Fan Electricity Rate",
613 : OutputProcessor::Unit::W,
614 : m_fanPower,
615 : OutputProcessor::SOVTimeStepType::System,
616 : OutputProcessor::SOVStoreType::Average,
617 171 : name);
618 342 : SetupOutputVariable(state,
619 : "Fan Rise in Air Temperature",
620 : OutputProcessor::Unit::deltaC,
621 : m_deltaTemp,
622 : OutputProcessor::SOVTimeStepType::System,
623 : OutputProcessor::SOVStoreType::Average,
624 171 : name);
625 342 : SetupOutputVariable(state,
626 : "Fan Heat Gain to Air",
627 : OutputProcessor::Unit::W,
628 : m_powerLossToAir,
629 : OutputProcessor::SOVTimeStepType::System,
630 : OutputProcessor::SOVStoreType::Average,
631 171 : name);
632 342 : SetupOutputVariable(state,
633 : "Fan Electricity Energy",
634 : OutputProcessor::Unit::J,
635 : m_fanEnergy,
636 : OutputProcessor::SOVTimeStepType::System,
637 : OutputProcessor::SOVStoreType::Summed,
638 : name,
639 : _,
640 : "Electricity",
641 : "Fans",
642 : m_endUseSubcategoryName,
643 171 : "System");
644 342 : SetupOutputVariable(state,
645 : "Fan Air Mass Flow Rate",
646 : OutputProcessor::Unit::kg_s,
647 : m_outletAirMassFlowRate,
648 : OutputProcessor::SOVTimeStepType::System,
649 : OutputProcessor::SOVStoreType::Average,
650 171 : name);
651 171 : if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds == 1) {
652 260 : SetupOutputVariable(state,
653 : "Fan Runtime Fraction",
654 : OutputProcessor::Unit::None,
655 130 : m_fanRunTimeFractionAtSpeed[0],
656 : OutputProcessor::SOVTimeStepType::System,
657 : OutputProcessor::SOVStoreType::Average,
658 130 : name);
659 41 : } else if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1) {
660 63 : for (auto speedLoop = 0; speedLoop < m_numSpeeds; ++speedLoop) {
661 129 : SetupOutputVariable(state,
662 86 : "Fan Runtime Fraction Speed " + fmt::to_string(speedLoop + 1),
663 : OutputProcessor::Unit::None,
664 43 : m_fanRunTimeFractionAtSpeed[speedLoop],
665 : OutputProcessor::SOVTimeStepType::System,
666 : OutputProcessor::SOVStoreType::Average,
667 : name);
668 : }
669 : }
670 :
671 171 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
672 33 : SetupEMSInternalVariable(state, "Fan Maximum Mass Flow Rate", name, "[kg/s]", m_maxAirMassFlowRate);
673 33 : SetupEMSActuator(state, "Fan", name, "Fan Air Mass Flow Rate", "[kg/s]", m_eMSMaxMassFlowOverrideOn, m_eMSAirMassFlowValue);
674 33 : SetupEMSInternalVariable(state, "Fan Nominal Pressure Rise", name, "[Pa]", deltaPress);
675 33 : SetupEMSActuator(state, "Fan", name, "Fan Pressure Rise", "[Pa]", m_eMSFanPressureOverrideOn, m_eMSFanPressureValue);
676 33 : SetupEMSInternalVariable(state, "Fan Nominal Total Efficiency", name, "[fraction]", m_fanTotalEff);
677 33 : SetupEMSActuator(state, "Fan", name, "Fan Total Efficiency", "[fraction]", m_eMSFanEffOverrideOn, m_eMSFanEffValue);
678 99 : SetupEMSActuator(
679 66 : state, "Fan", name, "Fan Autosized Air Flow Rate", "[m3/s]", m_maxAirFlowRateEMSOverrideOn, m_maxAirFlowRateEMSOverrideValue);
680 : }
681 :
682 171 : if (m_heatLossesDestination == ThermalLossDestination::ZoneGains) {
683 0 : SetupZoneInternalGain(state, m_zoneNum, name, DataHeatBalance::IntGainType::FanSystemModel, &m_qdotConvZone, nullptr, &m_qdotRadZone);
684 : }
685 :
686 171 : alphaArgs.deallocate();
687 171 : alphaFieldNames.deallocate();
688 171 : isAlphaFieldBlank.deallocate();
689 171 : numericArgs.deallocate();
690 171 : numericFieldNames.deallocate();
691 171 : isNumericFieldBlank.deallocate();
692 :
693 171 : bool anyEMSRan = false;
694 171 : EMSManager::ManageEMS(state, EMSManager::EMSCallFrom::ComponentGetInput, anyEMSRan, ObjexxFCL::Optional_int_const());
695 171 : }
696 :
697 : void
698 7213218 : FanSystem::calcSimpleSystemFan(EnergyPlusData &state,
699 : Optional<Real64 const> flowFraction, // Flow fraction for entire timestep (not used if flow ratios are present)
700 : Optional<Real64 const> pressureRise, // Pressure difference to use for DeltaPress
701 : Optional<Real64 const> flowRatio1, // Flow ratio in operating mode 1
702 : Optional<Real64 const> runTimeFrac1, // Run time fraction in operating mode 1
703 : Optional<Real64 const> flowRatio2, // Flow ratio in operating mode 2
704 : Optional<Real64 const> runTimeFrac2, // Run time fraction in operating mode 2
705 : Optional<Real64 const> pressureRise2 // Pressure difference to use for operating mode 2
706 : )
707 : {
708 14426436 : std::vector<Real64> localPressureRise; // [0] is operating mode 1, [1] is operating mode 2
709 : Real64 localFlowFraction;
710 : Real64 localFanTotEff;
711 14426436 : std::vector<Real64> localAirMassFlow;
712 14426436 : std::vector<Real64> localFlowRatio;
713 14426436 : std::vector<Real64> localRunTimeFrac;
714 7213218 : bool localUseFlowRatiosAndRunTimeFracs = false;
715 :
716 7213218 : int localNumModes = 1; // Number of operating modes, 1 or 2 ( e.g. heating, ventilating, cooling)
717 7213218 : if (present(flowRatio2) && present(runTimeFrac2)) localNumModes = 2;
718 7213218 : localPressureRise.resize(2, 0.0);
719 7213218 : localAirMassFlow.resize(2, 0.0);
720 7213218 : localFlowRatio.resize(2, 0.0);
721 7213218 : localRunTimeFrac.resize(2, 1.0);
722 :
723 7213218 : if (state.dataHVACGlobal->NightVentOn) {
724 : // assume if non-zero inputs for night data then this fan is to be used with that data
725 0 : if (m_nightVentPressureDelta > 0.0) {
726 0 : localPressureRise[0] = m_nightVentPressureDelta;
727 0 : localPressureRise[1] = m_nightVentPressureDelta;
728 : }
729 :
730 0 : if (m_maxAirMassFlowRate > 0.0) { // protect div by 0
731 0 : localFlowFraction = m_inletAirMassFlowRate / m_maxAirMassFlowRate;
732 : } else {
733 0 : localFlowFraction = 1.0;
734 : }
735 0 : localAirMassFlow[0] = m_inletAirMassFlowRate;
736 :
737 : } else { // not in night mode
738 7213218 : if (present(pressureRise)) {
739 4628 : localPressureRise[0] = pressureRise;
740 : } else {
741 7208590 : localPressureRise[0] = deltaPress;
742 : }
743 7213218 : if (present(pressureRise2)) {
744 0 : localPressureRise[1] = pressureRise2;
745 : } else {
746 7213218 : localPressureRise[1] = deltaPress;
747 : }
748 7213218 : if (present(flowFraction)) {
749 936396 : localFlowFraction = flowFraction;
750 936396 : localAirMassFlow[0] = localFlowFraction * m_maxAirMassFlowRate;
751 : } else {
752 6276822 : if (m_maxAirMassFlowRate > 0.0) { // protect div by 0
753 6276822 : localFlowFraction = m_inletAirMassFlowRate / m_maxAirMassFlowRate;
754 : } else {
755 0 : localFlowFraction = 1.0;
756 : }
757 6276822 : localAirMassFlow[0] = m_inletAirMassFlowRate;
758 : }
759 7213218 : if (present(flowRatio1) && present(flowRatio2) && present(runTimeFrac1) && present(runTimeFrac2)) {
760 3008078 : localUseFlowRatiosAndRunTimeFracs = true;
761 3008078 : localRunTimeFrac[0] = runTimeFrac1;
762 3008078 : localRunTimeFrac[1] = runTimeFrac2;
763 3008078 : localFlowRatio[0] = flowRatio1;
764 3008078 : localAirMassFlow[0] = localFlowRatio[0] * m_maxAirMassFlowRate * localRunTimeFrac[0];
765 3008078 : localFlowRatio[1] = flowRatio2;
766 3008078 : localAirMassFlow[1] = localFlowRatio[1] * m_maxAirMassFlowRate * localRunTimeFrac[1];
767 : } else {
768 4205140 : localRunTimeFrac[0] = 1.0; // if runTimeFracs are not present, assume single-mode operation
769 4205140 : localRunTimeFrac[1] = 0.0; // if runTimeFracs are not present, assume single-mode operation
770 : }
771 : }
772 :
773 7213218 : Real64 localFaultMaxAirMassFlow = 0.0;
774 7213218 : bool faultActive = false;
775 7213218 : Real64 localFaultPressureRise = 0.0;
776 14426436 : if (m_faultyFilterFlag && (state.dataFaultsMgr->NumFaultyAirFilter > 0) && (!state.dataGlobal->WarmupFlag) &&
777 7213218 : (!state.dataGlobal->DoingSizing) && state.dataGlobal->DoWeathSim && (!m_eMSMaxMassFlowOverrideOn) && (!m_eMSFanPressureOverrideOn)) {
778 0 : if (ScheduleManager::GetCurrentScheduleValue(state, state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).AvaiSchedPtr) > 0) {
779 0 : faultActive = true;
780 0 : Real64 FanDesignFlowRateDec = 0; // Decrease of the Fan Design Volume Flow Rate [m3/sec]
781 0 : FanDesignFlowRateDec = Fans::CalFaultyFanAirFlowReduction(
782 : state,
783 : name,
784 : designAirVolFlowRate,
785 : deltaPress,
786 0 : (ScheduleManager::GetCurrentScheduleValue(
787 0 : state, state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).FaultyAirFilterPressFracSchePtr) -
788 : 1) *
789 0 : deltaPress,
790 0 : state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).FaultyAirFilterFanCurvePtr);
791 :
792 0 : localFaultMaxAirMassFlow = m_maxAirMassFlowRate - FanDesignFlowRateDec * m_rhoAirStdInit;
793 :
794 0 : localFaultPressureRise =
795 0 : ScheduleManager::GetCurrentScheduleValue(
796 0 : state, state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).FaultyAirFilterPressFracSchePtr) *
797 0 : deltaPress;
798 : }
799 : }
800 :
801 17434514 : for (int mode = 0; mode < localNumModes; ++mode) {
802 : // EMS override MassFlow, DeltaPress, and FanEff
803 10221296 : if (m_eMSFanPressureOverrideOn) localPressureRise[mode] = m_eMSFanPressureValue;
804 10221296 : if (m_eMSFanEffOverrideOn) localFanTotEff = m_eMSFanEffValue;
805 10221296 : if (m_eMSMaxMassFlowOverrideOn) {
806 0 : localAirMassFlow[mode] = m_eMSAirMassFlowValue;
807 : }
808 :
809 10221296 : localAirMassFlow[mode] = min(localAirMassFlow[mode], m_maxAirMassFlowRate);
810 10221296 : if (faultActive) {
811 0 : localAirMassFlow[mode] = min(localAirMassFlow[mode], localFaultMaxAirMassFlow);
812 0 : localPressureRise[mode] = localFaultPressureRise;
813 : }
814 10221296 : localFlowFraction = localAirMassFlow[0] / m_maxAirMassFlowRate;
815 10221296 : localFlowFraction = min(1.0, localFlowFraction);
816 :
817 10221296 : if (localRunTimeFrac[mode] > 0.0) {
818 7629948 : localFlowRatio[mode] = localAirMassFlow[mode] / (m_maxAirMassFlowRate * localRunTimeFrac[mode]);
819 : }
820 10221296 : localFlowRatio[mode] = min(1.0, localFlowRatio[mode]);
821 : }
822 :
823 : // zero these now, because the may accumulate across multiple operating modes
824 7213218 : m_powerLossToAir = 0.0;
825 7213218 : m_fanPower = 0.0;
826 7213218 : m_outletAirMassFlowRate = 0.0;
827 7213218 : if (speedControl == SpeedControlMethod::Discrete) {
828 14910145 : for (auto loop = 0; loop < m_numSpeeds; ++loop) {
829 8307133 : m_fanRunTimeFractionAtSpeed[loop] = 0.0;
830 : }
831 : }
832 :
833 14183572 : if ((ScheduleManager::GetCurrentScheduleValue(state, availSchedIndex) > 0.0 || m_objTurnFansOn) && !m_objTurnFansOff &&
834 6970354 : ((localAirMassFlow[0] + localAirMassFlow[1]) > 0.0)) {
835 : // fan is running
836 :
837 14996976 : for (int mode = 0; mode < localNumModes; ++mode) {
838 :
839 : // if no flow for this mode then continue to the next mode
840 8844859 : if (localAirMassFlow[mode] == 0.0) continue;
841 :
842 6846631 : switch (speedControl) {
843 :
844 6330470 : case SpeedControlMethod::Discrete: {
845 : //
846 6330470 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction <= 0.0) {
847 0 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0;
848 : }
849 6330470 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction < 0.7) {
850 2124 : state.dataHVACGlobal->OnOffFanPartLoadFraction =
851 : 0.7; // a warning message is already issued from the DX coils or gas heating coil
852 : }
853 6330470 : if (localUseFlowRatiosAndRunTimeFracs) {
854 : // Use flow ratios and runtimefractions pass from parent (allows fan to cycle at a specified speed)
855 3231922 : Real64 locRunTimeFraction(0.0);
856 3231922 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0) {
857 3059507 : locRunTimeFraction = localRunTimeFrac[mode];
858 : } else {
859 172415 : locRunTimeFraction = max(0.0, min(1.0, localRunTimeFrac[mode] / state.dataHVACGlobal->OnOffFanPartLoadFraction));
860 : }
861 3231922 : Real64 locFlowRatio = localFlowRatio[mode]; // Current mode flow rate / max flow rate
862 3231922 : Real64 locLowSpeedFanRunTimeFrac = 0.0;
863 3231922 : Real64 locHiSpeedFanRunTimeFrac = 0.0;
864 3231922 : if (m_numSpeeds == 1) { // CV or OnOff
865 1717572 : localFanTotEff = m_fanTotalEff;
866 1717572 : locHiSpeedFanRunTimeFrac = locRunTimeFraction * locFlowRatio;
867 1717572 : m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
868 1717572 : m_fanPower += max(
869 1717572 : 0.0, locHiSpeedFanRunTimeFrac * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit));
870 1514350 : } else if (m_numSpeeds > 1) { // multi speed
871 :
872 : // find which two speed levels bracket flow ratios and calculate runtimefraction at each speed
873 : // ideally the flow ratios passed in will match one of the fan m_flowFractionAtSpeed but it is not required
874 1514350 : int lowSideSpeed = -1;
875 1514350 : int hiSideSpeed = -1;
876 :
877 1514350 : if (locFlowRatio <= m_flowFractionAtSpeed[0]) { // on/off at lowest speed
878 640964 : hiSideSpeed = 0;
879 640964 : locHiSpeedFanRunTimeFrac = locFlowRatio * locRunTimeFraction / m_flowFractionAtSpeed[0];
880 640964 : m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
881 : } else {
882 873386 : lowSideSpeed = 0; // hush up cppcheck
883 873386 : hiSideSpeed = 0; // hush up cppcheck
884 873386 : for (auto loop = 0; loop < m_numSpeeds - 1; ++loop) {
885 873386 : if ((m_flowFractionAtSpeed[loop] <= locFlowRatio) && (locFlowRatio <= m_flowFractionAtSpeed[loop + 1])) {
886 873386 : lowSideSpeed = loop;
887 873386 : hiSideSpeed = loop + 1;
888 873386 : break;
889 : }
890 : }
891 873386 : Real64 locLowSpeedTimeFrac = (m_flowFractionAtSpeed[hiSideSpeed] - locFlowRatio) /
892 873386 : (m_flowFractionAtSpeed[hiSideSpeed] - m_flowFractionAtSpeed[lowSideSpeed]);
893 873386 : locLowSpeedFanRunTimeFrac = locLowSpeedTimeFrac * localRunTimeFrac[mode];
894 873386 : locHiSpeedFanRunTimeFrac = (1 - locLowSpeedTimeFrac) * localRunTimeFrac[mode];
895 873386 : m_fanRunTimeFractionAtSpeed[lowSideSpeed] += locLowSpeedFanRunTimeFrac;
896 873386 : m_fanRunTimeFractionAtSpeed[hiSideSpeed] += locHiSpeedFanRunTimeFrac;
897 : }
898 1514350 : if (lowSideSpeed != -1 && hiSideSpeed != -1) {
899 873386 : m_fanPower += max(0.0,
900 1746772 : locLowSpeedFanRunTimeFrac * m_massFlowAtSpeed[lowSideSpeed] * localPressureRise[mode] /
901 873386 : (m_totEfficAtSpeed[lowSideSpeed] * m_rhoAirStdInit) +
902 1746772 : locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
903 873386 : (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
904 640964 : } else if (lowSideSpeed == -1 && hiSideSpeed == 0) {
905 640964 : m_fanPower += max(0.0,
906 640964 : locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
907 640964 : (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
908 : }
909 : }
910 : } else {
911 : // Use localFlowFraction which is not locked at a particular flow ratio (legacy method for fan:onoff)
912 3098548 : Real64 locFanRunTimeFraction(0.0);
913 3098548 : Real64 locLowSpeedFanRunTimeFrac = 0.0;
914 3098548 : Real64 locHiSpeedFanRunTimeFrac = 0.0;
915 3098548 : if (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0) {
916 3085641 : locFanRunTimeFraction = localFlowFraction;
917 : } else {
918 12907 : locFanRunTimeFraction = max(0.0, min(1.0, localFlowFraction / state.dataHVACGlobal->OnOffFanPartLoadFraction));
919 : }
920 3098548 : if (m_numSpeeds == 1) { // CV or OnOff
921 2906783 : localFanTotEff = m_fanTotalEff;
922 2906783 : locHiSpeedFanRunTimeFrac = locFanRunTimeFraction;
923 2906783 : m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
924 2906783 : m_fanPower += max(
925 2906783 : 0.0, locHiSpeedFanRunTimeFrac * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit));
926 191765 : } else if (m_numSpeeds > 1) { // multi speed
927 :
928 : // find which two speed levels bracket flow fraction and calculate runtimefraction
929 191765 : int lowSideSpeed = -1;
930 191765 : int hiSideSpeed = -1;
931 :
932 191765 : if (locFanRunTimeFraction < m_flowFractionAtSpeed[0]) { // on/off between zero and lowest speed
933 5090 : hiSideSpeed = 0;
934 5090 : locHiSpeedFanRunTimeFrac = locFanRunTimeFraction / m_flowFractionAtSpeed[0];
935 5090 : m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
936 : } else {
937 186675 : lowSideSpeed = 0; // hush up cppcheck
938 186675 : hiSideSpeed = 0; // hush up cppcheck
939 192965 : for (auto loop = 0; loop < m_numSpeeds - 1; ++loop) {
940 385930 : if ((m_flowFractionAtSpeed[loop] <= locFanRunTimeFraction) &&
941 192965 : (locFanRunTimeFraction <= m_flowFractionAtSpeed[loop + 1])) {
942 186675 : lowSideSpeed = loop;
943 186675 : hiSideSpeed = loop + 1;
944 186675 : break;
945 : }
946 : }
947 373350 : locLowSpeedFanRunTimeFrac = (m_flowFractionAtSpeed[hiSideSpeed] - locFanRunTimeFraction) /
948 186675 : (m_flowFractionAtSpeed[hiSideSpeed] - m_flowFractionAtSpeed[lowSideSpeed]);
949 373350 : locHiSpeedFanRunTimeFrac = (locFanRunTimeFraction - m_flowFractionAtSpeed[lowSideSpeed]) /
950 186675 : (m_flowFractionAtSpeed[hiSideSpeed] - m_flowFractionAtSpeed[lowSideSpeed]);
951 186675 : m_fanRunTimeFractionAtSpeed[lowSideSpeed] += locLowSpeedFanRunTimeFrac;
952 186675 : m_fanRunTimeFractionAtSpeed[hiSideSpeed] += locHiSpeedFanRunTimeFrac;
953 : }
954 191765 : if (lowSideSpeed != -1 && hiSideSpeed != -1) {
955 186675 : m_fanPower += max(0.0,
956 373350 : locLowSpeedFanRunTimeFrac * m_massFlowAtSpeed[lowSideSpeed] * localPressureRise[mode] /
957 186675 : (m_totEfficAtSpeed[lowSideSpeed] * m_rhoAirStdInit) +
958 373350 : locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
959 186675 : (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
960 5090 : } else if (lowSideSpeed == -1 && hiSideSpeed == 0) {
961 5090 : m_fanPower += max(0.0,
962 5090 : locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
963 5090 : (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
964 : }
965 : }
966 : }
967 6330470 : localFanTotEff = m_fanTotalEff;
968 6330470 : break;
969 : }
970 516161 : case SpeedControlMethod::Continuous: {
971 516161 : localFanTotEff = m_fanTotalEff;
972 516161 : Real64 locFlowRatio(0.0);
973 516161 : Real64 locFanRunTimeFraction(0.0);
974 516161 : if (localUseFlowRatiosAndRunTimeFracs) {
975 155334 : locFlowRatio = localFlowRatio[mode];
976 155334 : locFanRunTimeFraction = localRunTimeFrac[mode];
977 : } else {
978 360827 : locFlowRatio = localFlowFraction;
979 360827 : locFanRunTimeFraction = 1.0;
980 : }
981 :
982 516161 : Real64 localFlowFractionForPower = max(m_minPowerFlowFrac, locFlowRatio);
983 516161 : Real64 localPowerFraction(0.0);
984 516161 : if (state.dataHVACGlobal->NightVentOn) {
985 0 : localPowerFraction = 1.0; // not sure why, but legacy fan had this for night ventilation
986 : } else {
987 516161 : localPowerFraction = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, localFlowFractionForPower);
988 : }
989 516161 : Real64 localfanPower = max(0.0,
990 516161 : locFanRunTimeFraction * localPowerFraction * m_maxAirMassFlowRate * localPressureRise[mode] /
991 1032322 : (localFanTotEff * m_rhoAirStdInit));
992 516161 : Real64 fanShaftPower = m_motorEff * localfanPower;
993 516161 : Real64 localpowerLossToAir = fanShaftPower + (localfanPower - fanShaftPower) * m_motorInAirFrac;
994 516161 : m_outletAirEnthalpy = m_inletAirEnthalpy + localpowerLossToAir / localAirMassFlow[mode]; // this will get revised later
995 516161 : m_outletAirHumRat = m_inletAirHumRat; // this will get revised later
996 516161 : m_outletAirTemp = Psychrometrics::PsyTdbFnHW(m_outletAirEnthalpy, m_outletAirHumRat); // this will get revised later
997 : // When fan air flow is less than 10%, the fan power curve is linearized between the 10% to 0% to
998 : // avoid the unrealistic high temperature rise across the fan.
999 516161 : Real64 deltaTAcrossFan = m_outletAirTemp - m_inletAirTemp;
1000 516161 : if (deltaTAcrossFan > 20.0) {
1001 0 : Real64 minFlowFracLimitFanHeat = 0.10;
1002 0 : Real64 powerFractionAtLowMin = 0.0;
1003 0 : Real64 fanPoweratLowMinimum = 0.0;
1004 0 : if (localFlowFractionForPower < minFlowFracLimitFanHeat) {
1005 0 : powerFractionAtLowMin = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, minFlowFracLimitFanHeat);
1006 0 : fanPoweratLowMinimum =
1007 0 : powerFractionAtLowMin * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit);
1008 0 : localfanPower = max(0.0, localFlowFractionForPower * fanPoweratLowMinimum / minFlowFracLimitFanHeat);
1009 0 : } else if (locFlowRatio < minFlowFracLimitFanHeat) {
1010 0 : powerFractionAtLowMin = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, minFlowFracLimitFanHeat);
1011 0 : fanPoweratLowMinimum =
1012 0 : powerFractionAtLowMin * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit);
1013 0 : localfanPower = max(0.0, locFlowRatio * fanPoweratLowMinimum / minFlowFracLimitFanHeat);
1014 : }
1015 : }
1016 516161 : m_fanPower += localfanPower;
1017 516161 : break;
1018 : } // continuous speed control case
1019 0 : case SpeedControlMethod::NotSet: {
1020 : // do nothing
1021 0 : break;
1022 : }
1023 0 : default:
1024 0 : assert(false);
1025 : } // end switch
1026 6846631 : m_outletAirMassFlowRate += localAirMassFlow[mode];
1027 :
1028 : } // end of operating mode loop
1029 :
1030 6152117 : if (m_outletAirMassFlowRate > 0.0) {
1031 6152117 : Real64 fanShaftPower = m_motorEff * m_fanPower; // power delivered to shaft
1032 6152117 : m_powerLossToAir = fanShaftPower + (m_fanPower - fanShaftPower) * m_motorInAirFrac;
1033 6152117 : m_outletAirEnthalpy = m_inletAirEnthalpy + m_powerLossToAir / m_outletAirMassFlowRate;
1034 : // This fan does not change the moisture or Mass Flow across the component
1035 6152117 : m_outletAirHumRat = m_inletAirHumRat;
1036 6152117 : m_outletAirTemp = Psychrometrics::PsyTdbFnHW(m_outletAirEnthalpy, m_outletAirHumRat);
1037 : } else {
1038 0 : m_fanPower = 0.0;
1039 0 : m_powerLossToAir = 0.0;
1040 0 : m_outletAirHumRat = m_inletAirHumRat;
1041 0 : m_outletAirEnthalpy = m_inletAirEnthalpy;
1042 0 : m_outletAirTemp = m_inletAirTemp;
1043 0 : m_massFlowRateMaxAvail = 0.0;
1044 0 : m_massFlowRateMinAvail = 0.0;
1045 : }
1046 :
1047 : } else { // fan is off
1048 : // Fan is off and not operating no power consumed and mass flow rate.
1049 1061101 : m_fanPower = 0.0;
1050 1061101 : m_powerLossToAir = 0.0;
1051 1061101 : m_outletAirHumRat = m_inletAirHumRat;
1052 1061101 : m_outletAirEnthalpy = m_inletAirEnthalpy;
1053 1061101 : m_outletAirTemp = m_inletAirTemp;
1054 : // Set the Control Flow variables to 0.0 flow when OFF.
1055 1061101 : if (fanIsSecondaryDriver) {
1056 72239 : m_outletAirMassFlowRate =
1057 144478 : localAirMassFlow[0] +
1058 72239 : localAirMassFlow[1]; // sometimes the air is moving with the fan off, eg. AirTerminal:SingleDuct:VAV:Reheat:VariableSpeedFan
1059 72239 : if (m_outletAirMassFlowRate == 0.0) {
1060 52 : m_massFlowRateMaxAvail = 0.0;
1061 52 : m_massFlowRateMinAvail = 0.0;
1062 : }
1063 : } else {
1064 988862 : m_outletAirMassFlowRate = 0.0;
1065 988862 : m_massFlowRateMaxAvail = 0.0;
1066 988862 : m_massFlowRateMinAvail = 0.0;
1067 : }
1068 : }
1069 :
1070 7213218 : if (m_heatLossesDestination == ThermalLossDestination::ZoneGains) {
1071 0 : Real64 powerLossToZone = m_fanPower - m_powerLossToAir;
1072 0 : m_qdotConvZone = powerLossToZone * (1.0 - m_zoneRadFract);
1073 0 : m_qdotRadZone = powerLossToZone * m_zoneRadFract;
1074 : }
1075 7213218 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // reset to 1
1076 7213218 : }
1077 :
1078 7213218 : void FanSystem::update(EnergyPlusData &state) const // does not change state of object, only update elsewhere
1079 : {
1080 : // Set the outlet air node of the fan
1081 7213218 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRate = m_outletAirMassFlowRate;
1082 7213218 : state.dataLoopNodes->Node(outletNodeNum).Temp = m_outletAirTemp;
1083 7213218 : state.dataLoopNodes->Node(outletNodeNum).HumRat = m_outletAirHumRat;
1084 7213218 : state.dataLoopNodes->Node(outletNodeNum).Enthalpy = m_outletAirEnthalpy;
1085 : // Set the outlet nodes for properties that just pass through & not used
1086 7213218 : state.dataLoopNodes->Node(outletNodeNum).Quality = state.dataLoopNodes->Node(inletNodeNum).Quality;
1087 7213218 : state.dataLoopNodes->Node(outletNodeNum).Press = state.dataLoopNodes->Node(inletNodeNum).Press;
1088 :
1089 : // Set the Node Flow Control Variables from the Fan Control Variables
1090 7213218 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMaxAvail = m_massFlowRateMaxAvail;
1091 7213218 : state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMinAvail = m_massFlowRateMinAvail;
1092 :
1093 : // make sure inlet has the same mass flow
1094 7213218 : state.dataLoopNodes->Node(inletNodeNum).MassFlowRate = m_outletAirMassFlowRate;
1095 :
1096 7213218 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1097 0 : state.dataLoopNodes->Node(outletNodeNum).CO2 = state.dataLoopNodes->Node(inletNodeNum).CO2;
1098 : }
1099 7213218 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1100 0 : state.dataLoopNodes->Node(outletNodeNum).GenContam = state.dataLoopNodes->Node(inletNodeNum).GenContam;
1101 : }
1102 :
1103 7213218 : if (speedControl == SpeedControlMethod::Continuous) {
1104 610206 : if (AirLoopNum > 0) {
1105 0 : state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = m_fanRunTimeFractionAtSpeed[0];
1106 : }
1107 : } else {
1108 6603012 : if (AirLoopNum > 0) {
1109 108550 : if (m_numSpeeds == 1) {
1110 108550 : state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = m_outletAirMassFlowRate / m_maxAirMassFlowRate;
1111 : } else {
1112 0 : if (m_outletAirMassFlowRate <= m_massFlowAtSpeed[0]) {
1113 0 : state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = m_outletAirMassFlowRate / m_massFlowAtSpeed[0];
1114 : } else {
1115 0 : state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = 1.0;
1116 : }
1117 : }
1118 : }
1119 : }
1120 7213218 : }
1121 :
1122 7213218 : void FanSystem::report(EnergyPlusData &state)
1123 : {
1124 7213218 : m_fanEnergy = m_fanPower * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
1125 7213218 : m_deltaTemp = m_outletAirTemp - m_inletAirTemp;
1126 7213218 : }
1127 :
1128 2452399 : Real64 FanSystem::fanPower() const
1129 : {
1130 2452399 : return m_fanPower;
1131 : }
1132 :
1133 0 : Real64 FanSystem::powerLossToAir() const
1134 : {
1135 0 : return m_powerLossToAir;
1136 : }
1137 :
1138 225 : Real64 FanSystem::maxAirMassFlowRate() const
1139 : {
1140 225 : return m_maxAirMassFlowRate;
1141 : }
1142 :
1143 0 : Real64 FanSystem::getFanDesignTemperatureRise(EnergyPlusData &state) const
1144 : {
1145 0 : if (!m_objSizingFlag) {
1146 0 : Real64 cpAir = Psychrometrics::PsyCpAirFnW(DataPrecisionGlobals::constant_zero);
1147 0 : Real64 designDeltaT = (deltaPress / (m_rhoAirStdInit * cpAir * m_fanTotalEff)) * (m_motorEff + m_motorInAirFrac * (1.0 - m_motorEff));
1148 0 : return designDeltaT;
1149 : } else {
1150 : // TODO throw warning, exception, call sizing?
1151 0 : ShowWarningError(state, "FanSystem::getFanDesignTemperatureRise called before fan sizing completed ");
1152 0 : return 0.0;
1153 : }
1154 : }
1155 :
1156 12 : Real64 FanSystem::getFanDesignHeatGain(EnergyPlusData &state, Real64 const FanVolFlow // fan volume flow rate [m3/s]
1157 : )
1158 : {
1159 12 : if (!m_objSizingFlag) {
1160 12 : Real64 fanPowerTot = (FanVolFlow * deltaPress) / m_fanTotalEff;
1161 12 : Real64 designHeatGain = m_motorEff * fanPowerTot + (fanPowerTot - m_motorEff * fanPowerTot) * m_motorInAirFrac;
1162 12 : return designHeatGain;
1163 : } else {
1164 0 : set_size(state);
1165 0 : Real64 fanPowerTot = (FanVolFlow * deltaPress) / m_fanTotalEff;
1166 0 : Real64 designHeatGain = m_motorEff * fanPowerTot + (fanPowerTot - m_motorEff * fanPowerTot) * m_motorInAirFrac;
1167 0 : return designHeatGain;
1168 : }
1169 : }
1170 :
1171 1564 : void FanSystem::FanInputsForDesignHeatGain(EnergyPlusData &state, Real64 &deltaP, Real64 &motEff, Real64 &totEff, Real64 &motInAirFrac)
1172 : {
1173 1564 : if (!m_objSizingFlag) {
1174 1447 : deltaP = deltaPress;
1175 1447 : motEff = m_motorEff;
1176 1447 : totEff = m_fanTotalEff;
1177 1447 : motInAirFrac = m_motorInAirFrac;
1178 1447 : return;
1179 : } else {
1180 117 : set_size(state);
1181 117 : deltaP = deltaPress;
1182 117 : motEff = m_motorEff;
1183 117 : totEff = m_fanTotalEff;
1184 117 : motInAirFrac = m_motorInAirFrac;
1185 117 : return;
1186 : }
1187 : }
1188 :
1189 : // void
1190 : // FanSystem::fanIsSecondaryDriver()
1191 : //{
1192 : // // this concept is used when the fan may be operating in a situation where there is airflow without it running at all
1193 : // // call this when some other fan is feeding the device containing this fan, making it a secondary fan.
1194 : // // example is the fan in a VS VAV air terminal used for UFAD.
1195 : // fanIsSecondaryDriver = true;
1196 : //}
1197 :
1198 : // void
1199 : // FanSystem::setFaultyFilterOn()
1200 : //{
1201 : // // call this to set flag to direct model to use fault for filter
1202 : // faultyFilterFlag_ = true;
1203 : //}
1204 :
1205 : // void
1206 : // FanSystem::setFaultyFilterIndex( int const faultyAirFilterIndex )
1207 : //{
1208 : // // this is the index in the FaultsFouledAirFilters structure array in FaultsManager
1209 : // m_faultyFilterIndex = faultyAirFilterIndex;
1210 : //}
1211 :
1212 : } // namespace HVACFan
1213 :
1214 2313 : } // namespace EnergyPlus
|