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