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