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