Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cassert>
50 : #include <cmath>
51 : #include <cstdlib>
52 :
53 : // ObjexxFCL Headers
54 : #include <ObjexxFCL/Fmath.hh>
55 : #include <ObjexxFCL/string.functions.hh>
56 :
57 : // EnergyPlus Headers
58 : #include <EnergyPlus/Data/EnergyPlusData.hh>
59 : #include <EnergyPlus/DataHVACGlobals.hh>
60 : #include <EnergyPlus/DataLoopNode.hh>
61 : #include <EnergyPlus/General.hh>
62 : #include <EnergyPlus/PlantUtilities.hh>
63 : #include <EnergyPlus/Psychrometrics.hh>
64 : #include <EnergyPlus/SZVAVModel.hh>
65 : #include <EnergyPlus/UnitarySystem.hh>
66 : #include <EnergyPlus/UtilityRoutines.hh>
67 :
68 : namespace EnergyPlus {
69 :
70 : namespace SZVAVModel {
71 :
72 : // Module containing routines for general use
73 :
74 : // Data
75 : // This module should not contain variables in the module sense as it is
76 : // intended strictly to provide "interfaces" to routines used by other
77 : // parts of the simulation.
78 :
79 : // MODULE PARAMETER DEFINITIONS
80 :
81 : // Functions
82 :
83 16016 : void calcSZVAVModel(EnergyPlusData &state,
84 : FanCoilUnits::FanCoilData &SZVAVModel,
85 : int const SysIndex,
86 : bool const FirstHVACIteration,
87 : bool const CoolingLoad,
88 : bool const HeatingLoad,
89 : Real64 const ZoneLoad,
90 : [[maybe_unused]] Real64 &OnOffAirFlowRatio,
91 : [[maybe_unused]] bool const HXUnitOn,
92 : [[maybe_unused]] int const AirLoopNum,
93 : Real64 &PartLoadRatio,
94 : [[maybe_unused]] HVAC::CompressorOp const CompressorONFlag)
95 : {
96 :
97 16016 : int constexpr MaxIter(100); // maximum number of iterations
98 16016 : int SolFlag(0); // return flag from RegulaFalsi for sensible load
99 16016 : std::string MessagePrefix; // label for warning reporting
100 :
101 16016 : Real64 lowBoundaryLoad(0.0);
102 16016 : Real64 highBoundaryLoad(0.0);
103 16016 : Real64 minHumRat(0.0);
104 16016 : Real64 outletTemp(0.0);
105 16016 : bool coilActive(false);
106 16016 : Real64 AirMassFlow(0.0);
107 :
108 16016 : Real64 maxCoilFluidFlow(0.0);
109 16016 : Real64 maxOutletTemp(0.0);
110 16016 : Real64 minAirMassFlow(0.0);
111 16016 : Real64 maxAirMassFlow(0.0);
112 16016 : Real64 lowSpeedFanRatio(0.0);
113 16016 : int coilFluidInletNode(0);
114 16016 : int coilFluidOutletNode(0);
115 16016 : PlantLocation coilPlantLoc{};
116 16016 : int coilAirInletNode(0);
117 16016 : int coilAirOutletNode(0);
118 :
119 : Real64 TempSensOutput; // iterative sensible capacity [W]
120 : // Real64 TempLatOutput; // iterative latent capacity [W]
121 :
122 : // set up mode specific variables to use in common function calls
123 16016 : if (CoolingLoad) {
124 9930 : maxCoilFluidFlow = SZVAVModel.MaxCoolCoilFluidFlow;
125 9930 : maxOutletTemp = SZVAVModel.DesignMinOutletTemp;
126 9930 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
127 9930 : maxAirMassFlow = SZVAVModel.MaxCoolAirMassFlow;
128 9930 : lowSpeedFanRatio = SZVAVModel.LowSpeedCoolFanRatio;
129 9930 : coilFluidInletNode = SZVAVModel.CoolCoilFluidInletNode;
130 9930 : coilFluidOutletNode = SZVAVModel.CoolCoilFluidOutletNodeNum;
131 9930 : coilPlantLoc = SZVAVModel.CoolCoilPlantLoc;
132 9930 : coilAirInletNode = SZVAVModel.CoolCoilInletNodeNum;
133 9930 : coilAirOutletNode = SZVAVModel.CoolCoilOutletNodeNum;
134 6086 : } else if (HeatingLoad) {
135 6086 : maxCoilFluidFlow = SZVAVModel.MaxHeatCoilFluidFlow;
136 6086 : maxOutletTemp = SZVAVModel.DesignMaxOutletTemp;
137 6086 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
138 6086 : maxAirMassFlow = SZVAVModel.MaxHeatAirMassFlow;
139 6086 : lowSpeedFanRatio = SZVAVModel.LowSpeedHeatFanRatio;
140 6086 : coilFluidInletNode = SZVAVModel.HeatCoilFluidInletNode;
141 6086 : coilFluidOutletNode = SZVAVModel.HeatCoilFluidOutletNodeNum;
142 6086 : coilPlantLoc = SZVAVModel.HeatCoilPlantLoc;
143 6086 : coilAirInletNode = SZVAVModel.HeatCoilInletNodeNum;
144 6086 : coilAirOutletNode = SZVAVModel.HeatCoilOutletNodeNum;
145 : } else { // should never get here, protect against uninitialized variables
146 0 : maxCoilFluidFlow = 0.0;
147 0 : maxOutletTemp = 0.0;
148 0 : minAirMassFlow = 0.0;
149 0 : maxAirMassFlow = 0.0;
150 0 : lowSpeedFanRatio = 0.0;
151 0 : coilFluidInletNode = 0;
152 0 : coilFluidOutletNode = 0;
153 0 : coilPlantLoc = {0, DataPlant::LoopSideLocation::Invalid, 0, 0};
154 0 : coilAirInletNode = 0;
155 0 : coilAirOutletNode = 0;
156 : }
157 16016 : int InletNode = SZVAVModel.AirInNode;
158 16016 : Real64 InletTemp = state.dataLoopNodes->Node(InletNode).Temp;
159 16016 : int OutletNode = SZVAVModel.AirOutNode;
160 16016 : Real64 ZoneTemp = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).Temp;
161 16016 : Real64 ZoneHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
162 : // initialize flow variables to 0
163 16016 : Real64 lowWaterMdot = 0.0;
164 : // Real64 SupHeaterLoad = 0.0;
165 :
166 : // model attempts to control air flow rate and coil capacity in specific operating regions:
167 : // Region 1 (R1) - minimum air flow rate at modulated coil capacity (up to min/max temperature limits)
168 : // Region 2 (R2) - modultated air flow rate and coil capacity (up to max air flow rate while maintaining min/max temperature limits)
169 : // Region 3 (R3) - maximum air flow rate and modulated/increased coil capacity (allow increased capacity at full air flow rate to meet
170 : // remaining load)
171 : //
172 : // | | | | ^ ^ = supply air temperature
173 : // | | | | ^ * = supply air flow rate
174 : // | | |^^^^| <--- maximum supply air temperture
175 : // | | ^ | |
176 : // | | ^ | |
177 : // ***********| | ^ | |************** <-- max unit air flow rate
178 : // |* | ^ | *|
179 : // | * | ^ | * |
180 : // | * | ^ | * |
181 : // | *| ^ |* |
182 : // | |*******************| | <-- min unit air flow rate
183 : // R3 | R2 | ^ R1 | R2 | R3
184 : // min SAT --> |^^^^| | |
185 : // ^
186 : // ^ |
187 : // (-) increasing cooling load < 0 > increasing heating load (+)
188 : //
189 : // Notes: SAT is allowed to decrease/increase once air flow rate is max'ed out otherwise load would not be met
190 : // Load here is not the zone load, it's the load the system must meet to meet the Tstat set point (i.e., OA can alter required
191 : // capacity) lowSpeedFanRatio = min/max unit air flow rate
192 : //
193 : // Step 1: calculate load at Region 1 lower (cooling) or upper (heating) boundary at minimum air flow rate
194 : // - if load can be met, test maximum output (PLR = 1) before calling RootSolver
195 : // - if maximum capacity is greater than load, solve for PLR
196 : // Step 2: calculate load at Region 3 lower (cooling) or upper (heating) boundary at maximum air flow rate
197 : // - if load is less than boundary load, solve for air flow and PLR that meet the load
198 : // - ELSE
199 : // Step 3: solve for Region 3 PLR
200 : // DONE
201 : //
202 :
203 : // Step 1: Determine boundary for region 1
204 : // calculate sensible load based on minimum air flow rate and specified supply air temperature limit
205 16016 : if (SZVAVModel.ATMixerExists) {
206 0 : if (SZVAVModel.ATMixerType == HVAC::MixerType::SupplySide) {
207 : // Air terminal supply side mixer
208 0 : lowBoundaryLoad =
209 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(SZVAVModel.ATMixerOutNode).Temp, ZoneHumRat) -
210 0 : Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
211 : } else {
212 : // Air terminal inlet side mixer
213 0 : lowBoundaryLoad =
214 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, ZoneHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
215 : }
216 : } else {
217 16016 : minHumRat = min(state.dataLoopNodes->Node(InletNode).HumRat, state.dataLoopNodes->Node(OutletNode).HumRat);
218 16016 : lowBoundaryLoad =
219 16016 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(InletTemp, minHumRat));
220 : }
221 :
222 16016 : if ((CoolingLoad && lowBoundaryLoad < ZoneLoad) || (HeatingLoad && lowBoundaryLoad > ZoneLoad)) { // in Region 1 of figure
223 : // Step 1: set min air flow and full coil capacity
224 6644 : PartLoadRatio = 1.0; // full coil capacity
225 6644 : SZVAVModel.FanPartLoadRatio =
226 : 0.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
227 6644 : state.dataLoopNodes->Node(InletNode).MassFlowRate = minAirMassFlow;
228 : // set max water flow rate and check to see if plant limits flow
229 6644 : if (coilPlantLoc.loopNum > 0)
230 6644 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
231 :
232 6644 : if (HeatingLoad) { // Function UnitarySystems::calcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
233 : // set the water flow ratio so water coil gets proper flow
234 0 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
235 : }
236 6644 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
237 6644 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
238 :
239 6644 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
240 0 : if (coilPlantLoc.loopNum > 0) {
241 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
242 0 : PlantUtilities::SetComponentFlowRate(
243 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
244 : }
245 0 : return;
246 : }
247 :
248 6644 : if ((CoolingLoad && TempSensOutput < ZoneLoad) || (HeatingLoad && TempSensOutput > ZoneLoad)) { // low speed fan can meet load
249 :
250 42998 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, minAirMassFlow](
251 42998 : Real64 const PLR) {
252 42998 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
253 : PLR,
254 : SysIndex,
255 : FirstHVACIteration,
256 : SZVAVModel.ControlZoneNum,
257 : ZoneLoad,
258 : SZVAVModel.AirInNode,
259 : coilFluidInletNode,
260 : maxCoilFluidFlow,
261 42998 : minAirMassFlow);
262 2620 : };
263 2620 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
264 2620 : if (SolFlag < 0) {
265 0 : MessagePrefix = "Step 1: ";
266 : }
267 :
268 2620 : if (coilPlantLoc.loopNum > 0)
269 2620 : PlantUtilities::SetComponentFlowRate(
270 2620 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
271 : }
272 :
273 6644 : } else {
274 :
275 : // Step 2: Load is greater then allowed in region 1, determine boundary load for region 3
276 : // only difference in this calculation is using maxAirMassFlow instead of minAirMassFlow, just use the ratio to adjust previous
277 : // calculation
278 9372 : highBoundaryLoad = lowBoundaryLoad * maxAirMassFlow / minAirMassFlow;
279 :
280 9372 : if ((CoolingLoad && highBoundaryLoad < ZoneLoad) || (HeatingLoad && highBoundaryLoad > ZoneLoad)) { // in Region 2 of figure
281 :
282 3286 : outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
283 3286 : minHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
284 3286 : if (outletTemp < ZoneTemp) minHumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
285 3286 : outletTemp = maxOutletTemp;
286 3286 : AirMassFlow = min(maxAirMassFlow,
287 3286 : (ZoneLoad / (Psychrometrics::PsyHFnTdbW(outletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, minHumRat))));
288 3286 : AirMassFlow = max(minAirMassFlow, AirMassFlow);
289 3286 : SZVAVModel.FanPartLoadRatio = ((AirMassFlow - (maxAirMassFlow * lowSpeedFanRatio)) / ((1.0 - lowSpeedFanRatio) * maxAirMassFlow));
290 :
291 3286 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
292 : // does unit have capacity less than load at this air flow rate
293 3286 : if (coilFluidInletNode > 0) state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = lowWaterMdot;
294 3286 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, 0.0);
295 3286 : if ((CoolingLoad && (TempSensOutput > ZoneLoad)) || (HeatingLoad && (TempSensOutput < ZoneLoad))) {
296 : // can unit get there with max water flow?
297 3286 : if (coilFluidInletNode > 0) state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = maxCoilFluidFlow;
298 3286 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, 1.0);
299 :
300 : // set max water flow rate and check to see if plant limits flow
301 3286 : if (coilPlantLoc.loopNum > 0)
302 3286 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
303 :
304 3286 : if ((CoolingLoad && (TempSensOutput < ZoneLoad)) || (HeatingLoad && (TempSensOutput > ZoneLoad))) {
305 0 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
306 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, AirMassFlow](
307 0 : Real64 const PLR) {
308 0 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
309 : PLR,
310 : SysIndex,
311 : FirstHVACIteration,
312 : SZVAVModel.ControlZoneNum,
313 : ZoneLoad,
314 : SZVAVModel.AirInNode,
315 : coilFluidInletNode,
316 : maxCoilFluidFlow,
317 0 : AirMassFlow);
318 0 : };
319 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
320 0 : } else {
321 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
322 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
323 0 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
324 0 : };
325 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
326 : }
327 0 : outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
328 : if ((CoolingLoad && outletTemp < maxOutletTemp) || (HeatingLoad && outletTemp > maxOutletTemp)) {
329 : // must do something here to maintain outlet temp while in Region 2
330 : }
331 0 : if (SolFlag < 0) {
332 0 : MessagePrefix = "Step 2: ";
333 : }
334 0 : } else { // not enough capacity at this air flow rate. Unit does have enough capacity a full water/air, otherwise wouldn't be here
335 : // this is different from the PTUnit and UnitarySys routines in this module
336 : // find the water flow rate that meets the min load at region 1/2 bounday
337 3286 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
338 : auto f = // (AUTO_OK_LAMBDA)
339 6572 : [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, minAirMassFlow](
340 6572 : Real64 const PLR) {
341 6572 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
342 : PLR,
343 : SysIndex,
344 : FirstHVACIteration,
345 : SZVAVModel.ControlZoneNum,
346 : ZoneLoad,
347 : SZVAVModel.AirInNode,
348 : coilFluidInletNode,
349 : maxCoilFluidFlow,
350 6572 : minAirMassFlow);
351 3286 : };
352 3286 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, lowWaterMdot, f, 0.0, 1.0);
353 3286 : Real64 minFlow = lowWaterMdot;
354 3286 : if (SolFlag < 0) {
355 3286 : MessagePrefix = "Step 2a: ";
356 : } else {
357 0 : minFlow = 0.0;
358 : }
359 20960 : auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, minFlow](Real64 const PLR) {
360 20960 : return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
361 : PLR,
362 : SysIndex,
363 : FirstHVACIteration,
364 : SZVAVModel.ControlZoneNum,
365 : ZoneLoad,
366 : SZVAVModel.AirInNode,
367 : coilFluidInletNode,
368 20960 : minFlow);
369 3286 : };
370 3286 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
371 3286 : if (SolFlag < 0) {
372 0 : MessagePrefix = "Step 2b: ";
373 : }
374 3286 : } else {
375 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
376 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
377 0 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
378 0 : };
379 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
380 0 : if (SolFlag < 0) {
381 0 : MessagePrefix = "Step 2: ";
382 : }
383 : }
384 : }
385 3286 : } else { // too much capacity when coil off, could lower air flow rate here to meet load if air flow is above minimum
386 0 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
387 0 : auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode](Real64 const PLR) {
388 0 : return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
389 : PLR,
390 : SysIndex,
391 : FirstHVACIteration,
392 : SZVAVModel.ControlZoneNum,
393 : ZoneLoad,
394 : SZVAVModel.AirInNode,
395 : coilFluidInletNode,
396 0 : 0.0);
397 0 : };
398 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
399 0 : } else {
400 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
401 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
402 0 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
403 0 : };
404 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
405 : }
406 0 : if (SolFlag < 0) {
407 0 : MessagePrefix = "Step 2c: ";
408 : }
409 : }
410 :
411 3286 : } else { // in region 3 of figure
412 :
413 6086 : PartLoadRatio = 1.0; // full coil capacity
414 6086 : SZVAVModel.FanPartLoadRatio =
415 : 1.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
416 6086 : state.dataLoopNodes->Node(InletNode).MassFlowRate = maxAirMassFlow;
417 : // set max water flow rate and check to see if plant limits flow
418 6086 : if (coilPlantLoc.loopNum > 0)
419 4050 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
420 :
421 6086 : if (HeatingLoad) { // Function UnitarySystems::calcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
422 : // set the water flow ratio so water coil gets proper flow
423 6086 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
424 : }
425 6086 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
426 6086 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
427 6086 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
428 0 : if (coilPlantLoc.loopNum > 0) {
429 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
430 0 : PlantUtilities::SetComponentFlowRate(
431 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
432 : }
433 0 : return;
434 : }
435 :
436 6086 : if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput))
437 0 : return; // system cannot meet load, leave at max capacity
438 :
439 : // check if coil off is less than load
440 6086 : PartLoadRatio = 0.0; // no coil capacity at full air flow
441 6086 : if (coilPlantLoc.loopNum > 0) {
442 4050 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
443 4050 : PlantUtilities::SetComponentFlowRate(
444 4050 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
445 : }
446 6086 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
447 6086 : if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput)) {
448 : // otherwise iterate on load
449 6086 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
450 41590 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, maxAirMassFlow](
451 41590 : Real64 const PLR) {
452 41590 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
453 : PLR,
454 : SysIndex,
455 : FirstHVACIteration,
456 : SZVAVModel.ControlZoneNum,
457 : ZoneLoad,
458 : SZVAVModel.AirInNode,
459 : coilFluidInletNode,
460 : maxCoilFluidFlow,
461 41590 : maxAirMassFlow);
462 4050 : };
463 4050 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
464 4050 : } else {
465 6108 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
466 6108 : return FanCoilUnits::CalcFanCoilLoadResidual(
467 6108 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
468 2036 : };
469 2036 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
470 : }
471 6086 : if (SolFlag < 0) {
472 0 : MessagePrefix = "Step 3: ";
473 : }
474 6086 : } else { // too much capacity at full air flow with coil off, operate coil and fan in unison
475 0 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
476 0 : auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode](Real64 const PLR) {
477 0 : return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
478 : PLR,
479 : SysIndex,
480 : FirstHVACIteration,
481 : SZVAVModel.ControlZoneNum,
482 : ZoneLoad,
483 : SZVAVModel.AirInNode,
484 : coilFluidInletNode,
485 0 : 0.0);
486 0 : };
487 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
488 0 : } else {
489 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
490 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
491 0 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
492 0 : };
493 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
494 : }
495 0 : if (SolFlag < 0) {
496 0 : MessagePrefix = "Step 3a: ";
497 : }
498 : }
499 : }
500 :
501 9372 : if (coilPlantLoc.loopNum > 0)
502 7336 : PlantUtilities::SetComponentFlowRate(
503 7336 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
504 : }
505 :
506 16016 : if (SolFlag < 0) {
507 0 : if (SolFlag == -1) {
508 : // get capacity for warning
509 0 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
510 :
511 0 : if (std::abs(TempSensOutput - ZoneLoad) * SZVAVModel.ControlZoneMassFlowFrac >
512 : 15.0) { // water coil can provide same output at varying water PLR (model discontinuity?)
513 0 : if (SZVAVModel.MaxIterIndex == 0) {
514 0 : ShowWarningMessage(
515 0 : state, format("{}Coil control failed to converge for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
516 0 : ShowContinueError(state, " Iteration limit exceeded in calculating system sensible part-load ratio.");
517 0 : ShowContinueErrorTimeStamp(
518 : state,
519 0 : format("Sensible load to be met = {:.2T} (watts), sensible output = {:.2T} (watts), and the simulation continues.",
520 : ZoneLoad,
521 : TempSensOutput));
522 : }
523 0 : ShowRecurringWarningErrorAtEnd(
524 : state,
525 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
526 : "\" - Iteration limit exceeded in calculating sensible part-load ratio error continues. Sensible load statistics:",
527 0 : SZVAVModel.MaxIterIndex,
528 : ZoneLoad,
529 : ZoneLoad);
530 : }
531 0 : } else if (SolFlag == -2) {
532 0 : if (SZVAVModel.RegulaFalsiFailedIndex == 0) {
533 0 : ShowWarningMessage(state, format("{}Coil control failed for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
534 0 : ShowContinueError(state, " sensible part-load ratio determined to be outside the range of 0-1.");
535 0 : ShowContinueErrorTimeStamp(state, format("Sensible load to be met = {:.2T} (watts), and the simulation continues.", ZoneLoad));
536 : }
537 0 : ShowRecurringWarningErrorAtEnd(state,
538 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
539 : "\" - sensible part-load ratio out of range error continues. Sensible load statistics:",
540 0 : SZVAVModel.RegulaFalsiFailedIndex,
541 : ZoneLoad,
542 : ZoneLoad);
543 : }
544 : }
545 16016 : }
546 :
547 0 : void calcSZVAVModel(EnergyPlusData &state,
548 : UnitarySystems::UnitarySys &SZVAVModel,
549 : int const SysIndex,
550 : bool const FirstHVACIteration,
551 : bool const CoolingLoad,
552 : bool const HeatingLoad,
553 : Real64 const ZoneLoad,
554 : Real64 &OnOffAirFlowRatio,
555 : bool const HXUnitOn,
556 : int const AirLoopNum,
557 : Real64 &PartLoadRatio,
558 : HVAC::CompressorOp const CompressorONFlag)
559 : {
560 :
561 0 : UnitarySystems::UnitarySys &thisSys = state.dataUnitarySystems->unitarySys[SysIndex];
562 :
563 0 : int constexpr MaxIter(100); // maximum number of iterations
564 0 : int SolFlag(0); // return flag from RegulaFalsi for sensible load
565 0 : std::string MessagePrefix; // label for warning reporting
566 :
567 0 : Real64 boundaryLoadMet(0.0);
568 0 : Real64 minHumRat(0.0);
569 0 : Real64 outletTemp(0.0);
570 0 : bool coilActive(false);
571 0 : Real64 AirMassFlow(0.0);
572 :
573 0 : Real64 maxCoilFluidFlow(0.0);
574 0 : Real64 maxOutletTemp(0.0);
575 0 : Real64 minAirMassFlow(0.0);
576 0 : Real64 maxAirMassFlow(0.0);
577 0 : Real64 lowSpeedFanRatio(0.0);
578 0 : int coilFluidInletNode(0);
579 0 : int coilFluidOutletNode(0);
580 0 : PlantLocation coilPlantLoc{};
581 0 : int coilAirInletNode(0);
582 0 : int coilAirOutletNode(0);
583 0 : Real64 HeatCoilLoad(0.0);
584 0 : Real64 SupHeaterLoad(0.0);
585 :
586 : Real64 TempSensOutput; // iterative sensible capacity [W]
587 : Real64 TempLatOutput; // iterative latent capacity [W]
588 :
589 : // set up mode specific variables to use in common function calls
590 0 : if (CoolingLoad) {
591 0 : maxCoilFluidFlow = SZVAVModel.MaxCoolCoilFluidFlow;
592 0 : maxOutletTemp = SZVAVModel.DesignMinOutletTemp;
593 0 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
594 0 : maxAirMassFlow = SZVAVModel.MaxCoolAirMassFlow;
595 0 : lowSpeedFanRatio = SZVAVModel.LowSpeedCoolFanRatio;
596 0 : coilFluidInletNode = SZVAVModel.CoolCoilFluidInletNode;
597 0 : coilFluidOutletNode = SZVAVModel.CoolCoilFluidOutletNodeNum;
598 0 : coilPlantLoc = SZVAVModel.CoolCoilPlantLoc;
599 0 : coilAirInletNode = SZVAVModel.CoolCoilInletNodeNum;
600 0 : coilAirOutletNode = SZVAVModel.CoolCoilOutletNodeNum;
601 0 : } else if (HeatingLoad) {
602 0 : maxCoilFluidFlow = SZVAVModel.MaxHeatCoilFluidFlow;
603 0 : maxOutletTemp = SZVAVModel.DesignMaxOutletTemp;
604 0 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
605 0 : maxAirMassFlow = SZVAVModel.MaxHeatAirMassFlow;
606 0 : lowSpeedFanRatio = SZVAVModel.LowSpeedHeatFanRatio;
607 0 : coilFluidInletNode = SZVAVModel.HeatCoilFluidInletNode;
608 0 : coilFluidOutletNode = SZVAVModel.HeatCoilFluidOutletNodeNum;
609 0 : coilPlantLoc = SZVAVModel.HeatCoilPlantLoc;
610 0 : coilAirInletNode = SZVAVModel.HeatCoilInletNodeNum;
611 0 : coilAirOutletNode = SZVAVModel.HeatCoilOutletNodeNum;
612 : } else { // should never get here, protect against uninitialized variables
613 0 : maxCoilFluidFlow = 0.0;
614 0 : maxOutletTemp = 0.0;
615 0 : minAirMassFlow = 0.0;
616 0 : maxAirMassFlow = 0.0;
617 0 : lowSpeedFanRatio = 0.0;
618 0 : coilFluidInletNode = 0;
619 0 : coilFluidOutletNode = 0;
620 0 : coilPlantLoc = {0, DataPlant::LoopSideLocation::Invalid, 0, 0};
621 0 : coilAirInletNode = 0;
622 0 : coilAirOutletNode = 0;
623 : }
624 : // set up RegulaFalsi variables
625 0 : int InletNode = SZVAVModel.AirInNode;
626 0 : Real64 InletTemp = state.dataLoopNodes->Node(InletNode).Temp;
627 0 : int OutletNode = SZVAVModel.AirOutNode;
628 0 : Real64 ZoneTemp = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).Temp;
629 0 : Real64 ZoneHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
630 :
631 : // model attempts to control air flow rate and coil capacity in specific operating regions:
632 : // Region 1 (R1) - minimum air flow rate at modulated coil capacity (up to min/max temperature limits)
633 : // Region 2 (R2) - modultated air flow rate and coil capacity (up to max air flow rate while maintaining min/max temperature limits)
634 : // Region 3 (R3) - maximum air flow rate and modulated/increased coil capacity (allow increased capacity at full air flow rate to meet
635 : // remaining load)
636 : //
637 : // | | | | ^ ^ = supply air temperature
638 : // | | | | ^ * = supply air flow rate
639 : // | | |^^^^| <--- maximum supply air temperture
640 : // | | ^ | |
641 : // | | ^ | |
642 : // ***********| | ^ | |************** <-- max unit air flow rate
643 : // |* | ^ | *|
644 : // | * | ^ | * |
645 : // | * | ^ | * |
646 : // | *| ^ |* |
647 : // | |*******************| | <-- min unit air flow rate
648 : // R3 | R2 | ^ R1 | R2 | R3
649 : // min SAT --> |^^^^| | |
650 : // ^
651 : // ^ |
652 : // (-) increasing cooling load < 0 > increasing heating load (+)
653 : //
654 : // Notes: SAT is allowed to decrease/increase once air flow rate is max'ed out otherwise load would not be met
655 : // Load here is not the zone load, it's the load the system must meet to meet the Tstat set point (i.e., OA can alter required
656 : // capacity) lowSpeedFanRatio = min/max unit air flow rate
657 : //
658 : // Step 1: calculate load at Region 1 lower (cooling) or upper (heating) boundary at minimum air flow rate
659 : // - if load can be met, test maximum output (PLR = 1) before calling RootSolver
660 : // - if maximum capacity is greater than load, solve for PLR
661 : // Step 2: calculate load at Region 3 lower (cooling) or upper (heating) boundary at maximum air flow rate
662 : // - if load is less than boundary load, solve for air flow and PLR that meet the load
663 : // - ELSE
664 : // Step 3: solve for Region 3 PLR
665 : // DONE
666 : //
667 :
668 : // Step 1: Determine boundary for region 1
669 : // calculate sensible load based on minimum air flow rate and specified supply air temperature limit
670 0 : if (SZVAVModel.ATMixerExists) {
671 0 : if (SZVAVModel.ATMixerType == HVAC::MixerType::SupplySide) {
672 : // Air terminal supply side mixer
673 0 : boundaryLoadMet =
674 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(SZVAVModel.ATMixerOutNode).Temp, ZoneHumRat) -
675 0 : Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
676 : } else {
677 : // Air terminal inlet side mixer
678 0 : boundaryLoadMet =
679 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, ZoneHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
680 : }
681 : } else {
682 0 : minHumRat = min(state.dataLoopNodes->Node(InletNode).HumRat, state.dataLoopNodes->Node(OutletNode).HumRat);
683 0 : boundaryLoadMet =
684 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(InletTemp, minHumRat));
685 : }
686 :
687 0 : if ((CoolingLoad && boundaryLoadMet < ZoneLoad) || (HeatingLoad && boundaryLoadMet > ZoneLoad)) { // in Region 1 of figure
688 : // Step 1: set min air flow and full coil capacity
689 0 : PartLoadRatio = 1.0; // full coil capacity
690 0 : SZVAVModel.FanPartLoadRatio =
691 : 0.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
692 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = minAirMassFlow;
693 : // set max water flow rate and check to see if plant limits flow
694 0 : if (coilPlantLoc.loopNum > 0)
695 0 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
696 :
697 0 : if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
698 : // set the water flow ratio so water coil gets proper flow
699 0 : if (SZVAVModel.MaxCoolCoilFluidFlow > 0.0) SZVAVModel.CoolCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxCoolCoilFluidFlow;
700 0 : thisSys.calcUnitarySystemToLoad(state,
701 : AirLoopNum,
702 : FirstHVACIteration,
703 : PartLoadRatio,
704 : 0.0,
705 : OnOffAirFlowRatio,
706 : TempSensOutput,
707 : TempLatOutput,
708 : HXUnitOn,
709 : HeatCoilLoad,
710 : SupHeaterLoad,
711 : CompressorONFlag);
712 : } else {
713 0 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
714 0 : thisSys.calcUnitarySystemToLoad(state,
715 : AirLoopNum,
716 : FirstHVACIteration,
717 : 0.0,
718 : PartLoadRatio,
719 : OnOffAirFlowRatio,
720 : TempSensOutput,
721 : TempLatOutput,
722 : HXUnitOn,
723 : ZoneLoad,
724 : SupHeaterLoad,
725 : CompressorONFlag);
726 : }
727 0 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
728 :
729 0 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
730 0 : if (coilPlantLoc.loopNum > 0) {
731 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
732 0 : PlantUtilities::SetComponentFlowRate(
733 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
734 : }
735 0 : return;
736 : }
737 :
738 0 : if ((CoolingLoad && TempSensOutput < ZoneLoad) || (HeatingLoad && TempSensOutput > ZoneLoad)) { // low speed fan can meet load
739 0 : auto f = [&state,
740 : SysIndex,
741 : FirstHVACIteration,
742 : ZoneLoad,
743 : &SZVAVModel,
744 0 : OnOffAirFlowRatio,
745 : AirLoopNum,
746 : coilFluidInletNode,
747 : lowSpeedFanRatio,
748 : maxCoilFluidFlow,
749 : minAirMassFlow,
750 : maxAirMassFlow,
751 0 : CoolingLoad](Real64 const PartLoadRatio) {
752 0 : return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
753 : PartLoadRatio, // coil part load ratio
754 : SysIndex,
755 : FirstHVACIteration,
756 : ZoneLoad,
757 : SZVAVModel.AirInNode,
758 : OnOffAirFlowRatio,
759 : AirLoopNum,
760 : coilFluidInletNode,
761 : maxCoilFluidFlow,
762 : lowSpeedFanRatio,
763 : minAirMassFlow,
764 : 0.0,
765 : maxAirMassFlow,
766 : CoolingLoad,
767 0 : 1.0);
768 0 : };
769 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
770 0 : if (SolFlag < 0) {
771 0 : MessagePrefix = "Step 1: ";
772 : }
773 :
774 0 : if (coilPlantLoc.loopNum > 0)
775 0 : PlantUtilities::SetComponentFlowRate(
776 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
777 : }
778 :
779 0 : } else {
780 :
781 : // Step 2: Load is greater than allowed in region 1, determine boundary load for region 3
782 : // only difference in this calculation is using maxAirMassFlow instead of minAirMassFlow, just use the ratio to adjust previous
783 : // calculation
784 0 : boundaryLoadMet *= maxAirMassFlow / minAirMassFlow;
785 :
786 0 : if ((CoolingLoad && boundaryLoadMet < ZoneLoad) || (HeatingLoad && boundaryLoadMet > ZoneLoad)) { // in Region 2 of figure
787 :
788 0 : outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
789 0 : minHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
790 0 : if (outletTemp < ZoneTemp) minHumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
791 0 : outletTemp = maxOutletTemp;
792 0 : AirMassFlow = min(maxAirMassFlow,
793 0 : (ZoneLoad / (Psychrometrics::PsyHFnTdbW(outletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, minHumRat))));
794 0 : AirMassFlow = max(minAirMassFlow, AirMassFlow);
795 0 : SZVAVModel.FanPartLoadRatio = ((AirMassFlow - (maxAirMassFlow * lowSpeedFanRatio)) / ((1.0 - lowSpeedFanRatio) * maxAirMassFlow));
796 :
797 0 : auto f = [&state,
798 : SysIndex,
799 : FirstHVACIteration,
800 : ZoneLoad,
801 : &SZVAVModel,
802 0 : OnOffAirFlowRatio,
803 : AirLoopNum,
804 : coilFluidInletNode,
805 : lowSpeedFanRatio,
806 : AirMassFlow,
807 : maxAirMassFlow,
808 : CoolingLoad,
809 0 : maxCoilFluidFlow](Real64 const PartLoadRatio) {
810 0 : return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
811 : PartLoadRatio, // coil part load ratio
812 : SysIndex,
813 : FirstHVACIteration,
814 : ZoneLoad,
815 : SZVAVModel.AirInNode,
816 : OnOffAirFlowRatio,
817 : AirLoopNum,
818 : coilFluidInletNode,
819 : maxCoilFluidFlow,
820 : lowSpeedFanRatio,
821 : AirMassFlow,
822 : 0.0,
823 : maxAirMassFlow,
824 : CoolingLoad,
825 0 : 1.0);
826 0 : };
827 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
828 0 : if (SolFlag < 0) {
829 0 : MessagePrefix = "Step 2: ";
830 : }
831 :
832 0 : } else { // in region 3 of figure
833 :
834 0 : PartLoadRatio = 1.0; // full coil capacity
835 0 : SZVAVModel.FanPartLoadRatio =
836 : 1.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
837 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = maxAirMassFlow;
838 : // set max water flow rate and check to see if plant limits flow
839 0 : if (coilPlantLoc.loopNum > 0)
840 0 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
841 :
842 0 : if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
843 : // set the water flow ratio so water coil gets proper flow
844 0 : if (SZVAVModel.MaxCoolCoilFluidFlow > 0.0) SZVAVModel.CoolCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxCoolCoilFluidFlow;
845 0 : thisSys.calcUnitarySystemToLoad(state,
846 : AirLoopNum,
847 : FirstHVACIteration,
848 : PartLoadRatio,
849 : 0.0,
850 : OnOffAirFlowRatio,
851 : TempSensOutput,
852 : TempLatOutput,
853 : HXUnitOn,
854 : HeatCoilLoad,
855 : SupHeaterLoad,
856 : CompressorONFlag);
857 : } else {
858 0 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
859 0 : thisSys.calcUnitarySystemToLoad(state,
860 : AirLoopNum,
861 : FirstHVACIteration,
862 : 0.0,
863 : PartLoadRatio,
864 : OnOffAirFlowRatio,
865 : TempSensOutput,
866 : TempLatOutput,
867 : HXUnitOn,
868 : ZoneLoad,
869 : SupHeaterLoad,
870 : CompressorONFlag);
871 : }
872 0 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
873 0 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
874 0 : if (coilPlantLoc.loopNum > 0) {
875 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
876 0 : PlantUtilities::SetComponentFlowRate(
877 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
878 : }
879 0 : return;
880 : }
881 :
882 0 : if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput))
883 0 : return; // system cannot meet load, leave at max capacity
884 :
885 : // otherwise iterate on load
886 0 : auto f = [&state,
887 : SysIndex,
888 : FirstHVACIteration,
889 : ZoneLoad,
890 : &SZVAVModel,
891 0 : OnOffAirFlowRatio,
892 : AirLoopNum,
893 : coilFluidInletNode,
894 : lowSpeedFanRatio,
895 : maxCoilFluidFlow,
896 : maxAirMassFlow,
897 0 : CoolingLoad](Real64 const PartLoadRatio) {
898 0 : return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
899 : PartLoadRatio, // coil part load ratio
900 : SysIndex,
901 : FirstHVACIteration,
902 : ZoneLoad,
903 : SZVAVModel.AirInNode,
904 : OnOffAirFlowRatio,
905 : AirLoopNum,
906 : coilFluidInletNode,
907 : maxCoilFluidFlow,
908 : lowSpeedFanRatio,
909 : maxAirMassFlow,
910 : 0.0,
911 : maxAirMassFlow,
912 : CoolingLoad,
913 0 : 1.0);
914 0 : };
915 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
916 : // Par[12] = maxAirMassFlow; // operating air flow rate, minAirMassFlow indicates low speed air flow rate,
917 : // maxAirMassFlow indicates full
918 : // // air flow
919 : // Par[13] = 0.0; // SA Temp target, 0 means iterate on load and not SA temperature
920 : // General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, thisSys.calcUnitarySystemWaterFlowResidual,
921 : // 0.0, 1.0, Par);
922 0 : if (SolFlag < 0) {
923 0 : MessagePrefix = "Step 3: ";
924 : }
925 : }
926 :
927 0 : if (coilPlantLoc.loopNum > 0)
928 0 : PlantUtilities::SetComponentFlowRate(
929 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
930 : }
931 :
932 0 : if (SolFlag < 0) {
933 0 : if (SolFlag == -1) {
934 : // get capacity for warning
935 0 : if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
936 0 : thisSys.calcUnitarySystemToLoad(state,
937 : AirLoopNum,
938 : FirstHVACIteration,
939 : PartLoadRatio,
940 : 0.0,
941 : OnOffAirFlowRatio,
942 : TempSensOutput,
943 : TempLatOutput,
944 : HXUnitOn,
945 : HeatCoilLoad,
946 : SupHeaterLoad,
947 : CompressorONFlag);
948 : } else {
949 0 : thisSys.calcUnitarySystemToLoad(state,
950 : AirLoopNum,
951 : FirstHVACIteration,
952 : 0.0,
953 : PartLoadRatio,
954 : OnOffAirFlowRatio,
955 : TempSensOutput,
956 : TempLatOutput,
957 : HXUnitOn,
958 : ZoneLoad,
959 : SupHeaterLoad,
960 : CompressorONFlag);
961 : }
962 :
963 0 : if (std::abs(TempSensOutput - ZoneLoad) * SZVAVModel.ControlZoneMassFlowFrac >
964 : 15.0) { // water coil can provide same output at varying water PLR (model discontinuity?)
965 0 : if (SZVAVModel.MaxIterIndex == 0) {
966 0 : ShowWarningMessage(
967 0 : state, format("{}Coil control failed to converge for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
968 0 : ShowContinueError(state, " Iteration limit exceeded in calculating system sensible part-load ratio.");
969 0 : ShowContinueErrorTimeStamp(
970 : state,
971 0 : format("Sensible load to be met = {:.2T} (watts), sensible output = {:.2T} (watts), and the simulation continues.",
972 : ZoneLoad,
973 : TempSensOutput));
974 : }
975 0 : ShowRecurringWarningErrorAtEnd(
976 : state,
977 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
978 : "\" - Iteration limit exceeded in calculating sensible part-load ratio error continues. Sensible load statistics:",
979 0 : SZVAVModel.MaxIterIndex,
980 : ZoneLoad,
981 : ZoneLoad);
982 : }
983 0 : } else if (SolFlag == -2) {
984 0 : if (SZVAVModel.RegulaFalsiFailedIndex == 0) {
985 0 : ShowWarningMessage(state, format("{}Coil control failed for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
986 0 : ShowContinueError(state, " sensible part-load ratio determined to be outside the range of 0-1.");
987 0 : ShowContinueErrorTimeStamp(state, format("Sensible load to be met = {:.2T} (watts), and the simulation continues.", ZoneLoad));
988 : }
989 0 : ShowRecurringWarningErrorAtEnd(state,
990 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
991 : "\" - sensible part-load ratio out of range error continues. Sensible load statistics:",
992 0 : SZVAVModel.RegulaFalsiFailedIndex,
993 : ZoneLoad,
994 : ZoneLoad);
995 : }
996 : }
997 0 : }
998 :
999 : } // namespace SZVAVModel
1000 :
1001 : } // namespace EnergyPlus
|