Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Autosizing/All_Simple_Sizing.hh>
57 : #include <EnergyPlus/Autosizing/CoolingAirFlowSizing.hh>
58 : #include <EnergyPlus/Autosizing/CoolingCapacitySizing.hh>
59 : #include <EnergyPlus/Autosizing/HeatingAirFlowSizing.hh>
60 : #include <EnergyPlus/Autosizing/HeatingCapacitySizing.hh>
61 : #include <EnergyPlus/Autosizing/SystemAirFlowSizing.hh>
62 : #include <EnergyPlus/BranchNodeConnections.hh>
63 : #include <EnergyPlus/Data/EnergyPlusData.hh>
64 : #include <EnergyPlus/DataAirSystems.hh>
65 : #include <EnergyPlus/DataEnvironment.hh>
66 : #include <EnergyPlus/DataHVACGlobals.hh>
67 : #include <EnergyPlus/DataHeatBalFanSys.hh>
68 : #include <EnergyPlus/DataHeatBalance.hh>
69 : #include <EnergyPlus/DataLoopNode.hh>
70 : #include <EnergyPlus/DataPrecisionGlobals.hh>
71 : #include <EnergyPlus/DataSizing.hh>
72 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
73 : #include <EnergyPlus/DataZoneEquipment.hh>
74 : #include <EnergyPlus/FanCoilUnits.hh>
75 : #include <EnergyPlus/Fans.hh>
76 : #include <EnergyPlus/FluidProperties.hh>
77 : #include <EnergyPlus/GeneralRoutines.hh>
78 : #include <EnergyPlus/HVACHXAssistedCoolingCoil.hh>
79 : #include <EnergyPlus/HeatingCoils.hh>
80 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
81 : #include <EnergyPlus/MixedAir.hh>
82 : #include <EnergyPlus/NodeInputManager.hh>
83 : #include <EnergyPlus/OutputProcessor.hh>
84 : #include <EnergyPlus/Plant/DataPlant.hh>
85 : #include <EnergyPlus/PlantUtilities.hh>
86 : #include <EnergyPlus/Psychrometrics.hh>
87 : #include <EnergyPlus/ReportCoilSelection.hh>
88 : #include <EnergyPlus/SZVAVModel.hh>
89 : #include <EnergyPlus/ScheduleManager.hh>
90 : #include <EnergyPlus/SingleDuct.hh>
91 : #include <EnergyPlus/UtilityRoutines.hh>
92 : #include <EnergyPlus/WaterCoils.hh>
93 : #include <EnergyPlus/ZoneEquipmentManager.hh>
94 : #include <EnergyPlus/ZonePlenum.hh>
95 :
96 : namespace EnergyPlus {
97 :
98 : namespace FanCoilUnits {
99 :
100 : // Module containing the routines dealing with 2 and 4 pipe fan coil units
101 :
102 : // MODULE INFORMATION:
103 : // AUTHOR Fred Buhl
104 : // DATE WRITTEN March 2000
105 : // MODIFIED October 2003 (FSEC added cooling coil type)
106 : // June 2010 Arnaud Flament LBNL added 3-speed and variables-speed fan capacity control;
107 : // outside air schedule; and removed coil water inlet node inputs
108 : // Sept 2010 Brent Griffith, plant upgrades for water coils, fluid properties
109 :
110 : // PURPOSE OF THIS MODULE:
111 : // To encapsulate the data and algorithms needed to simulate 2 and 4 pipe
112 : // fan coil units.
113 :
114 : // METHODOLOGY EMPLOYED:
115 : // Units are modeled as a collection of components: outside air mixer,
116 : // fan, heating coil and/or cooling coil plus an integrated control
117 : // algorithm that adjusts the hot or cold water flow to meet the zone
118 : // load. Or varies the air flow rate to meet the zone load. Or both.
119 :
120 515028 : void SimFanCoilUnit(EnergyPlusData &state,
121 : std::string_view CompName, // name of the fan coil unit
122 : int const ControlledZoneNum, // number of zone being served
123 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
124 : Real64 &PowerMet, // Sensible power supplied (W)
125 : Real64 &LatOutputProvided, // Latent add/removal supplied by window AC (kg/s), dehumid = negative
126 : int &CompIndex)
127 : {
128 :
129 : // SUBROUTINE INFORMATION:
130 : // AUTHOR Fred Buhl
131 : // DATE WRITTEN March 2000
132 : // MODIFIED Don Shirey, Aug 2009 (LatOutputProvided)
133 :
134 : // PURPOSE OF THIS SUBROUTINE:
135 : // Manages the simulation of a fan coil unit. Called from SimZone Equipment
136 :
137 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
138 : int FanCoilNum; // index of fan coil unit being simulated
139 :
140 : // First time SimFanCoilUnit is called, get the input for all the fan coil units
141 515028 : if (state.dataFanCoilUnits->GetFanCoilInputFlag) {
142 19 : GetFanCoilUnits(state);
143 19 : state.dataFanCoilUnits->GetFanCoilInputFlag = false;
144 : }
145 :
146 : // Find the correct Fan Coil Equipment
147 515028 : if (CompIndex == 0) {
148 81 : FanCoilNum = Util::FindItemInList(CompName, state.dataFanCoilUnits->FanCoil);
149 81 : if (FanCoilNum == 0) {
150 0 : ShowFatalError(state, format("SimFanCoil: Unit not found={}", CompName));
151 : }
152 81 : CompIndex = FanCoilNum;
153 : } else {
154 514947 : FanCoilNum = CompIndex;
155 514947 : if (FanCoilNum > state.dataFanCoilUnits->NumFanCoils || FanCoilNum < 1) {
156 0 : ShowFatalError(state,
157 0 : format("SimFanCoil: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
158 : FanCoilNum,
159 0 : state.dataFanCoilUnits->NumFanCoils,
160 : CompName));
161 : }
162 514947 : if (state.dataFanCoilUnits->CheckEquipName(FanCoilNum)) {
163 81 : if (CompName != state.dataFanCoilUnits->FanCoil(FanCoilNum).Name) {
164 0 : ShowFatalError(state,
165 0 : format("SimFanCoil: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
166 : FanCoilNum,
167 : CompName,
168 0 : state.dataFanCoilUnits->FanCoil(FanCoilNum).Name));
169 : }
170 81 : state.dataFanCoilUnits->CheckEquipName(FanCoilNum) = false;
171 : }
172 : }
173 :
174 515028 : state.dataSize->ZoneEqFanCoil = true;
175 :
176 : // Initialize the fan coil unit
177 515028 : InitFanCoilUnits(state, FanCoilNum, ControlledZoneNum);
178 :
179 : // Select the correct unit type
180 515028 : if (state.dataFanCoilUnits->FanCoil(FanCoilNum).UnitType_Num == FanCoilUnit_4Pipe) {
181 515028 : Sim4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, PowerMet, LatOutputProvided);
182 : }
183 :
184 : // Report the result of the simulation
185 515028 : ReportFanCoilUnit(state, FanCoilNum);
186 :
187 515028 : state.dataSize->ZoneEqFanCoil = false;
188 515028 : }
189 :
190 19 : void GetFanCoilUnits(EnergyPlusData &state)
191 : {
192 :
193 : // SUBROUTINE INFORMATION:
194 : // AUTHOR Fred Buhl
195 : // DATE WRITTEN March 2000
196 : // MODIFIED Bereket Nigusse, FSEC, April 2011: eliminated input node names
197 : // added OA Mixer object type
198 : // and fan object type
199 : // Chandan Sharma, FSEC, July 2012: Added zone sys avail managers
200 :
201 : // PURPOSE OF THIS SUBROUTINE:
202 : // Obtains input data for fan coil units and stores it in fan coil data structures
203 :
204 : // METHODOLOGY EMPLOYED:
205 : // Uses "Get" routines to read in data.
206 :
207 : // Locals
208 : // SUBROUTINE ARGUMENT DEFINITIONS:
209 : static constexpr std::string_view RoutineName("GetFanCoilUnits: "); // include trailing blank space
210 : static constexpr std::string_view routineName = "GetFanCoilUnits";
211 :
212 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
213 : int NumAlphas; // Number of Alphas for each GetObjectItem call
214 : int NumNumbers; // Number of Numbers for each GetObjectItem call
215 19 : Array1D_int OANodeNums(4); // Node numbers of Outdoor air mixer (OA, EA, RA, MA)
216 : int IOStatus; // Used in GetObjectItem
217 : bool IsNotOK; // Flag to verify name
218 19 : Array1D_string Alphas; // Alpha input items for object
219 19 : Array1D_string cAlphaFields; // Alpha field names
220 19 : Array1D_string cNumericFields; // Numeric field names
221 19 : Array1D<Real64> Numbers; // Numeric input items for object
222 19 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
223 19 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
224 : int NodeNum; // index to loop counter
225 19 : std::string ATMixerName;
226 :
227 19 : auto &ErrorsFound = state.dataFanCoilUnits->ErrorsFound;
228 19 : bool &errFlag = state.dataFanCoilUnits->errFlag;
229 :
230 : // find the number of each type of fan coil unit
231 :
232 19 : std::string CurrentModuleObject = state.dataFanCoilUnits->cMO_FanCoil;
233 19 : state.dataFanCoilUnits->Num4PipeFanCoils = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
234 19 : state.dataFanCoilUnits->NumFanCoils = state.dataFanCoilUnits->Num4PipeFanCoils;
235 : // allocate the data structures
236 19 : state.dataFanCoilUnits->FanCoil.allocate(state.dataFanCoilUnits->NumFanCoils);
237 19 : state.dataFanCoilUnits->FanCoilNumericFields.allocate(state.dataFanCoilUnits->NumFanCoils);
238 19 : state.dataFanCoilUnits->CheckEquipName.dimension(state.dataFanCoilUnits->NumFanCoils, true);
239 :
240 38 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
241 19 : state, CurrentModuleObject, state.dataFanCoilUnits->TotalArgs, NumAlphas, NumNumbers);
242 19 : Alphas.allocate(NumAlphas);
243 19 : cAlphaFields.allocate(NumAlphas);
244 19 : cNumericFields.allocate(NumNumbers);
245 19 : Numbers.dimension(NumNumbers, 0.0);
246 19 : lAlphaBlanks.dimension(NumAlphas, true);
247 19 : lNumericBlanks.dimension(NumNumbers, true);
248 :
249 : // loop over 4 pipe fan coil units; get and load the input data
250 100 : for (int FanCoilIndex = 1; FanCoilIndex <= state.dataFanCoilUnits->Num4PipeFanCoils; ++FanCoilIndex) {
251 81 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilIndex);
252 :
253 81 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
254 : CurrentModuleObject,
255 : FanCoilIndex,
256 : Alphas,
257 : NumAlphas,
258 : Numbers,
259 : NumNumbers,
260 : IOStatus,
261 : lNumericBlanks,
262 : lAlphaBlanks,
263 : cAlphaFields,
264 : cNumericFields);
265 :
266 81 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
267 :
268 81 : state.dataFanCoilUnits->FanCoilNumericFields(FanCoilIndex).FieldNames.allocate(NumNumbers);
269 81 : state.dataFanCoilUnits->FanCoilNumericFields(FanCoilIndex).FieldNames = "";
270 81 : state.dataFanCoilUnits->FanCoilNumericFields(FanCoilIndex).FieldNames = cNumericFields;
271 :
272 81 : fanCoil.Name = Alphas(1);
273 81 : fanCoil.UnitType = CurrentModuleObject;
274 81 : fanCoil.UnitType_Num = FanCoilUnit_4Pipe;
275 :
276 81 : if (lAlphaBlanks(2)) {
277 0 : fanCoil.availSched = Sched::GetScheduleAlwaysOn(state);
278 81 : } else if ((fanCoil.availSched = Sched::GetSchedule(state, Alphas(2))) == nullptr) {
279 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
280 0 : ErrorsFound = true;
281 : }
282 81 : constexpr std::array<std::string_view, static_cast<int>(CCM::Num)> CapCtrlMethUC{"CONSTANTFANVARIABLEFLOW",
283 : "CYCLINGFAN",
284 : "VARIABLEFANVARIABLEFLOW",
285 : "VARIABLEFANCONSTANTFLOW",
286 : "MULTISPEEDFAN",
287 : "ASHRAE90VARIABLEFAN"};
288 81 : std::string capCtrlMeth = Alphas(3);
289 81 : fanCoil.CapCtrlMeth_Num = static_cast<CCM>(getEnumValue(CapCtrlMethUC, capCtrlMeth));
290 81 : if (fanCoil.CapCtrlMeth_Num == CCM::ASHRAE) {
291 3 : fanCoil.DesZoneCoolingLoad = DataSizing::AutoSize;
292 3 : fanCoil.DesZoneHeatingLoad = DataSizing::AutoSize;
293 3 : fanCoil.fanOp = HVAC::FanOp::Continuous;
294 : }
295 :
296 81 : if (!lAlphaBlanks(4) && ((fanCoil.oaSched = Sched::GetSchedule(state, Alphas(4))) == nullptr)) {
297 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(4), Alphas(4));
298 0 : ErrorsFound = true;
299 : }
300 81 : fanCoil.MaxAirVolFlow = Numbers(1);
301 81 : fanCoil.LowSpeedRatio = Numbers(2);
302 81 : fanCoil.MedSpeedRatio = Numbers(3);
303 : // check if low speed ratio < medium speed ratio, if not : warning & set to default values
304 81 : if (fanCoil.LowSpeedRatio > fanCoil.MedSpeedRatio) {
305 0 : ShowWarningError(state, format("{}{}=\"{}\",", RoutineName, CurrentModuleObject, fanCoil.Name));
306 0 : ShowContinueError(state, format("... {} is greater than the medium speed supply air flow ratio.", cNumericFields(2)));
307 0 : ShowContinueError(state, format("... Fan Coil Unit low speed supply air flow ratio = {:.5T} ", fanCoil.LowSpeedRatio));
308 0 : ShowContinueError(state, format("... Fan Coit Unit medium speed supply air flow ratio = {:.5T} ", fanCoil.MedSpeedRatio));
309 0 : ShowContinueError(state,
310 : "... Fan Coil Unit low speed supply air flow ratio and medium speed supply air flow ratio set to default values");
311 0 : fanCoil.LowSpeedRatio = 1.0 / 3.0;
312 0 : fanCoil.MedSpeedRatio = 2.0 / 3.0;
313 : }
314 :
315 81 : fanCoil.OutAirVolFlow = Numbers(4);
316 :
317 81 : fanCoil.AirInNode = NodeInputManager::GetOnlySingleNode(state,
318 81 : Alphas(5),
319 : ErrorsFound,
320 : DataLoopNode::ConnectionObjectType::ZoneHVACFourPipeFanCoil,
321 81 : Alphas(1),
322 : DataLoopNode::NodeFluidType::Air,
323 : DataLoopNode::ConnectionType::Inlet,
324 : NodeInputManager::CompFluidStream::Primary,
325 : DataLoopNode::ObjectIsParent); // air input node
326 :
327 81 : fanCoil.AirOutNode = NodeInputManager::GetOnlySingleNode(state,
328 81 : Alphas(6),
329 : ErrorsFound,
330 : DataLoopNode::ConnectionObjectType::ZoneHVACFourPipeFanCoil,
331 81 : Alphas(1),
332 : DataLoopNode::NodeFluidType::Air,
333 : DataLoopNode::ConnectionType::Outlet,
334 : NodeInputManager::CompFluidStream::Primary,
335 : DataLoopNode::ObjectIsParent); // air outlet node
336 :
337 81 : fanCoil.OAMixType = Alphas(7);
338 81 : fanCoil.OAMixName = Alphas(8);
339 : // check to see if local OA mixer specified
340 81 : if (!lAlphaBlanks(8)) {
341 71 : errFlag = false;
342 71 : ValidateComponent(state, fanCoil.OAMixType, fanCoil.OAMixName, errFlag, CurrentModuleObject);
343 71 : if (errFlag) {
344 0 : ShowContinueError(state, format("specified in {} = \"{}\".", CurrentModuleObject, fanCoil.Name));
345 0 : ErrorsFound = true;
346 : } else {
347 : // Get outdoor air mixer node numbers
348 71 : OANodeNums = MixedAir::GetOAMixerNodeNumbers(state, fanCoil.OAMixName, errFlag);
349 71 : if (errFlag) {
350 0 : ShowContinueError(state, format("that was specified in {} = {}", CurrentModuleObject, fanCoil.Name));
351 0 : ShowContinueError(state, "..OutdoorAir:Mixer is required. Enter an OutdoorAir:Mixer object with this name.");
352 0 : ErrorsFound = true;
353 : } else {
354 71 : fanCoil.OutsideAirNode = OANodeNums(1);
355 71 : fanCoil.AirReliefNode = OANodeNums(2);
356 71 : fanCoil.MixedAirNode = OANodeNums(4);
357 : }
358 : }
359 : }
360 :
361 81 : fanCoil.CCoilName = Alphas(12);
362 81 : fanCoil.MaxColdWaterVolFlow = Numbers(5);
363 81 : fanCoil.MinColdWaterVolFlow = Numbers(6);
364 81 : fanCoil.ColdControlOffset = Numbers(7);
365 81 : fanCoil.HCoilName = Alphas(14);
366 81 : fanCoil.HCoilType = Alphas(13);
367 81 : fanCoil.MaxHotWaterVolFlow = Numbers(8);
368 81 : fanCoil.MinHotWaterVolFlow = Numbers(9);
369 81 : fanCoil.HotControlOffset = Numbers(10);
370 :
371 81 : if (Util::SameString(Alphas(11), "Coil:Cooling:Water") || Util::SameString(Alphas(11), "Coil:Cooling:Water:DetailedGeometry") ||
372 81 : Util::SameString(Alphas(11), "CoilSystem:Cooling:Water:HeatExchangerAssisted")) {
373 81 : fanCoil.CCoilType = Alphas(11);
374 81 : if (Util::SameString(Alphas(11), "Coil:Cooling:Water")) {
375 81 : fanCoil.CCoilType_Num = CCoil::Water;
376 81 : fanCoil.CCoilPlantName = fanCoil.CCoilName;
377 81 : fanCoil.CCoilPlantType = DataPlant::PlantEquipmentType::CoilWaterCooling;
378 : }
379 81 : if (Util::SameString(Alphas(11), "Coil:Cooling:Water:DetailedGeometry")) {
380 0 : fanCoil.CCoilType_Num = CCoil::Detailed;
381 0 : fanCoil.CCoilPlantName = fanCoil.CCoilName;
382 0 : fanCoil.CCoilPlantType = DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling;
383 : }
384 81 : std::string CCoilType;
385 81 : if (Util::SameString(Alphas(11), "CoilSystem:Cooling:Water:HeatExchangerAssisted")) {
386 0 : fanCoil.CCoilType_Num = CCoil::HXAssist;
387 0 : HVACHXAssistedCoolingCoil::GetHXCoilTypeAndName(
388 0 : state, fanCoil.CCoilType, fanCoil.CCoilName, ErrorsFound, CCoilType, fanCoil.CCoilPlantName);
389 0 : if (Util::SameString(CCoilType, "Coil:Cooling:Water")) {
390 0 : fanCoil.CCoilPlantType = DataPlant::PlantEquipmentType::CoilWaterCooling;
391 0 : } else if (Util::SameString(CCoilType, "Coil:Cooling:Water:DetailedGeometry")) {
392 0 : fanCoil.CCoilPlantType = DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling;
393 : } else {
394 0 : ShowSevereError(state, format("{}{}=\"{}\", invalid", RoutineName, CurrentModuleObject, fanCoil.Name));
395 0 : ShowContinueError(state, format("For: {}=\"{}\".", cAlphaFields(11), Alphas(11)));
396 0 : ShowContinueError(state, format("Invalid Coil Type={}, Name={}", CCoilType, fanCoil.CCoilPlantName));
397 0 : ShowContinueError(state, "must be \"Coil:Cooling:Water\" or \"Coil:Cooling:Water:DetailedGeometry\"");
398 0 : ErrorsFound = true;
399 : }
400 : }
401 81 : IsNotOK = false;
402 81 : ValidateComponent(state, fanCoil.CCoilType, fanCoil.CCoilName, IsNotOK, fanCoil.UnitType);
403 81 : if (IsNotOK) {
404 0 : ShowContinueError(state, format("...specified in {}=\"{}\".", CurrentModuleObject, fanCoil.Name));
405 0 : ErrorsFound = true;
406 : } else {
407 81 : if (fanCoil.CCoilType_Num != CCoil::HXAssist) {
408 : // mine the cold water node from the coil object
409 81 : int coilIndex = WaterCoils::GetWaterCoilIndex(state, fanCoil.CCoilType, fanCoil.CCoilName, IsNotOK);
410 : // Other error checks should trap before it gets to this point in the code, but including just in case.
411 81 : if (IsNotOK) {
412 0 : ShowContinueError(state, format("...specified in {}=\"{}\".", CurrentModuleObject, fanCoil.Name));
413 0 : ErrorsFound = true;
414 : } else {
415 81 : fanCoil.CoolCoilFluidInletNode = state.dataWaterCoils->WaterCoil(coilIndex).WaterInletNodeNum;
416 81 : fanCoil.CoolCoilInletNodeNum = state.dataWaterCoils->WaterCoil(coilIndex).AirInletNodeNum;
417 81 : fanCoil.CoolCoilOutletNodeNum = state.dataWaterCoils->WaterCoil(coilIndex).AirOutletNodeNum;
418 : }
419 : } else {
420 : // mine the cold water node from the coil object
421 0 : int coilIndex = WaterCoils::GetWaterCoilIndex(state, CCoilType, fanCoil.CCoilPlantName, IsNotOK);
422 : // Other error checks should trap before it gets to this point in the code, but including just in case.
423 0 : if (IsNotOK) {
424 0 : ShowContinueError(state, format("...specified in {}=\"{}\".", CurrentModuleObject, fanCoil.Name));
425 0 : ErrorsFound = true;
426 : } else {
427 0 : fanCoil.CoolCoilFluidInletNode = state.dataWaterCoils->WaterCoil(coilIndex).WaterInletNodeNum;
428 0 : fanCoil.CoolCoilInletNodeNum = state.dataWaterCoils->WaterCoil(coilIndex).AirInletNodeNum;
429 0 : fanCoil.CoolCoilOutletNodeNum = state.dataWaterCoils->WaterCoil(coilIndex).AirOutletNodeNum;
430 : }
431 : }
432 : }
433 81 : } else {
434 0 : ShowSevereError(state, format("{}{}=\"{}\", invalid", RoutineName, CurrentModuleObject, fanCoil.Name));
435 0 : ShowContinueError(state, format("illegal value: {}=\"{}\".", cAlphaFields(11), Alphas(11)));
436 0 : ErrorsFound = true;
437 : }
438 :
439 81 : if (Util::SameString(Alphas(13), "Coil:Heating:Water")) {
440 80 : fanCoil.HCoilType_Num = HCoil::Water;
441 80 : fanCoil.HCoilPlantTypeOf = DataPlant::PlantEquipmentType::CoilWaterSimpleHeating;
442 80 : IsNotOK = false;
443 80 : ValidateComponent(state, fanCoil.HCoilType, fanCoil.HCoilName, IsNotOK, CurrentModuleObject);
444 80 : if (IsNotOK) {
445 0 : ShowContinueError(state, format("...specified in {}=\"{}\".", CurrentModuleObject, fanCoil.Name));
446 0 : ErrorsFound = true;
447 : } else {
448 : // mine the hot water node from the coil object
449 80 : int coilIndex = WaterCoils::GetWaterCoilIndex(state, fanCoil.HCoilType, fanCoil.HCoilName, IsNotOK);
450 80 : if (IsNotOK) {
451 0 : ShowContinueError(state, format("...specified in {}=\"{}\".", CurrentModuleObject, fanCoil.Name));
452 0 : ErrorsFound = true;
453 : } else {
454 80 : fanCoil.HeatCoilFluidInletNode = state.dataWaterCoils->WaterCoil(coilIndex).WaterInletNodeNum;
455 80 : fanCoil.HeatCoilInletNodeNum = state.dataWaterCoils->WaterCoil(coilIndex).AirInletNodeNum;
456 80 : fanCoil.HeatCoilOutletNodeNum = state.dataWaterCoils->WaterCoil(coilIndex).AirOutletNodeNum;
457 : }
458 : }
459 1 : } else if (Util::SameString(Alphas(13), "Coil:Heating:Electric")) {
460 1 : fanCoil.HCoilType_Num = HCoil::Electric;
461 1 : IsNotOK = false;
462 1 : ValidateComponent(state, fanCoil.HCoilType, fanCoil.HCoilName, IsNotOK, CurrentModuleObject);
463 1 : if (IsNotOK) {
464 0 : ShowContinueError(state, format("...specified in {}=\"{}\".", CurrentModuleObject, fanCoil.Name));
465 0 : ErrorsFound = true;
466 : } else {
467 : int coilIndex;
468 1 : HeatingCoils::GetCoilIndex(state, fanCoil.HCoilName, coilIndex, IsNotOK);
469 1 : if (IsNotOK) {
470 0 : ShowContinueError(state, format("Occurs in {} = {}", CurrentModuleObject, fanCoil.Name));
471 0 : ErrorsFound = true;
472 : } else {
473 1 : fanCoil.DesignHeatingCapacity = state.dataHeatingCoils->HeatingCoil(coilIndex).NominalCapacity;
474 1 : fanCoil.HeatCoilInletNodeNum = state.dataHeatingCoils->HeatingCoil(coilIndex).AirInletNodeNum;
475 1 : fanCoil.HeatCoilOutletNodeNum = state.dataHeatingCoils->HeatingCoil(coilIndex).AirOutletNodeNum;
476 : }
477 : }
478 : } else {
479 0 : ShowSevereError(state, format("{}{}=\"{}\", invalid", RoutineName, CurrentModuleObject, fanCoil.Name));
480 0 : ShowContinueError(state, format("illegal value: {}=\"{}\".", cAlphaFields(13), Alphas(13)));
481 0 : ErrorsFound = true;
482 : }
483 :
484 81 : fanCoil.FanName = Alphas(10);
485 :
486 81 : if (!lAlphaBlanks(15)) {
487 0 : fanCoil.AvailManagerListName = Alphas(15);
488 : }
489 :
490 81 : fanCoil.HVACSizingIndex = 0;
491 81 : if (!lAlphaBlanks(16)) {
492 3 : fanCoil.HVACSizingIndex = Util::FindItemInList(Alphas(16), state.dataSize->ZoneHVACSizing);
493 3 : if (fanCoil.HVACSizingIndex == 0) {
494 0 : ShowSevereError(state, format("{} = {} not found.", cAlphaFields(16), Alphas(16)));
495 0 : ShowContinueError(state, format("Occurs in {} = {}", state.dataFanCoilUnits->cMO_FanCoil, fanCoil.Name));
496 0 : ErrorsFound = true;
497 : }
498 : }
499 :
500 81 : fanCoil.fanType = static_cast<HVAC::FanType>(getEnumValue(HVAC::fanTypeNamesUC, Alphas(9)));
501 81 : if (fanCoil.fanType != HVAC::FanType::Constant && fanCoil.fanType != HVAC::FanType::VAV && fanCoil.fanType != HVAC::FanType::OnOff &&
502 18 : fanCoil.fanType != HVAC::FanType::SystemModel) {
503 0 : ShowSevereInvalidKey(state,
504 : eoh,
505 0 : cAlphaFields(9),
506 0 : Alphas(9),
507 : "Fan Type must be Fan:OnOff, Fan:ConstantVolume, Fan:VariableVolume, or Fan:SystemModel.");
508 0 : ErrorsFound = true;
509 81 : } else if ((fanCoil.FanIndex = Fans::GetFanIndex(state, fanCoil.FanName)) == 0) {
510 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(10), Alphas(10));
511 0 : ErrorsFound = true;
512 : } else {
513 81 : auto *fan = state.dataFans->fans(fanCoil.FanIndex);
514 :
515 81 : if (fanCoil.fanType != fan->type) {
516 0 : ShowSevereCustom(state,
517 : eoh,
518 0 : format("{} was specified as having type {}, but has type {}",
519 0 : fanCoil.FanName,
520 0 : HVAC::fanTypeNamesUC[(int)fanCoil.fanType],
521 0 : HVAC::fanTypeNamesUC[(int)fan->type]));
522 0 : ErrorsFound = true;
523 : }
524 :
525 81 : fanCoil.fanAvailSched = fan->availSched;
526 81 : fanCoil.FanAirVolFlow = fan->maxAirFlowRate;
527 :
528 81 : if (fanCoil.MaxAirVolFlow > fanCoil.FanAirVolFlow && fanCoil.FanAirVolFlow != DataSizing::AutoSize) {
529 0 : ShowWarningError(state, format("{}{}: {}", RoutineName, fanCoil.UnitType, fanCoil.Name));
530 0 : ShowContinueError(state, format("... {} is greater than the maximum fan flow rate.", cNumericFields(1)));
531 0 : ShowContinueError(state, format("... Fan Coil Unit flow = {:.5T} m3/s.", fanCoil.MaxAirVolFlow));
532 0 : ShowContinueError(state, format("... Fan = {}: {}", HVAC::fanTypeNames[(int)fanCoil.fanType], fanCoil.FanName));
533 0 : ShowContinueError(state, format("... Fan flow = {:.5T} m3/s.", fanCoil.FanAirVolFlow));
534 0 : ShowContinueError(state, "... Fan Coil Unit flow rate reduced to match the fan flow rate and the simulation continues.");
535 0 : fanCoil.MaxAirVolFlow = fanCoil.FanAirVolFlow;
536 : }
537 :
538 81 : if (fanCoil.fanType == HVAC::FanType::Constant || fanCoil.fanType == HVAC::FanType::VAV || fanCoil.fanType == HVAC::FanType::OnOff) {
539 : // Get fan air volume flow rate
540 : // Check that the fan volumetric flow rate is greater than or equal to the FCU volumetric flow rate
541 :
542 : // Check that the fan type match with the capacity control method selected
543 63 : if ((fanCoil.CapCtrlMeth_Num == CCM::ConsFanVarFlow && (fanCoil.fanType == HVAC::FanType::VAV)) ||
544 63 : (fanCoil.CapCtrlMeth_Num == CCM::CycFan && fanCoil.fanType != HVAC::FanType::OnOff) ||
545 63 : (fanCoil.CapCtrlMeth_Num == CCM::VarFanVarFlow && fanCoil.fanType != HVAC::FanType::VAV) ||
546 63 : (fanCoil.CapCtrlMeth_Num == CCM::VarFanConsFlow && fanCoil.fanType != HVAC::FanType::VAV)) {
547 0 : ShowSevereError(state, format("{}{}: {}", RoutineName, fanCoil.UnitType, fanCoil.Name));
548 0 : ShowContinueError(state,
549 0 : format("...the fan type of the object : {} does not match with the capacity control method selected : "
550 : "{} please see I/O reference",
551 0 : fanCoil.FanName,
552 : capCtrlMeth));
553 0 : ShowContinueError(state, "...for ConstantFanVariableFlow a Fan:OnOff or Fan:ConstantVolume is valid.");
554 0 : ShowContinueError(state, "...for CyclingFan a Fan:OnOff is valid.");
555 0 : ShowContinueError(state, "...for VariableFanVariableFlow or VariableFanConstantFlow a Fan:VariableVolume is valid.");
556 0 : ErrorsFound = true;
557 : }
558 : } else {
559 : // check that for VariableFanVariableFlow or VariableFanConstantFlow that the fan speed control is continuous
560 18 : if (fanCoil.CapCtrlMeth_Num == CCM::VarFanVarFlow || fanCoil.CapCtrlMeth_Num == CCM::VarFanConsFlow ||
561 18 : fanCoil.CapCtrlMeth_Num == CCM::ASHRAE) { // then expect continuous speed control fan
562 3 : if (dynamic_cast<Fans::FanSystem *>(state.dataFans->fans(fanCoil.FanIndex))->speedControl != Fans::SpeedControl::Continuous) {
563 0 : ShowSevereError(state, format("{}{}: {}", RoutineName, fanCoil.UnitType, fanCoil.Name));
564 0 : ShowContinueError(state,
565 0 : format("...the fan type of the object : {} does not match with the capacity control method selected : "
566 : "{} please see I/O reference",
567 0 : fanCoil.FanName,
568 : capCtrlMeth));
569 0 : ShowContinueError(
570 : state,
571 : "...for VariableFanVariableFlow or VariableFanConstantFlow a Fan:SystemModel should have Continuous speed control.");
572 0 : ErrorsFound = true;
573 : }
574 : }
575 : }
576 : }
577 :
578 : // check low speed fan ratio when using ASHRAE90.1 capacity control method
579 81 : if (fanCoil.CapCtrlMeth_Num == CCM::ASHRAE) {
580 3 : if (fanCoil.LowSpeedRatio > 0.5) {
581 0 : ShowWarningError(state, format("{}{}=\"{}\",", RoutineName, CurrentModuleObject, fanCoil.Name));
582 0 : ShowContinueError(state, format("... {} is greater than the 50% of the supply air flow ratio.", cNumericFields(2)));
583 0 : ShowContinueError(state, format("... Fan Coil Unit low speed supply air flow ratio = {:.5T} ", fanCoil.LowSpeedRatio));
584 3 : } else if (fanCoil.LowSpeedRatio == 0.0) {
585 0 : ShowWarningError(state, format("{}{}=\"{}\",", RoutineName, CurrentModuleObject, fanCoil.Name));
586 0 : ShowContinueError(state, format("... {} is equal to 0.", cNumericFields(2)));
587 0 : ShowContinueError(state, "... Fan Coil Unit low speed supply air flow ratio should be greater than 0 to comply with ASHRAE90.1.");
588 0 : ShowContinueError(state, "... Fan Coil Unit low speed supply air flow ratio set to 0.5");
589 0 : fanCoil.LowSpeedRatio = 0.5;
590 : }
591 : }
592 :
593 : // Set defaults for convergence tolerance
594 81 : if (fanCoil.ColdControlOffset <= 0.0) {
595 0 : fanCoil.ColdControlOffset = 0.001;
596 : }
597 81 : if (fanCoil.HotControlOffset <= 0.0) {
598 0 : fanCoil.HotControlOffset = 0.001;
599 : }
600 :
601 : // check for inlet side air mixer
602 162 : SingleDuct::GetATMixer(state,
603 81 : fanCoil.Name,
604 : ATMixerName,
605 81 : state.dataFanCoilUnits->ATMixerNum,
606 81 : state.dataFanCoilUnits->ATMixerType,
607 81 : state.dataFanCoilUnits->ATMixerPriNode,
608 81 : state.dataFanCoilUnits->ATMixerSecNode,
609 81 : state.dataFanCoilUnits->ATMixerOutNode,
610 : fanCoil.AirOutNode);
611 81 : fanCoil.ControlZoneNum =
612 81 : DataZoneEquipment::GetZoneEquipControlledZoneNum(state, DataZoneEquipment::ZoneEquipType::FourPipeFanCoil, fanCoil.Name);
613 81 : if (fanCoil.ControlZoneNum == 0) {
614 0 : ErrorsFound = true;
615 : }
616 81 : if (state.dataFanCoilUnits->ATMixerType == HVAC::MixerType::InletSide) {
617 : // save the air terminal mixer data in the fan coil data array
618 5 : fanCoil.ATMixerExists = true;
619 5 : fanCoil.ATMixerIndex = state.dataFanCoilUnits->ATMixerNum;
620 5 : fanCoil.ATMixerName = ATMixerName;
621 5 : fanCoil.ATMixerType = HVAC::MixerType::InletSide;
622 5 : fanCoil.ATMixerPriNode = state.dataFanCoilUnits->ATMixerPriNode;
623 5 : fanCoil.ATMixerSecNode = state.dataFanCoilUnits->ATMixerSecNode;
624 5 : fanCoil.ATMixerOutNode = state.dataFanCoilUnits->ATMixerOutNode;
625 : // check that fan coil doesn' have local outside air
626 5 : if (!lAlphaBlanks(8)) {
627 0 : ShowSevereError(
628 : state,
629 0 : format("{} = \"{}\". Fan coil unit has local as well as central outdoor air specified", CurrentModuleObject, fanCoil.Name));
630 : }
631 : // check that the air teminal mixer out node is the fan coil inlet node
632 5 : if (fanCoil.AirInNode != state.dataFanCoilUnits->ATMixerOutNode) {
633 0 : ShowSevereError(
634 : state,
635 0 : format("{} = \"{}\". Fan coil unit air inlet node name must be the same as an air terminal mixer outlet node name.",
636 : CurrentModuleObject,
637 0 : fanCoil.Name));
638 0 : ShowContinueError(state, "..Air terminal mixer outlet node name is specified in AirTerminal:SingleDuct:InletSideMixer object.");
639 0 : ShowContinueError(state, format("..Fan coil unit air inlet node name = {}", state.dataLoopNodes->NodeID(fanCoil.AirInNode)));
640 0 : ErrorsFound = true;
641 : }
642 : // check for supply side air terminal mixer
643 76 : } else if (state.dataFanCoilUnits->ATMixerType == HVAC::MixerType::SupplySide) {
644 : // save the air terminal mixer data in the fan coil data array
645 5 : fanCoil.ATMixerExists = true;
646 5 : fanCoil.ATMixerIndex = state.dataFanCoilUnits->ATMixerNum;
647 5 : fanCoil.ATMixerName = ATMixerName;
648 5 : fanCoil.ATMixerType = HVAC::MixerType::SupplySide;
649 5 : fanCoil.ATMixerPriNode = state.dataFanCoilUnits->ATMixerPriNode;
650 5 : fanCoil.ATMixerSecNode = state.dataFanCoilUnits->ATMixerSecNode;
651 5 : fanCoil.ATMixerOutNode = state.dataFanCoilUnits->ATMixerOutNode;
652 : // check that fan coil doesn' have local outside air
653 5 : if (!lAlphaBlanks(8)) {
654 0 : ShowSevereError(
655 : state,
656 0 : format("{} = \"{}\". Fan coil unit has local as well as central outdoor air specified", CurrentModuleObject, fanCoil.Name));
657 : }
658 : // check that the air teminal mixer secondary air inlet node is the fan coil outlet node
659 5 : if (fanCoil.AirOutNode != state.dataFanCoilUnits->ATMixerSecNode) {
660 0 : ShowSevereError(state,
661 0 : format("{} = \"{}\". Fan coil unit air outlet node name must be the same as the air terminal mixer secondary air "
662 : "inlet node name.",
663 : CurrentModuleObject,
664 0 : fanCoil.Name));
665 0 : ShowContinueError(
666 : state, "..Air terminal mixer secondary inlet node name is specified in AirTerminal:SingleDuct:SupplySideMixer object.");
667 0 : ShowContinueError(state, format("..Fan coil unit air outlet node name = {}", state.dataLoopNodes->NodeID(fanCoil.AirOutNode)));
668 0 : ErrorsFound = true;
669 : }
670 5 : bool ZoneNodeNotFound = true;
671 5 : for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).NumExhaustNodes; ++NodeNum) {
672 5 : if (fanCoil.AirInNode == state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).ExhaustNode(NodeNum)) {
673 5 : ZoneNodeNotFound = false;
674 5 : break;
675 : }
676 : }
677 5 : if (ZoneNodeNotFound) {
678 0 : bool InletNodeFound = false;
679 0 : if (fanCoil.ControlZoneNum > 0) {
680 0 : InletNodeFound = ZonePlenum::ValidateInducedNode(state,
681 : fanCoil.AirInNode,
682 0 : state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).NumReturnNodes,
683 0 : state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).ReturnNode);
684 : }
685 0 : if (!InletNodeFound) {
686 0 : ShowSevereError(state, format("{}{}=\"{}\"", RoutineName, CurrentModuleObject, fanCoil.Name));
687 0 : ShowContinueError(state,
688 : "..FanCoil inlet node name must be the same as either a zone exhaust node name or an induced "
689 : "air node in ZonePlenum.");
690 0 : ShowContinueError(state, "..Zone exhaust node name is specified in ZoneHVAC:EquipmentConnections object.");
691 0 : ShowContinueError(state, "..Induced Air Outlet Node name is specified in AirLoopHVAC:ReturnPlenum object.");
692 0 : ShowContinueError(state, format("..FanCoil inlet node name = {}", state.dataLoopNodes->NodeID(fanCoil.AirInNode)));
693 0 : ErrorsFound = true;
694 : }
695 : }
696 : // no air terminal mixer; do the normal connectivity checks
697 : } else {
698 : // check that the fan coil inlet node is the same as one of the zone exhaust nodes
699 71 : state.dataFanCoilUnits->ZoneExNodeNotFound = true;
700 71 : for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).NumExhaustNodes; ++NodeNum) {
701 71 : if (fanCoil.AirInNode == state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).ExhaustNode(NodeNum)) {
702 71 : state.dataFanCoilUnits->ZoneExNodeNotFound = false;
703 71 : break;
704 : }
705 : }
706 71 : if (state.dataFanCoilUnits->ZoneExNodeNotFound) {
707 0 : bool InletNodeFound = false;
708 0 : InletNodeFound = ZonePlenum::ValidateInducedNode(state,
709 : fanCoil.AirInNode,
710 0 : state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).NumReturnNodes,
711 0 : state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).ReturnNode);
712 :
713 0 : if (!InletNodeFound) {
714 0 : ShowSevereError(state,
715 0 : format("{} = \"{}\". Fan coil unit air inlet node name must be the same either as a zone exhaust node name "
716 : "or an induce air node in ZoePlenum.",
717 : CurrentModuleObject,
718 0 : fanCoil.Name));
719 0 : ShowContinueError(state, "..Zone exhaust node name is specified in ZoneHVAC:EquipmentConnections object.");
720 0 : ShowContinueError(state, "..Induced Air Outlet Node name is specified in AirLoopHVAC:ReturnPlenum object.");
721 0 : ShowContinueError(state, format("..Fan coil unit air inlet node name = {}", state.dataLoopNodes->NodeID(fanCoil.AirInNode)));
722 0 : ErrorsFound = true;
723 : }
724 : }
725 : // check that the fan coil outlet node is the same as one of the zone inlet nodes
726 71 : state.dataFanCoilUnits->ZoneInNodeNotFound = true;
727 71 : if (fanCoil.ControlZoneNum > 0) {
728 71 : fanCoil.NodeNumOfControlledZone = state.dataZoneEquip->ZoneEquipConfig(fanCoil.ControlZoneNum).ZoneNode;
729 71 : state.dataFanCoilUnits->ZoneInNodeNotFound = false;
730 : }
731 :
732 71 : if (state.dataFanCoilUnits->ZoneInNodeNotFound) {
733 0 : ShowSevereError(state,
734 0 : format("{} = \"{}\". Fan coil unit air outlet node name must be the same as a zone inlet node name.",
735 : CurrentModuleObject,
736 0 : fanCoil.Name));
737 0 : ShowContinueError(state, "..Zone inlet node name is specified in ZoneHVAC:EquipmentConnections object.");
738 0 : ShowContinueError(state, format("..Fan coil unit air outlet node name = {}", state.dataLoopNodes->NodeID(fanCoil.AirOutNode)));
739 :
740 0 : ErrorsFound = true;
741 : }
742 : }
743 81 : if (fanCoil.CapCtrlMeth_Num == CCM::MultiSpeedFan) {
744 3 : if (!lAlphaBlanks(17)) {
745 2 : fanCoil.fanOpModeSched = Sched::GetSchedule(state, Alphas(17));
746 2 : if (fanCoil.fanType != HVAC::FanType::OnOff && fanCoil.fanType != HVAC::FanType::SystemModel) {
747 0 : ShowSevereError(state, format("{} = {}", CurrentModuleObject, fanCoil.Name));
748 0 : ShowContinueError(state, format("For {} = {}", cAlphaFields(17), Alphas(17)));
749 0 : ShowContinueError(state, format("Illegal {} = {}", cAlphaFields(9), Alphas(9)));
750 0 : ShowContinueError(state, "...fan operating schedule is allowed for on off or system model fan type only )");
751 0 : ErrorsFound = true;
752 2 : } else if (fanCoil.fanOpModeSched == nullptr) {
753 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(17), Alphas(17));
754 0 : ErrorsFound = true;
755 : }
756 1 : } else if (fanCoil.fanType == HVAC::FanType::OnOff || fanCoil.fanType == HVAC::FanType::SystemModel) {
757 1 : fanCoil.fanOp = HVAC::FanOp::Cycling;
758 : }
759 : }
760 :
761 81 : if (!lNumericBlanks(11)) {
762 0 : fanCoil.DesignMinOutletTemp = Numbers(11);
763 0 : if (lNumericBlanks(12)) {
764 0 : ShowWarningError(state, format("{}{}=\"{}\",", RoutineName, CurrentModuleObject, fanCoil.Name));
765 0 : ShowContinueError(state, format("... {} and {} must be used in unison.", cNumericFields(11), cNumericFields(12)));
766 0 : ErrorsFound = true;
767 : }
768 : }
769 :
770 81 : if (!lNumericBlanks(12)) {
771 0 : fanCoil.DesignMaxOutletTemp = Numbers(12);
772 0 : if (fanCoil.DesignMinOutletTemp != DataSizing::AutoSize && fanCoil.DesignMaxOutletTemp != DataSizing::AutoSize) {
773 0 : if (fanCoil.DesignMaxOutletTemp < fanCoil.DesignMinOutletTemp) {
774 0 : ShowWarningError(state, format("{}{}=\"{}\",", RoutineName, CurrentModuleObject, fanCoil.Name));
775 0 : ShowContinueError(state, format("... {} is greater than {}.", cNumericFields(11), cNumericFields(12)));
776 0 : ShowContinueError(state, format("... {} = {:.2T} [C].", cNumericFields(11), fanCoil.DesignMinOutletTemp));
777 0 : ShowContinueError(state, format("... {} = {:.2T} [C].", cNumericFields(12), fanCoil.DesignMaxOutletTemp));
778 0 : ErrorsFound = true;
779 : }
780 : }
781 0 : if (lNumericBlanks(11)) {
782 0 : ShowWarningError(state, format("{}{}=\"{}\",", RoutineName, CurrentModuleObject, fanCoil.Name));
783 0 : ShowContinueError(state, format("... {} and {} must be used in unison.", cNumericFields(11), cNumericFields(12)));
784 0 : ErrorsFound = true;
785 : }
786 : }
787 :
788 81 : if (fanCoil.DesignMinOutletTemp > 0.0 && fanCoil.DesignMaxOutletTemp > 0.0) {
789 0 : fanCoil.ASHRAETempControl = true;
790 81 : } else if (fanCoil.DesignMinOutletTemp == DataSizing::AutoSize || fanCoil.DesignMaxOutletTemp == DataSizing::AutoSize) {
791 0 : fanCoil.ASHRAETempControl = true;
792 : }
793 :
794 : // Set up component set for supply fan
795 81 : if (fanCoil.OutsideAirNode > 0) {
796 213 : BranchNodeConnections::SetUpCompSets(state,
797 : fanCoil.UnitType,
798 : fanCoil.Name,
799 71 : HVAC::fanTypeNames[(int)fanCoil.fanType],
800 : fanCoil.FanName,
801 71 : state.dataLoopNodes->NodeID(fanCoil.MixedAirNode),
802 : "UNDEFINED");
803 : } else {
804 30 : BranchNodeConnections::SetUpCompSets(state,
805 : fanCoil.UnitType,
806 : fanCoil.Name,
807 10 : HVAC::fanTypeNames[(int)fanCoil.fanType],
808 : fanCoil.FanName,
809 10 : state.dataLoopNodes->NodeID(fanCoil.AirInNode),
810 : "UNDEFINED");
811 : }
812 : // Set up component set for cooling coil
813 81 : BranchNodeConnections::SetUpCompSets(
814 : state, fanCoil.UnitType, fanCoil.Name, fanCoil.CCoilType, fanCoil.CCoilName, "UNDEFINED", "UNDEFINED");
815 :
816 : // Set up component set for heating coil
817 162 : BranchNodeConnections::SetUpCompSets(state,
818 : fanCoil.UnitType,
819 : fanCoil.Name,
820 : fanCoil.HCoilType,
821 : fanCoil.HCoilName,
822 : "UNDEFINED",
823 81 : state.dataLoopNodes->NodeID(fanCoil.AirOutNode));
824 :
825 : // Set up component set for OA mixer - use OA node and Mixed air node
826 81 : if (fanCoil.OutsideAirNode > 0) {
827 142 : BranchNodeConnections::SetUpCompSets(state,
828 : fanCoil.UnitType,
829 : fanCoil.Name,
830 : fanCoil.OAMixType,
831 : fanCoil.OAMixName,
832 71 : state.dataLoopNodes->NodeID(fanCoil.OutsideAirNode),
833 71 : state.dataLoopNodes->NodeID(fanCoil.MixedAirNode));
834 : }
835 81 : }
836 :
837 19 : Alphas.deallocate();
838 19 : cAlphaFields.deallocate();
839 19 : cNumericFields.deallocate();
840 19 : Numbers.deallocate();
841 19 : lAlphaBlanks.deallocate();
842 19 : lNumericBlanks.deallocate();
843 :
844 19 : if (ErrorsFound) {
845 0 : ShowFatalError(state, format("{}Errors found in input. Preceding condition(s) cause termination.", RoutineName));
846 : }
847 :
848 100 : for (auto &fanCoil : state.dataFanCoilUnits->FanCoil) {
849 : // Setup Report variables for the Fan Coils
850 : // CurrentModuleObject='ZoneHVAC:FourPipeFanCoil'
851 162 : SetupOutputVariable(state,
852 : "Fan Coil Heating Rate",
853 : Constant::Units::W,
854 81 : fanCoil.HeatPower,
855 : OutputProcessor::TimeStepType::System,
856 : OutputProcessor::StoreType::Average,
857 81 : fanCoil.Name);
858 162 : SetupOutputVariable(state,
859 : "Fan Coil Heating Energy",
860 : Constant::Units::J,
861 81 : fanCoil.HeatEnergy,
862 : OutputProcessor::TimeStepType::System,
863 : OutputProcessor::StoreType::Sum,
864 81 : fanCoil.Name);
865 162 : SetupOutputVariable(state,
866 : "Fan Coil Total Cooling Rate",
867 : Constant::Units::W,
868 81 : fanCoil.TotCoolPower,
869 : OutputProcessor::TimeStepType::System,
870 : OutputProcessor::StoreType::Average,
871 81 : fanCoil.Name);
872 162 : SetupOutputVariable(state,
873 : "Fan Coil Total Cooling Energy",
874 : Constant::Units::J,
875 81 : fanCoil.TotCoolEnergy,
876 : OutputProcessor::TimeStepType::System,
877 : OutputProcessor::StoreType::Sum,
878 81 : fanCoil.Name);
879 162 : SetupOutputVariable(state,
880 : "Fan Coil Sensible Cooling Rate",
881 : Constant::Units::W,
882 81 : fanCoil.SensCoolPower,
883 : OutputProcessor::TimeStepType::System,
884 : OutputProcessor::StoreType::Average,
885 81 : fanCoil.Name);
886 162 : SetupOutputVariable(state,
887 : "Fan Coil Sensible Cooling Energy",
888 : Constant::Units::J,
889 81 : fanCoil.SensCoolEnergy,
890 : OutputProcessor::TimeStepType::System,
891 : OutputProcessor::StoreType::Sum,
892 81 : fanCoil.Name);
893 162 : SetupOutputVariable(state,
894 : "Fan Coil Fan Electricity Rate",
895 : Constant::Units::W,
896 81 : fanCoil.ElecPower,
897 : OutputProcessor::TimeStepType::System,
898 : OutputProcessor::StoreType::Average,
899 81 : fanCoil.Name);
900 162 : SetupOutputVariable(state,
901 : "Fan Coil Fan Electricity Energy",
902 : Constant::Units::J,
903 81 : fanCoil.ElecEnergy,
904 : OutputProcessor::TimeStepType::System,
905 : OutputProcessor::StoreType::Sum,
906 81 : fanCoil.Name);
907 81 : if (fanCoil.CapCtrlMeth_Num == CCM::CycFan || fanCoil.CapCtrlMeth_Num == CCM::MultiSpeedFan) {
908 84 : SetupOutputVariable(state,
909 : "Fan Coil Runtime Fraction",
910 : Constant::Units::None,
911 42 : fanCoil.PLR,
912 : OutputProcessor::TimeStepType::System,
913 : OutputProcessor::StoreType::Average,
914 42 : fanCoil.Name);
915 42 : SetupOutputVariable(state,
916 : "Fan Coil Fan Speed Level",
917 : Constant::Units::None,
918 42 : fanCoil.SpeedFanSel,
919 : OutputProcessor::TimeStepType::System,
920 : OutputProcessor::StoreType::Average,
921 42 : fanCoil.Name);
922 42 : if (fanCoil.CapCtrlMeth_Num == CCM::MultiSpeedFan) {
923 6 : SetupOutputVariable(state,
924 : "Fan Coil Speed Ratio",
925 : Constant::Units::None,
926 3 : fanCoil.SpeedRatio,
927 : OutputProcessor::TimeStepType::System,
928 : OutputProcessor::StoreType::Average,
929 3 : fanCoil.Name);
930 6 : SetupOutputVariable(state,
931 : "Fan Coil Part Load Ratio",
932 : Constant::Units::None,
933 3 : fanCoil.PLR,
934 : OutputProcessor::TimeStepType::System,
935 : OutputProcessor::StoreType::Average,
936 3 : fanCoil.Name);
937 : }
938 : }
939 81 : if (fanCoil.CapCtrlMeth_Num == CCM::VarFanVarFlow || fanCoil.CapCtrlMeth_Num == CCM::VarFanConsFlow) {
940 0 : SetupOutputVariable(state,
941 : "Fan Coil Part Load Ratio",
942 : Constant::Units::None,
943 0 : fanCoil.PLR,
944 : OutputProcessor::TimeStepType::System,
945 : OutputProcessor::StoreType::Average,
946 0 : fanCoil.Name);
947 : }
948 81 : SetupOutputVariable(state,
949 : "Fan Coil Availability Status",
950 : Constant::Units::None,
951 81 : (int &)fanCoil.availStatus,
952 : OutputProcessor::TimeStepType::System,
953 : OutputProcessor::StoreType::Average,
954 81 : fanCoil.Name);
955 :
956 81 : state.dataRptCoilSelection->coilSelectionReportObj->setCoilSupplyFanInfo(
957 81 : state, fanCoil.CCoilName, fanCoil.CCoilType, fanCoil.FanName, fanCoil.fanType, fanCoil.FanIndex);
958 81 : state.dataRptCoilSelection->coilSelectionReportObj->setCoilSupplyFanInfo(
959 81 : state, fanCoil.HCoilName, fanCoil.HCoilType, fanCoil.FanName, fanCoil.fanType, fanCoil.FanIndex);
960 : }
961 19 : }
962 :
963 515028 : void InitFanCoilUnits(EnergyPlusData &state,
964 : int const FanCoilNum, // number of the current fan coil unit being simulated
965 : int const ControlledZoneNum // number of zone being served
966 : )
967 : {
968 :
969 : // SUBROUTINE INFORMATION:
970 : // AUTHOR Fred Buhl
971 : // DATE WRITTEN March 2000
972 : // MODIFIED July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
973 :
974 : // PURPOSE OF THIS SUBROUTINE:
975 : // This subroutine is for initializations of the Fan Coil Components.
976 :
977 : // METHODOLOGY EMPLOYED:
978 : // Uses the status flags to trigger initializations.
979 :
980 : // SUBROUTINE PARAMETER DEFINITIONS:
981 : static constexpr std::string_view RoutineName("InitFanCoilUnits");
982 :
983 515028 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilNum);
984 :
985 : // Do the one time initializations
986 515028 : if (state.dataFanCoilUnits->InitFanCoilUnitsOneTimeFlag) {
987 :
988 19 : state.dataFanCoilUnits->MyEnvrnFlag.allocate(state.dataFanCoilUnits->NumFanCoils);
989 19 : state.dataFanCoilUnits->MySizeFlag.allocate(state.dataFanCoilUnits->NumFanCoils);
990 19 : state.dataFanCoilUnits->MyPlantScanFlag.allocate(state.dataFanCoilUnits->NumFanCoils);
991 19 : state.dataFanCoilUnits->MyZoneEqFlag.allocate(state.dataFanCoilUnits->NumFanCoils);
992 19 : state.dataFanCoilUnits->MyEnvrnFlag = true;
993 19 : state.dataFanCoilUnits->MySizeFlag = true;
994 19 : state.dataFanCoilUnits->MyPlantScanFlag = true;
995 19 : state.dataFanCoilUnits->MyZoneEqFlag = true;
996 19 : state.dataFanCoilUnits->InitFanCoilUnitsOneTimeFlag = false;
997 : }
998 :
999 515028 : if (allocated(state.dataAvail->ZoneComp)) {
1000 515028 : auto &availMgr = state.dataAvail->ZoneComp(DataZoneEquipment::ZoneEquipType::FourPipeFanCoil).ZoneCompAvailMgrs(FanCoilNum);
1001 515028 : if (state.dataFanCoilUnits->MyZoneEqFlag(FanCoilNum)) { // initialize the name of each availability manager list and zone number
1002 81 : availMgr.AvailManagerListName = fanCoil.AvailManagerListName;
1003 81 : availMgr.ZoneNum = ControlledZoneNum;
1004 81 : state.dataFanCoilUnits->MyZoneEqFlag(FanCoilNum) = false;
1005 : }
1006 515028 : fanCoil.availStatus = availMgr.availStatus;
1007 : }
1008 :
1009 515028 : if (state.dataFanCoilUnits->MyPlantScanFlag(FanCoilNum) && allocated(state.dataPlnt->PlantLoop)) {
1010 81 : bool errFlag = false;
1011 81 : if (fanCoil.HCoilType_Num == HCoil::Water) {
1012 160 : PlantUtilities::ScanPlantLoopsForObject(
1013 80 : state, fanCoil.HCoilName, fanCoil.HCoilPlantTypeOf, fanCoil.HeatCoilPlantLoc, errFlag, _, _, _, _, _);
1014 :
1015 80 : if (errFlag) {
1016 0 : ShowContinueError(state, format("Reference Unit=\"{}\", type={}", fanCoil.Name, fanCoil.UnitType));
1017 0 : ShowFatalError(state, "InitFanCoilUnits: Program terminated for previous conditions.");
1018 : }
1019 :
1020 80 : fanCoil.HeatCoilFluidOutletNodeNum = DataPlant::CompData::getPlantComponent(state, fanCoil.HeatCoilPlantLoc).NodeNumOut;
1021 :
1022 1 : } else if (fanCoil.HCoilType_Num == HCoil::Electric) {
1023 : // do nothing, valid type
1024 : } else {
1025 0 : ShowFatalError(state, format("InitFanCoilUnits: FanCoil={}, invalid heating coil type. Program terminated.", fanCoil.Name));
1026 : }
1027 :
1028 81 : if ((fanCoil.CCoilPlantType == DataPlant::PlantEquipmentType::CoilWaterCooling) ||
1029 0 : (fanCoil.CCoilPlantType == DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling)) {
1030 162 : PlantUtilities::ScanPlantLoopsForObject(
1031 81 : state, fanCoil.CCoilPlantName, fanCoil.CCoilPlantType, fanCoil.CoolCoilPlantLoc, errFlag, _, _, _, _, _);
1032 81 : if (errFlag) {
1033 0 : ShowContinueError(state, format("Reference Unit=\"{}\", type={}", fanCoil.Name, fanCoil.UnitType));
1034 0 : ShowFatalError(state, "InitFanCoilUnits: Program terminated for previous conditions.");
1035 : }
1036 81 : fanCoil.CoolCoilFluidOutletNodeNum = DataPlant::CompData::getPlantComponent(state, fanCoil.CoolCoilPlantLoc).NodeNumOut;
1037 : } else {
1038 0 : ShowFatalError(state, format("InitFanCoilUnits: FanCoil={}, invalid cooling coil type. Program terminated.", fanCoil.Name));
1039 : }
1040 :
1041 81 : state.dataFanCoilUnits->MyPlantScanFlag(FanCoilNum) = false;
1042 : }
1043 :
1044 515028 : if (!state.dataFanCoilUnits->InitFanCoilUnitsCheckInZoneEquipmentListFlag && state.dataZoneEquip->ZoneEquipInputsFilled) {
1045 19 : state.dataFanCoilUnits->InitFanCoilUnitsCheckInZoneEquipmentListFlag = true;
1046 100 : for (int Loop = 1; Loop <= state.dataFanCoilUnits->NumFanCoils; ++Loop) {
1047 162 : if (DataZoneEquipment::CheckZoneEquipmentList(
1048 81 : state, state.dataFanCoilUnits->FanCoil(Loop).UnitType, state.dataFanCoilUnits->FanCoil(Loop).Name)) {
1049 81 : continue;
1050 : }
1051 0 : ShowSevereError(state,
1052 0 : format("InitFanCoil: FanCoil Unit=[{},{}] is not on any ZoneHVAC:EquipmentList. It will not be simulated.",
1053 0 : state.dataFanCoilUnits->FanCoil(Loop).UnitType,
1054 0 : state.dataFanCoilUnits->FanCoil(Loop).Name));
1055 : }
1056 : }
1057 :
1058 515109 : if (!state.dataGlobal->SysSizingCalc && state.dataFanCoilUnits->MySizeFlag(FanCoilNum) &&
1059 81 : !state.dataFanCoilUnits->MyPlantScanFlag(FanCoilNum)) {
1060 :
1061 81 : SizeFanCoilUnit(state, FanCoilNum, ControlledZoneNum);
1062 :
1063 81 : state.dataFanCoilUnits->MySizeFlag(FanCoilNum) = false;
1064 : }
1065 :
1066 : // Do the Begin Environment initializations
1067 515508 : if (state.dataGlobal->BeginEnvrnFlag && state.dataFanCoilUnits->MyEnvrnFlag(FanCoilNum) &&
1068 480 : !state.dataFanCoilUnits->MyPlantScanFlag(FanCoilNum)) {
1069 480 : Real64 RhoAir = state.dataEnvrn->StdRhoAir;
1070 : // set the mass flow rates from the input volume flow rates
1071 480 : fanCoil.MaxAirMassFlow = RhoAir * fanCoil.MaxAirVolFlow;
1072 480 : fanCoil.OutAirMassFlow = RhoAir * fanCoil.OutAirVolFlow;
1073 :
1074 480 : if (fanCoil.HCoilType_Num == HCoil::Water) {
1075 : Real64 rho =
1076 474 : state.dataPlnt->PlantLoop(fanCoil.HeatCoilPlantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
1077 474 : fanCoil.MaxHeatCoilFluidFlow = rho * fanCoil.MaxHotWaterVolFlow;
1078 474 : fanCoil.MinHotWaterFlow = rho * fanCoil.MinHotWaterVolFlow;
1079 : }
1080 :
1081 480 : Real64 rho = state.dataPlnt->PlantLoop(fanCoil.CoolCoilPlantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, RoutineName);
1082 480 : fanCoil.MaxCoolCoilFluidFlow = rho * fanCoil.MaxColdWaterVolFlow;
1083 480 : fanCoil.MinColdWaterFlow = rho * fanCoil.MinColdWaterVolFlow;
1084 :
1085 : // set the node max and min mass flow rates
1086 480 : if (fanCoil.HCoilType_Num == HCoil::Water) {
1087 474 : PlantUtilities::InitComponentNodes(
1088 : state, fanCoil.MinHotWaterFlow, fanCoil.MaxHeatCoilFluidFlow, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum);
1089 : }
1090 :
1091 480 : PlantUtilities::InitComponentNodes(
1092 : state, fanCoil.MinColdWaterFlow, fanCoil.MaxCoolCoilFluidFlow, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum);
1093 :
1094 480 : if (fanCoil.OutsideAirNode > 0) {
1095 420 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMax = fanCoil.OutAirMassFlow;
1096 420 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMin = 0.0;
1097 : }
1098 480 : state.dataLoopNodes->Node(fanCoil.AirOutNode).MassFlowRateMax = fanCoil.MaxAirMassFlow;
1099 480 : state.dataLoopNodes->Node(fanCoil.AirOutNode).MassFlowRateMin = 0.0;
1100 480 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRateMax = fanCoil.MaxAirMassFlow;
1101 480 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRateMin = 0.0;
1102 480 : state.dataFanCoilUnits->MyEnvrnFlag(FanCoilNum) = false;
1103 : } // end one time inits
1104 :
1105 515028 : if (!state.dataGlobal->BeginEnvrnFlag) {
1106 511762 : state.dataFanCoilUnits->MyEnvrnFlag(FanCoilNum) = true;
1107 : }
1108 :
1109 : // These initializations are done every iteration
1110 515028 : fanCoil.SpeedRatio = 0.0;
1111 515028 : if (fanCoil.fanOpModeSched != nullptr) {
1112 8250 : fanCoil.fanOp = (fanCoil.fanOpModeSched->getCurrentVal() == 0.0) ? HVAC::FanOp::Cycling : HVAC::FanOp::Continuous;
1113 : }
1114 : // Set the inlet node mass flow rate
1115 1029552 : if (((fanCoil.availSched->getCurrentVal() > 0.0 && fanCoil.fanAvailSched->getCurrentVal() > 0.0) || state.dataHVACGlobal->TurnFansOn) &&
1116 514524 : !state.dataHVACGlobal->TurnFansOff) {
1117 511746 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRate = fanCoil.MaxAirMassFlow;
1118 511746 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRateMaxAvail = state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRate;
1119 511746 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRateMinAvail = 0.0;
1120 :
1121 511746 : if (fanCoil.OutsideAirNode > 0) {
1122 475126 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRate = fanCoil.OutAirMassFlow;
1123 475126 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMaxAvail = fanCoil.OutAirMassFlow;
1124 475126 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMinAvail = fanCoil.OutAirMassFlow;
1125 475126 : state.dataLoopNodes->Node(fanCoil.AirReliefNode).MassFlowRate = fanCoil.OutAirMassFlow;
1126 475126 : state.dataLoopNodes->Node(fanCoil.AirReliefNode).MassFlowRateMaxAvail = fanCoil.OutAirMassFlow;
1127 475126 : state.dataLoopNodes->Node(fanCoil.AirReliefNode).MassFlowRateMinAvail = fanCoil.OutAirMassFlow;
1128 : }
1129 :
1130 : } else {
1131 3282 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRate = 0.0;
1132 3282 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRateMaxAvail = 0.0;
1133 3282 : state.dataLoopNodes->Node(fanCoil.AirInNode).MassFlowRateMinAvail = 0.0;
1134 3282 : if (fanCoil.OutsideAirNode > 0) {
1135 3142 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRate = 0.0;
1136 3142 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMaxAvail = 0.0;
1137 3142 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMinAvail = 0.0;
1138 3142 : state.dataLoopNodes->Node(fanCoil.AirReliefNode).MassFlowRate = 0.0;
1139 3142 : state.dataLoopNodes->Node(fanCoil.AirReliefNode).MassFlowRateMaxAvail = 0.0;
1140 3142 : state.dataLoopNodes->Node(fanCoil.AirReliefNode).MassFlowRateMinAvail = 0.0;
1141 : }
1142 : }
1143 515028 : }
1144 :
1145 81 : void SizeFanCoilUnit(EnergyPlusData &state,
1146 : int const FanCoilNum,
1147 : int const ControlledZoneNum // index into ZoneEquipConfig array
1148 : )
1149 : {
1150 :
1151 : // SUBROUTINE INFORMATION:
1152 : // AUTHOR Fred Buhl
1153 : // DATE WRITTEN January 2002
1154 : // MODIFIED August 2013 Daeho Kang, add component sizing table entries
1155 : // July 2014, B. Nigusse, added scalable sizing
1156 :
1157 : // PURPOSE OF THIS SUBROUTINE:
1158 : // This subroutine is for sizing Fan Coil Unit components for which flow rates have not been
1159 : // specified in the input.
1160 :
1161 : // METHODOLOGY EMPLOYED:
1162 : // Obtains flow rates from the zone or system sizing arrays and plant sizing data.
1163 :
1164 : // SUBROUTINE PARAMETER DEFINITIONS:
1165 : static constexpr std::string_view RoutineName("SizeFanCoilUnit: "); // include trailing blank space
1166 : static constexpr std::string_view RoutineNameNoSpace("SizeFanCoilUnit");
1167 :
1168 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1169 : Real64 DesCoilLoad; // coil load used for sizing [W]
1170 81 : std::string CoolingCoilName;
1171 81 : std::string CoolingCoilType;
1172 : Real64 rho;
1173 : Real64 Cp;
1174 : int zoneHVACIndex; // index of zoneHVAC equipment sizing specification
1175 81 : std::string SizingString; // input field sizing description (e.g., Nominal Capacity)
1176 : Real64 TempSize; // autosized value of coil input field
1177 : int SizingMethod; // Integer representation of sizing method name (e.g., CoolingAirflowSizing, HeatingAirflowSizing, CoolingCapacitySizing,
1178 : // HeatingCapacitySizing, etc.)
1179 : bool PrintFlag; // TRUE when sizing information is reported in the eio file
1180 : // FractionOfAutosizedHeatingAirflow ...)
1181 : Real64 WaterCoilSizDeltaT; // water coil deltaT for design water flow rate autosizing
1182 : int CoilNum; // index of water coil object
1183 :
1184 81 : bool ErrorsFound = false; // TRUE if errors found during sizing
1185 81 : bool IsAutoSize = false; // Indicator to autosize for reporting
1186 81 : Real64 MaxAirVolFlowDes = 0.0; // Autosized max air flow for reporting
1187 81 : Real64 MaxAirVolFlowUser = 0.0; // Hardsized max air flow for reporting
1188 81 : Real64 OutAirVolFlowDes = 0.0; // Autosized outdoor air flow for reporting
1189 81 : Real64 OutAirVolFlowUser = 0.0; // Hardsized outdoor air flow for reporting
1190 81 : Real64 MaxHotWaterVolFlowDes = 0.0; // Autosized hot water flow for reporting
1191 81 : Real64 MaxHotWaterVolFlowUser = 0.0; // Hardsized hot water flow for reporting
1192 81 : Real64 MaxColdWaterVolFlowDes = 0.0; // Autosized cold water flow for reporting
1193 81 : Real64 MaxColdWaterVolFlowUser = 0.0; // Hardsized cold water flow for reporting
1194 81 : Real64 CoolingAirVolFlowDes = 0.0; // cooling supply air flow rate
1195 81 : Real64 HeatingAirVolFlowDes = 0.0; // heating supply air flow rate
1196 :
1197 81 : state.dataSize->ZoneHeatingOnlyFan = false;
1198 81 : state.dataSize->ZoneCoolingOnlyFan = false;
1199 81 : state.dataSize->DataScalableSizingON = false;
1200 81 : state.dataSize->DataScalableCapSizingON = false;
1201 :
1202 81 : state.dataSize->DataFracOfAutosizedCoolingAirflow = 1.0;
1203 81 : state.dataSize->DataFracOfAutosizedHeatingAirflow = 1.0;
1204 81 : state.dataSize->DataFracOfAutosizedCoolingCapacity = 1.0;
1205 81 : state.dataSize->DataFracOfAutosizedHeatingCapacity = 1.0;
1206 :
1207 81 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilNum);
1208 :
1209 81 : std::string CompType = fanCoil.UnitType;
1210 81 : std::string CompName = fanCoil.Name;
1211 81 : state.dataSize->DataZoneNumber = fanCoil.ControlZoneNum;
1212 81 : state.dataSize->DataFanType = fanCoil.fanType;
1213 81 : state.dataSize->DataFanIndex = fanCoil.FanIndex;
1214 : // fan coil unit is always blow thru
1215 81 : state.dataSize->DataFanPlacement = HVAC::FanPlace::BlowThru;
1216 :
1217 81 : auto &zoneEqSizing = state.dataSize->ZoneEqSizing(state.dataSize->CurZoneEqNum);
1218 :
1219 81 : if (state.dataSize->CurZoneEqNum > 0) {
1220 81 : if (fanCoil.HVACSizingIndex > 0) {
1221 :
1222 : // initialize OA flow for sizing other inputs (e.g., inlet temp, capacity, etc.)
1223 3 : if (fanCoil.OutAirVolFlow == DataSizing::AutoSize) {
1224 3 : zoneEqSizing.OAVolFlow = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
1225 : } else {
1226 0 : zoneEqSizing.OAVolFlow = fanCoil.OutAirVolFlow;
1227 : }
1228 3 : if (fanCoil.ATMixerExists) { // set up ATMixer conditions for scalable capacity sizing
1229 0 : zoneEqSizing.OAVolFlow = 0.0; // Equipment OA flow should always be 0 when ATMixer is used
1230 0 : SingleDuct::setATMixerSizingProperties(state, fanCoil.ATMixerIndex, ControlledZoneNum, state.dataSize->CurZoneEqNum);
1231 : }
1232 :
1233 3 : zoneHVACIndex = fanCoil.HVACSizingIndex;
1234 3 : int FieldNum = 1; // IDD numeric field number where input field description is found
1235 3 : PrintFlag = true;
1236 : int SAFMethod; // supply air flow rate sizing method (SupplyAirFlowRate, FlowPerFloorArea, FractionOfAutosizedCoolingAirflow,
1237 3 : SizingString = state.dataFanCoilUnits->FanCoilNumericFields(FanCoilNum).FieldNames(FieldNum) + " [m3/s]";
1238 3 : if (state.dataGlobal->isEpJSON) {
1239 0 : SizingString = "maximum_supply_air_flow_rate [m3/s]";
1240 : }
1241 3 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingSAFMethod > 0) {
1242 3 : SizingMethod = HVAC::CoolingAirflowSizing;
1243 3 : SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingSAFMethod;
1244 3 : zoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1245 3 : if (SAFMethod == DataSizing::SupplyAirFlowRate || SAFMethod == DataSizing::FlowPerFloorArea ||
1246 : SAFMethod == DataSizing::FractionOfAutosizedCoolingAirflow) {
1247 3 : if (SAFMethod == DataSizing::SupplyAirFlowRate) {
1248 1 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow > 0.0) {
1249 0 : zoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1250 0 : zoneEqSizing.SystemAirFlow = true;
1251 : }
1252 1 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1253 2 : } else if (SAFMethod == DataSizing::FlowPerFloorArea) {
1254 1 : zoneEqSizing.SystemAirFlow = true;
1255 1 : zoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow *
1256 1 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1257 1 : TempSize = zoneEqSizing.AirVolFlow;
1258 1 : state.dataSize->DataScalableSizingON = true;
1259 1 : } else if (SAFMethod == DataSizing::FractionOfAutosizedCoolingAirflow) {
1260 1 : state.dataSize->DataFracOfAutosizedCoolingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1261 1 : TempSize = DataSizing::AutoSize;
1262 1 : state.dataSize->DataScalableSizingON = true;
1263 : } else {
1264 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1265 : }
1266 3 : CoolingAirFlowSizer sizingCoolingAirFlow;
1267 3 : sizingCoolingAirFlow.overrideSizingString(SizingString);
1268 : // sizingCoolingAirFlow.setHVACSizingIndexData(fanCoil.HVACSizingIndex);
1269 3 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1270 3 : CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
1271 :
1272 3 : } else if (SAFMethod == DataSizing::FlowPerCoolingCapacity) {
1273 0 : SizingMethod = HVAC::CoolingCapacitySizing;
1274 0 : TempSize = DataSizing::AutoSize;
1275 0 : PrintFlag = false;
1276 0 : CoolingCapacitySizer sizerCoolingCapacity;
1277 0 : sizerCoolingCapacity.overrideSizingString(SizingString);
1278 0 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1279 0 : state.dataSize->DataAutosizedCoolingCapacity = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1280 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingCapMethod == DataSizing::FractionOfAutosizedCoolingCapacity) {
1281 0 : state.dataSize->DataFracOfAutosizedCoolingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
1282 : }
1283 0 : state.dataSize->DataFlowPerCoolingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
1284 0 : PrintFlag = true;
1285 0 : TempSize = DataSizing::AutoSize;
1286 0 : state.dataSize->DataScalableSizingON = true;
1287 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1288 0 : sizingCoolingAirFlow.overrideSizingString(SizingString);
1289 : // sizingCoolingAirFlow.setHVACSizingIndexData(fanCoil.HVACSizingIndex);
1290 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1291 0 : CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
1292 0 : }
1293 0 : } else if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod > 0) {
1294 : // now do heating supply air flow rate sizing
1295 0 : SizingMethod = HVAC::HeatingAirflowSizing;
1296 0 : SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod;
1297 0 : zoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1298 0 : if (SAFMethod == DataSizing::SupplyAirFlowRate || SAFMethod == DataSizing::FlowPerFloorArea ||
1299 : SAFMethod == DataSizing::FractionOfAutosizedHeatingAirflow) {
1300 0 : if (SAFMethod == DataSizing::SupplyAirFlowRate) {
1301 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow > 0.0) {
1302 0 : zoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1303 0 : zoneEqSizing.SystemAirFlow = true;
1304 : }
1305 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1306 0 : } else if (SAFMethod == DataSizing::FlowPerFloorArea) {
1307 0 : zoneEqSizing.SystemAirFlow = true;
1308 0 : zoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow *
1309 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1310 0 : TempSize = zoneEqSizing.AirVolFlow;
1311 0 : state.dataSize->DataScalableSizingON = true;
1312 0 : } else if (SAFMethod == DataSizing::FractionOfAutosizedHeatingAirflow) {
1313 0 : state.dataSize->DataFracOfAutosizedHeatingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1314 0 : TempSize = DataSizing::AutoSize;
1315 0 : state.dataSize->DataScalableSizingON = true;
1316 : } else {
1317 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1318 : }
1319 0 : bool errorsFound = false;
1320 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1321 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1322 : // sizingHeatingAirFlow.setHVACSizingIndexData(fanCoil.HVACSizingIndex);
1323 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1324 0 : HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1325 0 : } else if (SAFMethod == DataSizing::FlowPerHeatingCapacity) {
1326 0 : SizingMethod = HVAC::HeatingCapacitySizing;
1327 0 : TempSize = DataSizing::AutoSize;
1328 0 : PrintFlag = false;
1329 0 : state.dataSize->DataScalableSizingON = true;
1330 : // initialize OA flow for sizing capacity
1331 0 : if (fanCoil.OutAirVolFlow == DataSizing::AutoSize) {
1332 0 : zoneEqSizing.OAVolFlow = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
1333 : } else {
1334 0 : zoneEqSizing.OAVolFlow = fanCoil.OutAirVolFlow;
1335 : }
1336 0 : bool errorsFound = false;
1337 0 : HeatingCapacitySizer sizerHeatingCapacity;
1338 0 : sizerHeatingCapacity.overrideSizingString(SizingString);
1339 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1340 0 : TempSize = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1341 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
1342 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1343 : }
1344 0 : state.dataSize->DataAutosizedHeatingCapacity = TempSize;
1345 0 : state.dataSize->DataFlowPerHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1346 0 : PrintFlag = true;
1347 0 : TempSize = DataSizing::AutoSize;
1348 0 : errorsFound = false;
1349 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1350 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1351 : // sizingHeatingAirFlow.setHVACSizingIndexData(fanCoil.HVACSizingIndex);
1352 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1353 0 : HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1354 0 : }
1355 : }
1356 :
1357 5 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow == DataSizing::AutoSize ||
1358 2 : state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow == DataSizing::AutoSize) {
1359 1 : IsAutoSize = true;
1360 1 : fanCoil.MaxAirVolFlow = DataSizing::AutoSize;
1361 1 : MaxAirVolFlowDes = max(CoolingAirVolFlowDes, HeatingAirVolFlowDes);
1362 : } else {
1363 2 : fanCoil.MaxAirVolFlow = max(CoolingAirVolFlowDes, HeatingAirVolFlowDes);
1364 2 : MaxAirVolFlowDes = 0.0;
1365 : }
1366 : } else {
1367 : // SizingString = "Supply Air Maximum Flow Rate [m3/s]";
1368 78 : TempSize = fanCoil.MaxAirVolFlow;
1369 78 : PrintFlag = true;
1370 78 : if (fanCoil.MaxAirVolFlow == DataSizing::AutoSize) {
1371 78 : IsAutoSize = true;
1372 78 : SystemAirFlowSizer sizerSystemAirFlow;
1373 : // sizerSystemAirFlow.setHVACSizingIndexData(fanCoil.HVACSizingIndex);
1374 78 : sizerSystemAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1375 78 : MaxAirVolFlowDes = sizerSystemAirFlow.size(state, TempSize, ErrorsFound);
1376 78 : } else {
1377 0 : MaxAirVolFlowDes = 0.0;
1378 : }
1379 : }
1380 : }
1381 :
1382 81 : if (state.dataSize->CurZoneEqNum > 0) {
1383 81 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) {
1384 :
1385 : } else {
1386 81 : if (MaxAirVolFlowDes < HVAC::SmallAirVolFlow) {
1387 2 : MaxAirVolFlowDes = 0.0;
1388 : }
1389 :
1390 : // If fan is autosized, get fan volumetric flow rate
1391 81 : if (fanCoil.FanAirVolFlow == DataSizing::AutoSize) {
1392 81 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, true, _, _);
1393 81 : fanCoil.FanAirVolFlow = state.dataFans->fans(fanCoil.FanIndex)->maxAirFlowRate;
1394 : }
1395 : // Check that the fan volumetric flow rate is greater than or equal to the FCU volumetric flow rate
1396 81 : if (MaxAirVolFlowDes > fanCoil.FanAirVolFlow) {
1397 0 : ShowWarningError(state, format("{}{}: {}", RoutineName, fanCoil.UnitType, fanCoil.Name));
1398 0 : ShowContinueError(state, "... Maximum supply air flow rate is greater than the maximum fan flow rate.");
1399 0 : ShowContinueError(state, format("... Fan Coil Unit flow = {:.5T} [m3/s].", MaxAirVolFlowDes));
1400 0 : ShowContinueError(state, format("... Fan = {}: {}", HVAC::fanTypeNames[(int)fanCoil.fanType], fanCoil.FanName));
1401 0 : ShowContinueError(state, format("... Fan flow = {:.5T} [m3/s].", fanCoil.FanAirVolFlow));
1402 0 : ShowContinueError(state, "... Fan Coil Unit flow rate reduced to match the fan flow rate and the simulation continues.");
1403 0 : MaxAirVolFlowDes = fanCoil.FanAirVolFlow;
1404 : }
1405 :
1406 81 : if (IsAutoSize) {
1407 79 : fanCoil.MaxAirVolFlow = MaxAirVolFlowDes;
1408 : } else { // Hard size with sizing data
1409 2 : if (fanCoil.MaxAirVolFlow > 0.0 && MaxAirVolFlowDes > 0.0) {
1410 0 : MaxAirVolFlowUser = fanCoil.MaxAirVolFlow;
1411 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1412 0 : if ((std::abs(MaxAirVolFlowDes - MaxAirVolFlowUser) / MaxAirVolFlowUser) > state.dataSize->AutoVsHardSizingThreshold) {
1413 0 : ShowMessage(
1414 : state,
1415 0 : format("SizeFanCoilUnit: Potential issue with equipment sizing for {} {}", fanCoil.UnitType, fanCoil.Name));
1416 0 : ShowContinueError(state, format("User-Specified Supply Air Maximum Flow Rate of {:.5R} [m3/s]", MaxAirVolFlowUser));
1417 0 : ShowContinueError(state,
1418 0 : format("differs from Design Size Supply Air Maximum Flow Rate of {:.5R} [m3/s]", MaxAirVolFlowDes));
1419 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1420 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1421 : }
1422 : }
1423 : }
1424 : }
1425 : }
1426 0 : } else if (fanCoil.FanAirVolFlow == DataSizing::AutoSize) {
1427 0 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, true, _, _);
1428 0 : fanCoil.FanAirVolFlow = state.dataFans->fans(fanCoil.FanIndex)->maxAirFlowRate;
1429 :
1430 : // Check that the fan volumetric flow rate is greater than or equal to the FCU volumetric flow rate
1431 0 : if (fanCoil.MaxAirVolFlow > fanCoil.FanAirVolFlow) {
1432 0 : ShowWarningError(state, format("{}{}: {}", RoutineName, fanCoil.UnitType, fanCoil.Name));
1433 0 : ShowContinueError(state, "... Maximum supply air flow rate is greater than the maximum fan flow rate.");
1434 0 : ShowContinueError(state, format("... Fan Coil Unit flow = {:.5T} m3/s.", fanCoil.MaxAirVolFlow));
1435 0 : ShowContinueError(state, format("... Fan = {}: {}", HVAC::fanTypeNames[(int)fanCoil.fanType], fanCoil.FanName));
1436 0 : ShowContinueError(state, format("... Fan flow = {:.5T} m3/s.", fanCoil.FanAirVolFlow));
1437 0 : ShowContinueError(state, "... Fan Coil Unit flow rate reduced to match the fan flow rate and the simulation continues.");
1438 0 : fanCoil.MaxAirVolFlow = fanCoil.FanAirVolFlow;
1439 : }
1440 : }
1441 :
1442 81 : IsAutoSize = false;
1443 81 : if (fanCoil.OutAirVolFlow == DataSizing::AutoSize) {
1444 23 : IsAutoSize = true;
1445 : }
1446 :
1447 81 : if (state.dataSize->CurZoneEqNum > 0) {
1448 81 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) {
1449 0 : if (fanCoil.OutAirVolFlow > 0.0) {
1450 0 : BaseSizer::reportSizerOutput(
1451 : state, fanCoil.UnitType, fanCoil.Name, "User-Specified Maximum Outdoor Air Flow Rate [m3/s]", fanCoil.OutAirVolFlow);
1452 : }
1453 : } else {
1454 81 : CheckZoneSizing(state, fanCoil.UnitType, fanCoil.Name);
1455 81 : OutAirVolFlowDes = min(state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA, fanCoil.MaxAirVolFlow);
1456 81 : if (OutAirVolFlowDes < HVAC::SmallAirVolFlow) {
1457 2 : OutAirVolFlowDes = 0.0;
1458 : }
1459 81 : if (IsAutoSize) {
1460 23 : fanCoil.OutAirVolFlow = OutAirVolFlowDes;
1461 23 : BaseSizer::reportSizerOutput(
1462 : state, fanCoil.UnitType, fanCoil.Name, "Design Size Maximum Outdoor Air Flow Rate [m3/s]", OutAirVolFlowDes);
1463 : } else {
1464 58 : if (fanCoil.OutAirVolFlow > 0.0 && OutAirVolFlowDes > 0.0) {
1465 0 : OutAirVolFlowUser = fanCoil.OutAirVolFlow;
1466 0 : BaseSizer::reportSizerOutput(state,
1467 : fanCoil.UnitType,
1468 : fanCoil.Name,
1469 : "Design Size Maximum Outdoor Air Flow Rate [m3/s]",
1470 : OutAirVolFlowDes,
1471 : "User-Specified Maximum Outdoor Air Flow Rate [m3/s]",
1472 : OutAirVolFlowUser);
1473 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1474 0 : if ((std::abs(OutAirVolFlowDes - OutAirVolFlowUser) / OutAirVolFlowUser) > state.dataSize->AutoVsHardSizingThreshold) {
1475 0 : ShowMessage(
1476 : state,
1477 0 : format("SizeFanCoilUnit: Potential issue with equipment sizing for {} {}", fanCoil.UnitType, fanCoil.Name));
1478 0 : ShowContinueError(state, format("User-Specified Maximum Outdoor Air Flow Rate of {:.5R} [m3/s]", OutAirVolFlowUser));
1479 0 : ShowContinueError(
1480 0 : state, format("differs from Design Size Maximum Outdoor Air Flow Rate of {:.5R} [m3/s]", OutAirVolFlowDes));
1481 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1482 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1483 : }
1484 : }
1485 : }
1486 : }
1487 : }
1488 81 : zoneEqSizing.OAVolFlow = fanCoil.OutAirVolFlow; // sets OA frac in sizing
1489 :
1490 81 : if (fanCoil.ATMixerExists) { // set up ATMixer conditions for use in component sizing
1491 10 : zoneEqSizing.OAVolFlow = 0.0; // Equipment OA flow should always be 0 when ATMixer is used
1492 10 : SingleDuct::setATMixerSizingProperties(state, fanCoil.ATMixerIndex, ControlledZoneNum, state.dataSize->CurZoneEqNum);
1493 : }
1494 : }
1495 :
1496 81 : if (fanCoil.HCoilType_Num == HCoil::Water) {
1497 80 : IsAutoSize = false;
1498 80 : if (fanCoil.MaxHotWaterVolFlow == DataSizing::AutoSize) {
1499 80 : IsAutoSize = true;
1500 : }
1501 :
1502 80 : if (state.dataSize->CurZoneEqNum > 0) {
1503 80 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) {
1504 0 : if (fanCoil.MaxHotWaterVolFlow > 0.0) {
1505 0 : BaseSizer::reportSizerOutput(
1506 : state, fanCoil.UnitType, fanCoil.Name, "User-Specified Maximum Hot Water Flow [m3/s]", fanCoil.MaxHotWaterVolFlow);
1507 : }
1508 : } else {
1509 160 : state.dataFanCoilUnits->CoilWaterInletNode =
1510 80 : WaterCoils::GetCoilWaterInletNode(state, "Coil:Heating:Water", fanCoil.HCoilName, ErrorsFound);
1511 160 : state.dataFanCoilUnits->CoilWaterOutletNode =
1512 80 : WaterCoils::GetCoilWaterOutletNode(state, "Coil:Heating:Water", fanCoil.HCoilName, ErrorsFound);
1513 80 : if (IsAutoSize) {
1514 80 : int PltSizHeatNum = PlantUtilities::MyPlantSizingIndex(state,
1515 : "Coil:Heating:Water",
1516 : fanCoil.HCoilName,
1517 80 : state.dataFanCoilUnits->CoilWaterInletNode,
1518 80 : state.dataFanCoilUnits->CoilWaterOutletNode,
1519 : ErrorsFound);
1520 80 : CoilNum = WaterCoils::GetWaterCoilIndex(state, "COIL:HEATING:WATER", fanCoil.HCoilName, ErrorsFound);
1521 : bool DoWaterCoilSizing; // if TRUE do water coil sizing calculation
1522 80 : if (state.dataWaterCoils->WaterCoil(CoilNum).UseDesignWaterDeltaTemp) {
1523 0 : WaterCoilSizDeltaT = state.dataWaterCoils->WaterCoil(CoilNum).DesignWaterDeltaTemp;
1524 0 : DoWaterCoilSizing = true;
1525 : } else {
1526 80 : if (PltSizHeatNum > 0) {
1527 80 : WaterCoilSizDeltaT = state.dataSize->PlantSizData(PltSizHeatNum).DeltaT;
1528 80 : DoWaterCoilSizing = true;
1529 : } else {
1530 0 : DoWaterCoilSizing = false;
1531 : // If there is no heating Plant Sizing object and autosizing was requested, issue fatal error message
1532 0 : ShowSevereError(state, "Autosizing of water coil requires a heating loop Sizing:Plant object");
1533 0 : ShowContinueError(state, format("Occurs in {} Object={}", fanCoil.UnitType, fanCoil.Name));
1534 0 : ErrorsFound = true;
1535 : }
1536 : }
1537 80 : if (DoWaterCoilSizing) {
1538 80 : SizingMethod = HVAC::HeatingCapacitySizing;
1539 80 : if (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatMassFlow > 0.0) {
1540 80 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatOAFlowFrac =
1541 80 : min(fanCoil.OutAirVolFlow / state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatMassFlow, 1.0);
1542 : } else {
1543 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatOAFlowFrac = 0.0;
1544 : }
1545 80 : if (fanCoil.HVACSizingIndex > 0) {
1546 3 : zoneHVACIndex = fanCoil.HVACSizingIndex;
1547 3 : int CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
1548 3 : zoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
1549 3 : if (CapSizingMethod == DataSizing::HeatingDesignCapacity || CapSizingMethod == DataSizing::CapacityPerFloorArea ||
1550 : CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
1551 3 : if (CapSizingMethod == DataSizing::HeatingDesignCapacity) {
1552 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity > 0.0) {
1553 0 : zoneEqSizing.HeatingCapacity = true;
1554 0 : zoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1555 : }
1556 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1557 3 : } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
1558 2 : if (state.dataSize->ZoneSizingRunDone) {
1559 2 : PrintFlag = false;
1560 2 : TempSize = DataSizing::AutoSize;
1561 4 : state.dataSize->DataFlowUsedForSizing =
1562 2 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1563 2 : bool errorsFound = false;
1564 2 : HeatingCapacitySizer sizerHeatingCapacity;
1565 2 : sizerHeatingCapacity.overrideSizingString(SizingString);
1566 2 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1567 2 : zoneEqSizing.DesHeatingLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1568 2 : zoneEqSizing.HeatingCapacity = true;
1569 2 : }
1570 2 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
1571 2 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1572 2 : state.dataSize->DataScalableCapSizingON = true;
1573 1 : } else if (CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
1574 1 : CheckZoneSizing(state, CompType, CompName);
1575 1 : PrintFlag = false;
1576 1 : TempSize = DataSizing::AutoSize;
1577 2 : state.dataSize->DataFlowUsedForSizing =
1578 1 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1579 1 : bool errorsFound = false;
1580 1 : HeatingCapacitySizer sizerHeatingCapacity;
1581 1 : sizerHeatingCapacity.overrideSizingString(SizingString);
1582 1 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1583 1 : zoneEqSizing.DesHeatingLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1584 1 : zoneEqSizing.HeatingCapacity = true;
1585 1 : TempSize = zoneEqSizing.DesHeatingLoad * state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1586 1 : state.dataSize->DataScalableCapSizingON = true;
1587 1 : }
1588 : }
1589 3 : SizingString = "Heating Design Capacity [W]";
1590 3 : PrintFlag = false;
1591 3 : bool errorsFound = false;
1592 3 : HeatingCapacitySizer sizerHeatingCapacity;
1593 3 : sizerHeatingCapacity.overrideSizingString(SizingString);
1594 3 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1595 3 : DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1596 3 : state.dataSize->DataScalableCapSizingON = false;
1597 3 : state.dataSize->DataFlowUsedForSizing = 0.0;
1598 :
1599 3 : } else {
1600 77 : SizingString = "Heating Design Capacity [W]";
1601 77 : PrintFlag = false;
1602 77 : TempSize = DataSizing::AutoSize;
1603 77 : bool errorsFound = false;
1604 77 : HeatingCapacitySizer sizerHeatingCapacity;
1605 77 : sizerHeatingCapacity.overrideSizingString(SizingString);
1606 77 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1607 77 : DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1608 77 : }
1609 80 : fanCoil.DesHeatingLoad = DesCoilLoad;
1610 80 : if (DesCoilLoad >= HVAC::SmallLoad) {
1611 80 : rho = state.dataPlnt->PlantLoop(fanCoil.HeatCoilPlantLoc.loopNum)
1612 80 : .glycol->getDensity(state, Constant::HWInitConvTemp, RoutineNameNoSpace);
1613 80 : Cp = state.dataPlnt->PlantLoop(fanCoil.HeatCoilPlantLoc.loopNum)
1614 80 : .glycol->getSpecificHeat(state, Constant::HWInitConvTemp, RoutineNameNoSpace);
1615 :
1616 80 : MaxHotWaterVolFlowDes = DesCoilLoad / (WaterCoilSizDeltaT * Cp * rho);
1617 : } else {
1618 0 : MaxHotWaterVolFlowDes = 0.0;
1619 : }
1620 : }
1621 : }
1622 : }
1623 :
1624 80 : if (IsAutoSize) {
1625 80 : fanCoil.MaxHotWaterVolFlow = MaxHotWaterVolFlowDes;
1626 80 : BaseSizer::reportSizerOutput(
1627 : state, fanCoil.UnitType, fanCoil.Name, "Design Size Maximum Hot Water Flow [m3/s]", MaxHotWaterVolFlowDes);
1628 : } else { // Hard size with sizing data
1629 0 : if (fanCoil.MaxHotWaterVolFlow > 0.0 && MaxHotWaterVolFlowDes > 0.0) {
1630 0 : MaxHotWaterVolFlowDes = fanCoil.MaxHotWaterVolFlow;
1631 0 : BaseSizer::reportSizerOutput(state,
1632 : fanCoil.UnitType,
1633 : fanCoil.Name,
1634 : "Design Size Maximum Hot Water Flow [m3/s]",
1635 : MaxHotWaterVolFlowDes,
1636 : "User-Specified Maximum Hot Water Flow [m3/s]",
1637 : MaxHotWaterVolFlowUser);
1638 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1639 0 : if ((std::abs(MaxHotWaterVolFlowDes - MaxHotWaterVolFlowUser) / MaxHotWaterVolFlowUser) >
1640 0 : state.dataSize->AutoVsHardSizingThreshold) {
1641 0 : ShowMessage(
1642 : state,
1643 0 : format("SizeFanCoilUnit: Potential issue with equipment sizing for {} {}", fanCoil.UnitType, fanCoil.Name));
1644 0 : ShowContinueError(state, format("User-Specified Maximum Hot Water Flow of {:.5R} [m3/s]", MaxHotWaterVolFlowUser));
1645 0 : ShowContinueError(state,
1646 0 : format("differs from Design Size Maximum Hot Water Flow of {:.5R} [m3/s]", MaxHotWaterVolFlowDes));
1647 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1648 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1649 : }
1650 : }
1651 : }
1652 : }
1653 : }
1654 1 : } else if (fanCoil.HCoilType_Num == HCoil::Electric) {
1655 1 : if (fanCoil.DesignHeatingCapacity == DataSizing::AutoSize) {
1656 1 : CompName = fanCoil.HCoilName;
1657 1 : CompType = fanCoil.HCoilType;
1658 1 : SizingMethod = HVAC::HeatingCapacitySizing;
1659 1 : PrintFlag = false;
1660 1 : TempSize = fanCoil.DesignHeatingCapacity;
1661 1 : SizingString = "Nominal Heating Capacity [W]";
1662 1 : bool errorsFound = false;
1663 1 : HeatingCapacitySizer sizerHeatingCapacity;
1664 1 : sizerHeatingCapacity.overrideSizingString(SizingString);
1665 1 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1666 1 : fanCoil.DesignHeatingCapacity = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1667 1 : fanCoil.DesHeatingLoad = fanCoil.DesignHeatingCapacity;
1668 1 : }
1669 : }
1670 :
1671 81 : IsAutoSize = false;
1672 81 : if (fanCoil.MaxColdWaterVolFlow == DataSizing::AutoSize) {
1673 81 : IsAutoSize = true;
1674 : }
1675 81 : if (state.dataSize->CurZoneEqNum > 0) {
1676 81 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) {
1677 0 : if (fanCoil.MaxColdWaterVolFlow > 0.0) {
1678 0 : BaseSizer::reportSizerOutput(
1679 : state, fanCoil.UnitType, fanCoil.Name, "User-Specified Maximum Cold Water Flow [m3/s]", fanCoil.MaxColdWaterVolFlow);
1680 : }
1681 : } else {
1682 81 : if (Util::SameString(fanCoil.CCoilType, "CoilSystem:Cooling:Water:HeatExchangerAssisted")) {
1683 0 : CoolingCoilName = HVACHXAssistedCoolingCoil::GetHXDXCoilName(state, fanCoil.CCoilType, fanCoil.CCoilName, ErrorsFound);
1684 0 : CoolingCoilType = HVACHXAssistedCoolingCoil::GetHXCoilType(state, fanCoil.CCoilType, fanCoil.CCoilName, ErrorsFound);
1685 : } else {
1686 81 : CoolingCoilName = fanCoil.CCoilName;
1687 81 : CoolingCoilType = fanCoil.CCoilType;
1688 : }
1689 81 : state.dataFanCoilUnits->CoilWaterInletNode = WaterCoils::GetCoilWaterInletNode(state, CoolingCoilType, CoolingCoilName, ErrorsFound);
1690 162 : state.dataFanCoilUnits->CoilWaterOutletNode =
1691 81 : WaterCoils::GetCoilWaterOutletNode(state, CoolingCoilType, CoolingCoilName, ErrorsFound);
1692 81 : if (IsAutoSize) {
1693 81 : int PltSizCoolNum = PlantUtilities::MyPlantSizingIndex(state,
1694 : CoolingCoilType,
1695 : CoolingCoilName,
1696 81 : state.dataFanCoilUnits->CoilWaterInletNode,
1697 81 : state.dataFanCoilUnits->CoilWaterOutletNode,
1698 : ErrorsFound);
1699 81 : CoilNum = WaterCoils::GetWaterCoilIndex(state, CoolingCoilType, CoolingCoilName, ErrorsFound);
1700 : bool DoWaterCoilSizing; // if TRUE do water coil sizing calculation
1701 81 : if (state.dataWaterCoils->WaterCoil(CoilNum).UseDesignWaterDeltaTemp) {
1702 0 : WaterCoilSizDeltaT = state.dataWaterCoils->WaterCoil(CoilNum).DesignWaterDeltaTemp;
1703 0 : DoWaterCoilSizing = true;
1704 : } else {
1705 81 : if (PltSizCoolNum > 0) {
1706 81 : WaterCoilSizDeltaT = state.dataSize->PlantSizData(PltSizCoolNum).DeltaT;
1707 81 : DoWaterCoilSizing = true;
1708 : } else {
1709 0 : DoWaterCoilSizing = false;
1710 : // If there is no cooling Plant Sizing object and autosizing was requested, issue fatal error message
1711 0 : ShowSevereError(state, "Autosizing of water coil requires a cooling loop Sizing:Plant object");
1712 0 : ShowContinueError(state, format("Occurs in {} Object={}", fanCoil.UnitType, fanCoil.Name));
1713 0 : ErrorsFound = true;
1714 : }
1715 : }
1716 :
1717 81 : if (DoWaterCoilSizing) {
1718 81 : SizingMethod = HVAC::CoolingCapacitySizing;
1719 81 : if (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolMassFlow > 0.0) {
1720 81 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolOAFlowFrac =
1721 81 : min(fanCoil.OutAirVolFlow / state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolMassFlow, 1.0);
1722 : } else {
1723 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolOAFlowFrac = 0.0;
1724 : }
1725 81 : if (fanCoil.HVACSizingIndex > 0) {
1726 3 : zoneHVACIndex = fanCoil.HVACSizingIndex;
1727 3 : int CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingCapMethod;
1728 3 : zoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
1729 3 : if (CapSizingMethod == DataSizing::CoolingDesignCapacity || CapSizingMethod == DataSizing::CapacityPerFloorArea ||
1730 : CapSizingMethod == DataSizing::FractionOfAutosizedCoolingCapacity) {
1731 3 : if (CapSizingMethod == DataSizing::CoolingDesignCapacity) {
1732 1 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity > 0.0) {
1733 0 : zoneEqSizing.CoolingCapacity = true;
1734 0 : zoneEqSizing.DesCoolingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
1735 : } else {
1736 1 : state.dataSize->DataFlowUsedForSizing =
1737 1 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
1738 : }
1739 1 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
1740 2 : } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
1741 1 : if (state.dataSize->ZoneSizingRunDone) {
1742 1 : CheckZoneSizing(state, CompType, CompName);
1743 1 : PrintFlag = false;
1744 1 : TempSize = DataSizing::AutoSize;
1745 2 : state.dataSize->DataFlowUsedForSizing =
1746 1 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
1747 1 : CoolingCapacitySizer sizerCoolingCapacity;
1748 1 : sizerCoolingCapacity.overrideSizingString(SizingString);
1749 1 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1750 1 : zoneEqSizing.DesCoolingLoad = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1751 1 : zoneEqSizing.CoolingCapacity = true;
1752 1 : }
1753 1 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity *
1754 1 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1755 1 : state.dataSize->DataScalableCapSizingON = true;
1756 1 : } else if (CapSizingMethod == DataSizing::FractionOfAutosizedCoolingCapacity) {
1757 1 : PrintFlag = false;
1758 1 : TempSize = DataSizing::AutoSize;
1759 2 : state.dataSize->DataFlowUsedForSizing =
1760 1 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
1761 1 : CoolingCapacitySizer sizerCoolingCapacity2;
1762 1 : sizerCoolingCapacity2.overrideSizingString(SizingString);
1763 1 : sizerCoolingCapacity2.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1764 1 : zoneEqSizing.DesCoolingLoad = sizerCoolingCapacity2.size(state, TempSize, ErrorsFound);
1765 1 : zoneEqSizing.CoolingCapacity = true;
1766 1 : TempSize = zoneEqSizing.DesCoolingLoad * state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
1767 1 : state.dataSize->DataScalableCapSizingON = true;
1768 1 : }
1769 : }
1770 3 : SizingString = "Cooling Design Capacity [W]";
1771 3 : PrintFlag = false;
1772 3 : CoolingCapacitySizer sizerCoolingCapacity3;
1773 3 : sizerCoolingCapacity3.overrideSizingString(SizingString);
1774 3 : sizerCoolingCapacity3.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1775 3 : DesCoilLoad = sizerCoolingCapacity3.size(state, TempSize, ErrorsFound);
1776 3 : state.dataSize->DataScalableCapSizingON = false;
1777 3 : state.dataSize->DataFlowUsedForSizing = 0.0;
1778 3 : } else {
1779 78 : SizingString = "Cooling Design Capacity [W]";
1780 78 : PrintFlag = false;
1781 78 : TempSize = DataSizing::AutoSize;
1782 78 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
1783 78 : CoolingCapacitySizer sizerCoolingCapacity;
1784 78 : sizerCoolingCapacity.overrideSizingString(SizingString);
1785 78 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1786 78 : DesCoilLoad = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1787 78 : }
1788 81 : fanCoil.DesCoolingLoad = DesCoilLoad;
1789 81 : if (DesCoilLoad >= HVAC::SmallLoad) {
1790 81 : rho = state.dataPlnt->PlantLoop(fanCoil.CoolCoilPlantLoc.loopNum).glycol->getDensity(state, 5., RoutineNameNoSpace);
1791 81 : Cp = state.dataPlnt->PlantLoop(fanCoil.CoolCoilPlantLoc.loopNum).glycol->getSpecificHeat(state, 5., RoutineNameNoSpace);
1792 81 : MaxColdWaterVolFlowDes = DesCoilLoad / (WaterCoilSizDeltaT * Cp * rho);
1793 : } else {
1794 0 : MaxColdWaterVolFlowDes = 0.0;
1795 : }
1796 : }
1797 81 : fanCoil.MaxColdWaterVolFlow = MaxColdWaterVolFlowDes;
1798 81 : BaseSizer::reportSizerOutput(
1799 : state, fanCoil.UnitType, fanCoil.Name, "Design Size Maximum Cold Water Flow [m3/s]", MaxColdWaterVolFlowDes);
1800 : } else { // Hard size with sizing data
1801 0 : if (fanCoil.MaxColdWaterVolFlow > 0.0 && MaxColdWaterVolFlowDes > 0.0) {
1802 0 : MaxColdWaterVolFlowUser = fanCoil.MaxColdWaterVolFlow;
1803 0 : BaseSizer::reportSizerOutput(state,
1804 : fanCoil.UnitType,
1805 : fanCoil.Name,
1806 : "Design Size Maximum Cold Water Flow [m3/s]",
1807 : MaxColdWaterVolFlowDes,
1808 : "User-Specified Maximum Cold Water Flow [m3/s]",
1809 : MaxColdWaterVolFlowUser);
1810 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1811 0 : if ((std::abs(MaxColdWaterVolFlowDes - MaxColdWaterVolFlowUser) / MaxColdWaterVolFlowUser) >
1812 0 : state.dataSize->AutoVsHardSizingThreshold) {
1813 0 : ShowMessage(
1814 : state,
1815 0 : format("SizeFanCoilUnit: Potential issue with equipment sizing for {} {}", fanCoil.UnitType, fanCoil.Name));
1816 0 : ShowContinueError(state, format("User-Specified Maximum Cold Water Flow of {:.5R}[m3/s]", MaxColdWaterVolFlowUser));
1817 0 : ShowContinueError(state,
1818 0 : format("differs from Design Size Maximum Cold Water Flow of {:.5R}[m3/s]", MaxColdWaterVolFlowDes));
1819 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1820 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1821 : }
1822 : }
1823 : }
1824 : }
1825 : }
1826 :
1827 81 : if (fanCoil.CapCtrlMeth_Num == CCM::ASHRAE && !fanCoil.ASHRAETempControl) {
1828 :
1829 3 : CompType = fanCoil.UnitType;
1830 3 : CompName = fanCoil.Name;
1831 3 : PrintFlag = true;
1832 :
1833 3 : ZoneCoolingLoadSizer sizerZoneCoolingLoad;
1834 3 : sizerZoneCoolingLoad.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1835 3 : fanCoil.DesZoneCoolingLoad = sizerZoneCoolingLoad.size(state, fanCoil.DesZoneCoolingLoad, ErrorsFound);
1836 3 : fanCoil.DesZoneCoolingLoad *= -1.0;
1837 :
1838 3 : ZoneHeatingLoadSizer sizerZoneHeatingLoad;
1839 3 : sizerZoneHeatingLoad.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1840 3 : fanCoil.DesZoneHeatingLoad = sizerZoneHeatingLoad.size(state, fanCoil.DesZoneHeatingLoad, ErrorsFound);
1841 :
1842 3 : fanCoil.DSOAPtr = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).ZoneDesignSpecOAIndex;
1843 :
1844 81 : } else if (fanCoil.CapCtrlMeth_Num == CCM::ASHRAE && fanCoil.ASHRAETempControl) {
1845 :
1846 0 : CompType = fanCoil.UnitType;
1847 0 : CompName = fanCoil.Name;
1848 0 : Real64 capacityMultiplier = 0.6; // 60% of design zone load for water coils
1849 0 : state.dataSize->DataCapacityUsedForSizing = fanCoil.DesCoolingLoad * capacityMultiplier;
1850 0 : bool SizingDesRunThisZone = false;
1851 0 : CheckThisZoneForSizing(state, state.dataSize->CurZoneEqNum, SizingDesRunThisZone);
1852 0 : if (SizingDesRunThisZone) {
1853 0 : state.dataSize->DataCapacityUsedForSizing =
1854 0 : state.dataSize->FinalZoneSizing(fanCoil.ControlZoneNum).DesCoolLoad * capacityMultiplier;
1855 : } else {
1856 0 : state.dataSize->DataCapacityUsedForSizing = fanCoil.DesCoolingLoad * capacityMultiplier;
1857 : }
1858 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
1859 0 : PrintFlag = true;
1860 0 : ASHRAEMinSATCoolingSizer sizerASHRAEMinSATCooling;
1861 0 : sizerASHRAEMinSATCooling.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1862 0 : fanCoil.DesignMinOutletTemp = sizerASHRAEMinSATCooling.size(state, fanCoil.DesignMinOutletTemp, ErrorsFound);
1863 :
1864 0 : if (SizingDesRunThisZone) {
1865 0 : state.dataSize->DataCapacityUsedForSizing =
1866 0 : state.dataSize->FinalZoneSizing(fanCoil.ControlZoneNum).DesHeatLoad * capacityMultiplier;
1867 : } else {
1868 0 : state.dataSize->DataCapacityUsedForSizing = fanCoil.DesHeatingLoad * capacityMultiplier;
1869 : }
1870 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1871 0 : ASHRAEMaxSATHeatingSizer sizerASHRAEMaxSATHeating;
1872 0 : sizerASHRAEMaxSATHeating.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1873 0 : fanCoil.DesignMaxOutletTemp = sizerASHRAEMaxSATHeating.size(state, fanCoil.DesignMaxOutletTemp, ErrorsFound);
1874 :
1875 0 : state.dataSize->DataCapacityUsedForSizing = 0.0; // reset so other routines don't use this inadvertently
1876 0 : state.dataSize->DataFlowUsedForSizing = 0.0;
1877 :
1878 0 : SizingDesRunThisZone = false;
1879 0 : CheckThisZoneForSizing(state, state.dataSize->CurZoneEqNum, SizingDesRunThisZone);
1880 :
1881 0 : if (SizingDesRunThisZone) {
1882 :
1883 0 : fanCoil.DesZoneCoolingLoad =
1884 0 : -1.0 * (fanCoil.DesCoolingLoad / state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).CoolSizingFactor);
1885 0 : fanCoil.DesZoneHeatingLoad =
1886 0 : fanCoil.DesHeatingLoad / state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).HeatSizingFactor;
1887 0 : fanCoil.DSOAPtr = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).ZoneDesignSpecOAIndex;
1888 :
1889 : } else {
1890 :
1891 0 : fanCoil.DesZoneCoolingLoad = -1.0 * fanCoil.DesCoolingLoad;
1892 0 : fanCoil.DesZoneHeatingLoad = fanCoil.DesHeatingLoad;
1893 : }
1894 0 : }
1895 :
1896 : } // if ( CurZoneEqNum > 0 )
1897 :
1898 : // set the design air flow rates for the heating and cooling coils
1899 81 : if (Util::SameString(fanCoil.CCoilType, "CoilSystem:Cooling:Water:HeatExchangerAssisted")) {
1900 0 : CoolingCoilName = HVACHXAssistedCoolingCoil::GetHXDXCoilName(state, fanCoil.CCoilType, fanCoil.CCoilName, ErrorsFound);
1901 0 : CoolingCoilType = HVACHXAssistedCoolingCoil::GetHXCoilType(state, fanCoil.CCoilType, fanCoil.CCoilName, ErrorsFound);
1902 : } else {
1903 81 : CoolingCoilName = fanCoil.CCoilName;
1904 81 : CoolingCoilType = fanCoil.CCoilType;
1905 : }
1906 81 : if (state.dataSize->ZoneSizingRunDone) {
1907 81 : WaterCoils::SetCoilDesFlow(
1908 81 : state, CoolingCoilType, CoolingCoilName, state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow, ErrorsFound);
1909 81 : WaterCoils::SetCoilDesFlow(state,
1910 : fanCoil.HCoilType,
1911 81 : fanCoil.HCoilName,
1912 81 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow,
1913 : ErrorsFound);
1914 : } else {
1915 0 : WaterCoils::SetCoilDesFlow(state, CoolingCoilType, CoolingCoilName, fanCoil.MaxAirVolFlow, ErrorsFound);
1916 0 : WaterCoils::SetCoilDesFlow(state, fanCoil.HCoilType, fanCoil.HCoilName, fanCoil.MaxAirVolFlow, ErrorsFound);
1917 : }
1918 81 : if (state.dataSize->CurZoneEqNum > 0) {
1919 81 : zoneEqSizing.MaxHWVolFlow = fanCoil.MaxHotWaterVolFlow;
1920 81 : zoneEqSizing.MaxCWVolFlow = fanCoil.MaxColdWaterVolFlow;
1921 81 : zoneEqSizing.AirVolFlow = fanCoil.MaxAirVolFlow;
1922 81 : zoneEqSizing.DesCoolingLoad = fanCoil.DesCoolingLoad;
1923 81 : zoneEqSizing.DesHeatingLoad = fanCoil.DesHeatingLoad;
1924 81 : zoneEqSizing.DesignSizeFromParent = true;
1925 : }
1926 :
1927 81 : if (ErrorsFound) {
1928 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
1929 : }
1930 81 : }
1931 :
1932 515028 : void Sim4PipeFanCoil(EnergyPlusData &state,
1933 : int &FanCoilNum, // number of the current fan coil unit being simulated
1934 : int const ControlledZoneNum, // index into ZoneEqupConfig
1935 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
1936 : Real64 &PowerMet, // Sensible power supplied (W)
1937 : Real64 &LatOutputProvided // Latent power supplied (kg/s), negative = dehumidification
1938 : )
1939 : {
1940 :
1941 : // SUBROUTINE INFORMATION:
1942 : // AUTHOR Fred Buhl
1943 : // DATE WRITTEN March 2000
1944 : // MODIFIED Don Shirey, Aug 2009 (LatOutputProvided)
1945 : // MODIFIED Arnaud Flament June 2010 (added airflow capacity control methods)
1946 : // MODIFIED R. Raustad, FSEC, Feb 2016 (added ASHRAE 90.1 SZVAV system control)
1947 :
1948 : // PURPOSE OF THIS SUBROUTINE:
1949 : // Simulate a 4 pipe fan coil unit; adjust its output to match the
1950 : // remaining zone load.
1951 :
1952 : // METHODOLOGY EMPLOYED:
1953 : // If unit is on, calls ControlCompOutput to obtain the desired unit output
1954 :
1955 : // REFERENCES:
1956 : // SZVAV sysetm control:
1957 : // ASHRAE 90.1 2010 Section 6.4.3.10 - Single Zone Variable-Air-volume Controls (described in Trane newsletter entitled Understanding
1958 : // Single-Zone VAV Systems) Trane Engineers Newsletter -
1959 : // https://www.trane.com/content/dam/Trane/Commercial/global/products-systems/education-training/engineers-newsletters/airside-design/admapn047en_0413.pdf
1960 : //
1961 :
1962 515028 : int constexpr MaxIterCycl(100);
1963 :
1964 : Real64 PLRMin; // minimum PLR used for tighter control of air and water flow rate
1965 : Real64 PLRMax; // maximum PLR used for tighter control of air and water flow rate
1966 : Real64 QTotUnitOut; // total unit output [watts]
1967 : Real64 QUnitOutMaxC; // unit output with full active cooling [W]
1968 : Real64 QUnitOutMaxH; // unit output with full active heating [W]
1969 : Real64 SpecHumOut; // Specific humidity ratio of outlet air (kg moisture / kg moist air)
1970 : Real64 SpecHumIn; // Specific humidity ratio of inlet air (kg moisture / kg moist air)
1971 : Real64 DelPLR;
1972 : Real64 mdot;
1973 : // Real64 Low_mdot;
1974 : Real64 QSensUnitOutNoATM; // unit output not including air added by supply side air terminal mixer
1975 : int SolFlag; // return flag from RegulaFalsi for sensible load
1976 : Real64 OAVolumeFlowRate; // OA volume flow rate based on design specifications object [m3/s]
1977 : Real64 OAMassFlow; // OA mass flow rate based on design specifications object [kg/s]
1978 : Real64 RhoAir; // density of air [kg/m3]
1979 : Real64 MinSAMassFlowRate; // minimum supply air mass flow rate [kg/s]
1980 : Real64 MaxSAMassFlowRate; // maximum supply air mass flow rate [kg/s]
1981 : // Real64 FCOutletTempOn; // ASHRAE outlet air temperature when coil is on [C]
1982 : Real64 CWFlow; // cold water mass flow rate solution [kg/s]
1983 : Real64 CWFlowBypass; // cold water bypassed mass flow rate [kg/s]
1984 :
1985 515028 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilNum);
1986 :
1987 : // initialize local variables
1988 515028 : bool UnitOn = true; // TRUE if unit is on
1989 515028 : Real64 QUnitOut = 0.0; // heating or sens. cooling provided by fan coil unit [watts]
1990 515028 : Real64 QUnitOutMax = 0.0; // heating or sens. cooling provided by fan coil unit (running during an entire timestep)
1991 515028 : Real64 PLR = 0.0; // Part Load Ratio, fraction of time step fancoil is on
1992 515028 : Real64 LatentOutput = 0.0; // Latent (moisture) add/removal rate, negative is dehumidification [kg/s]
1993 515028 : Real64 QUnitOutNoHC = 0.0; // unit output with no active heating or cooling [W]
1994 515028 : Real64 QCoilHeatSP = 0.0; // coil load to the heating setpoint [W]
1995 515028 : Real64 QCoilCoolSP = 0.0; // coil load to the cooling setpoint [W]
1996 515028 : Real64 QZnReq = 0.0; // heating or cooling needed by zone [watts]
1997 515028 : Real64 ControlOffset = 0.0; // tolerance for output control
1998 515028 : Real64 MaxWaterFlow = 0.0; // maximum water flow for heating or cooling [kg/sec]
1999 515028 : Real64 MinWaterFlow = 0.0; // minimum water flow for heating or cooling [kg/sec]
2000 515028 : int OutletNode = fanCoil.AirOutNode;
2001 515028 : int InletNode = fanCoil.AirInNode;
2002 515028 : Real64 AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate; // air mass flow rate [kg/sec]
2003 515028 : Real64 Error = 1.0; // Error between QZnReq and QUnitOut
2004 515028 : Real64 AbsError = 2.0 * HVAC::SmallLoad; // Absolute error between QZnReq and QUnitOut [W] !FB
2005 515028 : Real64 Relax = 1.0;
2006 515028 : Real64 HWFlow = 0.0; // hot water mass flow rate solution [kg/s]
2007 515028 : Real64 HWFlowBypass = 0.0; // hot water bypassed mass flow rate [kg/s]
2008 515028 : Real64 MdotLockH = 0.0; // saved value of locked chilled water mass flow rate [kg/s]
2009 515028 : Real64 MdotLockC = 0.0; // saved value of locked hot water mass flow rate [kg/s]
2010 515028 : bool ColdFlowLocked = false; // if true cold water flow is locked
2011 515028 : bool HotFlowLocked = false; // if true Hot water flow is locked
2012 :
2013 : // select capacity control method
2014 515028 : switch (fanCoil.CapCtrlMeth_Num) {
2015 280846 : case CCM::ConsFanVarFlow: {
2016 :
2017 280846 : if (AirMassFlow < HVAC::SmallMassFlow) {
2018 3066 : UnitOn = false;
2019 : }
2020 : // zero the hot & cold water flows
2021 :
2022 : // set water coil flow rate to 0 to calculate coil off capacity (only valid while flow is unlocked)
2023 280846 : mdot = 0.0;
2024 280846 : PlantUtilities::SetComponentFlowRate(
2025 280846 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2026 280846 : if (state.dataPlnt->PlantLoop(fanCoil.CoolCoilPlantLoc.loopNum).LoopSide(fanCoil.CoolCoilPlantLoc.loopSideNum).FlowLock ==
2027 : DataPlant::FlowLock::Locked) {
2028 216 : ColdFlowLocked = true; // check for flow lock
2029 : }
2030 280846 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2031 280846 : mdot = 0.0;
2032 280846 : PlantUtilities::SetComponentFlowRate(
2033 280846 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2034 280846 : if (state.dataPlnt->PlantLoop(fanCoil.HeatCoilPlantLoc.loopNum).LoopSide(fanCoil.HeatCoilPlantLoc.loopSideNum).FlowLock ==
2035 : DataPlant::FlowLock::Locked) {
2036 216 : HotFlowLocked = true; // save locked flow
2037 : }
2038 : }
2039 : // obtain unit output with no active heating/cooling
2040 280846 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutNoHC, 0.0);
2041 :
2042 280846 : if (ColdFlowLocked || HotFlowLocked) {
2043 216 : QUnitOutNoHC = fanCoil.QUnitOutNoHC;
2044 : } else { // continue to update QUnitOutNoHC while flow is unlocked
2045 280630 : fanCoil.QUnitOutNoHC = QUnitOutNoHC;
2046 : }
2047 :
2048 : // then calculate the loads at the coils
2049 280846 : QCoilHeatSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP - QUnitOutNoHC;
2050 280846 : QCoilCoolSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP - QUnitOutNoHC;
2051 :
2052 : // if cooling
2053 432337 : if (UnitOn && QCoilCoolSP < -HVAC::SmallLoad &&
2054 151491 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleHeat) {
2055 151155 : int ControlNode = fanCoil.CoolCoilFluidInletNode;
2056 151155 : ControlOffset = fanCoil.ColdControlOffset;
2057 151155 : MaxWaterFlow = fanCoil.MaxCoolCoilFluidFlow;
2058 151155 : MinWaterFlow = fanCoil.MinColdWaterFlow;
2059 : // On the first HVAC iteration the system values are given to the controller, but after that
2060 : // the demand limits are in place and there needs to be feedback to the Zone Equipment
2061 151155 : if (!FirstHVACIteration) {
2062 90551 : MaxWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMaxAvail;
2063 90551 : MinWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMinAvail;
2064 : }
2065 : // get full load result
2066 151155 : mdot = MaxWaterFlow;
2067 151155 : PlantUtilities::SetComponentFlowRate(
2068 151155 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2069 151155 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMaxC);
2070 151155 : if (!ColdFlowLocked) {
2071 151093 : fanCoil.QUnitOutMaxC = QUnitOutMaxC;
2072 : } else {
2073 62 : QUnitOutMaxC = fanCoil.QUnitOutMaxC;
2074 62 : MdotLockC = mdot; // save locked flow
2075 : }
2076 151155 : QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP;
2077 151155 : if (QUnitOutMaxC < QZnReq) {
2078 : // more cooling than required, find reduced water flow rate to meet the load
2079 : // solve for the cold water flow rate with no limit set by flow rate lockdown
2080 1813145 : auto f = [&state, FanCoilNum, FirstHVACIteration, ControlledZoneNum, QZnReq](Real64 const CWFlow) {
2081 1663266 : return CalcFanCoilCWLoadResidual(state, CWFlow, FanCoilNum, FirstHVACIteration, ControlledZoneNum, QZnReq);
2082 149879 : };
2083 149879 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, CWFlow, f, 0.0, MaxWaterFlow);
2084 149879 : if (SolFlag == -1) {
2085 : // tighten limits on water flow rate to see if this allows convergence
2086 0 : state.dataFanCoilUnits->CoolingLoad = true;
2087 0 : state.dataFanCoilUnits->HeatingLoad = false;
2088 0 : TightenWaterFlowLimits(state,
2089 : FanCoilNum,
2090 0 : state.dataFanCoilUnits->CoolingLoad,
2091 0 : state.dataFanCoilUnits->HeatingLoad,
2092 : fanCoil.CoolCoilFluidInletNode,
2093 : ControlledZoneNum,
2094 : FirstHVACIteration,
2095 : QZnReq,
2096 : MinWaterFlow,
2097 : MaxWaterFlow);
2098 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, CWFlow, f, MinWaterFlow, MaxWaterFlow);
2099 0 : if (SolFlag == -1) {
2100 0 : ++fanCoil.ConvgErrCountC;
2101 0 : if (fanCoil.ConvgErrCountC < 2) {
2102 0 : ShowWarningError(state, format("Cold Water control failed in fan coil unit {}", fanCoil.Name));
2103 0 : ShowContinueError(state, " Iteration limit exceeded in calculating water flow rate ");
2104 0 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate = CWFlow;
2105 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
2106 0 : ShowContinueErrorTimeStamp(state, format("Load Request = {}, Final Capacity = {}", QZnReq, QUnitOut));
2107 0 : ShowContinueErrorTimeStamp(
2108 : state,
2109 0 : format("Min water flow used during iterations = {}, Max water flow used during iterations = {}",
2110 : MinWaterFlow,
2111 : MaxWaterFlow));
2112 0 : ShowContinueErrorTimeStamp(state, format("Water flow rate on last iteration = {}", CWFlow));
2113 0 : ShowContinueErrorTimeStamp(state, "..Water flow rate set to last iteration value ");
2114 : } else {
2115 0 : ShowRecurringWarningErrorAtEnd(
2116 0 : state, "Cold water flow Iteration limit exceeded in fan coil unit " + fanCoil.Name, fanCoil.MaxIterIndexC);
2117 : }
2118 0 : } else if (SolFlag == -2) {
2119 0 : ++fanCoil.LimitErrCountC;
2120 0 : if (fanCoil.LimitErrCountC < 2) {
2121 0 : ShowWarningError(state, format("Cold Water control failed in fan coil unit {}", fanCoil.Name));
2122 0 : ShowContinueError(state, " Bad cold water mass flow limits");
2123 0 : ShowContinueErrorTimeStamp(state, "..Water flow rate set to lower limit ");
2124 : } else {
2125 0 : ShowRecurringWarningErrorAtEnd(
2126 0 : state, "Cold Water control failed in fan coil unit " + fanCoil.Name, fanCoil.BadMassFlowLimIndexC);
2127 : }
2128 : }
2129 149879 : } else if (SolFlag == -2) {
2130 0 : ++fanCoil.LimitErrCountC;
2131 0 : if (fanCoil.LimitErrCountC < 2) {
2132 0 : ShowWarningError(state, format("Cold Water control failed in fan coil unit {}", fanCoil.Name));
2133 0 : ShowContinueError(state, " Bad cold water mass flow limits");
2134 0 : ShowContinueErrorTimeStamp(state, "..Water flow rate set to lower limit ");
2135 : } else {
2136 0 : ShowRecurringWarningErrorAtEnd(
2137 0 : state, "Cold Water control failed in fan coil unit " + fanCoil.Name, fanCoil.BadMassFlowLimIndexC);
2138 : }
2139 : }
2140 : } else {
2141 : // demand greater than capacity
2142 1276 : CWFlow = MaxWaterFlow;
2143 : }
2144 151155 : if (!ColdFlowLocked) {
2145 151093 : mdot = CWFlow; // not flowlocked - set flow to CWFlow
2146 151093 : PlantUtilities::SetComponentFlowRate(
2147 151093 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2148 151093 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut); // get QUnitOut
2149 : } else {
2150 : // flow lock on
2151 62 : if (MdotLockC > CWFlow) { // if mdot > CWFlow, bypass extra flow
2152 31 : Calc4PipeFanCoil(state,
2153 : FanCoilNum,
2154 : ControlledZoneNum,
2155 : FirstHVACIteration,
2156 : QUnitOut); // get QUnitOut with CWFlow; rest will be bypassed
2157 31 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate =
2158 : MdotLockC; // reset flow to locked value. Since lock is on, must do this by hand
2159 31 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidOutletNodeNum).MassFlowRate = MdotLockC;
2160 : // Keep soln flow rate but reset outlet water temperature - i.e. bypass extra water
2161 31 : CWFlowBypass = MdotLockC - CWFlow;
2162 : // change water outlet temperature and enthalpy
2163 31 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidOutletNodeNum).Temp =
2164 31 : (CWFlowBypass * state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).Temp +
2165 31 : CWFlow * state.dataLoopNodes->Node(fanCoil.CoolCoilFluidOutletNodeNum).Temp) /
2166 : MdotLockC;
2167 31 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidOutletNodeNum).Enthalpy =
2168 31 : (CWFlowBypass * state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).Enthalpy +
2169 31 : CWFlow * state.dataLoopNodes->Node(fanCoil.CoolCoilFluidOutletNodeNum).Enthalpy) /
2170 : MdotLockC;
2171 : } else {
2172 : // if MdotLockC <= CWFlow use MdotLockC as is
2173 31 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate =
2174 : MdotLockC; // reset flow to locked value. Since lock is on, must do this by hand
2175 31 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidOutletNodeNum).MassFlowRate = MdotLockC;
2176 31 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
2177 : }
2178 : }
2179 151155 : QUnitOut = calcZoneSensibleOutput(AirMassFlow,
2180 151155 : state.dataLoopNodes->Node(OutletNode).Temp,
2181 151155 : state.dataLoopNodes->Node(InletNode).Temp,
2182 151155 : state.dataLoopNodes->Node(InletNode).HumRat);
2183 :
2184 : // if heating
2185 246345 : } else if (UnitOn && QCoilHeatSP > HVAC::SmallLoad &&
2186 116654 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleCool) {
2187 : // get full load result
2188 113793 : if (fanCoil.HCoilType_Num == HCoil::Water) { // if HW Coil
2189 113793 : int ControlNode = fanCoil.HeatCoilFluidInletNode;
2190 113793 : ControlOffset = fanCoil.HotControlOffset;
2191 113793 : MaxWaterFlow = fanCoil.MaxHeatCoilFluidFlow;
2192 113793 : MinWaterFlow = fanCoil.MinHotWaterFlow;
2193 : // On the first HVAC iteration the system values are given to the controller, but after that
2194 : // the demand limits are in place and there needs to be feedback to the Zone Equipment
2195 113793 : if (!FirstHVACIteration) {
2196 56888 : MaxWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMaxAvail;
2197 56888 : MinWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMinAvail;
2198 : }
2199 113793 : mdot = MaxWaterFlow;
2200 113793 : PlantUtilities::SetComponentFlowRate(
2201 113793 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2202 113793 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMaxH);
2203 113793 : if (!HotFlowLocked) {
2204 113757 : fanCoil.QUnitOutMaxH = QUnitOutMaxH;
2205 : } else {
2206 36 : QUnitOutMaxH = fanCoil.QUnitOutMaxH;
2207 36 : MdotLockH = mdot; // save locked flow
2208 : }
2209 : } else {
2210 : // not HW coil
2211 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMaxH, 1.0);
2212 : }
2213 113793 : QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP;
2214 113793 : if (QUnitOutMaxH > QZnReq) {
2215 : // more heating than required, find reduced water flow rate to meet the load
2216 113244 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2217 : // solve for the hot water flow rate with no limit set by flow rate lockdown
2218 1150277 : auto f = [&state, FirstHVACIteration, FanCoilNum, ControlledZoneNum, QZnReq](Real64 HWFlow) {
2219 : // To calculate the part-load ratio for the FCU with electric heating coil
2220 : Real64 QUnitOut; // delivered capacity [W]
2221 1037033 : state.dataLoopNodes->Node(state.dataFanCoilUnits->FanCoil(FanCoilNum).HeatCoilFluidInletNode).MassFlowRate = HWFlow;
2222 1037033 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, 1.0);
2223 : // Calculate residual based on output magnitude
2224 1037033 : if (std::abs(QZnReq) <= 100.0) {
2225 330 : return (QUnitOut - QZnReq) / 100.0;
2226 : } else {
2227 1036703 : return (QUnitOut - QZnReq) / QZnReq;
2228 : }
2229 113244 : };
2230 113244 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, HWFlow, f, 0.0, MaxWaterFlow);
2231 113244 : if (SolFlag == -1) {
2232 : // tighten limits on water flow rate to see if this allows convergence
2233 0 : state.dataFanCoilUnits->CoolingLoad = false;
2234 0 : state.dataFanCoilUnits->HeatingLoad = true;
2235 0 : TightenWaterFlowLimits(state,
2236 : FanCoilNum,
2237 0 : state.dataFanCoilUnits->CoolingLoad,
2238 0 : state.dataFanCoilUnits->HeatingLoad,
2239 : fanCoil.HeatCoilFluidInletNode,
2240 : ControlledZoneNum,
2241 : FirstHVACIteration,
2242 : QZnReq,
2243 : MinWaterFlow,
2244 : MaxWaterFlow);
2245 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, HWFlow, f, MinWaterFlow, MaxWaterFlow);
2246 0 : if (SolFlag == -1) {
2247 0 : ++fanCoil.ConvgErrCountH;
2248 0 : if (fanCoil.ConvgErrCountH < 2) {
2249 0 : ShowWarningError(state, format("Hot Water control failed in fan coil unit {}", fanCoil.Name));
2250 0 : ShowContinueError(state, " Iteration limit exceeded in calculating water flow rate ");
2251 0 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidInletNode).MassFlowRate = HWFlow;
2252 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
2253 0 : ShowContinueErrorTimeStamp(state, format("Load Request = {}, Final Capacity = {}", QZnReq, QUnitOut));
2254 0 : ShowContinueErrorTimeStamp(
2255 : state,
2256 0 : format("Min water flow used during iterations = {}, Max water flow used during iterations = {}",
2257 : MinWaterFlow,
2258 : MaxWaterFlow));
2259 0 : ShowContinueErrorTimeStamp(state, format("Water flow rate on last iteration = {}", HWFlow));
2260 0 : ShowContinueErrorTimeStamp(state, "..Water flow rate set to last iteration value ");
2261 : } else {
2262 0 : ShowRecurringWarningErrorAtEnd(
2263 0 : state, "Hot water flow Iteration limit exceeded in fan coil unit " + fanCoil.Name, fanCoil.MaxIterIndexH);
2264 : }
2265 0 : } else if (SolFlag == -2) {
2266 0 : ++fanCoil.LimitErrCountH;
2267 0 : if (fanCoil.LimitErrCountH < 2) {
2268 0 : ShowWarningError(state, format("Hot Water control failed in fan coil unit {}", fanCoil.Name));
2269 0 : ShowContinueError(state, " Bad hot water mass flow limits");
2270 0 : ShowContinueErrorTimeStamp(state, "..Water flow rate set to lower limit ");
2271 : } else {
2272 0 : ShowRecurringWarningErrorAtEnd(
2273 0 : state, "Hot Water control failed in fan coil unit " + fanCoil.Name, fanCoil.BadMassFlowLimIndexH);
2274 : }
2275 : }
2276 113244 : } else if (SolFlag == -2) {
2277 0 : ++fanCoil.LimitErrCountH;
2278 0 : if (fanCoil.LimitErrCountH < 2) {
2279 0 : ShowWarningError(state, format("Hot Water control failed in fan coil unit {}", fanCoil.Name));
2280 0 : ShowContinueError(state, " Bad hot water mass flow limits");
2281 0 : ShowContinueErrorTimeStamp(state, "..Water flow rate set to lower limit ");
2282 : } else {
2283 0 : ShowRecurringWarningErrorAtEnd(
2284 0 : state, "Hot Water control failed in fan coil unit " + fanCoil.Name, fanCoil.BadMassFlowLimIndexH);
2285 : }
2286 : }
2287 : } else {
2288 0 : auto f = [&state, FirstHVACIteration, FanCoilNum, ControlledZoneNum, QZnReq](Real64 const PartLoadRatio) {
2289 0 : return CalcFanCoilLoadResidual(state, FanCoilNum, FirstHVACIteration, ControlledZoneNum, QZnReq, PartLoadRatio);
2290 0 : };
2291 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, PLR, f, 0.0, 1.0);
2292 : }
2293 : } else {
2294 : // demand greater than capacity
2295 549 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2296 549 : HWFlow = MaxWaterFlow;
2297 : } else {
2298 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, 1.0);
2299 : }
2300 : }
2301 113793 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2302 113793 : if (!HotFlowLocked) {
2303 113757 : mdot = HWFlow; // not flowlocked - set flow to HWFlow
2304 113757 : PlantUtilities::SetComponentFlowRate(
2305 113757 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2306 113757 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut); // get QUnitOut
2307 : } else {
2308 : // flow lock on
2309 36 : if (MdotLockH > HWFlow) { // if mdot > HWFlow, bypass extra flow
2310 18 : Calc4PipeFanCoil(state,
2311 : FanCoilNum,
2312 : ControlledZoneNum,
2313 : FirstHVACIteration,
2314 : QUnitOut); // get QUnitOut with HWFlow; rest will be bypassed
2315 18 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidInletNode).MassFlowRate =
2316 : MdotLockH; // reset flow to locked value. Since lock is on, must do this by hand
2317 18 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidOutletNodeNum).MassFlowRate = MdotLockH;
2318 : // Keep soln flow rate but reset outlet water temperature - i.e. bypass extra water
2319 18 : HWFlowBypass = MdotLockH - HWFlow;
2320 : // change outlet water temperature and enthalpy
2321 18 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidOutletNodeNum).Temp =
2322 18 : (HWFlowBypass * state.dataLoopNodes->Node(fanCoil.HeatCoilFluidInletNode).Temp +
2323 18 : HWFlow * state.dataLoopNodes->Node(fanCoil.HeatCoilFluidOutletNodeNum).Temp) /
2324 : MdotLockH;
2325 18 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidOutletNodeNum).Enthalpy =
2326 18 : (HWFlowBypass * state.dataLoopNodes->Node(fanCoil.HeatCoilFluidInletNode).Enthalpy +
2327 18 : HWFlow * state.dataLoopNodes->Node(fanCoil.HeatCoilFluidOutletNodeNum).Enthalpy) /
2328 : MdotLockH;
2329 : } else {
2330 : // if MdotLockH <= HWFlow use MdotLockH as is
2331 18 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidInletNode).MassFlowRate =
2332 : MdotLockH; // reset flow to locked value. Since lock is on, must do this by hand
2333 18 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidOutletNodeNum).MassFlowRate = MdotLockH;
2334 18 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
2335 : }
2336 : }
2337 : }
2338 113793 : QUnitOut = calcZoneSensibleOutput(AirMassFlow,
2339 113793 : state.dataLoopNodes->Node(OutletNode).Temp,
2340 113793 : state.dataLoopNodes->Node(InletNode).Temp,
2341 113793 : state.dataLoopNodes->Node(InletNode).HumRat);
2342 : } else {
2343 : // no action
2344 15898 : QUnitOut = QUnitOutNoHC;
2345 : }
2346 :
2347 : // CR9155 Remove specific humidity calculations
2348 280846 : SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
2349 280846 : SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
2350 280846 : LatentOutput = AirMassFlow * (SpecHumOut - SpecHumIn); // Latent rate (kg/s), dehumid = negative
2351 280846 : QTotUnitOut = AirMassFlow * (state.dataLoopNodes->Node(OutletNode).Enthalpy - state.dataLoopNodes->Node(InletNode).Enthalpy);
2352 : // report variables
2353 280846 : fanCoil.HeatPower = max(0.0, QUnitOut);
2354 280846 : fanCoil.SensCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QUnitOut));
2355 280846 : fanCoil.TotCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QTotUnitOut));
2356 280846 : fanCoil.ElecPower = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
2357 :
2358 280846 : PowerMet = QUnitOut;
2359 280846 : LatOutputProvided = LatentOutput;
2360 :
2361 : // cycling fan constant water flow AND VarFanVarFlow
2362 280846 : } break;
2363 204662 : case CCM::CycFan:
2364 : case CCM::VarFanVarFlow: {
2365 :
2366 204662 : if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ControlledZoneNum) || AirMassFlow < HVAC::SmallMassFlow) {
2367 37408 : UnitOn = false;
2368 : }
2369 :
2370 : // zero the hot & cold water flows
2371 204662 : mdot = 0.0;
2372 204662 : PlantUtilities::SetComponentFlowRate(
2373 204662 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2374 204662 : if (state.dataPlnt->PlantLoop(fanCoil.CoolCoilPlantLoc.loopNum).LoopSide(fanCoil.CoolCoilPlantLoc.loopSideNum).FlowLock ==
2375 : DataPlant::FlowLock::Locked) {
2376 156 : ColdFlowLocked = true; // check for flow lock
2377 : }
2378 204662 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2379 204662 : mdot = 0.0;
2380 204662 : PlantUtilities::SetComponentFlowRate(
2381 204662 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2382 204662 : if (state.dataPlnt->PlantLoop(fanCoil.HeatCoilPlantLoc.loopNum).LoopSide(fanCoil.HeatCoilPlantLoc.loopSideNum).FlowLock ==
2383 : DataPlant::FlowLock::Locked) {
2384 156 : HotFlowLocked = true; // save locked flow
2385 : }
2386 : }
2387 :
2388 : // obtain unit output with no active heating/cooling
2389 204662 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutNoHC, 0.0);
2390 :
2391 : // get the loads at the coil
2392 204662 : QCoilHeatSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP - QUnitOutNoHC;
2393 204662 : QCoilCoolSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP - QUnitOutNoHC;
2394 :
2395 : // speed fan selection only for multispeed cycling fan
2396 204662 : if (UnitOn && (fanCoil.CapCtrlMeth_Num == CCM::CycFan)) {
2397 167254 : QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputRequired;
2398 :
2399 : // set water side mass flow rate
2400 167254 : if (QCoilCoolSP < 0) {
2401 92042 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate = fanCoil.MaxCoolCoilFluidFlow;
2402 75212 : } else if (QCoilHeatSP > 0 && fanCoil.HCoilType_Num != HCoil::Electric) {
2403 75212 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidInletNode).MassFlowRate = fanCoil.MaxHeatCoilFluidFlow;
2404 : }
2405 :
2406 167254 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = fanCoil.LowSpeedRatio * fanCoil.MaxAirMassFlow;
2407 167254 : fanCoil.SpeedFanSel = 1;
2408 167254 : fanCoil.SpeedFanRatSel = fanCoil.LowSpeedRatio;
2409 167254 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMax);
2410 167254 : if (std::abs(QUnitOutMax) < std::abs(QZnReq)) {
2411 122880 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = fanCoil.MedSpeedRatio * fanCoil.MaxAirMassFlow;
2412 122880 : fanCoil.SpeedFanSel = 2;
2413 122880 : fanCoil.SpeedFanRatSel = fanCoil.MedSpeedRatio;
2414 122880 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMax);
2415 : }
2416 242107 : if (std::abs(QUnitOutMax) < std::abs(QZnReq)) {
2417 74853 : fanCoil.SpeedFanSel = 3;
2418 74853 : fanCoil.SpeedFanRatSel = 1.0;
2419 74853 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = fanCoil.MaxAirMassFlow;
2420 : }
2421 : } else {
2422 37408 : fanCoil.SpeedFanSel = 0;
2423 : }
2424 :
2425 : // meet the coil load adjusted for fan operation
2426 295967 : if (UnitOn && QCoilCoolSP < (-1.0 * HVAC::SmallLoad) &&
2427 91305 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleHeat) {
2428 : // cooling coil action, maximum cold water flow
2429 91305 : mdot = fanCoil.MaxCoolCoilFluidFlow;
2430 91305 : PlantUtilities::SetComponentFlowRate(
2431 91305 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2432 :
2433 91305 : QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP;
2434 91305 : ControlOffset = fanCoil.ColdControlOffset;
2435 :
2436 : // get the maximum output of the fcu
2437 91305 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMax); // call without PLR means PLR = 1
2438 :
2439 91305 : if (QUnitOutMax < QZnReq) {
2440 : // more cooling than required, find reduced air and water flow rate to meet the load
2441 : // solve for the cold water flow rate with no limit set by flow rate lockdown
2442 419814 : auto f = [&state, FanCoilNum, FirstHVACIteration, ControlledZoneNum, QZnReq](Real64 const PLR) {
2443 1036881 : return CalcFanCoilPLRResidual(state,
2444 : PLR,
2445 : FanCoilNum,
2446 : FirstHVACIteration,
2447 : ControlledZoneNum,
2448 345627 : state.dataFanCoilUnits->FanCoil(FanCoilNum).CoolCoilFluidInletNode,
2449 345627 : QZnReq);
2450 74187 : };
2451 74187 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, PLR, f, 0.0, 1.0);
2452 74187 : if (SolFlag == -1) {
2453 : // tighten limits on water flow rate to see if this allows convergence
2454 0 : state.dataFanCoilUnits->CoolingLoad = true;
2455 0 : state.dataFanCoilUnits->HeatingLoad = false;
2456 0 : TightenAirAndWaterFlowLimits(state,
2457 : FanCoilNum,
2458 0 : state.dataFanCoilUnits->CoolingLoad,
2459 0 : state.dataFanCoilUnits->HeatingLoad,
2460 : fanCoil.CoolCoilFluidInletNode,
2461 : ControlledZoneNum,
2462 : FirstHVACIteration,
2463 : QZnReq,
2464 : PLRMin,
2465 : PLRMax);
2466 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, PLR, f, PLRMin, PLRMax);
2467 0 : if (SolFlag == -1) {
2468 0 : ++fanCoil.ConvgErrCountC;
2469 0 : if (fanCoil.ConvgErrCountC < 2) {
2470 0 : ShowWarningError(state, format("Part-load ratio cooling control failed in fan coil unit {}", fanCoil.Name));
2471 0 : ShowContinueError(state, " Iteration limit exceeded in calculating FCU part-load ratio ");
2472 0 : state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate = PLR * fanCoil.MaxCoolCoilFluidFlow;
2473 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2474 0 : ShowContinueErrorTimeStamp(state, format("Load Request = {}, Final Capacity = {}", QZnReq, QUnitOut));
2475 0 : ShowContinueErrorTimeStamp(
2476 : state,
2477 0 : format("Min part-load used during iterations = {}, Max part-load used during iterations = {}", PLRMin, PLRMax));
2478 0 : ShowContinueErrorTimeStamp(state, format("Part-load ratio on last iteration = {}", PLR));
2479 0 : ShowContinueErrorTimeStamp(state, "..Part-load ratio set to last iteration value ");
2480 : } else {
2481 0 : ShowRecurringWarningErrorAtEnd(state,
2482 0 : "Part-load ratio cooling iteration limit exceeded in fan coil unit " + fanCoil.Name,
2483 0 : fanCoil.MaxIterIndexC);
2484 : }
2485 0 : } else if (SolFlag == -2) {
2486 0 : ++fanCoil.LimitErrCountC;
2487 0 : if (fanCoil.LimitErrCountC < 2) {
2488 0 : ShowWarningError(state, format("Part-load ratio cooling control failed in fan coil unit {}", fanCoil.Name));
2489 0 : ShowContinueError(state, " Bad part-load ratio limits");
2490 0 : ShowContinueErrorTimeStamp(state, format("..Part-load ratio set to {}", PLRMin));
2491 : } else {
2492 0 : ShowRecurringWarningErrorAtEnd(
2493 0 : state, "Part-load ratio cooling control failed in fan coil unit " + fanCoil.Name, fanCoil.BadMassFlowLimIndexC);
2494 : }
2495 : }
2496 74187 : } else if (SolFlag == -2) {
2497 0 : ++fanCoil.LimitErrCountC;
2498 0 : if (fanCoil.LimitErrCountC < 2) {
2499 0 : ShowWarningError(state, format("Part-load ratio control failed in fan coil unit {}", fanCoil.Name));
2500 0 : ShowContinueError(state, " Bad part-load ratio limits");
2501 0 : ShowContinueErrorTimeStamp(state, "..Part-load ratio set to 0");
2502 : } else {
2503 0 : ShowRecurringWarningErrorAtEnd(
2504 0 : state, "Part-load ratio control failed in fan coil unit " + fanCoil.Name, fanCoil.BadMassFlowLimIndexC);
2505 : }
2506 : }
2507 74187 : mdot = PLR * fanCoil.MaxCoolCoilFluidFlow;
2508 74187 : PlantUtilities::SetComponentFlowRate(
2509 74187 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2510 : } else {
2511 17118 : PLR = 1.0;
2512 17118 : mdot = PLR * fanCoil.MaxCoolCoilFluidFlow;
2513 17118 : PlantUtilities::SetComponentFlowRate(
2514 17118 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2515 : }
2516 :
2517 : // at the end calculate output
2518 91305 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2519 :
2520 188569 : } else if (UnitOn && QCoilHeatSP > HVAC::SmallLoad &&
2521 75212 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleCool) {
2522 : // heating coil action, maximun hot water flow
2523 :
2524 75212 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2525 75212 : mdot = fanCoil.MaxHeatCoilFluidFlow;
2526 75212 : PlantUtilities::SetComponentFlowRate(
2527 75212 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2528 : }
2529 :
2530 75212 : QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP;
2531 75212 : ControlOffset = fanCoil.HotControlOffset;
2532 :
2533 : // get the maximum output of the fcu
2534 75212 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMax);
2535 : // calculate the PLR, if load greater than output, PLR = 1 (output = max)
2536 75212 : if (QUnitOutMax > QZnReq) {
2537 : // more heating than required, find reduced water flow rate to meet the load
2538 67909 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2539 : // solve for the hot water flow rate with no limit set by flow rate lockdown
2540 378892 : auto f = [&state, FanCoilNum, FirstHVACIteration, ControlledZoneNum, QZnReq](Real64 const PLR) {
2541 932949 : return CalcFanCoilPLRResidual(state,
2542 : PLR,
2543 : FanCoilNum,
2544 : FirstHVACIteration,
2545 : ControlledZoneNum,
2546 310983 : state.dataFanCoilUnits->FanCoil(FanCoilNum).HeatCoilFluidInletNode,
2547 310983 : QZnReq);
2548 67909 : };
2549 67909 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, PLR, f, 0.0, 1.0);
2550 67909 : if (SolFlag == -1) {
2551 : // tighten limits on water flow rate to see if this allows convergence
2552 0 : state.dataFanCoilUnits->CoolingLoad = false;
2553 0 : state.dataFanCoilUnits->HeatingLoad = true;
2554 0 : TightenAirAndWaterFlowLimits(state,
2555 : FanCoilNum,
2556 0 : state.dataFanCoilUnits->CoolingLoad,
2557 0 : state.dataFanCoilUnits->HeatingLoad,
2558 : fanCoil.HeatCoilFluidInletNode,
2559 : ControlledZoneNum,
2560 : FirstHVACIteration,
2561 : QZnReq,
2562 : PLRMin,
2563 : PLRMax);
2564 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, PLR, f, PLRMin, PLRMax);
2565 0 : if (SolFlag == -1) {
2566 0 : ++fanCoil.ConvgErrCountH;
2567 0 : if (fanCoil.ConvgErrCountH < 2) {
2568 0 : ShowWarningError(state, format("Part-load ratio heating control failed in fan coil unit {}", fanCoil.Name));
2569 0 : ShowContinueError(state, " Iteration limit exceeded in calculating FCU part-load ratio ");
2570 0 : state.dataLoopNodes->Node(fanCoil.HeatCoilFluidInletNode).MassFlowRate = PLR * fanCoil.MaxHeatCoilFluidFlow;
2571 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2572 0 : ShowContinueErrorTimeStamp(state, format("Load Request = {}, Final Capacity = {}", QZnReq, QUnitOut));
2573 0 : ShowContinueErrorTimeStamp(
2574 : state,
2575 0 : format("Min part-load ratio used during iterations = {}, Max part-load used during iterations = {}",
2576 : PLRMin,
2577 : PLRMax));
2578 0 : ShowContinueErrorTimeStamp(state, format("Part-load ratio on last iteration = {}", PLR));
2579 0 : ShowContinueErrorTimeStamp(state, "..Part-load ratio set to last iteration value ");
2580 : } else {
2581 0 : ShowRecurringWarningErrorAtEnd(state,
2582 0 : "Part-load ratio heating iteration limit exceeded in fan coil unit " +
2583 0 : fanCoil.Name,
2584 0 : fanCoil.MaxIterIndexH);
2585 : }
2586 0 : } else if (SolFlag == -2) {
2587 0 : ++fanCoil.LimitErrCountH;
2588 0 : if (fanCoil.LimitErrCountH < 2) {
2589 0 : ShowWarningError(state, format("Part-load ratio heating control failed in fan coil unit {}", fanCoil.Name));
2590 0 : ShowContinueError(state, " Bad hot part-load ratio limits");
2591 0 : ShowContinueErrorTimeStamp(state, format("..Part-load ratio set to {}", PLRMin));
2592 : } else {
2593 0 : ShowRecurringWarningErrorAtEnd(state,
2594 0 : "Part-load ratio heating control failed in fan coil unit " + fanCoil.Name,
2595 0 : fanCoil.BadMassFlowLimIndexH);
2596 : }
2597 : }
2598 67909 : } else if (SolFlag == -2) {
2599 0 : ++fanCoil.LimitErrCountH;
2600 0 : if (fanCoil.LimitErrCountH < 2) {
2601 0 : ShowWarningError(state, format("Part-load ratio heating control failed in fan coil unit {}", fanCoil.Name));
2602 0 : ShowContinueError(state, " Bad part-load ratio limits");
2603 0 : ShowContinueErrorTimeStamp(state, "..Part-load ratio set to 0");
2604 : } else {
2605 0 : ShowRecurringWarningErrorAtEnd(
2606 0 : state, "Part-load ratio heating control failed in fan coil unit " + fanCoil.Name, fanCoil.BadMassFlowLimIndexH);
2607 : }
2608 : }
2609 67909 : HWFlow = PLR * fanCoil.MaxHeatCoilFluidFlow;
2610 67909 : PlantUtilities::SetComponentFlowRate(
2611 67909 : state, HWFlow, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2612 :
2613 : } else {
2614 0 : auto f = [&state, FirstHVACIteration, FanCoilNum, ControlledZoneNum, QZnReq](Real64 const PartLoadRatio) {
2615 0 : return CalcFanCoilLoadResidual(state, FanCoilNum, FirstHVACIteration, ControlledZoneNum, QZnReq, PartLoadRatio);
2616 0 : };
2617 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, PLR, f, 0.0, 1.0);
2618 : }
2619 : } else {
2620 7303 : PLR = 1.0;
2621 7303 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2622 7303 : mdot = PLR * fanCoil.MaxHeatCoilFluidFlow;
2623 7303 : PlantUtilities::SetComponentFlowRate(
2624 7303 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2625 : }
2626 : }
2627 :
2628 : // at the end calculate output with adjusted PLR
2629 75212 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2630 :
2631 : } else {
2632 : // no action, zero the air flow rate, the unit is off
2633 38145 : state.dataLoopNodes->Node(InletNode).MassFlowRate = 0.0;
2634 38145 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
2635 38145 : fanCoil.SpeedFanSel = 0;
2636 38145 : PLR = 0.0;
2637 38145 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2638 : }
2639 :
2640 204662 : AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
2641 : // CR9155 Remove specific humidity calculations
2642 204662 : SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
2643 204662 : SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
2644 204662 : LatentOutput = AirMassFlow * (SpecHumOut - SpecHumIn); // Latent rate (kg/s), dehumid = negative
2645 204662 : QTotUnitOut = AirMassFlow * (state.dataLoopNodes->Node(OutletNode).Enthalpy - state.dataLoopNodes->Node(InletNode).Enthalpy);
2646 : // report variables
2647 204662 : fanCoil.HeatPower = max(0.0, QUnitOut);
2648 204662 : fanCoil.SensCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QUnitOut));
2649 204662 : fanCoil.TotCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QTotUnitOut));
2650 204662 : fanCoil.ElecPower = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
2651 :
2652 204662 : fanCoil.PLR = PLR;
2653 204662 : PowerMet = QUnitOut;
2654 204662 : LatOutputProvided = LatentOutput;
2655 :
2656 204662 : } break;
2657 17145 : case CCM::ASHRAE: {
2658 :
2659 17145 : if (AirMassFlow < HVAC::SmallMassFlow) {
2660 24 : UnitOn = false;
2661 : }
2662 :
2663 : // zero the hot & cold water flows
2664 17145 : mdot = 0.0;
2665 17145 : PlantUtilities::SetComponentFlowRate(
2666 17145 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2667 :
2668 17145 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2669 11430 : mdot = 0.0;
2670 11430 : PlantUtilities::SetComponentFlowRate(
2671 11430 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2672 : }
2673 :
2674 17145 : OAMassFlow = 0.0;
2675 :
2676 : // determine minimum outdoor air flow rate
2677 17145 : if (fanCoil.DSOAPtr > 0 && fanCoil.OutsideAirNode > 0) {
2678 17142 : OAVolumeFlowRate = DataSizing::calcDesignSpecificationOutdoorAir(state, fanCoil.DSOAPtr, ControlledZoneNum, true, true);
2679 17142 : RhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state,
2680 17142 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).Press,
2681 17142 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).Temp,
2682 17142 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).HumRat);
2683 17142 : OAMassFlow = OAVolumeFlowRate * RhoAir;
2684 : }
2685 :
2686 17145 : MinSAMassFlowRate = min(max(OAMassFlow, fanCoil.MaxAirMassFlow * fanCoil.LowSpeedRatio), fanCoil.MaxAirMassFlow);
2687 17145 : MaxSAMassFlowRate = fanCoil.MaxAirMassFlow;
2688 17145 : state.dataFanCoilUnits->HeatingLoad = false;
2689 17145 : state.dataFanCoilUnits->CoolingLoad = false;
2690 17145 : if (UnitOn) {
2691 17121 : state.dataLoopNodes->Node(InletNode).MassFlowRate = MinSAMassFlowRate;
2692 17121 : fanCoil.MaxNoCoolHeatAirMassFlow = MinSAMassFlowRate;
2693 17121 : fanCoil.MaxCoolAirMassFlow = MaxSAMassFlowRate;
2694 17121 : fanCoil.MaxHeatAirMassFlow = MaxSAMassFlowRate;
2695 17121 : fanCoil.LowSpeedCoolFanRatio = MinSAMassFlowRate / MaxSAMassFlowRate;
2696 17121 : fanCoil.LowSpeedHeatFanRatio = MinSAMassFlowRate / MaxSAMassFlowRate;
2697 :
2698 17121 : Calc4PipeFanCoil(state,
2699 : FanCoilNum,
2700 : ControlledZoneNum,
2701 : FirstHVACIteration,
2702 : QUnitOutNoHC,
2703 17121 : 0.0); // needs PLR=0 for electric heating coil, otherwise will run at full capacity
2704 :
2705 17121 : QCoilCoolSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP;
2706 17121 : QCoilHeatSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP;
2707 :
2708 23539 : if (QCoilHeatSP > 0.0 && QCoilCoolSP > 0.0 &&
2709 6418 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleCool) {
2710 6090 : QZnReq = QCoilHeatSP;
2711 6090 : state.dataFanCoilUnits->HeatingLoad = true;
2712 11359 : } else if (QCoilHeatSP > 0.0 && QCoilCoolSP > 0.0 &&
2713 328 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) == HVAC::SetptType::SingleCool) {
2714 328 : QZnReq = 0.0;
2715 21406 : } else if (QCoilHeatSP < 0.0 && QCoilCoolSP < 0.0 &&
2716 10703 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleHeat) {
2717 10592 : QZnReq = QCoilCoolSP;
2718 10592 : state.dataFanCoilUnits->CoolingLoad = true;
2719 222 : } else if (QCoilHeatSP < 0.0 && QCoilCoolSP < 0.0 &&
2720 111 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) == HVAC::SetptType::SingleHeat) {
2721 111 : QZnReq = 0.0;
2722 0 : } else if (QCoilHeatSP <= 0.0 && QCoilCoolSP >= 0.0) {
2723 0 : QZnReq = 0.0;
2724 : }
2725 : }
2726 :
2727 17145 : if (state.dataFanCoilUnits->CoolingLoad) {
2728 :
2729 10592 : state.dataLoopNodes->Node(InletNode).MassFlowRate = MaxSAMassFlowRate;
2730 :
2731 10592 : mdot = fanCoil.MaxCoolCoilFluidFlow;
2732 10592 : PlantUtilities::SetComponentFlowRate(
2733 10592 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2734 :
2735 6553 : } else if (state.dataFanCoilUnits->HeatingLoad) {
2736 :
2737 6090 : state.dataLoopNodes->Node(InletNode).MassFlowRate = MaxSAMassFlowRate;
2738 :
2739 6090 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2740 4054 : mdot = fanCoil.MaxHeatCoilFluidFlow;
2741 4054 : PlantUtilities::SetComponentFlowRate(
2742 4054 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2743 : }
2744 : }
2745 :
2746 17145 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMax);
2747 :
2748 17145 : if ((state.dataFanCoilUnits->CoolingLoad && QUnitOutMax < QZnReq) || (state.dataFanCoilUnits->HeatingLoad && QUnitOutMax > QZnReq)) {
2749 32032 : if ((state.dataFanCoilUnits->CoolingLoad && QUnitOutNoHC < QZnReq) ||
2750 16016 : (state.dataFanCoilUnits->HeatingLoad && QUnitOutNoHC > QZnReq)) {
2751 0 : PLR = 0.0;
2752 0 : fanCoil.FanPartLoadRatio = 0.0; // set SZVAV model variable
2753 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate =
2754 : MinSAMassFlowRate; // = min air flow rate + ((max-min) air flow rate * FanPartLoadRatio)
2755 0 : mdot = 0.0;
2756 0 : PlantUtilities::SetComponentFlowRate(
2757 0 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2758 :
2759 0 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2760 0 : mdot = 0.0;
2761 0 : PlantUtilities::SetComponentFlowRate(
2762 0 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2763 : }
2764 : } else {
2765 16016 : Real64 OnOffAirFlowRatio = 1.0;
2766 16016 : bool HXUnitOn = false;
2767 16016 : int AirLoopNum = 0;
2768 16016 : HVAC::CompressorOp CompressorOnFlag = HVAC::CompressorOp::Off;
2769 16016 : auto &SZVAVModel(fanCoil);
2770 : // seems like passing these (arguments 2-n) as an array (similar to Par) would make this more uniform across different
2771 : // models
2772 48048 : SZVAVModel::calcSZVAVModel(state,
2773 : SZVAVModel,
2774 : FanCoilNum,
2775 : FirstHVACIteration,
2776 16016 : state.dataFanCoilUnits->CoolingLoad,
2777 16016 : state.dataFanCoilUnits->HeatingLoad,
2778 : QZnReq,
2779 : OnOffAirFlowRatio,
2780 : HXUnitOn,
2781 : AirLoopNum,
2782 : PLR,
2783 : CompressorOnFlag);
2784 : }
2785 1596 : } else if ((state.dataFanCoilUnits->CoolingLoad && QUnitOutMax > QZnReq && QZnReq < 0.0) ||
2786 467 : (state.dataFanCoilUnits->HeatingLoad && QUnitOutMax < QZnReq && QZnReq > 0.0)) {
2787 : // load is larger than capacity, thus run the fancoil unit at full capacity
2788 666 : PLR = 1.0;
2789 : }
2790 17145 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2791 17145 : PowerMet = QUnitOut;
2792 17145 : AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
2793 : // CR9155 Remove specific humidity calculations
2794 17145 : SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
2795 17145 : SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
2796 : // Latent rate (kg/s), dehumid = negative
2797 17145 : LatOutputProvided = AirMassFlow * (SpecHumOut - SpecHumIn);
2798 17145 : fanCoil.PLR = PLR;
2799 :
2800 : // cycling fan constant water flow AND VarFanVarFlow
2801 17145 : } break;
2802 0 : case CCM::VarFanConsFlow: {
2803 :
2804 0 : if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ControlledZoneNum) || AirMassFlow < HVAC::SmallMassFlow) {
2805 0 : UnitOn = false;
2806 : }
2807 :
2808 : // zero the hot & cold water flows
2809 : // Node(fanCoil%CoolCoilFluidInletNode)%MassFlowRate = 0.0
2810 : // Node(fanCoil%HeatCoilFluidInletNode)%MassFlowRate = 0.0
2811 0 : mdot = 0.0;
2812 0 : PlantUtilities::SetComponentFlowRate(
2813 0 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2814 :
2815 0 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2816 0 : mdot = 0.0;
2817 0 : PlantUtilities::SetComponentFlowRate(
2818 0 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2819 : }
2820 0 : Calc4PipeFanCoil(state,
2821 : FanCoilNum,
2822 : ControlledZoneNum,
2823 : FirstHVACIteration,
2824 : QUnitOutNoHC,
2825 0 : 0.0); // needs PLR=0 for electric heating coil, otherwise will run at full capacity
2826 :
2827 0 : int Iter = 0;
2828 0 : if (UnitOn && state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP < (-1.0 * HVAC::SmallLoad) &&
2829 0 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleHeat) {
2830 : // cooling coil action, maximum cold water flow
2831 0 : mdot = fanCoil.MaxCoolCoilFluidFlow;
2832 0 : PlantUtilities::SetComponentFlowRate(
2833 0 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
2834 0 : QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP;
2835 0 : ControlOffset = fanCoil.ColdControlOffset;
2836 :
2837 : // get the maximum output of the fcu
2838 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMax);
2839 : // calculate the PLR, if load greater than output, PLR = 1 (output = max)
2840 0 : if (QUnitOutMax != 0.0) {
2841 0 : PLR = std::abs(QZnReq / QUnitOutMax);
2842 : }
2843 0 : if (PLR > 1.0) {
2844 0 : PLR = 1.0;
2845 : }
2846 :
2847 : // adjust the PLR to meet the cooling load calling Calc4PipeFanCoil repeatedly with the PLR adjusted
2848 0 : while (std::abs(Error) > ControlOffset && std::abs(AbsError) > HVAC::SmallLoad && Iter < MaxIterCycl && PLR != 1.0) {
2849 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2850 0 : Error = (QZnReq - QUnitOut) / QZnReq;
2851 0 : AbsError = QZnReq - QUnitOut;
2852 0 : DelPLR = (QZnReq - QUnitOut) / QUnitOutMax;
2853 0 : PLR += Relax * DelPLR;
2854 0 : PLR = max(0.0, min(1.0, PLR));
2855 0 : ++Iter;
2856 0 : if (Iter == 32) {
2857 0 : Relax = 0.5;
2858 : }
2859 0 : if (Iter == 65) {
2860 0 : Relax = 0.25;
2861 : }
2862 : }
2863 :
2864 : // warning if not converged
2865 0 : if (Iter > (MaxIterCycl - 1)) {
2866 0 : if (fanCoil.MaxIterIndexC == 0) {
2867 0 : ShowWarningMessage(state,
2868 0 : format("ZoneHVAC:FourPipeFanCoil=\"{}\" -- Exceeded max iterations while adjusting cycling fan sensible "
2869 : "runtime to meet the zone load within the cooling convergence tolerance.",
2870 0 : fanCoil.Name));
2871 0 : ShowContinueErrorTimeStamp(state, format("Iterations={}", MaxIterCycl));
2872 : }
2873 0 : ShowRecurringWarningErrorAtEnd(state,
2874 0 : "ZoneHVAC:FourPipeFanCoil=\"" + fanCoil.Name +
2875 : "\" -- Exceeded max iterations error (sensible runtime) continues...",
2876 0 : fanCoil.MaxIterIndexC);
2877 : }
2878 :
2879 : // at the end calculate output with adjusted PLR
2880 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2881 :
2882 0 : } else if (UnitOn && state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP > HVAC::SmallLoad &&
2883 0 : state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleCool) {
2884 : // heating coil action, maximun hot water flow
2885 0 : if (fanCoil.HCoilType_Num == HCoil::Water) {
2886 0 : mdot = fanCoil.MaxHeatCoilFluidFlow;
2887 0 : PlantUtilities::SetComponentFlowRate(
2888 0 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
2889 : }
2890 0 : QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP;
2891 0 : ControlOffset = fanCoil.HotControlOffset;
2892 :
2893 : // get the maximum output of the fcu
2894 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOutMax);
2895 : // calculate the PLR, if load greater than output, PLR = 1 (output = max)
2896 0 : if (QUnitOutMax != 0.0) {
2897 0 : PLR = std::abs(QZnReq / QUnitOutMax);
2898 : }
2899 0 : if (PLR > 1.0) {
2900 0 : PLR = 1.0;
2901 : }
2902 :
2903 : // adjust the PLR to meet the heating load calling Calc4PipeFanCoil repeatedly with the PLR adjusted
2904 0 : while (std::abs(Error) > ControlOffset && std::abs(AbsError) > HVAC::SmallLoad && Iter < MaxIterCycl && PLR != 1.0) {
2905 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2906 0 : Error = (QZnReq - QUnitOut) / QZnReq;
2907 0 : AbsError = QZnReq - QUnitOut;
2908 0 : DelPLR = (QZnReq - QUnitOut) / QUnitOutMax;
2909 0 : PLR += Relax * DelPLR;
2910 0 : PLR = max(0.0, min(1.0, PLR));
2911 0 : ++Iter;
2912 0 : if (Iter == 32) {
2913 0 : Relax = 0.5;
2914 : }
2915 0 : if (Iter == 65) {
2916 0 : Relax = 0.25;
2917 : }
2918 : }
2919 :
2920 : // warning if not converged
2921 0 : if (Iter > (MaxIterCycl - 1)) {
2922 0 : if (fanCoil.MaxIterIndexH == 0) {
2923 0 : ShowWarningMessage(state,
2924 0 : format("ZoneHVAC:FourPipeFanCoil=\"{}\" -- Exceeded max iterations while adjusting cycling fan sensible "
2925 : "runtime to meet the zone load within the heating convergence tolerance.",
2926 0 : fanCoil.Name));
2927 0 : ShowContinueError(state, format("...Requested zone load = {:.3T} [W]", QZnReq));
2928 0 : ShowContinueError(state, format("...Fan coil capacity = {:.3T} [W]", QUnitOut));
2929 0 : ShowContinueErrorTimeStamp(state, format("Iterations={}", MaxIterCycl));
2930 : }
2931 0 : ShowRecurringWarningErrorAtEnd(state,
2932 0 : "ZoneHVAC:FourPipeFanCoil=\"" + fanCoil.Name +
2933 : "\" -- Exceeded max iterations error (sensible runtime) continues...",
2934 0 : fanCoil.MaxIterIndexH);
2935 : }
2936 :
2937 : // at the end calculate output with adjusted PLR
2938 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2939 :
2940 : // this part of the code is just if we want ventilation in the deadband zone
2941 : // ELSE IF (AirMassFlow .gt. 0.0d0) THEN
2942 : // if fan scheduled available : just ventilation, PLR = 1
2943 : // QUnitOut = QUnitOutNOHC
2944 : // PLR = 1.
2945 :
2946 : } else {
2947 : // no action, zero the air flow rate, the unit is off
2948 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = 0.0;
2949 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
2950 0 : fanCoil.SpeedFanSel = 0;
2951 0 : PLR = 0.0;
2952 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
2953 : }
2954 :
2955 0 : AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
2956 : // CR9155 Remove specific humidity calculations
2957 0 : SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
2958 0 : SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
2959 0 : LatentOutput = AirMassFlow * (SpecHumOut - SpecHumIn); // Latent rate (kg/s), dehumid = negative
2960 0 : QSensUnitOutNoATM = calcZoneSensibleOutput(AirMassFlow,
2961 0 : state.dataLoopNodes->Node(OutletNode).Temp,
2962 0 : state.dataLoopNodes->Node(InletNode).Temp,
2963 0 : state.dataLoopNodes->Node(InletNode).HumRat);
2964 0 : QTotUnitOut = AirMassFlow * (state.dataLoopNodes->Node(OutletNode).Enthalpy - state.dataLoopNodes->Node(InletNode).Enthalpy);
2965 : // report variables
2966 0 : fanCoil.HeatPower = max(0.0, QSensUnitOutNoATM);
2967 0 : fanCoil.SensCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QSensUnitOutNoATM));
2968 0 : fanCoil.TotCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QTotUnitOut));
2969 0 : fanCoil.ElecPower = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
2970 :
2971 0 : fanCoil.PLR = PLR;
2972 0 : PowerMet = QUnitOut;
2973 0 : LatOutputProvided = LatentOutput;
2974 :
2975 0 : } break;
2976 12375 : case CCM::MultiSpeedFan: {
2977 : // call multi-speed fan staging calculation
2978 12375 : SimMultiStage4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
2979 12375 : AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
2980 12375 : SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
2981 12375 : SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
2982 12375 : LatentOutput = AirMassFlow * (SpecHumOut - SpecHumIn); // Latent rate (kg/s), dehumid = negative
2983 49500 : QSensUnitOutNoATM = calcZoneSensibleOutput(AirMassFlow,
2984 12375 : state.dataLoopNodes->Node(OutletNode).Temp,
2985 12375 : state.dataLoopNodes->Node(InletNode).Temp,
2986 12375 : state.dataLoopNodes->Node(InletNode).HumRat);
2987 12375 : QTotUnitOut = AirMassFlow * (state.dataLoopNodes->Node(OutletNode).Enthalpy - state.dataLoopNodes->Node(InletNode).Enthalpy);
2988 : // report variables
2989 12375 : fanCoil.HeatPower = max(0.0, QSensUnitOutNoATM);
2990 12375 : fanCoil.SensCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QSensUnitOutNoATM));
2991 12375 : fanCoil.TotCoolPower = std::abs(min(DataPrecisionGlobals::constant_zero, QTotUnitOut));
2992 12375 : fanCoil.ElecPower = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
2993 :
2994 12375 : PowerMet = QUnitOut;
2995 12375 : LatOutputProvided = LatentOutput;
2996 12375 : } break;
2997 0 : default:
2998 0 : break;
2999 : }
3000 515028 : }
3001 :
3002 0 : void TightenWaterFlowLimits(EnergyPlusData &state,
3003 : int const FanCoilNum, // Unit index in fan coil array
3004 : bool const CoolingLoad, // true if zone requires cooling
3005 : bool const HeatingLoad, // true if zone requires heating
3006 : int const WaterControlNode, // water control node, either cold or hot water
3007 : int const ControlledZoneNum, // controlling zone index
3008 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
3009 : Real64 const QZnReq, // zone load [W]
3010 : Real64 &MinWaterFlow, // minimum water flow rate
3011 : Real64 &MaxWaterFlow // maximum water flow rate
3012 : )
3013 : {
3014 :
3015 : // SUBROUTINE INFORMATION:
3016 : // AUTHOR R. Raustad, FSEC
3017 : // DATE WRITTEN May 2016
3018 :
3019 : // PURPOSE OF THIS SUBROUTINE:
3020 : // Find tighter limits of water flow rate for fan coil unit.
3021 :
3022 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3023 : Real64 QUnitOut; // fan coil delivered capacity [W]
3024 : Real64 mdot; // water flow rate passed to fan coil unit [kg/s]
3025 :
3026 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 10% of flow before iterating
3027 0 : mdot = MaxWaterFlow * 0.1;
3028 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = mdot;
3029 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
3030 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3031 0 : MaxWaterFlow = mdot;
3032 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 1% of flow before iterating
3033 0 : mdot *= 0.1;
3034 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = mdot;
3035 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
3036 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3037 0 : MaxWaterFlow = mdot;
3038 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 0.1% of flow before iterating
3039 0 : mdot *= 0.1;
3040 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = mdot;
3041 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
3042 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3043 0 : MaxWaterFlow = mdot;
3044 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 0.01% of flow before iterating
3045 0 : mdot *= 0.1;
3046 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = mdot;
3047 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
3048 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3049 0 : MaxWaterFlow = mdot;
3050 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 0.001% of flow before
3051 : // iterating
3052 0 : mdot *= 0.1;
3053 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = mdot;
3054 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut);
3055 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3056 0 : MaxWaterFlow = mdot;
3057 : } else {
3058 0 : MinWaterFlow = mdot;
3059 : }
3060 : } else {
3061 0 : MinWaterFlow = mdot;
3062 : }
3063 : } else {
3064 0 : MinWaterFlow = mdot;
3065 : }
3066 : } else {
3067 0 : MinWaterFlow = mdot;
3068 : }
3069 : } else {
3070 0 : MinWaterFlow = mdot;
3071 : }
3072 0 : }
3073 :
3074 0 : void TightenAirAndWaterFlowLimits(EnergyPlusData &state,
3075 : int const FanCoilNum, // Unit index in fan coil array
3076 : bool const CoolingLoad, // true if zone requires cooling
3077 : bool const HeatingLoad, // true if zone requires heating
3078 : int const WaterControlNode, // water control node, either cold or hot water
3079 : int const ControlledZoneNum, // controlling zone index
3080 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
3081 : Real64 const QZnReq, // zone load [W]
3082 : Real64 &PLRMin, // minimum part-load ratio
3083 : Real64 &PLRMax // maximum part-load ratio
3084 : )
3085 : {
3086 :
3087 : // SUBROUTINE INFORMATION:
3088 : // AUTHOR R. Raustad, FSEC
3089 : // DATE WRITTEN August 2016
3090 :
3091 : // PURPOSE OF THIS SUBROUTINE:
3092 : // Find tighter limits of air and water flow rate for fan coil unit.
3093 :
3094 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3095 : Real64 QUnitOut; // fan coil delivered capacity [W]
3096 :
3097 0 : auto const &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilNum);
3098 :
3099 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 100% of flow before iterating
3100 0 : PLRMin = 0.0;
3101 0 : PLRMax = 1.0;
3102 0 : Real64 PLR = 1.0; // operating part-load ratio
3103 0 : if (WaterControlNode == fanCoil.CoolCoilFluidInletNode) {
3104 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxCoolCoilFluidFlow;
3105 0 : } else if (WaterControlNode == fanCoil.HeatCoilFluidInletNode && fanCoil.HCoilType_Num != HCoil::Electric) {
3106 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxHeatCoilFluidFlow;
3107 : }
3108 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
3109 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3110 0 : PLRMax = PLR;
3111 0 : PLR *= 0.1;
3112 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 10% of flow before iterating
3113 0 : if (WaterControlNode == fanCoil.CoolCoilFluidInletNode) {
3114 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxCoolCoilFluidFlow;
3115 0 : } else if (WaterControlNode == fanCoil.HeatCoilFluidInletNode && fanCoil.HCoilType_Num != HCoil::Electric) {
3116 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxHeatCoilFluidFlow;
3117 : }
3118 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
3119 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3120 0 : PLRMax = PLR;
3121 0 : PLR *= 0.1;
3122 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 1% of flow before iterating
3123 0 : if (WaterControlNode == fanCoil.CoolCoilFluidInletNode) {
3124 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxCoolCoilFluidFlow;
3125 0 : } else if (WaterControlNode == fanCoil.HeatCoilFluidInletNode && fanCoil.HCoilType_Num != HCoil::Electric) {
3126 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxHeatCoilFluidFlow;
3127 : }
3128 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
3129 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3130 0 : PLRMax = PLR;
3131 0 : PLR *= 0.1;
3132 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 0.1% of flow before iterating
3133 0 : if (WaterControlNode == fanCoil.CoolCoilFluidInletNode) {
3134 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxCoolCoilFluidFlow;
3135 0 : } else if (WaterControlNode == fanCoil.HeatCoilFluidInletNode && fanCoil.HCoilType_Num != HCoil::Electric) {
3136 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxHeatCoilFluidFlow;
3137 : }
3138 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
3139 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3140 0 : PLRMax = PLR;
3141 0 : PLR *= 0.1;
3142 : // RegulaFalsi can reach max iteration when low water flow rate is required to meet load. Test at 0.01% of flow before
3143 : // iterating
3144 0 : if (WaterControlNode == fanCoil.CoolCoilFluidInletNode) {
3145 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxCoolCoilFluidFlow;
3146 0 : } else if (WaterControlNode == fanCoil.HeatCoilFluidInletNode && fanCoil.HCoilType_Num != HCoil::Electric) {
3147 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * fanCoil.MaxHeatCoilFluidFlow;
3148 : }
3149 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
3150 0 : if ((CoolingLoad && QUnitOut < QZnReq) || (HeatingLoad && QUnitOut > QZnReq)) {
3151 0 : PLRMax = PLR;
3152 : } else {
3153 0 : PLRMin = PLR;
3154 : }
3155 : } else {
3156 0 : PLRMin = PLR;
3157 : }
3158 : } else {
3159 0 : PLRMin = PLR;
3160 : }
3161 : } else {
3162 0 : PLRMin = PLR;
3163 : }
3164 : } else {
3165 0 : PLRMin = PLR;
3166 : }
3167 0 : }
3168 :
3169 5420611 : void Calc4PipeFanCoil(EnergyPlusData &state,
3170 : int const FanCoilNum, // Unit index in fan coil array
3171 : int const ControlledZoneNum, // ZoneEquipConfig index
3172 : bool const FirstHVACIteration, // flag for 1st HVAV iteration in the time step
3173 : Real64 &LoadMet, // load met by unit (watts)
3174 : ObjexxFCL::Optional<Real64> PLR, // Part Load Ratio, fraction of time step fancoil is on
3175 : Real64 eHeatCoilCyclingR // electric heating coil cycling ratio used with MultiSpeedFan capacity control
3176 : )
3177 : {
3178 :
3179 : // SUBROUTINE INFORMATION:
3180 : // AUTHOR Fred Buhl
3181 : // DATE WRITTEN March 2000
3182 : // MODIFIED July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
3183 :
3184 : // PURPOSE OF THIS SUBROUTINE:
3185 : // Simulate the components making up the 4 pipe fan coil unit.
3186 :
3187 : // METHODOLOGY EMPLOYED:
3188 : // Simulates the unit components sequentially in the air flow direction.
3189 :
3190 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3191 : Real64 AirMassFlow; // total mass flow through the unit
3192 : Real64 PartLoad; // if PLR present PartLoad = PLR
3193 : Real64 OASchedValue; // value of OASchedValue, =1 if not schedule
3194 5420611 : Real64 ElecHeaterControl(1.0); // 1 or 0, enables or disables heating coil
3195 : Real64 FanSpeedRatio; // ratio of actual fan flow to max design fan flow
3196 :
3197 5420611 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilNum);
3198 :
3199 : // if PLR present in arguments, get its value, else default PLR = 1
3200 5420611 : if (present(PLR)) {
3201 4230578 : PartLoad = PLR;
3202 : } else {
3203 1190033 : PartLoad = 1.0;
3204 : }
3205 :
3206 5420611 : int OutletNode = fanCoil.AirOutNode;
3207 5420611 : int InletNode = fanCoil.AirInNode;
3208 5420611 : state.dataFanCoilUnits->ZoneNode = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneNode;
3209 :
3210 : // Assume the unit is able to vary the flow. A cycling unit is treated as
3211 : // if it were variable flow, with the flow being the averaqe flow over the time step
3212 10840466 : if (((fanCoil.availSched->getCurrentVal() > 0.0 && fanCoil.fanAvailSched->getCurrentVal() > 0.0) || state.dataHVACGlobal->TurnFansOn) &&
3213 5419855 : !state.dataHVACGlobal->TurnFansOff) {
3214 5417077 : if (fanCoil.CapCtrlMeth_Num != CCM::ConsFanVarFlow) {
3215 1909066 : if (fanCoil.CapCtrlMeth_Num != CCM::ASHRAE) {
3216 1714081 : state.dataLoopNodes->Node(InletNode).MassFlowRate = PartLoad * state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
3217 : }
3218 : } else {
3219 3508011 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
3220 : }
3221 : }
3222 :
3223 : // use the value of the outside air schedule if present
3224 5420611 : OASchedValue = (fanCoil.oaSched != nullptr) ? fanCoil.oaSched->getCurrentVal() : 1.0;
3225 :
3226 5420611 : if (fanCoil.ATMixerExists) {
3227 352428 : state.dataFanCoilUnits->ATMixOutNode = fanCoil.ATMixerOutNode;
3228 352428 : if (fanCoil.ATMixerType == HVAC::MixerType::InletSide) {
3229 : // set the primary air inlet mass flow rate
3230 169541 : state.dataLoopNodes->Node(fanCoil.ATMixerPriNode).MassFlowRate =
3231 169541 : min(state.dataLoopNodes->Node(fanCoil.ATMixerPriNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(InletNode).MassFlowRate);
3232 : // now calculate the the mixer outlet conditions (and the secondary air inlet flow rate)
3233 : // the mixer outlet flow rate has already been set above (it is the "inlet" node flow rate)
3234 169541 : SingleDuct::SimATMixer(state, fanCoil.ATMixerName, FirstHVACIteration, fanCoil.ATMixerIndex);
3235 : }
3236 352428 : AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
3237 : } else {
3238 : // OutdoorAir:Mixer
3239 5068183 : if (fanCoil.CapCtrlMeth_Num == CCM::CycFan) {
3240 1522585 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRate =
3241 1522585 : min(OASchedValue * state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMax * PartLoad * fanCoil.SpeedFanRatSel,
3242 1522585 : state.dataLoopNodes->Node(InletNode).MassFlowRate);
3243 3545598 : } else if (fanCoil.CapCtrlMeth_Num == CCM::MultiSpeedFan) {
3244 191958 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRate =
3245 191958 : min(OASchedValue * state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMax * PartLoad *
3246 191958 : state.dataFanCoilUnits->FanFlowRatio,
3247 191958 : state.dataLoopNodes->Node(InletNode).MassFlowRate);
3248 : } else {
3249 3353640 : if (fanCoil.CapCtrlMeth_Num != CCM::ConsFanVarFlow && fanCoil.CapCtrlMeth_Num != CCM::ASHRAE) {
3250 0 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRate =
3251 0 : min(OASchedValue * state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMax * PartLoad,
3252 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate);
3253 : } else {
3254 3353640 : state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRate =
3255 3353640 : min(OASchedValue * state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRateMax,
3256 3353640 : state.dataLoopNodes->Node(InletNode).MassFlowRate);
3257 : }
3258 : }
3259 5068183 : state.dataLoopNodes->Node(fanCoil.AirReliefNode).MassFlowRate = state.dataLoopNodes->Node(fanCoil.OutsideAirNode).MassFlowRate;
3260 5068183 : AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
3261 5068183 : MixedAir::SimOAMixer(state, fanCoil.OAMixName, fanCoil.OAMixIndex);
3262 : }
3263 :
3264 5420611 : if (fanCoil.CapCtrlMeth_Num == CCM::CycFan) {
3265 : // cycling fan coil unit calculation
3266 1522585 : if (fanCoil.SpeedFanSel == 1) {
3267 517141 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, FirstHVACIteration, fanCoil.LowSpeedRatio);
3268 1005444 : } else if (fanCoil.SpeedFanSel == 2) {
3269 484469 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, FirstHVACIteration, fanCoil.MedSpeedRatio);
3270 520975 : } else if (fanCoil.SpeedFanSel == 3) {
3271 444674 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, FirstHVACIteration, 1.0);
3272 : } else { // using 1.0 here for fan speed ratio seems wrong if FCU max flow rate is different than the fan maximum flow rate
3273 76301 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, FirstHVACIteration, 0.0, _, 0.0);
3274 : }
3275 1522585 : if (fanCoil.CCoilType_Num == CCoil::HXAssist) {
3276 0 : HVACHXAssistedCoolingCoil::SimHXAssistedCoolingCoil(
3277 0 : state, fanCoil.CCoilName, FirstHVACIteration, HVAC::CompressorOp::On, 0.0, fanCoil.CCoilName_Index, HVAC::FanOp::Continuous);
3278 : } else {
3279 6090340 : WaterCoils::SimulateWaterCoilComponents(
3280 4567755 : state, fanCoil.CCoilName, FirstHVACIteration, fanCoil.CCoilName_Index, _, HVAC::FanOp::Cycling, PLR);
3281 : }
3282 1522585 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3283 6090340 : WaterCoils::SimulateWaterCoilComponents(
3284 4567755 : state, fanCoil.HCoilName, FirstHVACIteration, fanCoil.HCoilName_Index, _, HVAC::FanOp::Cycling, PLR);
3285 : } else {
3286 0 : if (state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate > 0.0) {
3287 0 : ElecHeaterControl = 0.0;
3288 : }
3289 0 : HeatingCoils::SimulateHeatingCoilComponents(state,
3290 : fanCoil.HCoilName,
3291 : FirstHVACIteration,
3292 0 : fanCoil.DesignHeatingCapacity * PartLoad * ElecHeaterControl,
3293 0 : fanCoil.HCoilName_Index,
3294 : _,
3295 0 : false,
3296 0 : HVAC::FanOp::Continuous,
3297 : PartLoad);
3298 : }
3299 :
3300 3898026 : } else if (fanCoil.CapCtrlMeth_Num == CCM::MultiSpeedFan) {
3301 191958 : if (fanCoil.fanType != HVAC::FanType::SystemModel) {
3302 0 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, FirstHVACIteration, state.dataFanCoilUnits->FanFlowRatio);
3303 : } else {
3304 : // FanFlowRatio needs to be accurate here for new fan model
3305 191958 : Real64 ActFanFlowRatio = state.dataFanCoilUnits->FanFlowRatio * PartLoad;
3306 191958 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, FirstHVACIteration, _, _, ActFanFlowRatio);
3307 : }
3308 191958 : if (fanCoil.CCoilType_Num == CCoil::HXAssist) {
3309 0 : HVACHXAssistedCoolingCoil::SimHXAssistedCoolingCoil(
3310 0 : state, fanCoil.CCoilName, FirstHVACIteration, HVAC::CompressorOp::On, 0.0, fanCoil.CCoilName_Index, HVAC::FanOp::Continuous);
3311 : } else {
3312 767832 : WaterCoils::SimulateWaterCoilComponents(
3313 575874 : state, fanCoil.CCoilName, FirstHVACIteration, fanCoil.CCoilName_Index, _, HVAC::FanOp::Cycling, PLR);
3314 : }
3315 191958 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3316 767832 : WaterCoils::SimulateWaterCoilComponents(
3317 575874 : state, fanCoil.HCoilName, FirstHVACIteration, fanCoil.HCoilName_Index, _, HVAC::FanOp::Cycling, PLR);
3318 : } else {
3319 0 : if (state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate > 0.0) {
3320 0 : ElecHeaterControl = 0.0;
3321 : }
3322 0 : Real64 QZnReq = 0.0;
3323 0 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3324 0 : QZnReq = fanCoil.DesignHeatingCapacity * state.dataFanCoilUnits->FanFlowRatio * eHeatCoilCyclingR * ElecHeaterControl;
3325 : } else {
3326 : // proportionally reduce the full flow capacity based on fan flow fraction
3327 0 : QZnReq = fanCoil.DesignHeatingCapacity * state.dataFanCoilUnits->FanFlowRatio * PartLoad * eHeatCoilCyclingR * ElecHeaterControl;
3328 : }
3329 0 : HeatingCoils::SimulateHeatingCoilComponents(state,
3330 : fanCoil.HCoilName,
3331 : FirstHVACIteration,
3332 : QZnReq,
3333 0 : fanCoil.HCoilName_Index,
3334 : _,
3335 0 : false,
3336 0 : fanCoil.fanOp, // fanCoil.FanOpMode, // FanOp::Continuous, FanOp::Cycling
3337 : PartLoad);
3338 : }
3339 : } else { // capacity control method is VariableFanVariableFlow, VariableFanConstantFlow, or ASHRAE90.1
3340 :
3341 : // calculate fan speed ratio for Fan:OnOff or Fan:SystemModel (not used for other fan types). Only used in fan:OnOff model if performance
3342 : // curves are present.
3343 3706068 : FanSpeedRatio = state.dataLoopNodes->Node(InletNode).MassFlowRate / (fanCoil.FanAirVolFlow * state.dataEnvrn->StdRhoAir);
3344 :
3345 : // Constant fan and variable flow calculation AND variable fan
3346 :
3347 3706068 : state.dataFans->fans(fanCoil.FanIndex)->simulate(state, FirstHVACIteration, FanSpeedRatio, _, FanSpeedRatio);
3348 :
3349 3706068 : if (fanCoil.CCoilType_Num == CCoil::HXAssist) {
3350 0 : HVACHXAssistedCoolingCoil::SimHXAssistedCoolingCoil(
3351 0 : state, fanCoil.CCoilName, FirstHVACIteration, HVAC::CompressorOp::On, 0.0, fanCoil.CCoilName_Index, HVAC::FanOp::Continuous);
3352 : } else {
3353 3706068 : WaterCoils::SimulateWaterCoilComponents(state, fanCoil.CCoilName, FirstHVACIteration, fanCoil.CCoilName_Index);
3354 : }
3355 3706068 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3356 3651953 : WaterCoils::SimulateWaterCoilComponents(state, fanCoil.HCoilName, FirstHVACIteration, fanCoil.HCoilName_Index);
3357 : } else {
3358 54115 : if (state.dataLoopNodes->Node(fanCoil.CoolCoilFluidInletNode).MassFlowRate > 0.0) {
3359 29762 : ElecHeaterControl = 0.0;
3360 : }
3361 270575 : HeatingCoils::SimulateHeatingCoilComponents(state,
3362 : fanCoil.HCoilName,
3363 : FirstHVACIteration,
3364 108230 : fanCoil.DesignHeatingCapacity * PartLoad * ElecHeaterControl,
3365 54115 : fanCoil.HCoilName_Index,
3366 : _,
3367 108230 : false,
3368 108230 : HVAC::FanOp::Continuous,
3369 : PartLoad);
3370 : }
3371 : }
3372 :
3373 5420611 : if (fanCoil.ATMixerExists) {
3374 352428 : if (fanCoil.ATMixerType == HVAC::MixerType::SupplySide) {
3375 : // Now calculate the ATM mixer if it is on the supply side of the zone unit
3376 182887 : SingleDuct::SimATMixer(state, fanCoil.ATMixerName, FirstHVACIteration, fanCoil.ATMixerIndex);
3377 182887 : LoadMet = calcZoneSensibleOutput(state.dataLoopNodes->Node(state.dataFanCoilUnits->ATMixOutNode).MassFlowRate,
3378 182887 : state.dataLoopNodes->Node(state.dataFanCoilUnits->ATMixOutNode).Temp,
3379 182887 : state.dataLoopNodes->Node(state.dataFanCoilUnits->ZoneNode).Temp,
3380 182887 : state.dataLoopNodes->Node(state.dataFanCoilUnits->ZoneNode).HumRat);
3381 : } else {
3382 : // ATM Mixer on inlet side
3383 169541 : LoadMet = calcZoneSensibleOutput(AirMassFlow,
3384 169541 : state.dataLoopNodes->Node(OutletNode).Temp,
3385 169541 : state.dataLoopNodes->Node(state.dataFanCoilUnits->ZoneNode).Temp,
3386 169541 : state.dataLoopNodes->Node(state.dataFanCoilUnits->ZoneNode).HumRat);
3387 : }
3388 : } else {
3389 5068183 : LoadMet = calcZoneSensibleOutput(AirMassFlow,
3390 5068183 : state.dataLoopNodes->Node(OutletNode).Temp,
3391 5068183 : state.dataLoopNodes->Node(InletNode).Temp,
3392 5068183 : state.dataLoopNodes->Node(InletNode).HumRat);
3393 : }
3394 5420611 : }
3395 :
3396 12375 : void SimMultiStage4PipeFanCoil(EnergyPlusData &state,
3397 : int &FanCoilNum, // number of the current fan coil unit being simulated
3398 : int const ZoneNum, // number of zone being served
3399 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
3400 : Real64 &PowerMet // Sensible power supplied (W)
3401 : )
3402 : {
3403 :
3404 : // SUBROUTINE INFORMATION:
3405 : // AUTHOR Bereket Nigusse
3406 : // DATE WRITTEN July 2015
3407 :
3408 : // PURPOSE OF THIS SUBROUTINE:
3409 : // Manages multi-speed fancoil unit simulation;
3410 :
3411 : // METHODOLOGY EMPLOYED:
3412 : // Selects the appropriate fan speed for a given zone heating or cooling load
3413 : // and determines whether heating or cooling is required, then runs the hot
3414 : // or chilled water coils.
3415 :
3416 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3417 : Real64 mdot; // chilled or hot water flow rate through the water coils
3418 :
3419 12375 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilNum);
3420 12375 : auto &HeatingLoad = state.dataFanCoilUnits->HeatingLoad;
3421 12375 : auto &CoolingLoad = state.dataFanCoilUnits->CoolingLoad;
3422 :
3423 : // initialize local variables
3424 12375 : bool UnitOn = true; // TRUE if unit is on
3425 12375 : Real64 SpeedRatio = 0.0; // ratio between lower and higher fan speed
3426 12375 : Real64 PartLoadRatio = 0.0; // Part Load Ratio, fraction of time step fancoil is on
3427 12375 : Real64 QZnReq = 0.0; // heating or cooling needed by zone [watts]
3428 12375 : Real64 QUnitOut = 0.0; // heating or sens. cooling provided by fan coil unit [watts]
3429 12375 : Real64 QUnitOutMax = 0.0; // heating or sens. cooling provided by fan coil unit (running during an entire timestep)
3430 12375 : Real64 QUnitOutNoHC = 0.0; // unit output with no active heating or cooling [W]
3431 :
3432 12375 : int OutletNode = fanCoil.AirOutNode;
3433 12375 : int InletNode = fanCoil.AirInNode;
3434 12375 : Real64 AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
3435 :
3436 12375 : if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum) || AirMassFlow < HVAC::SmallMassFlow) {
3437 493 : UnitOn = false;
3438 : }
3439 :
3440 12375 : fanCoil.SpeedFanSel = 1;
3441 12375 : fanCoil.SpeedFanRatSel = fanCoil.LowSpeedRatio;
3442 12375 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3443 12375 : AirMassFlow = fanCoil.LowSpeedRatio * fanCoil.MaxAirMassFlow;
3444 12375 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3445 12375 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3446 12375 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3447 12375 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = AirMassFlow;
3448 :
3449 12375 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3450 12375 : mdot = 0.0;
3451 12375 : PlantUtilities::SetComponentFlowRate(
3452 12375 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
3453 : }
3454 12375 : mdot = 0.0;
3455 12375 : PlantUtilities::SetComponentFlowRate(
3456 12375 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
3457 : // no load output, requires setting eHeatCoilCyclingR = 0.0, for electric heating coils
3458 12375 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutNoHC, _, 0.0);
3459 :
3460 12375 : Real64 QCoilCoolSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToCoolSP;
3461 12375 : Real64 QCoilHeatSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP;
3462 12375 : state.dataFanCoilUnits->HeatingLoad = false;
3463 12375 : state.dataFanCoilUnits->CoolingLoad = false;
3464 :
3465 12375 : if (QCoilHeatSP > 0.0 && QCoilCoolSP > 0.0 && state.dataHeatBalFanSys->TempControlType(ZoneNum) != HVAC::SetptType::SingleCool) {
3466 6072 : QZnReq = QCoilHeatSP;
3467 6072 : HeatingLoad = true;
3468 6303 : } else if (QCoilHeatSP > 0.0 && QCoilCoolSP > 0.0 && state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::SetptType::SingleCool) {
3469 343 : QZnReq = 0.0;
3470 5960 : } else if (QCoilHeatSP < 0.0 && QCoilCoolSP < 0.0 && state.dataHeatBalFanSys->TempControlType(ZoneNum) != HVAC::SetptType::SingleHeat) {
3471 5813 : QZnReq = QCoilCoolSP;
3472 5813 : CoolingLoad = true;
3473 147 : } else if (QCoilHeatSP < 0.0 && QCoilCoolSP < 0.0 && state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::SetptType::SingleHeat) {
3474 147 : QZnReq = 0.0;
3475 0 : } else if (QCoilHeatSP <= 0.0 && QCoilCoolSP >= 0.0) {
3476 0 : QZnReq = 0.0;
3477 : }
3478 :
3479 : // Zone load calculation for constant fan systems, adopted from unitary system
3480 12375 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3481 8236 : switch (state.dataHeatBalFanSys->TempControlType(ZoneNum)) {
3482 4146 : case HVAC::SetptType::SingleHeat: {
3483 4146 : CoolingLoad = false;
3484 : // No heating load and constant fan pushes zone below heating set point
3485 4146 : if (QUnitOutNoHC < 0.0 && QCoilHeatSP < 0.0 && QUnitOutNoHC - QCoilHeatSP < -HVAC::SmallLoad) {
3486 16 : HeatingLoad = true;
3487 16 : CoolingLoad = false;
3488 16 : QZnReq = QCoilHeatSP;
3489 : }
3490 4146 : } break;
3491 4090 : case HVAC::SetptType::SingleCool: {
3492 4090 : HeatingLoad = false;
3493 : // No heating load and constant fan pushes zone above cooling set point
3494 4090 : if (QUnitOutNoHC > 0.0 && QCoilCoolSP > 0.0 && QUnitOutNoHC - QCoilCoolSP > HVAC::SmallLoad) {
3495 0 : HeatingLoad = false;
3496 0 : CoolingLoad = true;
3497 0 : QZnReq = QCoilCoolSP;
3498 : }
3499 4090 : } break;
3500 0 : case HVAC::SetptType::SingleHeatCool: {
3501 : // zone temp above cooling and heating set point temps
3502 0 : if (QCoilHeatSP < 0.0 && QCoilCoolSP < 0.0) {
3503 : // zone pushed below heating set point
3504 0 : if (QUnitOutNoHC < 0.0 && QCoilHeatSP - QUnitOutNoHC > HVAC::SmallLoad) {
3505 0 : HeatingLoad = true;
3506 0 : CoolingLoad = false;
3507 0 : QZnReq = QCoilHeatSP;
3508 : }
3509 : // zone temp below heating set point temp
3510 0 : } else if (QCoilHeatSP > 0.0 && QCoilCoolSP > 0.0) {
3511 : // zone pushed above cooling set point
3512 0 : if (QUnitOutNoHC > 0.0 && QCoilCoolSP - QUnitOutNoHC > HVAC::SmallLoad) {
3513 0 : HeatingLoad = false;
3514 0 : CoolingLoad = true;
3515 0 : QZnReq = QCoilCoolSP;
3516 : }
3517 : }
3518 0 : } break;
3519 0 : case HVAC::SetptType::DualHeatCool: {
3520 : // zone temp above cooling and heating set point temps
3521 0 : if (QCoilHeatSP < 0.0 && QCoilCoolSP < 0.0) {
3522 : // zone pushed into deadband
3523 0 : if (QUnitOutNoHC < 0.0 && QCoilCoolSP - QUnitOutNoHC > HVAC::SmallLoad) {
3524 0 : HeatingLoad = false;
3525 0 : CoolingLoad = false;
3526 0 : QZnReq = 0.0;
3527 : }
3528 : // zone pushed below heating set point
3529 0 : if (QUnitOutNoHC < 0.0 && QCoilHeatSP - QUnitOutNoHC > HVAC::SmallLoad) {
3530 0 : HeatingLoad = true;
3531 0 : CoolingLoad = false;
3532 0 : QZnReq = QCoilHeatSP;
3533 : }
3534 : // zone temp below heating set point temp
3535 0 : } else if (QCoilHeatSP > 0.0 && QCoilCoolSP > 0.0) {
3536 : // zone pushed into deadband
3537 0 : if (QUnitOutNoHC > 0.0 && QUnitOutNoHC - QCoilHeatSP > HVAC::SmallLoad) {
3538 0 : HeatingLoad = false;
3539 0 : CoolingLoad = false;
3540 0 : QZnReq = 0.0;
3541 : }
3542 : // zone pushed above cooling set point
3543 0 : if (QUnitOutNoHC > 0.0 && QUnitOutNoHC - QCoilCoolSP > HVAC::SmallLoad) {
3544 0 : HeatingLoad = false;
3545 0 : CoolingLoad = true;
3546 0 : QZnReq = QCoilCoolSP;
3547 : }
3548 : // zone temp between set point temps
3549 0 : } else if (QCoilHeatSP < 0.0 && QCoilCoolSP > 0.0) {
3550 : // zone pushed below heating set point
3551 0 : if (QUnitOutNoHC < 0.0 && QUnitOutNoHC - QCoilHeatSP < -HVAC::SmallLoad) {
3552 0 : HeatingLoad = true;
3553 0 : CoolingLoad = false;
3554 0 : QZnReq = QCoilHeatSP;
3555 : // zone pushed above cooling set point
3556 0 : } else if (QUnitOutNoHC > 0.0 && QUnitOutNoHC - QCoilCoolSP > HVAC::SmallLoad) {
3557 0 : HeatingLoad = false;
3558 0 : CoolingLoad = true;
3559 0 : QZnReq = QCoilCoolSP;
3560 : }
3561 : }
3562 0 : } break;
3563 0 : default:
3564 0 : break;
3565 : }
3566 : // IF small loads to meet, just shut down unit
3567 8236 : if (std::abs(QZnReq) < FanCoilUnits::Small5WLoad) {
3568 290 : QZnReq = 0.0;
3569 290 : CoolingLoad = false;
3570 290 : HeatingLoad = false;
3571 : }
3572 : }
3573 :
3574 12375 : if (UnitOn && QZnReq < (-1.0 * FanCoilUnits::Small5WLoad) && CoolingLoad) {
3575 5810 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3576 5810 : mdot = 0.0;
3577 5810 : PlantUtilities::SetComponentFlowRate(
3578 5810 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
3579 : }
3580 5810 : mdot = fanCoil.MaxCoolCoilFluidFlow;
3581 5810 : PlantUtilities::SetComponentFlowRate(
3582 5810 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
3583 : // select fan speed
3584 5810 : fanCoil.SpeedFanSel = 1;
3585 5810 : fanCoil.SpeedFanRatSel = fanCoil.LowSpeedRatio;
3586 5810 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3587 5810 : AirMassFlow = fanCoil.LowSpeedRatio * fanCoil.MaxAirMassFlow;
3588 5810 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3589 5810 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3590 5810 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3591 5810 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = AirMassFlow;
3592 5810 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMax);
3593 5810 : if (std::abs(QUnitOutMax) < std::abs(QZnReq)) {
3594 3070 : fanCoil.SpeedFanSel = 2;
3595 3070 : fanCoil.SpeedFanRatSel = fanCoil.MedSpeedRatio;
3596 3070 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3597 3070 : AirMassFlow = fanCoil.MedSpeedRatio * fanCoil.MaxAirMassFlow;
3598 3070 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3599 3070 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3600 3070 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3601 3070 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = fanCoil.LowSpeedRatio * fanCoil.MaxAirMassFlow;
3602 3070 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMax);
3603 : }
3604 5810 : if (std::abs(QUnitOutMax) < std::abs(QZnReq)) {
3605 1626 : fanCoil.SpeedFanSel = 3;
3606 1626 : fanCoil.SpeedFanRatSel = 1.0;
3607 1626 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3608 1626 : AirMassFlow = fanCoil.MaxAirMassFlow;
3609 1626 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3610 1626 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3611 1626 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3612 1626 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = fanCoil.MedSpeedRatio * fanCoil.MaxAirMassFlow;
3613 : }
3614 5810 : CalcMultiStage4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QZnReq, SpeedRatio, PartLoadRatio, QUnitOut);
3615 :
3616 6565 : } else if (UnitOn && QZnReq > FanCoilUnits::Small5WLoad && HeatingLoad) {
3617 :
3618 6072 : mdot = 0.0;
3619 6072 : PlantUtilities::SetComponentFlowRate(
3620 6072 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
3621 :
3622 6072 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3623 6072 : mdot = fanCoil.MaxHeatCoilFluidFlow;
3624 6072 : PlantUtilities::SetComponentFlowRate(
3625 6072 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
3626 : }
3627 : // select fan speed
3628 6072 : fanCoil.SpeedFanSel = 1;
3629 6072 : fanCoil.SpeedFanRatSel = fanCoil.LowSpeedRatio;
3630 6072 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3631 6072 : AirMassFlow = fanCoil.LowSpeedRatio * fanCoil.MaxAirMassFlow;
3632 6072 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3633 6072 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3634 6072 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3635 6072 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = AirMassFlow;
3636 6072 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMax);
3637 6072 : if (std::abs(QUnitOutMax) < std::abs(QZnReq)) {
3638 3212 : fanCoil.SpeedFanSel = 2;
3639 3212 : fanCoil.SpeedFanRatSel = fanCoil.MedSpeedRatio;
3640 3212 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3641 3212 : AirMassFlow = fanCoil.MedSpeedRatio * fanCoil.MaxAirMassFlow;
3642 3212 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3643 3212 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3644 3212 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3645 3212 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = fanCoil.LowSpeedRatio * fanCoil.MaxAirMassFlow;
3646 3212 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMax);
3647 : }
3648 6072 : if (std::abs(QUnitOutMax) < std::abs(QZnReq)) {
3649 0 : fanCoil.SpeedFanSel = 3;
3650 0 : fanCoil.SpeedFanRatSel = 1.0;
3651 0 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3652 0 : AirMassFlow = fanCoil.MaxAirMassFlow;
3653 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3654 0 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3655 0 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3656 0 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = fanCoil.MedSpeedRatio * fanCoil.MaxAirMassFlow;
3657 : }
3658 :
3659 6072 : CalcMultiStage4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QZnReq, SpeedRatio, PartLoadRatio, QUnitOut);
3660 :
3661 : } else {
3662 : // SpeedRatio = 0.0;
3663 493 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3664 308 : PartLoadRatio = 1.0;
3665 308 : fanCoil.SpeedFanSel = 1;
3666 308 : fanCoil.SpeedFanRatSel = fanCoil.LowSpeedRatio;
3667 308 : state.dataFanCoilUnits->FanFlowRatio = fanCoil.SpeedFanRatSel;
3668 308 : AirMassFlow = fanCoil.LowSpeedRatio * fanCoil.MaxAirMassFlow;
3669 308 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3670 308 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3671 308 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3672 308 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = AirMassFlow;
3673 : } else {
3674 185 : PartLoadRatio = 0.0;
3675 185 : AirMassFlow = 0.0;
3676 185 : state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
3677 185 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = AirMassFlow;
3678 185 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
3679 185 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = AirMassFlow;
3680 185 : state.dataLoopNodes->Node(InletNode).MassFlowRate = 0.0;
3681 185 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
3682 185 : fanCoil.SpeedFanSel = 0;
3683 185 : state.dataFanCoilUnits->FanFlowRatio = 0.0;
3684 : }
3685 :
3686 493 : mdot = 0.0;
3687 493 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3688 493 : PlantUtilities::SetComponentFlowRate(
3689 493 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
3690 : }
3691 493 : PlantUtilities::SetComponentFlowRate(
3692 493 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
3693 : // No load output, eHeatCoilCyclingR = 0.0 for electric heating coil
3694 493 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, PartLoadRatio, 0.0);
3695 : }
3696 : // output variable
3697 12375 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
3698 12375 : fanCoil.PLR = PartLoadRatio;
3699 12375 : fanCoil.SpeedRatio = SpeedRatio;
3700 12375 : PowerMet = QUnitOut;
3701 12375 : }
3702 :
3703 11882 : void CalcMultiStage4PipeFanCoil(EnergyPlusData &state,
3704 : int &FanCoilNum, // number of the current fan coil unit being simulated
3705 : int const ZoneNum, // number of zone being served
3706 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
3707 : Real64 const QZnReq, // current zone cooling or heating load
3708 : Real64 &SpeedRatio, // fan coil speed ratio
3709 : Real64 &PartLoadRatio, // fan coil part load ratio
3710 : Real64 &PowerMet // Sensible power supplied (W)
3711 : )
3712 : {
3713 :
3714 : // SUBROUTINE INFORMATION:
3715 : // AUTHOR Bereket Nigusse
3716 : // DATE WRITTEN July 2015
3717 :
3718 : // PURPOSE OF THIS SUBROUTINE:
3719 : // Simulate a multi-stage fan 4 pipe fan coil unit; adjust its output to
3720 : // match the remaining zone load.
3721 :
3722 : // METHODOLOGY EMPLOYED:
3723 : // If this unit is on, calculated the speed ratio when cycling between
3724 : // consecutive fan speeds. The hot or chilled water flows either at
3725 : // maximum or zero. The water flow rate is set to zero if there is no
3726 : // load.
3727 :
3728 : // SUBROUTINE PARAMETER DEFINITIONS:
3729 11882 : constexpr int MaxIterCycl(100);
3730 :
3731 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3732 : Real64 QUnitOutMaxHS; // higher fan speed output
3733 : Real64 QUnitOutMaxLS; // lower fan speed output
3734 : Real64 HighSpeedRatio; // fan flow ratio at low speed
3735 : Real64 LowSpeedRatio; // fan flow ratio at low speed
3736 : Real64 DelPLR;
3737 : int SolFlag; // return flag from RegulaFalsi for sensible load
3738 :
3739 11882 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilNum);
3740 :
3741 : // initialize local variables
3742 11882 : Real64 mdot = 0.0; // chilled or hot water flow rate through the water coils
3743 11882 : Real64 PLR = 1.0; // Part Load Ratio, fraction of time step fancoil is on
3744 11882 : Real64 SRatio = 0.0; // capacity speed ratio of the for multi-stage fan fancoil unit
3745 11882 : Real64 QUnitOut = 0.0; // heating or sens. cooling provided by fan coil unit [watts]
3746 11882 : Real64 QUnitOutMax = 0.0; // max heating or sens. cooling provided by fan coil unit [watts]
3747 11882 : Real64 ControlOffset = 0.0; // tolerance for output control
3748 11882 : Real64 FanElecPowerHS = 0.0; // fan electric power calculated at (fan) higher speed
3749 11882 : Real64 FanElecPowerLS = 0.0; // fan electric power calculated at (fan) lower speed
3750 11882 : Real64 AirMassFlowAvg = 0.0; // supply air flow rate weighted by speed ratio
3751 11882 : Real64 AirMassFlowLow = 0.0; // supply air flow rate at lower speed
3752 11882 : Real64 AirMassFlowHigh = 0.0; // supply air flow rate at higher speed
3753 11882 : Real64 AbsError = 2.0 * FanCoilUnits::Small5WLoad; // Absolute error between QZnReq and QUnitOut [W] !FB
3754 11882 : Real64 Error = 1.0; // Error between QZnReq and QUnitOut
3755 11882 : Real64 Relax = 1.0;
3756 11882 : int Iter = 0; // iteration counter
3757 :
3758 11882 : auto &inletNode = state.dataLoopNodes->Node(fanCoil.AirInNode);
3759 :
3760 11882 : if (QZnReq < (-1.0 * FanCoilUnits::Small5WLoad) && state.dataFanCoilUnits->CoolingLoad) {
3761 5810 : ControlOffset = fanCoil.ColdControlOffset;
3762 5810 : if (fanCoil.SpeedFanSel == 1) {
3763 2740 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMax);
3764 2740 : PLR = std::abs(QZnReq / QUnitOutMax);
3765 2740 : if (PLR > 1.0) {
3766 0 : PLR = 1.0;
3767 : }
3768 : // adjust the PLR to meet the cooling load by calling Calc4PipeFanCoil repeatedly
3769 87954 : while (std::abs(Error) > ControlOffset && std::abs(AbsError) > FanCoilUnits::Small5WLoad && Iter < MaxIterCycl && PLR != 1.0) {
3770 85214 : inletNode.MassFlowRateMinAvail = inletNode.MassFlowRate;
3771 85214 : mdot = PLR * fanCoil.MaxCoolCoilFluidFlow;
3772 85214 : PlantUtilities::SetComponentFlowRate(
3773 85214 : state, mdot, fanCoil.CoolCoilFluidInletNode, fanCoil.CoolCoilFluidOutletNodeNum, fanCoil.CoolCoilPlantLoc);
3774 85214 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3775 82946 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
3776 : } else {
3777 2268 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, PLR);
3778 : }
3779 85214 : Error = (QZnReq - QUnitOut) / QZnReq;
3780 85214 : AbsError = QZnReq - QUnitOut;
3781 85214 : DelPLR = (QZnReq - QUnitOut) / QUnitOutMax;
3782 85214 : PLR += Relax * DelPLR;
3783 85214 : PLR = max(0.0, min(1.0, PLR));
3784 85214 : ++Iter;
3785 85214 : if (Iter == 32) {
3786 1302 : Relax = 0.5;
3787 : }
3788 85214 : if (Iter == 65) {
3789 870 : Relax = 0.25;
3790 : }
3791 85214 : if (Iter > 70 && PLR == 0.0 && DelPLR < 0.0) {
3792 0 : Error = 0.0;
3793 : }
3794 : }
3795 2740 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3796 1870 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
3797 : } else {
3798 870 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, PLR);
3799 : }
3800 : // warning if not converged
3801 2740 : if (Iter > (MaxIterCycl - 1)) {
3802 0 : if (fanCoil.MaxIterIndexC == 0) {
3803 0 : ShowWarningMessage(state,
3804 0 : format("ZoneHVAC:FourPipeFanCoil=\"{}\" -- Exceeded max iterations while adjusting cycling fan sensible "
3805 : "runtime to meet the zone load within the cooling convergence tolerance.",
3806 0 : fanCoil.Name));
3807 0 : ShowContinueErrorTimeStamp(state, format("Iterations={}", MaxIterCycl));
3808 : }
3809 0 : ShowRecurringWarningErrorAtEnd(state,
3810 0 : "ZoneHVAC:FourPipeFanCoil=\"" + fanCoil.Name +
3811 : "\" -- Exceeded max iterations error (sensible runtime) continues...",
3812 0 : fanCoil.MaxIterIndexC);
3813 : }
3814 :
3815 : } else {
3816 3070 : if (fanCoil.SpeedFanSel == 2) {
3817 1444 : HighSpeedRatio = fanCoil.MedSpeedRatio;
3818 1444 : LowSpeedRatio = fanCoil.LowSpeedRatio;
3819 : } else {
3820 1626 : HighSpeedRatio = 1;
3821 1626 : LowSpeedRatio = fanCoil.MedSpeedRatio;
3822 : }
3823 : // get capacity at lower speed
3824 3070 : fanCoil.SpeedFanRatSel = LowSpeedRatio;
3825 3070 : fanCoil.SpeedFanSel = fanCoil.SpeedFanSel - 1;
3826 3070 : AirMassFlowLow = LowSpeedRatio * fanCoil.MaxAirMassFlow;
3827 3070 : inletNode.MassFlowRate = AirMassFlowLow;
3828 3070 : inletNode.MassFlowRateMax = AirMassFlowLow;
3829 3070 : inletNode.MassFlowRateMaxAvail = AirMassFlowLow;
3830 3070 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
3831 3070 : state.dataFanCoilUnits->FanFlowRatio = LowSpeedRatio;
3832 3070 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMaxLS);
3833 3070 : FanElecPowerLS = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
3834 : // get capacity at higher speed
3835 3070 : fanCoil.SpeedFanRatSel = HighSpeedRatio;
3836 3070 : fanCoil.SpeedFanSel = fanCoil.SpeedFanSel + 1;
3837 3070 : AirMassFlowHigh = HighSpeedRatio * fanCoil.MaxAirMassFlow;
3838 3070 : inletNode.MassFlowRate = AirMassFlowHigh;
3839 3070 : inletNode.MassFlowRateMax = AirMassFlowHigh;
3840 3070 : inletNode.MassFlowRateMaxAvail = AirMassFlowHigh;
3841 3070 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
3842 3070 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio;
3843 3070 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMaxHS);
3844 3070 : FanElecPowerHS = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
3845 : // calc speed ratio
3846 3070 : if (std::abs(QZnReq) > std::abs(QUnitOutMaxHS)) {
3847 0 : SRatio = 1.0;
3848 0 : AirMassFlowAvg = AirMassFlowHigh;
3849 0 : inletNode.MassFlowRate = AirMassFlowHigh;
3850 0 : inletNode.MassFlowRateMax = AirMassFlowHigh;
3851 0 : inletNode.MassFlowRateMaxAvail = AirMassFlowHigh;
3852 0 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
3853 0 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio;
3854 0 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
3855 : } else {
3856 3070 : SRatio = std::abs((QZnReq - QUnitOutMaxLS) / (QUnitOutMaxHS - QUnitOutMaxLS));
3857 3070 : if (SRatio > 1.0) {
3858 0 : SRatio = 1.0;
3859 : }
3860 3070 : AirMassFlowAvg = AirMassFlowHigh * SRatio + AirMassFlowLow * (1.0 - SRatio);
3861 3070 : inletNode.MassFlowRate = AirMassFlowAvg;
3862 3070 : inletNode.MassFlowRateMax = AirMassFlowAvg;
3863 3070 : inletNode.MassFlowRateMaxAvail = AirMassFlowAvg;
3864 3070 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
3865 3070 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio * SRatio + LowSpeedRatio * (1.0 - SRatio);
3866 3070 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
3867 : // adjust the PLR to meet the cooling load by calling Calc4PipeFanCoil repeatedly
3868 8960 : while (std::abs(Error) > ControlOffset && std::abs(AbsError) > FanCoilUnits::Small5WLoad && Iter < MaxIterCycl && SRatio != 1.0) {
3869 5890 : AirMassFlowAvg = AirMassFlowHigh * SRatio + AirMassFlowLow * (1.0 - SRatio);
3870 5890 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio * SRatio + LowSpeedRatio * (1.0 - SRatio);
3871 5890 : inletNode.MassFlowRate = AirMassFlowAvg;
3872 5890 : inletNode.MassFlowRateMax = AirMassFlowAvg;
3873 5890 : inletNode.MassFlowRateMaxAvail = AirMassFlowAvg;
3874 5890 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
3875 5890 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
3876 5890 : Error = (QZnReq - QUnitOut) / QZnReq;
3877 5890 : AbsError = QZnReq - QUnitOut;
3878 5890 : DelPLR = (QZnReq - QUnitOut) / (QUnitOutMaxHS - QUnitOutMaxLS);
3879 5890 : SRatio += Relax * DelPLR;
3880 5890 : SRatio = max(0.0, min(1.0, SRatio));
3881 5890 : ++Iter;
3882 5890 : if (Iter == 32) {
3883 0 : Relax = 0.5;
3884 : }
3885 5890 : if (Iter == 65) {
3886 0 : Relax = 0.25;
3887 : }
3888 5890 : if (Iter > 70 && SRatio == 0.0 && DelPLR < 0.0) {
3889 0 : Error = 0.0;
3890 : }
3891 : }
3892 : }
3893 : }
3894 6072 : } else if (QZnReq > FanCoilUnits::Small5WLoad && state.dataFanCoilUnits->HeatingLoad) {
3895 6072 : ControlOffset = fanCoil.HotControlOffset;
3896 6072 : if (fanCoil.SpeedFanSel == 1) {
3897 2860 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMax);
3898 2860 : PLR = std::abs(QZnReq / QUnitOutMax);
3899 2860 : if (PLR > 1.0) {
3900 0 : PLR = 1.0;
3901 : }
3902 2860 : if (fanCoil.HCoilType_Num == HCoil::Water) {
3903 : // adjust the PLR to meet the heating load by calling Calc4PipeFanCoil repeatedly
3904 32952 : while (std::abs(Error) > ControlOffset && std::abs(AbsError) > FanCoilUnits::Small5WLoad && Iter < MaxIterCycl && PLR != 1.0) {
3905 30092 : inletNode.MassFlowRateMinAvail = inletNode.MassFlowRate;
3906 30092 : mdot = PLR * fanCoil.MaxHeatCoilFluidFlow;
3907 30092 : PlantUtilities::SetComponentFlowRate(
3908 30092 : state, mdot, fanCoil.HeatCoilFluidInletNode, fanCoil.HeatCoilFluidOutletNodeNum, fanCoil.HeatCoilPlantLoc);
3909 30092 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3910 28394 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
3911 : } else {
3912 1698 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, PLR);
3913 : }
3914 30092 : Error = (QZnReq - QUnitOut) / QZnReq;
3915 30092 : AbsError = QZnReq - QUnitOut;
3916 30092 : DelPLR = (QZnReq - QUnitOut) / QUnitOutMax;
3917 30092 : PLR += Relax * DelPLR;
3918 30092 : PLR = max(0.0, min(1.0, PLR));
3919 30092 : ++Iter;
3920 30092 : if (Iter == 32) {
3921 124 : Relax = 0.5;
3922 : }
3923 30092 : if (Iter == 65) {
3924 26 : Relax = 0.25;
3925 : }
3926 30092 : if (Iter > 70 && PLR == 0.0 && DelPLR < 0.0) {
3927 1 : Error = 0.0; // exit loop if PLR = 0
3928 : }
3929 : }
3930 2860 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3931 2572 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
3932 : } else {
3933 288 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, PLR);
3934 : }
3935 : // warning if not converged
3936 2860 : if (Iter > (MaxIterCycl - 1)) {
3937 0 : if (fanCoil.MaxIterIndexH == 0) {
3938 0 : ShowWarningMessage(state,
3939 0 : format("ZoneHVAC:FourPipeFanCoil=\"{}\" -- Exceeded max iterations while adjusting cycling fan "
3940 : "sensible runtime to meet the zone load within the heating convergence tolerance.",
3941 0 : fanCoil.Name));
3942 0 : ShowContinueErrorTimeStamp(state, format("Iterations={}", MaxIterCycl));
3943 : }
3944 0 : ShowRecurringWarningErrorAtEnd(state,
3945 0 : "ZoneHVAC:FourPipeFanCoil=\"" + fanCoil.Name +
3946 : "\" -- Exceeded max iterations error (sensible runtime) continues...",
3947 0 : fanCoil.MaxIterIndexH);
3948 : }
3949 : } else {
3950 0 : Real64 eHeatCoilPLR = PLR;
3951 : // electric heating coil
3952 0 : if (QUnitOutMax > QZnReq) {
3953 : // heating coil output is larger than required, mudulate the electric heating coil output to meet the load
3954 0 : inletNode.MassFlowRateMinAvail = inletNode.MassFlowRate;
3955 :
3956 0 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
3957 0 : auto f = [&state, FanCoilNum, FirstHVACIteration, ZoneNum, QZnReq](Real64 const CyclingR) {
3958 0 : return CalcFanCoilHeatCoilPLRResidual(state, CyclingR, FanCoilNum, FirstHVACIteration, ZoneNum, QZnReq);
3959 0 : };
3960 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, eHeatCoilPLR, f, 0.0, 1.0);
3961 : } else {
3962 0 : auto f = [&state, FirstHVACIteration, FanCoilNum, ZoneNum, QZnReq](Real64 const PartLoadRatio) {
3963 0 : return CalcFanCoilLoadResidual(state, FanCoilNum, FirstHVACIteration, ZoneNum, QZnReq, PartLoadRatio);
3964 0 : };
3965 0 : General::SolveRoot(state, 0.001, MaxIterCycl, SolFlag, eHeatCoilPLR, f, 0.0, 1.0);
3966 : }
3967 0 : if (SolFlag == -1) {
3968 0 : ++fanCoil.ConvgErrCountH;
3969 0 : if (fanCoil.ConvgErrCountH < 2) {
3970 0 : ShowWarningError(state, format("Electric heating coil control failed in fan coil unit {}", fanCoil.Name));
3971 0 : ShowContinueError(state, " Iteration limit exceeded in calculating electric heating coil capacity modulation ");
3972 0 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, _, eHeatCoilPLR);
3973 0 : ShowContinueErrorTimeStamp(state, format("Load Request = {}, Final Capacity = {}", QZnReq, QUnitOut));
3974 0 : ShowContinueErrorTimeStamp(
3975 0 : state, format("Electric heating coil part load ratio used during last iterations = {}", eHeatCoilPLR));
3976 : } else {
3977 0 : ShowRecurringWarningErrorAtEnd(
3978 0 : state, "Electric heating coil Iteration limit exceeded in fan coil unit " + fanCoil.Name, fanCoil.MaxIterIndexH);
3979 : }
3980 0 : } else if (SolFlag == -2) {
3981 0 : ++fanCoil.LimitErrCountH;
3982 0 : if (fanCoil.LimitErrCountH < 2) {
3983 0 : ShowWarningError(state,
3984 0 : format("Part load ratio electric heating coil control failed in fan coil unit {}", fanCoil.Name));
3985 0 : ShowContinueError(state, " Bad par load ratio limits");
3986 0 : ShowContinueErrorTimeStamp(state, "..Par load ratio set to 0");
3987 : } else {
3988 0 : ShowRecurringWarningErrorAtEnd(state,
3989 0 : "Part load ratio electric heating coil control failed in fan coil unit " +
3990 0 : fanCoil.Name,
3991 0 : fanCoil.BadMassFlowLimIndexH);
3992 : }
3993 : }
3994 : } else {
3995 0 : eHeatCoilPLR = 1.0;
3996 : }
3997 0 : PLR = eHeatCoilPLR;
3998 : // at the end calculate output
3999 0 : if (fanCoil.fanOp == HVAC::FanOp::Continuous) {
4000 0 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, _, eHeatCoilPLR);
4001 : } else {
4002 0 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, PLR);
4003 : }
4004 : }
4005 :
4006 : } else {
4007 3212 : if (fanCoil.SpeedFanSel == 2) {
4008 3212 : HighSpeedRatio = fanCoil.MedSpeedRatio;
4009 3212 : LowSpeedRatio = fanCoil.LowSpeedRatio;
4010 : } else {
4011 0 : HighSpeedRatio = 1;
4012 0 : LowSpeedRatio = fanCoil.MedSpeedRatio;
4013 : }
4014 : // get capacity at lower speed ratio
4015 3212 : fanCoil.SpeedFanRatSel = LowSpeedRatio;
4016 3212 : fanCoil.SpeedFanSel = fanCoil.SpeedFanSel - 1;
4017 3212 : AirMassFlowLow = LowSpeedRatio * fanCoil.MaxAirMassFlow;
4018 3212 : inletNode.MassFlowRate = AirMassFlowLow;
4019 3212 : inletNode.MassFlowRateMax = AirMassFlowLow;
4020 3212 : inletNode.MassFlowRateMaxAvail = AirMassFlowLow;
4021 3212 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
4022 3212 : state.dataFanCoilUnits->FanFlowRatio = LowSpeedRatio;
4023 3212 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMaxLS);
4024 3212 : FanElecPowerLS = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
4025 : // get capacity at higher speed
4026 3212 : fanCoil.SpeedFanRatSel = HighSpeedRatio;
4027 3212 : fanCoil.SpeedFanSel = fanCoil.SpeedFanSel + 1;
4028 3212 : AirMassFlowHigh = HighSpeedRatio * fanCoil.MaxAirMassFlow;
4029 3212 : inletNode.MassFlowRate = AirMassFlowHigh;
4030 3212 : inletNode.MassFlowRateMax = AirMassFlowHigh;
4031 3212 : inletNode.MassFlowRateMaxAvail = AirMassFlowHigh;
4032 3212 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
4033 3212 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio;
4034 3212 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOutMaxHS);
4035 3212 : FanElecPowerHS = state.dataFans->fans(fanCoil.FanIndex)->totalPower;
4036 : // calc speed ratio
4037 3212 : if (std::abs(QZnReq) > std::abs(QUnitOutMaxHS)) {
4038 0 : SRatio = 1.0;
4039 0 : AirMassFlowAvg = AirMassFlowHigh;
4040 0 : inletNode.MassFlowRate = AirMassFlowAvg;
4041 0 : inletNode.MassFlowRateMax = AirMassFlowAvg;
4042 0 : inletNode.MassFlowRateMaxAvail = AirMassFlowAvg;
4043 0 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
4044 0 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio;
4045 0 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
4046 : } else {
4047 3212 : SRatio = std::abs((QZnReq - QUnitOutMaxLS) / (QUnitOutMaxHS - QUnitOutMaxLS));
4048 3212 : if (SRatio > 1.0) {
4049 0 : SRatio = 1.0;
4050 : }
4051 3212 : AirMassFlowAvg = AirMassFlowHigh * SRatio + AirMassFlowLow * (1.0 - SRatio);
4052 3212 : inletNode.MassFlowRate = AirMassFlowAvg;
4053 3212 : inletNode.MassFlowRateMax = AirMassFlowAvg;
4054 3212 : inletNode.MassFlowRateMaxAvail = AirMassFlowAvg;
4055 3212 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
4056 3212 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio * SRatio + LowSpeedRatio * (1.0 - SRatio);
4057 3212 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
4058 3212 : ControlOffset = fanCoil.HotControlOffset;
4059 : // adjust the PLR to meet the heating load calling Calc4PipeFanCoil repeatedly
4060 12896 : while (std::abs(Error) > ControlOffset && std::abs(AbsError) > FanCoilUnits::Small5WLoad && Iter < MaxIterCycl && SRatio != 1.0) {
4061 9684 : AirMassFlowAvg = AirMassFlowHigh * SRatio + AirMassFlowLow * (1.0 - SRatio);
4062 9684 : inletNode.MassFlowRate = AirMassFlowAvg;
4063 9684 : inletNode.MassFlowRateMax = AirMassFlowAvg;
4064 9684 : inletNode.MassFlowRateMaxAvail = AirMassFlowAvg;
4065 9684 : inletNode.MassFlowRateMinAvail = AirMassFlowLow;
4066 9684 : state.dataFanCoilUnits->FanFlowRatio = HighSpeedRatio * SRatio + LowSpeedRatio * (1.0 - SRatio);
4067 9684 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut);
4068 9684 : Error = (QZnReq - QUnitOut) / QZnReq;
4069 9684 : AbsError = QZnReq - QUnitOut;
4070 9684 : DelPLR = (QZnReq - QUnitOut) / (QUnitOutMaxHS - QUnitOutMaxLS);
4071 9684 : SRatio += Relax * DelPLR;
4072 9684 : SRatio = max(0.0, min(1.0, SRatio));
4073 9684 : ++Iter;
4074 9684 : if (Iter == 32) {
4075 0 : Relax = 0.5;
4076 : }
4077 9684 : if (Iter == 65) {
4078 0 : Relax = 0.25;
4079 : }
4080 9684 : if (Iter > 70 && SRatio == 0.0 && DelPLR < 0.0) {
4081 0 : Error = 0.0;
4082 : }
4083 : }
4084 : }
4085 : // FanElecPower = FanElecPowerHS * SRatio + FanElecPowerLS * ( 1.0 - SRatio ); // why set the ugly global here?
4086 3212 : fanCoil.ElecPower = FanElecPowerHS * SRatio + FanElecPowerLS * (1.0 - SRatio);
4087 : }
4088 : }
4089 11882 : state.dataLoopNodes->Node(fanCoil.AirOutNode).MassFlowRate = inletNode.MassFlowRate;
4090 11882 : PartLoadRatio = PLR;
4091 11882 : SpeedRatio = SRatio;
4092 11882 : PowerMet = QUnitOut;
4093 11882 : }
4094 :
4095 515028 : void ReportFanCoilUnit(EnergyPlusData &state, int const FanCoilNum) // number of the current fan coil unit being simulated
4096 : {
4097 :
4098 : // SUBROUTINE INFORMATION:
4099 : // AUTHOR Fred Buhl
4100 : // DATE WRITTEN March 2000
4101 :
4102 : // PURPOSE OF THIS SUBROUTINE:
4103 : // Fills some of the report variables for the fan coil units
4104 :
4105 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4106 515028 : Real64 ReportingConstant = state.dataHVACGlobal->TimeStepSysSec;
4107 :
4108 515028 : state.dataFanCoilUnits->FanCoil(FanCoilNum).HeatEnergy = state.dataFanCoilUnits->FanCoil(FanCoilNum).HeatPower * ReportingConstant;
4109 515028 : state.dataFanCoilUnits->FanCoil(FanCoilNum).SensCoolEnergy = state.dataFanCoilUnits->FanCoil(FanCoilNum).SensCoolPower * ReportingConstant;
4110 515028 : state.dataFanCoilUnits->FanCoil(FanCoilNum).TotCoolEnergy = state.dataFanCoilUnits->FanCoil(FanCoilNum).TotCoolPower * ReportingConstant;
4111 515028 : state.dataFanCoilUnits->FanCoil(FanCoilNum).ElecEnergy = state.dataFanCoilUnits->FanCoil(FanCoilNum).ElecPower * ReportingConstant;
4112 :
4113 515028 : if (state.dataFanCoilUnits->FanCoil(FanCoilNum).FirstPass) { // reset sizing flags so other zone equipment can size normally
4114 162 : if (!state.dataGlobal->SysSizingCalc) {
4115 81 : DataSizing::resetHVACSizingGlobals(state, state.dataSize->CurZoneEqNum, 0, state.dataFanCoilUnits->FanCoil(FanCoilNum).FirstPass);
4116 : }
4117 : }
4118 515028 : }
4119 :
4120 25404 : int GetFanCoilZoneInletAirNode(EnergyPlusData &state, int const FanCoilNum)
4121 : {
4122 :
4123 : // FUNCTION INFORMATION:
4124 : // AUTHOR B Griffith
4125 : // DATE WRITTEN Dec 2006
4126 :
4127 : // PURPOSE OF THIS FUNCTION:
4128 : // lookup function for OA inlet node for ventilation rate reporting
4129 :
4130 25404 : if (state.dataFanCoilUnits->GetFanCoilInputFlag) {
4131 0 : GetFanCoilUnits(state);
4132 0 : state.dataFanCoilUnits->GetFanCoilInputFlag = false;
4133 : }
4134 :
4135 25404 : if (FanCoilNum > 0 && FanCoilNum <= state.dataFanCoilUnits->NumFanCoils) {
4136 25404 : return state.dataFanCoilUnits->FanCoil(FanCoilNum).AirOutNode;
4137 : }
4138 :
4139 0 : return 0;
4140 : }
4141 :
4142 25404 : int GetFanCoilOutAirNode(EnergyPlusData &state, int const FanCoilNum)
4143 : {
4144 :
4145 : // FUNCTION INFORMATION:
4146 : // AUTHOR B Griffith
4147 : // DATE WRITTEN Dec 2006
4148 :
4149 : // PURPOSE OF THIS FUNCTION:
4150 : // lookup function for OA inlet node for ventilation rate reporting
4151 :
4152 25404 : if (state.dataFanCoilUnits->GetFanCoilInputFlag) {
4153 0 : GetFanCoilUnits(state);
4154 0 : state.dataFanCoilUnits->GetFanCoilInputFlag = false;
4155 : }
4156 :
4157 25404 : if (FanCoilNum > 0 && FanCoilNum <= state.dataFanCoilUnits->NumFanCoils) {
4158 25404 : return state.dataFanCoilUnits->FanCoil(FanCoilNum).OutsideAirNode;
4159 : }
4160 :
4161 0 : return 0;
4162 : }
4163 :
4164 25404 : int GetFanCoilReturnAirNode(EnergyPlusData &state, int const FanCoilNum)
4165 : {
4166 :
4167 : // FUNCTION INFORMATION:
4168 : // AUTHOR B Griffith
4169 : // DATE WRITTEN Dec 2006
4170 :
4171 : // PURPOSE OF THIS FUNCTION:
4172 : // lookup function for mixer's return node
4173 :
4174 25404 : if (state.dataFanCoilUnits->GetFanCoilInputFlag) {
4175 0 : GetFanCoilUnits(state);
4176 0 : state.dataFanCoilUnits->GetFanCoilInputFlag = false;
4177 : }
4178 :
4179 25404 : if (FanCoilNum > 0 && FanCoilNum <= state.dataFanCoilUnits->NumFanCoils) {
4180 25404 : if (state.dataFanCoilUnits->FanCoil(FanCoilNum).OAMixIndex > 0) {
4181 23174 : return MixedAir::GetOAMixerReturnNodeNumber(state, state.dataFanCoilUnits->FanCoil(FanCoilNum).OAMixIndex);
4182 : }
4183 : }
4184 :
4185 2230 : return 0;
4186 : }
4187 :
4188 25404 : int GetFanCoilMixedAirNode(EnergyPlusData &state, int const FanCoilNum)
4189 : {
4190 :
4191 : // FUNCTION INFORMATION:
4192 : // AUTHOR B Griffith
4193 : // DATE WRITTEN Dec 2006
4194 :
4195 : // PURPOSE OF THIS FUNCTION:
4196 : // lookup function for mixer's return node
4197 :
4198 25404 : if (state.dataFanCoilUnits->GetFanCoilInputFlag) {
4199 0 : GetFanCoilUnits(state);
4200 0 : state.dataFanCoilUnits->GetFanCoilInputFlag = false;
4201 : }
4202 :
4203 25404 : if (FanCoilNum > 0 && FanCoilNum <= state.dataFanCoilUnits->NumFanCoils) {
4204 25404 : if (state.dataFanCoilUnits->FanCoil(FanCoilNum).OAMixIndex > 0) {
4205 23174 : return MixedAir::GetOAMixerMixedNodeNumber(state, state.dataFanCoilUnits->FanCoil(FanCoilNum).OAMixIndex);
4206 : }
4207 : }
4208 :
4209 2230 : return 0;
4210 : }
4211 :
4212 6108 : Real64 CalcFanCoilLoadResidual(EnergyPlusData &state,
4213 : int FanCoilNum, // Index to this fan coil unit
4214 : bool FirstHVACIteration, // FirstHVACIteration flag
4215 : int ControlledZoneNum, // zone index
4216 : Real64 QZnReq, // Sensible load to be met [W]
4217 : Real64 const PartLoadRatio // coil part load ratio
4218 : )
4219 : {
4220 : // FUNCTION INFORMATION:
4221 : // AUTHOR Richard Raustad, FSEC
4222 : // DATE WRITTEN July 2015
4223 :
4224 : // PURPOSE OF THIS SUBROUTINE:
4225 : // To calculate the part-load ratio for the FCU with electric heating coil
4226 :
4227 : Real64 QUnitOut; // delivered capacity [W]
4228 6108 : Calc4PipeFanCoil(state,
4229 : FanCoilNum,
4230 : ControlledZoneNum,
4231 : FirstHVACIteration,
4232 : QUnitOut,
4233 : PartLoadRatio); // needs PLR=0 for electric heating coil, otherwise will run a full capacity
4234 : // Calculate residual based on output magnitude
4235 6108 : if (std::abs(QZnReq) <= 100.0) {
4236 0 : return (QUnitOut - QZnReq) / 100.0;
4237 : } else {
4238 6108 : return (QUnitOut - QZnReq) / QZnReq;
4239 : }
4240 : }
4241 :
4242 656610 : Real64 CalcFanCoilPLRResidual(EnergyPlusData &state,
4243 : Real64 const PLR, // part-load ratio of air and water mass flow rate
4244 : int FanCoilNum, // Index to this fan coil unit
4245 : bool FirstHVACIteration, // FirstHVACIteration flag
4246 : int ControlledZoneNum, // zone index
4247 : int WaterControlNode, // water node to control
4248 : Real64 QZnReq // Sensible load to be met [W] // Function parameters
4249 : )
4250 : {
4251 :
4252 : // FUNCTION INFORMATION:
4253 : // AUTHOR Richard Raustad
4254 : // DATE WRITTEN August 2016
4255 :
4256 : // PURPOSE OF THIS SUBROUTINE:
4257 : Real64 QUnitOut; // delivered capacity [W]
4258 656610 : if (WaterControlNode == state.dataFanCoilUnits->FanCoil(FanCoilNum).CoolCoilFluidInletNode) {
4259 345627 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * state.dataFanCoilUnits->FanCoil(FanCoilNum).MaxCoolCoilFluidFlow;
4260 345627 : Calc4PipeFanCoil(state,
4261 : FanCoilNum,
4262 : ControlledZoneNum,
4263 : FirstHVACIteration,
4264 : QUnitOut,
4265 : PLR); // needs PLR=0 for electric heating coil, otherwise will run a full capacity
4266 621966 : } else if (WaterControlNode == state.dataFanCoilUnits->FanCoil(FanCoilNum).HeatCoilFluidInletNode &&
4267 310983 : state.dataFanCoilUnits->FanCoil(FanCoilNum).HCoilType_Num != HCoil::Electric) {
4268 310983 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = PLR * state.dataFanCoilUnits->FanCoil(FanCoilNum).MaxHeatCoilFluidFlow;
4269 310983 : Calc4PipeFanCoil(state,
4270 : FanCoilNum,
4271 : ControlledZoneNum,
4272 : FirstHVACIteration,
4273 : QUnitOut,
4274 : PLR); // needs PLR=0 for electric heating coil, otherwise will run a full capacity
4275 : } else {
4276 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR); // needs PLR=1 for electric heating coil
4277 : }
4278 :
4279 : // Calculate residual based on output magnitude
4280 656610 : if (std::abs(QZnReq) <= 100.0) {
4281 16569 : return (QUnitOut - QZnReq) / 100.0;
4282 : } else {
4283 640041 : return (QUnitOut - QZnReq) / QZnReq;
4284 : }
4285 : }
4286 :
4287 0 : Real64 CalcFanCoilHeatCoilPLRResidual(EnergyPlusData &state,
4288 : Real64 const CyclingR, // electric heating coil cycling ratio
4289 : int const FanCoilNum,
4290 : bool const FirstHVACIteration,
4291 : int const ZoneNum,
4292 : Real64 const QZnReq)
4293 : {
4294 : // PURPOSE OF THIS SUBROUTINE:
4295 : // Calculate electric heating coil cycling ratio of FanCoilUnit with MultiSpeedFan
4296 : // capacity control method when running with at lowest speed for a continuous
4297 : // fan operating mode.
4298 :
4299 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4300 : Real64 QUnitOut; // delivered capacity [W]
4301 0 : Real64 constexpr PLR = 1.0; // fan coil unit PLR
4302 :
4303 : // electric heating coil cycling ratio at minimum air flow for constant fan operating mode
4304 0 : Calc4PipeFanCoil(state, FanCoilNum, ZoneNum, FirstHVACIteration, QUnitOut, PLR, CyclingR);
4305 :
4306 : // Calculate residual based on output magnitude
4307 0 : if (std::abs(QZnReq) <= 100.0) {
4308 0 : return (QUnitOut - QZnReq) / 100.0;
4309 : } else {
4310 0 : return (QUnitOut - QZnReq) / QZnReq;
4311 : }
4312 : }
4313 :
4314 1663266 : Real64 CalcFanCoilCWLoadResidual(EnergyPlusData &state,
4315 : Real64 const CWFlow, // water mass flow rate [kg/s]
4316 : int const FanCoilNum,
4317 : bool const FirstHVACIteration,
4318 : int const ControlledZoneNum,
4319 : Real64 const QZnReq)
4320 : {
4321 :
4322 : // FUNCTION INFORMATION:
4323 : // AUTHOR Fred Buhl Jan 2016
4324 : // DATE WRITTEN July 2015
4325 :
4326 : // PURPOSE OF THIS SUBROUTINE:
4327 : // To calculate the part-load ratio for the FCU with electric heating coil
4328 :
4329 : Real64 QUnitOut; // delivered capacity [W]
4330 1663266 : state.dataLoopNodes->Node(state.dataFanCoilUnits->FanCoil(FanCoilNum).CoolCoilFluidInletNode).MassFlowRate = CWFlow;
4331 1663266 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, 1.0);
4332 :
4333 : // Calculate residual based on output magnitude
4334 1663266 : if (std::abs(QZnReq) <= 100.0) {
4335 8226 : return (QUnitOut - QZnReq) / 100.0;
4336 : } else {
4337 1655040 : return (QUnitOut - QZnReq) / QZnReq;
4338 : }
4339 : }
4340 :
4341 91160 : Real64 CalcFanCoilWaterFlowResidual(EnergyPlusData &state,
4342 : Real64 const PLR,
4343 : int FanCoilNum,
4344 : bool FirstHVACIteration,
4345 : int ControlledZoneNum,
4346 : Real64 QZnReq,
4347 : int AirInNode,
4348 : int WaterControlNode,
4349 : Real64 maxCoilFluidFlow,
4350 : Real64 AirMassFlowRate)
4351 : {
4352 :
4353 : // FUNCTION INFORMATION:
4354 : // AUTHOR Richard Raustad, FSEC
4355 : // DATE WRITTEN December 2015
4356 :
4357 : // PURPOSE OF THIS SUBROUTINE:
4358 : // To calculate the part-load ratio for the FCU with varying water flow rate
4359 :
4360 91160 : Real64 const mDot = PLR * maxCoilFluidFlow;
4361 91160 : if (WaterControlNode > 0) {
4362 91160 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate = mDot;
4363 : }
4364 91160 : state.dataLoopNodes->Node(AirInNode).MassFlowRate = AirMassFlowRate;
4365 :
4366 : Real64 QUnitOut;
4367 132750 : if (WaterControlNode == state.dataFanCoilUnits->FanCoil(FanCoilNum).CoolCoilFluidInletNode ||
4368 41590 : (WaterControlNode == state.dataFanCoilUnits->FanCoil(FanCoilNum).HeatCoilFluidInletNode &&
4369 41590 : state.dataFanCoilUnits->FanCoil(FanCoilNum).HCoilType_Num != HCoil::Electric)) {
4370 :
4371 91160 : Calc4PipeFanCoil(state,
4372 : FanCoilNum,
4373 : ControlledZoneNum,
4374 : FirstHVACIteration,
4375 : QUnitOut,
4376 182320 : 0.0); // needs PLR=0 for electric heating coil, otherwise will run a full capacity
4377 :
4378 : } else {
4379 0 : Calc4PipeFanCoil(state, FanCoilNum, ControlledZoneNum, FirstHVACIteration, QUnitOut, PLR);
4380 : }
4381 :
4382 : // Calculate residual based on output magnitude
4383 91160 : if (std::abs(QZnReq) <= 100.0) {
4384 32 : return (QUnitOut - QZnReq) / 100.0;
4385 : } else {
4386 91128 : return (QUnitOut - QZnReq) / QZnReq;
4387 : }
4388 : }
4389 :
4390 20960 : Real64 CalcFanCoilAirAndWaterFlowResidual(EnergyPlusData &state,
4391 : Real64 const PLR, // water and air part load ratio
4392 : int FanCoilNum,
4393 : bool FirstHVACIteration,
4394 : int ControlledZoneNum,
4395 : Real64 QZnReq,
4396 : int AirInNode,
4397 : int WaterControlNode,
4398 : Real64 MinWaterFlow)
4399 : {
4400 :
4401 : // FUNCTION INFORMATION:
4402 : // AUTHOR Richard Raustad, FSEC
4403 : // DATE WRITTEN December 2015
4404 :
4405 : // PURPOSE OF THIS SUBROUTINE:
4406 : // To calculate the part-load ratio for the FCU with varying water flow rate
4407 :
4408 : // METHODOLOGY EMPLOYED:
4409 : // Use SolveRoot to CALL this Function to converge on a solution
4410 :
4411 : // set air flow rate
4412 20960 : state.dataLoopNodes->Node(AirInNode).MassFlowRate =
4413 20960 : state.dataFanCoilUnits->FanCoil(FanCoilNum).MaxAirMassFlow *
4414 20960 : (state.dataFanCoilUnits->FanCoil(FanCoilNum).LowSpeedRatio + (PLR * (1.0 - state.dataFanCoilUnits->FanCoil(FanCoilNum).LowSpeedRatio)));
4415 : // set water flow rate
4416 20960 : if (WaterControlNode == state.dataFanCoilUnits->FanCoil(FanCoilNum).CoolCoilFluidInletNode) {
4417 20960 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate =
4418 20960 : MinWaterFlow + (PLR * (state.dataFanCoilUnits->FanCoil(FanCoilNum).MaxCoolCoilFluidFlow - MinWaterFlow));
4419 0 : } else if (WaterControlNode == state.dataFanCoilUnits->FanCoil(FanCoilNum).HeatCoilFluidInletNode) {
4420 0 : state.dataLoopNodes->Node(WaterControlNode).MassFlowRate =
4421 0 : MinWaterFlow + (PLR * (state.dataFanCoilUnits->FanCoil(FanCoilNum).MaxHeatCoilFluidFlow - MinWaterFlow));
4422 : } else {
4423 : // developer error
4424 0 : ShowFatalError(state,
4425 0 : format("Developer Error - CalcFanCoilAirAndWaterFlowResidual: Water control node not found for {}",
4426 0 : state.dataFanCoilUnits->FanCoil(FanCoilNum).Name));
4427 : }
4428 : Real64 QUnitOut; // delivered capacity [W]
4429 20960 : Calc4PipeFanCoil(state,
4430 : FanCoilNum,
4431 : ControlledZoneNum,
4432 : FirstHVACIteration,
4433 : QUnitOut,
4434 : PLR); // needs PLR for electric heating coil to output a specific capacity
4435 :
4436 : // Calculate residual based on output magnitude
4437 20960 : if (std::abs(QZnReq) <= 100.0) {
4438 0 : return (QUnitOut - QZnReq) / 100.0;
4439 : } else {
4440 20960 : return (QUnitOut - QZnReq) / QZnReq;
4441 : }
4442 : }
4443 :
4444 0 : int getEqIndex(EnergyPlusData &state, std::string_view CompName)
4445 : {
4446 0 : if (state.dataFanCoilUnits->GetFanCoilInputFlag) {
4447 0 : GetFanCoilUnits(state);
4448 0 : state.dataFanCoilUnits->GetFanCoilInputFlag = false;
4449 : }
4450 :
4451 0 : for (int FanCoilIndex = 1; FanCoilIndex <= state.dataFanCoilUnits->NumFanCoils; ++FanCoilIndex) {
4452 0 : auto &fanCoil = state.dataFanCoilUnits->FanCoil(FanCoilIndex);
4453 0 : if (Util::SameString(fanCoil.Name, CompName)) {
4454 0 : return FanCoilIndex;
4455 : }
4456 : }
4457 :
4458 0 : return 0;
4459 : }
4460 :
4461 : } // namespace FanCoilUnits
4462 :
4463 : } // namespace EnergyPlus
|