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