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 : // 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 : // Using/Aliasing
75 : using DataHVACGlobals::Bisection;
76 :
77 : // Data
78 : // This module should not contain variables in the module sense as it is
79 : // intended strictly to provide "interfaces" to routines used by other
80 : // parts of the simulation.
81 :
82 : // MODULE PARAMETER DEFINITIONS
83 :
84 : // Functions
85 :
86 16066 : void calcSZVAVModel(EnergyPlusData &state,
87 : FanCoilUnits::FanCoilData &SZVAVModel,
88 : int const SysIndex,
89 : bool const FirstHVACIteration,
90 : bool const CoolingLoad,
91 : bool const HeatingLoad,
92 : Real64 const ZoneLoad,
93 : [[maybe_unused]] Real64 &OnOffAirFlowRatio,
94 : [[maybe_unused]] bool const HXUnitOn,
95 : [[maybe_unused]] int const AirLoopNum,
96 : Real64 &PartLoadRatio,
97 : [[maybe_unused]] DataHVACGlobals::CompressorOperation const CompressorONFlag)
98 : {
99 :
100 16066 : int constexpr MaxIter(100); // maximum number of iterations
101 16066 : int SolFlag(0); // return flag from RegulaFalsi for sensible load
102 32132 : std::string MessagePrefix; // label for warning reporting
103 :
104 16066 : Real64 lowBoundaryLoad(0.0);
105 16066 : Real64 highBoundaryLoad(0.0);
106 16066 : Real64 minHumRat(0.0);
107 16066 : Real64 outletTemp(0.0);
108 16066 : bool coilActive(false);
109 16066 : Real64 AirMassFlow(0.0);
110 :
111 16066 : Real64 maxCoilFluidFlow(0.0);
112 16066 : Real64 maxOutletTemp(0.0);
113 16066 : Real64 minAirMassFlow(0.0);
114 16066 : Real64 maxAirMassFlow(0.0);
115 16066 : Real64 lowSpeedFanRatio(0.0);
116 16066 : int coilFluidInletNode(0);
117 16066 : int coilFluidOutletNode(0);
118 16066 : PlantLocation coilPlantLoc{};
119 16066 : int coilAirInletNode(0);
120 16066 : int coilAirOutletNode(0);
121 :
122 : Real64 TempSensOutput; // iterative sensible capacity [W]
123 : // Real64 TempLatOutput; // iterative latent capacity [W]
124 :
125 : // set up mode specific variables to use in common function calls
126 16066 : if (CoolingLoad) {
127 9974 : maxCoilFluidFlow = SZVAVModel.MaxCoolCoilFluidFlow;
128 9974 : maxOutletTemp = SZVAVModel.DesignMinOutletTemp;
129 9974 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
130 9974 : maxAirMassFlow = SZVAVModel.MaxCoolAirMassFlow;
131 9974 : lowSpeedFanRatio = SZVAVModel.LowSpeedCoolFanRatio;
132 9974 : coilFluidInletNode = SZVAVModel.CoolCoilFluidInletNode;
133 9974 : coilFluidOutletNode = SZVAVModel.CoolCoilFluidOutletNodeNum;
134 9974 : coilPlantLoc = SZVAVModel.CoolCoilPlantLoc;
135 9974 : coilAirInletNode = SZVAVModel.CoolCoilInletNodeNum;
136 9974 : coilAirOutletNode = SZVAVModel.CoolCoilOutletNodeNum;
137 6092 : } else if (HeatingLoad) {
138 6092 : maxCoilFluidFlow = SZVAVModel.MaxHeatCoilFluidFlow;
139 6092 : maxOutletTemp = SZVAVModel.DesignMaxOutletTemp;
140 6092 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
141 6092 : maxAirMassFlow = SZVAVModel.MaxHeatAirMassFlow;
142 6092 : lowSpeedFanRatio = SZVAVModel.LowSpeedHeatFanRatio;
143 6092 : coilFluidInletNode = SZVAVModel.HeatCoilFluidInletNode;
144 6092 : coilFluidOutletNode = SZVAVModel.HeatCoilFluidOutletNodeNum;
145 6092 : coilPlantLoc = SZVAVModel.HeatCoilPlantLoc;
146 6092 : coilAirInletNode = SZVAVModel.HeatCoilInletNodeNum;
147 6092 : coilAirOutletNode = SZVAVModel.HeatCoilOutletNodeNum;
148 : } else { // should never get here, protect against uninitialized variables
149 0 : maxCoilFluidFlow = 0.0;
150 0 : maxOutletTemp = 0.0;
151 0 : minAirMassFlow = 0.0;
152 0 : maxAirMassFlow = 0.0;
153 0 : lowSpeedFanRatio = 0.0;
154 0 : coilFluidInletNode = 0;
155 0 : coilFluidOutletNode = 0;
156 0 : coilPlantLoc = {0, DataPlant::LoopSideLocation::Invalid, 0, 0};
157 0 : coilAirInletNode = 0;
158 0 : coilAirOutletNode = 0;
159 : }
160 16066 : int InletNode = SZVAVModel.AirInNode;
161 16066 : Real64 InletTemp = state.dataLoopNodes->Node(InletNode).Temp;
162 16066 : int OutletNode = SZVAVModel.AirOutNode;
163 16066 : Real64 ZoneTemp = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).Temp;
164 16066 : Real64 ZoneHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
165 : // initialize flow variables to 0
166 16066 : Real64 lowWaterMdot = 0.0;
167 : // Real64 SupHeaterLoad = 0.0;
168 :
169 : // model attempts to control air flow rate and coil capacity in specific operating regions:
170 : // Region 1 (R1) - minimum air flow rate at modulated coil capacity (up to min/max temperature limits)
171 : // Region 2 (R2) - modultated air flow rate and coil capacity (up to max air flow rate while maintaining min/max temperature limits)
172 : // Region 3 (R3) - maximum air flow rate and modulated/increased coil capacity (allow increased capacity at full air flow rate to meet
173 : // remaining load)
174 : //
175 : // | | | | ^ ^ = supply air temperature
176 : // | | | | ^ * = supply air flow rate
177 : // | | |^^^^| <--- maximum supply air temperture
178 : // | | ^ | |
179 : // | | ^ | |
180 : // ***********| | ^ | |************** <-- max unit air flow rate
181 : // |* | ^ | *|
182 : // | * | ^ | * |
183 : // | * | ^ | * |
184 : // | *| ^ |* |
185 : // | |*******************| | <-- min unit air flow rate
186 : // R3 | R2 | ^ R1 | R2 | R3
187 : // min SAT --> |^^^^| | |
188 : // ^
189 : // ^ |
190 : // (-) increasing cooling load < 0 > increasing heating load (+)
191 : //
192 : // Notes: SAT is allowed to decrease/increase once air flow rate is max'ed out otherwise load would not be met
193 : // 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
194 : // capacity) lowSpeedFanRatio = min/max unit air flow rate
195 : //
196 : // Step 1: calculate load at Region 1 lower (cooling) or upper (heating) boundary at minimum air flow rate
197 : // - if load can be met, test maximum output (PLR = 1) before calling RootSolver
198 : // - if maximum capacity is greater than load, solve for PLR
199 : // Step 2: calculate load at Region 3 lower (cooling) or upper (heating) boundary at maximum air flow rate
200 : // - if load is less than boundary load, solve for air flow and PLR that meet the load
201 : // - ELSE
202 : // Step 3: solve for Region 3 PLR
203 : // DONE
204 : //
205 :
206 : // Step 1: Determine boundary for region 1
207 : // calculate sensible load based on minimum air flow rate and specified supply air temperature limit
208 16066 : if (SZVAVModel.ATMixerExists) {
209 0 : if (SZVAVModel.ATMixerType == DataHVACGlobals::ATMixer_SupplySide) {
210 : // Air terminal supply side mixer
211 0 : lowBoundaryLoad =
212 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(SZVAVModel.ATMixerOutNode).Temp, ZoneHumRat) -
213 0 : Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
214 : } else {
215 : // Air terminal inlet side mixer
216 0 : lowBoundaryLoad =
217 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, ZoneHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
218 : }
219 : } else {
220 16066 : minHumRat = min(state.dataLoopNodes->Node(InletNode).HumRat, state.dataLoopNodes->Node(OutletNode).HumRat);
221 16066 : lowBoundaryLoad =
222 16066 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(InletTemp, minHumRat));
223 : }
224 :
225 16066 : if ((CoolingLoad && lowBoundaryLoad < ZoneLoad) || (HeatingLoad && lowBoundaryLoad > ZoneLoad)) { // in Region 1 of figure
226 : // Step 1: set min air flow and full coil capacity
227 6732 : PartLoadRatio = 1.0; // full coil capacity
228 6732 : SZVAVModel.FanPartLoadRatio =
229 : 0.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
230 6732 : state.dataLoopNodes->Node(InletNode).MassFlowRate = minAirMassFlow;
231 : // set max water flow rate and check to see if plant limits flow
232 6732 : if (coilPlantLoc.loopNum > 0)
233 6732 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
234 :
235 6732 : if (HeatingLoad) { // Function UnitarySystems::calcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
236 : // set the water flow ratio so water coil gets proper flow
237 0 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
238 : }
239 6732 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
240 6732 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
241 :
242 6732 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
243 0 : if (coilPlantLoc.loopNum > 0) {
244 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
245 0 : PlantUtilities::SetComponentFlowRate(
246 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
247 : }
248 0 : return;
249 : }
250 :
251 6732 : if ((CoolingLoad && TempSensOutput < ZoneLoad) || (HeatingLoad && TempSensOutput > ZoneLoad)) { // low speed fan can meet load
252 :
253 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, minAirMassFlow](
254 171944 : Real64 const PLR) {
255 128958 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
256 : PLR,
257 : SysIndex,
258 : FirstHVACIteration,
259 : SZVAVModel.ControlZoneNum,
260 : ZoneLoad,
261 : SZVAVModel.AirInNode,
262 : coilFluidInletNode,
263 : maxCoilFluidFlow,
264 : minAirMassFlow);
265 45604 : };
266 2618 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
267 2618 : if (SolFlag < 0) {
268 0 : MessagePrefix = "Step 1: ";
269 : }
270 :
271 2618 : if (coilPlantLoc.loopNum > 0)
272 2618 : PlantUtilities::SetComponentFlowRate(
273 2618 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
274 6732 : }
275 :
276 : } else {
277 :
278 : // Step 2: Load is greater then allowed in region 1, determine boundary load for region 3
279 : // only difference in this calculation is using maxAirMassFlow instead of minAirMassFlow, just use the ratio to adjust previous
280 : // calculation
281 9334 : highBoundaryLoad = lowBoundaryLoad * maxAirMassFlow / minAirMassFlow;
282 :
283 9334 : if ((CoolingLoad && highBoundaryLoad < ZoneLoad) || (HeatingLoad && highBoundaryLoad > ZoneLoad)) { // in Region 2 of figure
284 :
285 3242 : outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
286 3242 : minHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
287 3242 : if (outletTemp < ZoneTemp) minHumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
288 3242 : outletTemp = maxOutletTemp;
289 6484 : AirMassFlow = min(maxAirMassFlow,
290 3242 : (ZoneLoad / (Psychrometrics::PsyHFnTdbW(outletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, minHumRat))));
291 3242 : AirMassFlow = max(minAirMassFlow, AirMassFlow);
292 3242 : SZVAVModel.FanPartLoadRatio = ((AirMassFlow - (maxAirMassFlow * lowSpeedFanRatio)) / ((1.0 - lowSpeedFanRatio) * maxAirMassFlow));
293 :
294 3242 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
295 : // does unit have capacity less than load at this air flow rate
296 3242 : if (coilFluidInletNode > 0) state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = lowWaterMdot;
297 3242 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, 0.0);
298 3242 : if ((CoolingLoad && (TempSensOutput > ZoneLoad)) || (HeatingLoad && (TempSensOutput < ZoneLoad))) {
299 : // can unit get there with max water flow?
300 3242 : if (coilFluidInletNode > 0) state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = maxCoilFluidFlow;
301 3242 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, 1.0);
302 :
303 : // set max water flow rate and check to see if plant limits flow
304 3242 : if (coilPlantLoc.loopNum > 0)
305 3242 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
306 :
307 3242 : if ((CoolingLoad && (TempSensOutput < ZoneLoad)) || (HeatingLoad && (TempSensOutput > ZoneLoad))) {
308 0 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
309 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, AirMassFlow](
310 0 : Real64 const PLR) {
311 0 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
312 : PLR,
313 : SysIndex,
314 : FirstHVACIteration,
315 : SZVAVModel.ControlZoneNum,
316 : ZoneLoad,
317 : SZVAVModel.AirInNode,
318 : coilFluidInletNode,
319 : maxCoilFluidFlow,
320 : AirMassFlow);
321 0 : };
322 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
323 : } else {
324 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
325 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
326 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
327 0 : };
328 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
329 : }
330 0 : outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
331 : if ((CoolingLoad && outletTemp < maxOutletTemp) || (HeatingLoad && outletTemp > maxOutletTemp)) {
332 : // must do something here to maintain outlet temp while in Region 2
333 : }
334 0 : if (SolFlag < 0) {
335 0 : MessagePrefix = "Step 2: ";
336 0 : }
337 : } else { // not enough capacity at this air flow rate. Unit does have enough capacity a full water/air, otherwise wouldn't be here
338 : // this is different from the PTUnit and UnitarySys routines in this module
339 : // find the water flow rate that meets the min load at region 1/2 bounday
340 3242 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
341 : auto f =
342 : [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, minAirMassFlow](
343 25936 : Real64 const PLR) {
344 19452 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
345 : PLR,
346 : SysIndex,
347 : FirstHVACIteration,
348 : SZVAVModel.ControlZoneNum,
349 : ZoneLoad,
350 : SZVAVModel.AirInNode,
351 : coilFluidInletNode,
352 : maxCoilFluidFlow,
353 : minAirMassFlow);
354 9726 : };
355 3242 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, lowWaterMdot, f, 0.0, 1.0);
356 3242 : Real64 minFlow = lowWaterMdot;
357 3242 : if (SolFlag < 0) {
358 3242 : MessagePrefix = "Step 2a: ";
359 : } else {
360 0 : minFlow = 0.0;
361 : }
362 82248 : auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, minFlow](Real64 const PLR) {
363 61686 : return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
364 : PLR,
365 : SysIndex,
366 : FirstHVACIteration,
367 : SZVAVModel.ControlZoneNum,
368 : ZoneLoad,
369 : SZVAVModel.AirInNode,
370 : coilFluidInletNode,
371 : minFlow);
372 23804 : };
373 3242 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
374 3242 : if (SolFlag < 0) {
375 0 : MessagePrefix = "Step 2b: ";
376 3242 : }
377 : } else {
378 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
379 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
380 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
381 0 : };
382 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
383 0 : if (SolFlag < 0) {
384 0 : MessagePrefix = "Step 2: ";
385 : }
386 : }
387 3242 : }
388 : } else { // too much capacity when coil off, could lower air flow rate here to meet load if air flow is above minimum
389 0 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
390 0 : auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode](Real64 const PLR) {
391 0 : return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
392 : PLR,
393 : SysIndex,
394 : FirstHVACIteration,
395 : SZVAVModel.ControlZoneNum,
396 : ZoneLoad,
397 : SZVAVModel.AirInNode,
398 : coilFluidInletNode,
399 : 0.0);
400 0 : };
401 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
402 : } else {
403 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
404 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
405 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
406 0 : };
407 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
408 : }
409 0 : if (SolFlag < 0) {
410 0 : MessagePrefix = "Step 2c: ";
411 : }
412 3242 : }
413 :
414 : } else { // in region 3 of figure
415 :
416 6092 : PartLoadRatio = 1.0; // full coil capacity
417 6092 : SZVAVModel.FanPartLoadRatio =
418 : 1.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
419 6092 : state.dataLoopNodes->Node(InletNode).MassFlowRate = maxAirMassFlow;
420 : // set max water flow rate and check to see if plant limits flow
421 6092 : if (coilPlantLoc.loopNum > 0)
422 4050 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
423 :
424 6092 : if (HeatingLoad) { // Function UnitarySystems::calcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
425 : // set the water flow ratio so water coil gets proper flow
426 6092 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
427 : }
428 6092 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
429 6092 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
430 6092 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
431 0 : if (coilPlantLoc.loopNum > 0) {
432 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
433 0 : PlantUtilities::SetComponentFlowRate(
434 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
435 : }
436 0 : return;
437 : }
438 :
439 6092 : if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput))
440 0 : return; // system cannot meet load, leave at max capacity
441 :
442 : // check if coil off is less than load
443 6092 : PartLoadRatio = 0.0; // no coil capacity at full air flow
444 6092 : if (coilPlantLoc.loopNum > 0) {
445 4050 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
446 4050 : PlantUtilities::SetComponentFlowRate(
447 4050 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
448 : }
449 6092 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
450 6092 : if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput)) {
451 : // otherwise iterate on load
452 6092 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
453 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, maxAirMassFlow](
454 166360 : Real64 const PLR) {
455 124770 : return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
456 : PLR,
457 : SysIndex,
458 : FirstHVACIteration,
459 : SZVAVModel.ControlZoneNum,
460 : ZoneLoad,
461 : SZVAVModel.AirInNode,
462 : coilFluidInletNode,
463 : maxCoilFluidFlow,
464 : maxAirMassFlow);
465 45640 : };
466 4050 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
467 : } else {
468 18378 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
469 12252 : return FanCoilUnits::CalcFanCoilLoadResidual(
470 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
471 8168 : };
472 2042 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
473 : }
474 6092 : if (SolFlag < 0) {
475 0 : MessagePrefix = "Step 3: ";
476 6092 : }
477 : } else { // too much capacity at full air flow with coil off, operate coil and fan in unison
478 0 : if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
479 0 : auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode](Real64 const PLR) {
480 0 : return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
481 : PLR,
482 : SysIndex,
483 : FirstHVACIteration,
484 : SZVAVModel.ControlZoneNum,
485 : ZoneLoad,
486 : SZVAVModel.AirInNode,
487 : coilFluidInletNode,
488 : 0.0);
489 0 : };
490 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
491 : } else {
492 0 : auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
493 0 : return FanCoilUnits::CalcFanCoilLoadResidual(
494 : state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
495 0 : };
496 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
497 : }
498 0 : if (SolFlag < 0) {
499 0 : MessagePrefix = "Step 3a: ";
500 : }
501 : }
502 : }
503 :
504 9334 : if (coilPlantLoc.loopNum > 0)
505 7292 : PlantUtilities::SetComponentFlowRate(
506 7292 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
507 : }
508 :
509 16066 : if (SolFlag < 0) {
510 0 : if (SolFlag == -1) {
511 : // get capacity for warning
512 0 : FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
513 :
514 0 : if (std::abs(TempSensOutput - ZoneLoad) * SZVAVModel.ControlZoneMassFlowFrac >
515 : 15.0) { // water coil can provide same output at varying water PLR (model discontinuity?)
516 0 : if (SZVAVModel.MaxIterIndex == 0) {
517 0 : ShowWarningMessage(state,
518 0 : MessagePrefix + "Coil control failed to converge for " + SZVAVModel.UnitType + ':' + SZVAVModel.Name);
519 0 : ShowContinueError(state, " Iteration limit exceeded in calculating system sensible part-load ratio.");
520 0 : ShowContinueErrorTimeStamp(
521 : state,
522 0 : format("Sensible load to be met = {:.2T} (watts), sensible output = {:.2T} (watts), and the simulation continues.",
523 : ZoneLoad,
524 0 : TempSensOutput));
525 : }
526 0 : ShowRecurringWarningErrorAtEnd(
527 : state,
528 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
529 : "\" - Iteration limit exceeded in calculating sensible part-load ratio error continues. Sensible load statistics:",
530 : SZVAVModel.MaxIterIndex,
531 : ZoneLoad,
532 : ZoneLoad);
533 : }
534 0 : } else if (SolFlag == -2) {
535 0 : if (SZVAVModel.RegulaFalsiFailedIndex == 0) {
536 0 : ShowWarningMessage(state, MessagePrefix + "Coil control failed for " + SZVAVModel.UnitType + ':' + SZVAVModel.Name);
537 0 : ShowContinueError(state, " sensible part-load ratio determined to be outside the range of 0-1.");
538 0 : ShowContinueErrorTimeStamp(state, format("Sensible load to be met = {:.2T} (watts), and the simulation continues.", ZoneLoad));
539 : }
540 0 : ShowRecurringWarningErrorAtEnd(state,
541 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
542 : "\" - sensible part-load ratio out of range error continues. Sensible load statistics:",
543 : SZVAVModel.RegulaFalsiFailedIndex,
544 : ZoneLoad,
545 : ZoneLoad);
546 : }
547 : }
548 : }
549 :
550 0 : void calcSZVAVModel(EnergyPlusData &state,
551 : UnitarySystems::UnitarySys &SZVAVModel,
552 : int const SysIndex,
553 : bool const FirstHVACIteration,
554 : bool const CoolingLoad,
555 : bool const HeatingLoad,
556 : Real64 const ZoneLoad,
557 : Real64 &OnOffAirFlowRatio,
558 : bool const HXUnitOn,
559 : int const AirLoopNum,
560 : Real64 &PartLoadRatio,
561 : DataHVACGlobals::CompressorOperation const CompressorONFlag)
562 : {
563 :
564 0 : UnitarySystems::UnitarySys &thisSys = state.dataUnitarySystems->unitarySys[SysIndex];
565 :
566 0 : int constexpr MaxIter(100); // maximum number of iterations
567 0 : int SolFlag(0); // return flag from RegulaFalsi for sensible load
568 0 : std::string MessagePrefix; // label for warning reporting
569 :
570 0 : Real64 boundaryLoadMet(0.0);
571 0 : Real64 minHumRat(0.0);
572 0 : Real64 outletTemp(0.0);
573 0 : bool coilActive(false);
574 0 : Real64 AirMassFlow(0.0);
575 :
576 0 : Real64 maxCoilFluidFlow(0.0);
577 0 : Real64 maxOutletTemp(0.0);
578 0 : Real64 minAirMassFlow(0.0);
579 0 : Real64 maxAirMassFlow(0.0);
580 0 : Real64 lowSpeedFanRatio(0.0);
581 0 : int coilFluidInletNode(0);
582 0 : int coilFluidOutletNode(0);
583 0 : PlantLocation coilPlantLoc{};
584 0 : int coilAirInletNode(0);
585 0 : int coilAirOutletNode(0);
586 0 : Real64 HeatCoilLoad(0.0);
587 0 : Real64 SupHeaterLoad(0.0);
588 :
589 : Real64 TempSensOutput; // iterative sensible capacity [W]
590 : Real64 TempLatOutput; // iterative latent capacity [W]
591 :
592 : // set up mode specific variables to use in common function calls
593 0 : if (CoolingLoad) {
594 0 : maxCoilFluidFlow = SZVAVModel.MaxCoolCoilFluidFlow;
595 0 : maxOutletTemp = SZVAVModel.DesignMinOutletTemp;
596 0 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
597 0 : maxAirMassFlow = SZVAVModel.MaxCoolAirMassFlow;
598 0 : lowSpeedFanRatio = SZVAVModel.LowSpeedCoolFanRatio;
599 0 : coilFluidInletNode = SZVAVModel.CoolCoilFluidInletNode;
600 0 : coilFluidOutletNode = SZVAVModel.CoolCoilFluidOutletNodeNum;
601 0 : coilPlantLoc = SZVAVModel.CoolCoilPlantLoc;
602 0 : coilAirInletNode = SZVAVModel.CoolCoilInletNodeNum;
603 0 : coilAirOutletNode = SZVAVModel.CoolCoilOutletNodeNum;
604 0 : } else if (HeatingLoad) {
605 0 : maxCoilFluidFlow = SZVAVModel.MaxHeatCoilFluidFlow;
606 0 : maxOutletTemp = SZVAVModel.DesignMaxOutletTemp;
607 0 : minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
608 0 : maxAirMassFlow = SZVAVModel.MaxHeatAirMassFlow;
609 0 : lowSpeedFanRatio = SZVAVModel.LowSpeedHeatFanRatio;
610 0 : coilFluidInletNode = SZVAVModel.HeatCoilFluidInletNode;
611 0 : coilFluidOutletNode = SZVAVModel.HeatCoilFluidOutletNodeNum;
612 0 : coilPlantLoc = SZVAVModel.HeatCoilPlantLoc;
613 0 : coilAirInletNode = SZVAVModel.HeatCoilInletNodeNum;
614 0 : coilAirOutletNode = SZVAVModel.HeatCoilOutletNodeNum;
615 : } else { // should never get here, protect against uninitialized variables
616 0 : maxCoilFluidFlow = 0.0;
617 0 : maxOutletTemp = 0.0;
618 0 : minAirMassFlow = 0.0;
619 0 : maxAirMassFlow = 0.0;
620 0 : lowSpeedFanRatio = 0.0;
621 0 : coilFluidInletNode = 0;
622 0 : coilFluidOutletNode = 0;
623 0 : coilPlantLoc = {0, DataPlant::LoopSideLocation::Invalid, 0, 0};
624 0 : coilAirInletNode = 0;
625 0 : coilAirOutletNode = 0;
626 : }
627 : // set up RegulaFalsi variables
628 0 : int InletNode = SZVAVModel.AirInNode;
629 0 : Real64 InletTemp = state.dataLoopNodes->Node(InletNode).Temp;
630 0 : int OutletNode = SZVAVModel.AirOutNode;
631 0 : Real64 ZoneTemp = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).Temp;
632 0 : Real64 ZoneHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
633 :
634 : // model attempts to control air flow rate and coil capacity in specific operating regions:
635 : // Region 1 (R1) - minimum air flow rate at modulated coil capacity (up to min/max temperature limits)
636 : // Region 2 (R2) - modultated air flow rate and coil capacity (up to max air flow rate while maintaining min/max temperature limits)
637 : // Region 3 (R3) - maximum air flow rate and modulated/increased coil capacity (allow increased capacity at full air flow rate to meet
638 : // remaining load)
639 : //
640 : // | | | | ^ ^ = supply air temperature
641 : // | | | | ^ * = supply air flow rate
642 : // | | |^^^^| <--- maximum supply air temperture
643 : // | | ^ | |
644 : // | | ^ | |
645 : // ***********| | ^ | |************** <-- max unit air flow rate
646 : // |* | ^ | *|
647 : // | * | ^ | * |
648 : // | * | ^ | * |
649 : // | *| ^ |* |
650 : // | |*******************| | <-- min unit air flow rate
651 : // R3 | R2 | ^ R1 | R2 | R3
652 : // min SAT --> |^^^^| | |
653 : // ^
654 : // ^ |
655 : // (-) increasing cooling load < 0 > increasing heating load (+)
656 : //
657 : // Notes: SAT is allowed to decrease/increase once air flow rate is max'ed out otherwise load would not be met
658 : // 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
659 : // capacity) lowSpeedFanRatio = min/max unit air flow rate
660 : //
661 : // Step 1: calculate load at Region 1 lower (cooling) or upper (heating) boundary at minimum air flow rate
662 : // - if load can be met, test maximum output (PLR = 1) before calling RootSolver
663 : // - if maximum capacity is greater than load, solve for PLR
664 : // Step 2: calculate load at Region 3 lower (cooling) or upper (heating) boundary at maximum air flow rate
665 : // - if load is less than boundary load, solve for air flow and PLR that meet the load
666 : // - ELSE
667 : // Step 3: solve for Region 3 PLR
668 : // DONE
669 : //
670 :
671 : // Step 1: Determine boundary for region 1
672 : // calculate sensible load based on minimum air flow rate and specified supply air temperature limit
673 0 : if (SZVAVModel.ATMixerExists) {
674 0 : if (SZVAVModel.ATMixerType == DataHVACGlobals::ATMixer_SupplySide) {
675 : // Air terminal supply side mixer
676 0 : boundaryLoadMet =
677 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(SZVAVModel.ATMixerOutNode).Temp, ZoneHumRat) -
678 0 : Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
679 : } else {
680 : // Air terminal inlet side mixer
681 0 : boundaryLoadMet =
682 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, ZoneHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
683 : }
684 : } else {
685 0 : minHumRat = min(state.dataLoopNodes->Node(InletNode).HumRat, state.dataLoopNodes->Node(OutletNode).HumRat);
686 0 : boundaryLoadMet =
687 0 : minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(InletTemp, minHumRat));
688 : }
689 :
690 0 : if ((CoolingLoad && boundaryLoadMet < ZoneLoad) || (HeatingLoad && boundaryLoadMet > ZoneLoad)) { // in Region 1 of figure
691 : // Step 1: set min air flow and full coil capacity
692 0 : PartLoadRatio = 1.0; // full coil capacity
693 0 : SZVAVModel.FanPartLoadRatio =
694 : 0.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
695 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = minAirMassFlow;
696 : // set max water flow rate and check to see if plant limits flow
697 0 : if (coilPlantLoc.loopNum > 0)
698 0 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
699 :
700 0 : if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
701 : // set the water flow ratio so water coil gets proper flow
702 0 : if (SZVAVModel.MaxCoolCoilFluidFlow > 0.0) SZVAVModel.CoolCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxCoolCoilFluidFlow;
703 0 : thisSys.calcUnitarySystemToLoad(state,
704 : AirLoopNum,
705 : FirstHVACIteration,
706 : PartLoadRatio,
707 : 0.0,
708 : OnOffAirFlowRatio,
709 : TempSensOutput,
710 : TempLatOutput,
711 : HXUnitOn,
712 : HeatCoilLoad,
713 : SupHeaterLoad,
714 : CompressorONFlag);
715 : } else {
716 0 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
717 0 : thisSys.calcUnitarySystemToLoad(state,
718 : AirLoopNum,
719 : FirstHVACIteration,
720 : 0.0,
721 : PartLoadRatio,
722 : OnOffAirFlowRatio,
723 : TempSensOutput,
724 : TempLatOutput,
725 : HXUnitOn,
726 : ZoneLoad,
727 : SupHeaterLoad,
728 : CompressorONFlag);
729 : }
730 0 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
731 :
732 0 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
733 0 : if (coilPlantLoc.loopNum > 0) {
734 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
735 0 : PlantUtilities::SetComponentFlowRate(
736 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
737 : }
738 0 : return;
739 : }
740 :
741 0 : if ((CoolingLoad && TempSensOutput < ZoneLoad) || (HeatingLoad && TempSensOutput > ZoneLoad)) { // low speed fan can meet load
742 : auto f = [&state,
743 : SysIndex,
744 : FirstHVACIteration,
745 : ZoneLoad,
746 : &SZVAVModel,
747 : OnOffAirFlowRatio,
748 : AirLoopNum,
749 : coilFluidInletNode,
750 : lowSpeedFanRatio,
751 : maxCoilFluidFlow,
752 : minAirMassFlow,
753 : maxAirMassFlow,
754 0 : CoolingLoad](Real64 const PartLoadRatio) {
755 0 : return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
756 : PartLoadRatio, // coil part load ratio
757 : SysIndex,
758 : FirstHVACIteration,
759 : ZoneLoad,
760 : SZVAVModel.AirInNode,
761 : OnOffAirFlowRatio,
762 : AirLoopNum,
763 : coilFluidInletNode,
764 : maxCoilFluidFlow,
765 : lowSpeedFanRatio,
766 : minAirMassFlow,
767 : 0.0,
768 : maxAirMassFlow,
769 : CoolingLoad,
770 : 1.0);
771 0 : };
772 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
773 0 : if (SolFlag < 0) {
774 0 : MessagePrefix = "Step 1: ";
775 : }
776 :
777 0 : if (coilPlantLoc.loopNum > 0)
778 0 : PlantUtilities::SetComponentFlowRate(
779 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
780 0 : }
781 :
782 : } else {
783 :
784 : // Step 2: Load is greater than allowed in region 1, determine boundary load for region 3
785 : // only difference in this calculation is using maxAirMassFlow instead of minAirMassFlow, just use the ratio to adjust previous
786 : // calculation
787 0 : boundaryLoadMet *= maxAirMassFlow / minAirMassFlow;
788 :
789 0 : if ((CoolingLoad && boundaryLoadMet < ZoneLoad) || (HeatingLoad && boundaryLoadMet > ZoneLoad)) { // in Region 2 of figure
790 :
791 0 : outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
792 0 : minHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
793 0 : if (outletTemp < ZoneTemp) minHumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
794 0 : outletTemp = maxOutletTemp;
795 0 : AirMassFlow = min(maxAirMassFlow,
796 0 : (ZoneLoad / (Psychrometrics::PsyHFnTdbW(outletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, minHumRat))));
797 0 : AirMassFlow = max(minAirMassFlow, AirMassFlow);
798 0 : SZVAVModel.FanPartLoadRatio = ((AirMassFlow - (maxAirMassFlow * lowSpeedFanRatio)) / ((1.0 - lowSpeedFanRatio) * maxAirMassFlow));
799 :
800 : auto f = [&state,
801 : SysIndex,
802 : FirstHVACIteration,
803 : ZoneLoad,
804 : &SZVAVModel,
805 : OnOffAirFlowRatio,
806 : AirLoopNum,
807 : coilFluidInletNode,
808 : lowSpeedFanRatio,
809 : AirMassFlow,
810 : maxAirMassFlow,
811 : CoolingLoad,
812 0 : maxCoilFluidFlow](Real64 const PartLoadRatio) {
813 0 : return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
814 : PartLoadRatio, // coil part load ratio
815 : SysIndex,
816 : FirstHVACIteration,
817 : ZoneLoad,
818 : SZVAVModel.AirInNode,
819 : OnOffAirFlowRatio,
820 : AirLoopNum,
821 : coilFluidInletNode,
822 : maxCoilFluidFlow,
823 : lowSpeedFanRatio,
824 : AirMassFlow,
825 : 0.0,
826 : maxAirMassFlow,
827 : CoolingLoad,
828 : 1.0);
829 0 : };
830 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
831 0 : if (SolFlag < 0) {
832 0 : MessagePrefix = "Step 2: ";
833 0 : }
834 :
835 : } else { // in region 3 of figure
836 :
837 0 : PartLoadRatio = 1.0; // full coil capacity
838 0 : SZVAVModel.FanPartLoadRatio =
839 : 1.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
840 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = maxAirMassFlow;
841 : // set max water flow rate and check to see if plant limits flow
842 0 : if (coilPlantLoc.loopNum > 0)
843 0 : PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
844 :
845 0 : if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
846 : // set the water flow ratio so water coil gets proper flow
847 0 : if (SZVAVModel.MaxCoolCoilFluidFlow > 0.0) SZVAVModel.CoolCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxCoolCoilFluidFlow;
848 0 : thisSys.calcUnitarySystemToLoad(state,
849 : AirLoopNum,
850 : FirstHVACIteration,
851 : PartLoadRatio,
852 : 0.0,
853 : OnOffAirFlowRatio,
854 : TempSensOutput,
855 : TempLatOutput,
856 : HXUnitOn,
857 : HeatCoilLoad,
858 : SupHeaterLoad,
859 : CompressorONFlag);
860 : } else {
861 0 : if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
862 0 : thisSys.calcUnitarySystemToLoad(state,
863 : AirLoopNum,
864 : FirstHVACIteration,
865 : 0.0,
866 : PartLoadRatio,
867 : OnOffAirFlowRatio,
868 : TempSensOutput,
869 : TempLatOutput,
870 : HXUnitOn,
871 : ZoneLoad,
872 : SupHeaterLoad,
873 : CompressorONFlag);
874 : }
875 0 : coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
876 0 : if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
877 0 : if (coilPlantLoc.loopNum > 0) {
878 0 : state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
879 0 : PlantUtilities::SetComponentFlowRate(
880 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
881 : }
882 0 : return;
883 : }
884 :
885 0 : if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput))
886 0 : return; // system cannot meet load, leave at max capacity
887 :
888 : // otherwise iterate on load
889 : auto f = [&state,
890 : SysIndex,
891 : FirstHVACIteration,
892 : ZoneLoad,
893 : &SZVAVModel,
894 : OnOffAirFlowRatio,
895 : AirLoopNum,
896 : coilFluidInletNode,
897 : lowSpeedFanRatio,
898 : maxCoilFluidFlow,
899 : maxAirMassFlow,
900 0 : CoolingLoad](Real64 const PartLoadRatio) {
901 0 : return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
902 : PartLoadRatio, // coil part load ratio
903 : SysIndex,
904 : FirstHVACIteration,
905 : ZoneLoad,
906 : SZVAVModel.AirInNode,
907 : OnOffAirFlowRatio,
908 : AirLoopNum,
909 : coilFluidInletNode,
910 : maxCoilFluidFlow,
911 : lowSpeedFanRatio,
912 : maxAirMassFlow,
913 : 0.0,
914 : maxAirMassFlow,
915 : CoolingLoad,
916 : 1.0);
917 0 : };
918 0 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
919 : // Par[12] = maxAirMassFlow; // operating air flow rate, minAirMassFlow indicates low speed air flow rate,
920 : // maxAirMassFlow indicates full
921 : // // air flow
922 : // Par[13] = 0.0; // SA Temp target, 0 means iterate on load and not SA temperature
923 : // General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, thisSys.calcUnitarySystemWaterFlowResidual,
924 : // 0.0, 1.0, Par);
925 0 : if (SolFlag < 0) {
926 0 : MessagePrefix = "Step 3: ";
927 : }
928 : }
929 :
930 0 : if (coilPlantLoc.loopNum > 0)
931 0 : PlantUtilities::SetComponentFlowRate(
932 0 : state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
933 : }
934 :
935 0 : if (SolFlag < 0) {
936 0 : if (SolFlag == -1) {
937 : // get capacity for warning
938 0 : if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
939 0 : thisSys.calcUnitarySystemToLoad(state,
940 : AirLoopNum,
941 : FirstHVACIteration,
942 : PartLoadRatio,
943 : 0.0,
944 : OnOffAirFlowRatio,
945 : TempSensOutput,
946 : TempLatOutput,
947 : HXUnitOn,
948 : HeatCoilLoad,
949 : SupHeaterLoad,
950 : CompressorONFlag);
951 : } else {
952 0 : thisSys.calcUnitarySystemToLoad(state,
953 : AirLoopNum,
954 : FirstHVACIteration,
955 : 0.0,
956 : PartLoadRatio,
957 : OnOffAirFlowRatio,
958 : TempSensOutput,
959 : TempLatOutput,
960 : HXUnitOn,
961 : ZoneLoad,
962 : SupHeaterLoad,
963 : CompressorONFlag);
964 : }
965 :
966 0 : if (std::abs(TempSensOutput - ZoneLoad) * SZVAVModel.ControlZoneMassFlowFrac >
967 : 15.0) { // water coil can provide same output at varying water PLR (model discontinuity?)
968 0 : if (SZVAVModel.MaxIterIndex == 0) {
969 0 : ShowWarningMessage(state,
970 0 : MessagePrefix + "Coil control failed to converge for " + SZVAVModel.UnitType + ':' + SZVAVModel.Name);
971 0 : ShowContinueError(state, " Iteration limit exceeded in calculating system sensible part-load ratio.");
972 0 : ShowContinueErrorTimeStamp(
973 : state,
974 0 : format("Sensible load to be met = {:.2T} (watts), sensible output = {:.2T} (watts), and the simulation continues.",
975 : ZoneLoad,
976 0 : TempSensOutput));
977 : }
978 0 : ShowRecurringWarningErrorAtEnd(
979 : state,
980 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
981 : "\" - Iteration limit exceeded in calculating sensible part-load ratio error continues. Sensible load statistics:",
982 : SZVAVModel.MaxIterIndex,
983 : ZoneLoad,
984 : ZoneLoad);
985 : }
986 0 : } else if (SolFlag == -2) {
987 0 : if (SZVAVModel.RegulaFalsiFailedIndex == 0) {
988 0 : ShowWarningMessage(state, MessagePrefix + "Coil control failed for " + SZVAVModel.UnitType + ':' + SZVAVModel.Name);
989 0 : ShowContinueError(state, " sensible part-load ratio determined to be outside the range of 0-1.");
990 0 : ShowContinueErrorTimeStamp(state, format("Sensible load to be met = {:.2T} (watts), and the simulation continues.", ZoneLoad));
991 : }
992 0 : ShowRecurringWarningErrorAtEnd(state,
993 0 : SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
994 : "\" - sensible part-load ratio out of range error continues. Sensible load statistics:",
995 : SZVAVModel.RegulaFalsiFailedIndex,
996 : ZoneLoad,
997 : ZoneLoad);
998 : }
999 : }
1000 : }
1001 :
1002 : } // namespace SZVAVModel
1003 :
1004 2313 : } // namespace EnergyPlus
|