Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, 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/HeatingAirFlowSizing.hh>
57 : #include <EnergyPlus/Autosizing/HeatingCapacitySizing.hh>
58 : #include <EnergyPlus/BranchNodeConnections.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataEnvironment.hh>
61 : #include <EnergyPlus/DataHVACGlobals.hh>
62 : #include <EnergyPlus/DataHeatBalance.hh>
63 : #include <EnergyPlus/DataLoopNode.hh>
64 : #include <EnergyPlus/DataSizing.hh>
65 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
66 : #include <EnergyPlus/DataZoneEquipment.hh>
67 : #include <EnergyPlus/Fans.hh>
68 : #include <EnergyPlus/FluidProperties.hh>
69 : #include <EnergyPlus/General.hh>
70 : #include <EnergyPlus/GeneralRoutines.hh>
71 : #include <EnergyPlus/HVACFan.hh>
72 : #include <EnergyPlus/HeatingCoils.hh>
73 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
74 : #include <EnergyPlus/NodeInputManager.hh>
75 : #include <EnergyPlus/OutputProcessor.hh>
76 : #include <EnergyPlus/PlantUtilities.hh>
77 : #include <EnergyPlus/Psychrometrics.hh>
78 : #include <EnergyPlus/ReportCoilSelection.hh>
79 : #include <EnergyPlus/ScheduleManager.hh>
80 : #include <EnergyPlus/SteamCoils.hh>
81 : #include <EnergyPlus/UnitHeater.hh>
82 : #include <EnergyPlus/UtilityRoutines.hh>
83 : #include <EnergyPlus/WaterCoils.hh>
84 :
85 : namespace EnergyPlus {
86 :
87 : namespace UnitHeater {
88 :
89 : // Module containing the routines dealing with the Unit Heater
90 :
91 : // MODULE INFORMATION:
92 : // AUTHOR Rick Strand
93 : // DATE WRITTEN May 2000
94 : // MODIFIED Brent Griffith, Sept 2010, plant upgrades, fluid properties
95 : // MODIFIED Bereket Nigusse, FSEC, October 2013, Added cycling fan operating mode
96 : // RE-ENGINEERED na
97 :
98 : // PURPOSE OF THIS MODULE:
99 : // To simulate unit heaters. It is assumed that unit heaters are zone equipment
100 : // without any connection to outside air other than through a separately defined
101 : // air loop.
102 :
103 : // METHODOLOGY EMPLOYED:
104 : // Units are modeled as a collection of a fan and a heating coil. The fan
105 : // can either be a continuously running fan or an on-off fan which turns on
106 : // only when there is actually a heating load. This fan control works together
107 : // with the unit operation schedule to determine what the unit heater actually
108 : // does at a given point in time.
109 :
110 : // REFERENCES:
111 : // ASHRAE Systems and Equipment Handbook (SI), 1996. pp. 31.3-31.8
112 : // Rick Strand's unit heater module which was based upon Fred Buhl's fan coil
113 : // module (FanCoilUnits.cc)
114 :
115 : // Using/Aliasing
116 : using namespace DataLoopNode;
117 : using DataHVACGlobals::cFanTypes;
118 : using DataHVACGlobals::ContFanCycCoil;
119 : using DataHVACGlobals::CycFanCycCoil;
120 : using DataHVACGlobals::SmallAirVolFlow;
121 : using DataHVACGlobals::SmallLoad;
122 : using DataHVACGlobals::SmallMassFlow;
123 : using namespace ScheduleManager;
124 : using Psychrometrics::PsyCpAirFnW;
125 : using Psychrometrics::PsyHFnTdbW;
126 : using Psychrometrics::PsyRhoAirFnPbTdbW;
127 : using namespace FluidProperties;
128 :
129 : static constexpr std::string_view fluidNameSteam("STEAM");
130 :
131 638739 : void SimUnitHeater(EnergyPlusData &state,
132 : std::string_view CompName, // name of the fan coil unit
133 : int const ZoneNum, // number of zone being served
134 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
135 : Real64 &PowerMet, // Sensible power supplied (W)
136 : Real64 &LatOutputProvided, // Latent add/removal supplied by window AC (kg/s), dehumid = negative
137 : int &CompIndex)
138 : {
139 :
140 : // SUBROUTINE INFORMATION:
141 : // AUTHOR Rick Strand
142 : // DATE WRITTEN May 2000
143 : // MODIFIED Don Shirey, Aug 2009 (LatOutputProvided)
144 : // RE-ENGINEERED na
145 :
146 : // PURPOSE OF THIS SUBROUTINE:
147 : // This is the main driver subroutine for the Unit Heater simulation.
148 :
149 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
150 : int UnitHeatNum; // index of unit heater being simulated
151 :
152 638739 : if (state.dataUnitHeaters->GetUnitHeaterInputFlag) {
153 14 : GetUnitHeaterInput(state);
154 14 : state.dataUnitHeaters->GetUnitHeaterInputFlag = false;
155 : }
156 :
157 : // Find the correct Unit Heater Equipment
158 638739 : if (CompIndex == 0) {
159 63 : UnitHeatNum = UtilityRoutines::FindItemInList(CompName, state.dataUnitHeaters->UnitHeat);
160 63 : if (UnitHeatNum == 0) {
161 0 : ShowFatalError(state, "SimUnitHeater: Unit not found=" + std::string{CompName});
162 : }
163 63 : CompIndex = UnitHeatNum;
164 : } else {
165 638676 : UnitHeatNum = CompIndex;
166 638676 : if (UnitHeatNum > state.dataUnitHeaters->NumOfUnitHeats || UnitHeatNum < 1) {
167 0 : ShowFatalError(state,
168 0 : format("SimUnitHeater: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
169 : UnitHeatNum,
170 0 : state.dataUnitHeaters->NumOfUnitHeats,
171 0 : CompName));
172 : }
173 638676 : if (state.dataUnitHeaters->CheckEquipName(UnitHeatNum)) {
174 63 : if (CompName != state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name) {
175 0 : ShowFatalError(state,
176 0 : format("SimUnitHeater: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
177 : UnitHeatNum,
178 : CompName,
179 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
180 : }
181 63 : state.dataUnitHeaters->CheckEquipName(UnitHeatNum) = false;
182 : }
183 : }
184 :
185 638739 : state.dataSize->ZoneEqUnitHeater = true;
186 :
187 638739 : InitUnitHeater(state, UnitHeatNum, ZoneNum, FirstHVACIteration);
188 :
189 638739 : state.dataSize->ZoneHeatingOnlyFan = true;
190 :
191 638739 : CalcUnitHeater(state, UnitHeatNum, ZoneNum, FirstHVACIteration, PowerMet, LatOutputProvided);
192 :
193 638739 : state.dataSize->ZoneHeatingOnlyFan = false;
194 :
195 : // CALL UpdateUnitHeater
196 :
197 638739 : ReportUnitHeater(state, UnitHeatNum);
198 :
199 638739 : state.dataSize->ZoneEqUnitHeater = false;
200 638739 : }
201 :
202 14 : void GetUnitHeaterInput(EnergyPlusData &state)
203 : {
204 :
205 : // SUBROUTINE INFORMATION:
206 : // AUTHOR Rick Strand
207 : // DATE WRITTEN May 2000
208 : // MODIFIED Chandan Sharma, FSEC, March 2011: Added ZoneHVAC sys avail manager
209 : // Bereket Nigusse, FSEC, April 2011: eliminated input node names
210 : // & added fan object type
211 : // RE-ENGINEERED na
212 :
213 : // PURPOSE OF THIS SUBROUTINE:
214 : // Obtain the user input data for all of the unit heaters in the input file.
215 :
216 : // METHODOLOGY EMPLOYED:
217 : // Standard EnergyPlus methodology.
218 :
219 : // REFERENCES:
220 : // Fred Buhl's fan coil module (FanCoilUnits.cc)
221 :
222 : // Using/Aliasing
223 : using BranchNodeConnections::SetUpCompSets;
224 : using DataHVACGlobals::FanType_SimpleConstVolume;
225 : using DataHVACGlobals::FanType_SimpleOnOff;
226 : using DataHVACGlobals::FanType_SimpleVAV;
227 :
228 : using DataSizing::AutoSize;
229 : using Fans::GetFanAvailSchPtr;
230 : using Fans::GetFanIndex;
231 : using Fans::GetFanOutletNode;
232 : using Fans::GetFanType;
233 : using Fans::GetFanVolFlow;
234 : using NodeInputManager::GetOnlySingleNode;
235 : using SteamCoils::GetCoilSteamInletNode;
236 : using SteamCoils::GetSteamCoilIndex;
237 : using WaterCoils::GetCoilWaterInletNode;
238 :
239 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
240 14 : bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
241 : int IOStatus; // Used in GetObjectItem
242 : bool IsNotOK; // TRUE if there was a problem with a list name
243 14 : bool errFlag(false); // interim error flag
244 : int NumAlphas; // Number of Alphas for each GetObjectItem call
245 : int NumNumbers; // Number of Numbers for each GetObjectItem call
246 : int NumFields; // Total number of fields in object
247 : int UnitHeatNum; // Item to be "gotten"
248 : static constexpr std::string_view RoutineName("GetUnitHeaterInput: "); // include trailing blank space
249 : Real64 FanVolFlow; // Fan volumetric flow rate
250 28 : std::string CurrentModuleObject;
251 28 : Array1D_string Alphas; // Alpha items for object
252 28 : Array1D<Real64> Numbers; // Numeric items for object
253 28 : Array1D_string cAlphaFields; // Alpha field names
254 28 : Array1D_string cNumericFields; // Numeric field names
255 28 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
256 28 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
257 : int CtrlZone; // index to loop counter
258 : int NodeNum; // index to loop counter
259 : bool ZoneNodeNotFound; // used in error checking
260 :
261 : // Figure out how many unit heaters there are in the input file
262 14 : CurrentModuleObject = state.dataUnitHeaters->cMO_UnitHeater;
263 14 : state.dataUnitHeaters->NumOfUnitHeats = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
264 14 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNumbers);
265 :
266 14 : Alphas.allocate(NumAlphas);
267 14 : Numbers.dimension(NumNumbers, 0.0);
268 14 : cAlphaFields.allocate(NumAlphas);
269 14 : cNumericFields.allocate(NumNumbers);
270 14 : lAlphaBlanks.dimension(NumAlphas, true);
271 14 : lNumericBlanks.dimension(NumNumbers, true);
272 :
273 : // Allocate the local derived type and do one-time initializations for all parts of it
274 14 : if (state.dataUnitHeaters->NumOfUnitHeats > 0) {
275 14 : state.dataUnitHeaters->UnitHeat.allocate(state.dataUnitHeaters->NumOfUnitHeats);
276 14 : state.dataUnitHeaters->CheckEquipName.allocate(state.dataUnitHeaters->NumOfUnitHeats);
277 14 : state.dataUnitHeaters->UnitHeatNumericFields.allocate(state.dataUnitHeaters->NumOfUnitHeats);
278 : }
279 14 : state.dataUnitHeaters->CheckEquipName = true;
280 :
281 77 : for (UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats;
282 : ++UnitHeatNum) { // Begin looping over all of the unit heaters found in the input file...
283 :
284 63 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
285 : CurrentModuleObject,
286 : UnitHeatNum,
287 : Alphas,
288 : NumAlphas,
289 : Numbers,
290 : NumNumbers,
291 : IOStatus,
292 : lNumericBlanks,
293 : lAlphaBlanks,
294 : cAlphaFields,
295 : cNumericFields);
296 :
297 63 : state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames.allocate(NumNumbers);
298 63 : state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames = "";
299 63 : state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames = cNumericFields;
300 63 : UtilityRoutines::IsNameEmpty(state, Alphas(1), CurrentModuleObject, ErrorsFound);
301 :
302 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name = Alphas(1);
303 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).SchedName = Alphas(2);
304 63 : if (lAlphaBlanks(2)) {
305 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).SchedPtr = DataGlobalConstants::ScheduleAlwaysOn;
306 : } else {
307 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).SchedPtr = GetScheduleIndex(state, Alphas(2)); // convert schedule name to pointer
308 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).SchedPtr == 0) {
309 0 : ShowSevereError(state,
310 0 : std::string{RoutineName} + CurrentModuleObject + ": invalid " + cAlphaFields(2) + " entered =" + Alphas(2) +
311 0 : " for " + cAlphaFields(1) + '=' + Alphas(1));
312 0 : ErrorsFound = true;
313 : }
314 : }
315 :
316 : // Main air nodes (except outside air node):
317 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode = GetOnlySingleNode(state,
318 63 : Alphas(3),
319 : ErrorsFound,
320 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitHeater,
321 63 : Alphas(1),
322 : DataLoopNode::NodeFluidType::Air,
323 : DataLoopNode::ConnectionType::Inlet,
324 : NodeInputManager::CompFluidStream::Primary,
325 63 : ObjectIsParent);
326 :
327 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode = GetOnlySingleNode(state,
328 63 : Alphas(4),
329 : ErrorsFound,
330 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitHeater,
331 63 : Alphas(1),
332 : DataLoopNode::NodeFluidType::Air,
333 : DataLoopNode::ConnectionType::Outlet,
334 : NodeInputManager::CompFluidStream::Primary,
335 63 : ObjectIsParent);
336 :
337 : // Fan information:
338 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType = Alphas(5);
339 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName = Alphas(6);
340 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = Numbers(1);
341 :
342 63 : errFlag = false;
343 126 : ValidateComponent(state,
344 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType,
345 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
346 : errFlag,
347 : CurrentModuleObject);
348 63 : if (errFlag) {
349 0 : ShowContinueError(state, "specified in " + CurrentModuleObject + " = \"" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name + "\".");
350 0 : ErrorsFound = true;
351 : } else {
352 63 : if (!UtilityRoutines::SameString(state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType, "Fan:SystemModel")) {
353 180 : GetFanType(state,
354 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
355 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num,
356 : errFlag,
357 : CurrentModuleObject,
358 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
359 :
360 : {
361 60 : switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num) {
362 60 : case FanType_SimpleConstVolume:
363 : case FanType_SimpleVAV:
364 : case FanType_SimpleOnOff:
365 : // Get fan outlet node
366 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode =
367 120 : GetFanOutletNode(state,
368 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType,
369 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
370 : errFlag);
371 60 : if (errFlag) {
372 0 : ShowContinueError(state,
373 0 : "specified in " + CurrentModuleObject + " = \"" +
374 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name + "\".");
375 0 : ErrorsFound = true;
376 : }
377 60 : break;
378 0 : default:
379 0 : ShowSevereError(state, std::string{RoutineName} + CurrentModuleObject + " = \"" + Alphas(1) + "\"");
380 0 : ShowContinueError(state, "Fan Type must be Fan:ConstantVolume or Fan:VariableVolume");
381 0 : ErrorsFound = true;
382 : }
383 : }
384 180 : GetFanIndex(state,
385 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
386 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index,
387 : errFlag,
388 : CurrentModuleObject);
389 60 : if (errFlag) {
390 0 : ErrorsFound = true;
391 : } else {
392 60 : GetFanVolFlow(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index, FanVolFlow);
393 :
394 70 : if (FanVolFlow != AutoSize && state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow != AutoSize &&
395 10 : FanVolFlow < state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow) {
396 0 : ShowSevereError(state, "Specified in " + CurrentModuleObject + " = " + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
397 0 : ShowContinueError(
398 : state,
399 0 : format(
400 : "...air flow rate ({:.7T}) in fan object {} is less than the unit heater maximum supply air flow rate ({:.7T}).",
401 : FanVolFlow,
402 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
403 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow));
404 0 : ShowContinueError(state,
405 : "...the fan flow rate must be greater than or equal to the unit heater maximum supply air flow rate.");
406 0 : ErrorsFound = true;
407 60 : } else if (FanVolFlow == AutoSize && state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow != AutoSize) {
408 0 : ShowWarningError(state,
409 0 : "Specified in " + CurrentModuleObject + " = " + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
410 0 : ShowContinueError(state, "...the fan flow rate is autosized while the unit heater flow rate is not.");
411 0 : ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
412 60 : } else if (FanVolFlow != AutoSize && state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow == AutoSize) {
413 0 : ShowWarningError(state,
414 0 : "Specified in " + CurrentModuleObject + " = " + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
415 0 : ShowContinueError(state, "...the unit heater flow rate is autosized while the fan flow rate is not.");
416 0 : ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
417 : }
418 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanAvailSchedPtr =
419 120 : GetFanAvailSchPtr(state,
420 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType,
421 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
422 : errFlag);
423 : }
424 3 : } else if (UtilityRoutines::SameString(state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType, "Fan:SystemModel")) {
425 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num = DataHVACGlobals::FanType_SystemModelObject;
426 6 : state.dataHVACFan->fanObjs.emplace_back(
427 6 : new HVACFan::FanSystem(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName)); // call constructor
428 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index =
429 3 : HVACFan::getFanObjectVectorIndex(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName); // zero-based
430 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode =
431 3 : state.dataHVACFan->fanObjs[state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index]->outletNodeNum;
432 3 : FanVolFlow = state.dataHVACFan->fanObjs[state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index]->designAirVolFlowRate;
433 3 : if (FanVolFlow != AutoSize && state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow != AutoSize &&
434 0 : FanVolFlow < state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow) {
435 0 : ShowSevereError(state, "Specified in " + CurrentModuleObject + " = " + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
436 0 : ShowContinueError(
437 : state,
438 0 : format("...air flow rate ({:.7T}) in fan object {} is less than the unit heater maximum supply air flow rate ({:.7T}).",
439 : FanVolFlow,
440 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
441 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow));
442 0 : ShowContinueError(state,
443 : "...the fan flow rate must be greater than or equal to the unit heater maximum supply air flow rate.");
444 0 : ErrorsFound = true;
445 3 : } else if (FanVolFlow == AutoSize && state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow != AutoSize) {
446 0 : ShowWarningError(state, "Specified in " + CurrentModuleObject + " = " + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
447 0 : ShowContinueError(state, "...the fan flow rate is autosized while the unit heater flow rate is not.");
448 0 : ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
449 3 : } else if (FanVolFlow != AutoSize && state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow == AutoSize) {
450 0 : ShowWarningError(state, "Specified in " + CurrentModuleObject + " = " + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
451 0 : ShowContinueError(state, "...the unit heater flow rate is autosized while the fan flow rate is not.");
452 0 : ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
453 : }
454 :
455 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanAvailSchedPtr =
456 3 : state.dataHVACFan->fanObjs[state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index]->availSchedIndex;
457 : }
458 : }
459 :
460 : // Heating coil information:
461 : {
462 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type =
463 126 : static_cast<HCoilType>(getEnumerationValue(HCoilTypeNamesUC, UtilityRoutines::MakeUPPERCase(Alphas(7))));
464 63 : switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
465 6 : case HCoilType::WaterHeatingCoil:
466 6 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType = DataPlant::PlantEquipmentType::CoilWaterSimpleHeating;
467 6 : break;
468 0 : case HCoilType::SteamCoil:
469 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType = DataPlant::PlantEquipmentType::CoilSteamAirHeating;
470 0 : break;
471 57 : case HCoilType::Electric:
472 : case HCoilType::Gas:
473 57 : break;
474 0 : default: {
475 0 : ShowSevereError(state, "Illegal " + cAlphaFields(7) + " = " + Alphas(7));
476 0 : ShowContinueError(state, "Occurs in " + CurrentModuleObject + '=' + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
477 0 : ErrorsFound = true;
478 0 : errFlag = true;
479 : }
480 : }
481 : }
482 63 : if (!errFlag) {
483 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilTypeCh = Alphas(7);
484 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName = Alphas(8);
485 63 : ValidateComponent(state, Alphas(7), state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, IsNotOK, CurrentModuleObject);
486 63 : if (IsNotOK) {
487 0 : ShowContinueError(state,
488 0 : "specified in " + CurrentModuleObject + " = \"" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name + "\"");
489 0 : ErrorsFound = true;
490 : } else {
491 : // The heating coil control node is necessary for hot water and steam coils, but not necessary for an
492 : // electric or gas coil.
493 120 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil ||
494 57 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
495 : // mine the hot water or steam node from the coil object
496 6 : errFlag = false;
497 6 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
498 6 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode =
499 12 : GetCoilWaterInletNode(state, "Coil:Heating:Water", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, errFlag);
500 : } else { // its a steam coil
501 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index =
502 0 : GetSteamCoilIndex(state, "COIL:HEATING:STEAM", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, errFlag);
503 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode =
504 0 : GetCoilSteamInletNode(state,
505 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
506 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
507 : errFlag);
508 : }
509 : // Other error checks should trap before it gets to this point in the code, but including just in case.
510 6 : if (errFlag) {
511 0 : ShowContinueError(state,
512 0 : "that was specified in " + CurrentModuleObject + " = \"" +
513 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name + "\"");
514 0 : ErrorsFound = true;
515 : }
516 : }
517 : }
518 : }
519 :
520 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanSchedPtr = GetScheduleIndex(state, Alphas(9));
521 : // Default to cycling fan when fan operating mode schedule is not present
522 63 : if (!lAlphaBlanks(9) && state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanSchedPtr == 0) {
523 0 : ShowSevereError(state,
524 0 : CurrentModuleObject + " \"" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name + "\" " + cAlphaFields(9) +
525 0 : " not found: " + Alphas(9));
526 0 : ErrorsFound = true;
527 63 : } else if (lAlphaBlanks(9)) {
528 120 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num == FanType_SimpleOnOff ||
529 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
530 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).OpMode = CycFanCycCoil;
531 : } else {
532 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).OpMode = ContFanCycCoil;
533 : }
534 : }
535 :
536 : // Check fan's schedule for cycling fan operation if constant volume fan is used
537 66 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanSchedPtr > 0 &&
538 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num == FanType_SimpleConstVolume) {
539 0 : if (!CheckScheduleValueMinMax(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanSchedPtr, ">", 0.0, "<=", 1.0)) {
540 0 : ShowSevereError(state, CurrentModuleObject + " = " + Alphas(1));
541 0 : ShowContinueError(state, "For " + cAlphaFields(5) + " = " + Alphas(5));
542 0 : ShowContinueError(state, "Fan operating mode must be continuous (fan operating mode schedule values > 0).");
543 0 : ShowContinueError(state, "Error found in " + cAlphaFields(9) + " = " + Alphas(9));
544 0 : ShowContinueError(state, "...schedule values must be (>0., <=1.)");
545 0 : ErrorsFound = true;
546 : }
547 : }
548 :
549 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOperatesDuringNoHeating = Alphas(10);
550 170 : if ((!UtilityRoutines::SameString(state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOperatesDuringNoHeating, "Yes")) &&
551 107 : (!UtilityRoutines::SameString(state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOperatesDuringNoHeating, "No"))) {
552 0 : ErrorsFound = true;
553 0 : ShowSevereError(state, "Illegal " + cAlphaFields(10) + " = " + Alphas(10));
554 0 : ShowContinueError(state, "Occurs in " + CurrentModuleObject + '=' + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
555 63 : } else if (UtilityRoutines::SameString(state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOperatesDuringNoHeating, "No")) {
556 44 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating = true;
557 : }
558 :
559 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow = Numbers(2);
560 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotWaterFlow = Numbers(3);
561 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow = Numbers(2);
562 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotSteamFlow = Numbers(3);
563 :
564 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlOffset = Numbers(4);
565 : // Set default convergence tolerance
566 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlOffset <= 0.0) {
567 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlOffset = 0.001;
568 : }
569 :
570 63 : if (!lAlphaBlanks(11)) {
571 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).AvailManagerListName = Alphas(11);
572 : }
573 :
574 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex = 0;
575 63 : if (!lAlphaBlanks(12)) {
576 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex =
577 0 : UtilityRoutines::FindItemInList(Alphas(12), state.dataSize->ZoneHVACSizing);
578 0 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex == 0) {
579 0 : ShowSevereError(state, cAlphaFields(12) + " = " + Alphas(12) + " not found.");
580 0 : ShowContinueError(state, "Occurs in " + CurrentModuleObject + " = " + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
581 0 : ErrorsFound = true;
582 : }
583 : }
584 :
585 : // check that unit heater air inlet node must be the same as a zone exhaust node
586 63 : ZoneNodeNotFound = true;
587 3017 : for (CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
588 2954 : if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
589 4780 : for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumExhaustNodes; ++NodeNum) {
590 4268 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode ==
591 2134 : state.dataZoneEquip->ZoneEquipConfig(CtrlZone).ExhaustNode(NodeNum)) {
592 63 : ZoneNodeNotFound = false;
593 63 : break;
594 : }
595 : }
596 : }
597 63 : if (ZoneNodeNotFound) {
598 0 : ShowSevereError(state,
599 0 : CurrentModuleObject + " = \"" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name +
600 : "\". Unit heater air inlet node name must be the same as a zone exhaust node name.");
601 0 : ShowContinueError(state, "..Zone exhaust node name is specified in ZoneHVAC:EquipmentConnections object.");
602 0 : ShowContinueError(state,
603 0 : "..Unit heater air inlet node name = " +
604 0 : state.dataLoopNodes->NodeID(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode));
605 0 : ErrorsFound = true;
606 : }
607 : // check that unit heater air outlet node is a zone inlet node.
608 63 : ZoneNodeNotFound = true;
609 3017 : for (CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
610 2954 : if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
611 5341 : for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++NodeNum) {
612 5390 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode ==
613 2695 : state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(NodeNum)) {
614 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ZonePtr = CtrlZone;
615 63 : ZoneNodeNotFound = false;
616 63 : break;
617 : }
618 : }
619 : }
620 63 : if (ZoneNodeNotFound) {
621 0 : ShowSevereError(state,
622 0 : CurrentModuleObject + " = \"" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name +
623 : "\". Unit heater air outlet node name must be the same as a zone inlet node name.");
624 0 : ShowContinueError(state, "..Zone inlet node name is specified in ZoneHVAC:EquipmentConnections object.");
625 0 : ShowContinueError(state,
626 0 : "..Unit heater air outlet node name = " +
627 0 : state.dataLoopNodes->NodeID(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode));
628 0 : ErrorsFound = true;
629 : }
630 :
631 : // Add fan to component sets array
632 315 : SetUpCompSets(state,
633 : CurrentModuleObject,
634 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
635 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType,
636 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
637 63 : state.dataLoopNodes->NodeID(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode),
638 63 : state.dataLoopNodes->NodeID(state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode));
639 :
640 : // Add heating coil to component sets array
641 315 : SetUpCompSets(state,
642 : CurrentModuleObject,
643 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
644 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilTypeCh,
645 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
646 63 : state.dataLoopNodes->NodeID(state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode),
647 63 : state.dataLoopNodes->NodeID(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode));
648 :
649 : } // ...loop over all of the unit heaters found in the input file
650 :
651 14 : Alphas.deallocate();
652 14 : Numbers.deallocate();
653 14 : cAlphaFields.deallocate();
654 14 : cNumericFields.deallocate();
655 14 : lAlphaBlanks.deallocate();
656 14 : lNumericBlanks.deallocate();
657 :
658 14 : if (ErrorsFound) ShowFatalError(state, std::string{RoutineName} + "Errors found in input");
659 :
660 : // Setup Report variables for the Unit Heaters, CurrentModuleObject='ZoneHVAC:UnitHeater'
661 77 : for (UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats; ++UnitHeatNum) {
662 252 : SetupOutputVariable(state,
663 : "Zone Unit Heater Heating Rate",
664 : OutputProcessor::Unit::W,
665 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatPower,
666 : OutputProcessor::SOVTimeStepType::System,
667 : OutputProcessor::SOVStoreType::Average,
668 126 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
669 252 : SetupOutputVariable(state,
670 : "Zone Unit Heater Heating Energy",
671 : OutputProcessor::Unit::J,
672 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatEnergy,
673 : OutputProcessor::SOVTimeStepType::System,
674 : OutputProcessor::SOVStoreType::Summed,
675 126 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
676 252 : SetupOutputVariable(state,
677 : "Zone Unit Heater Fan Electricity Rate",
678 : OutputProcessor::Unit::W,
679 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower,
680 : OutputProcessor::SOVTimeStepType::System,
681 : OutputProcessor::SOVStoreType::Average,
682 126 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
683 : // Note that the unit heater fan electric is NOT metered because this value is already metered through the fan component
684 252 : SetupOutputVariable(state,
685 : "Zone Unit Heater Fan Electricity Energy",
686 : OutputProcessor::Unit::J,
687 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecEnergy,
688 : OutputProcessor::SOVTimeStepType::System,
689 : OutputProcessor::SOVStoreType::Summed,
690 126 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
691 252 : SetupOutputVariable(state,
692 : "Zone Unit Heater Fan Availability Status",
693 : OutputProcessor::Unit::None,
694 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).AvailStatus,
695 : OutputProcessor::SOVTimeStepType::System,
696 : OutputProcessor::SOVStoreType::Average,
697 126 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
698 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num == FanType_SimpleOnOff) {
699 0 : SetupOutputVariable(state,
700 : "Zone Unit Heater Fan Part Load Ratio",
701 : OutputProcessor::Unit::None,
702 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio,
703 : OutputProcessor::SOVTimeStepType::System,
704 : OutputProcessor::SOVStoreType::Average,
705 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
706 : }
707 : }
708 :
709 77 : for (UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats; ++UnitHeatNum) {
710 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
711 15 : state.dataRptCoilSelection->coilSelectionReportObj->setCoilSupplyFanInfo(state,
712 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
713 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilTypeCh,
714 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
715 : DataAirSystems::ObjectVectorOOFanSystemModel,
716 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index);
717 : } else {
718 300 : state.dataRptCoilSelection->coilSelectionReportObj->setCoilSupplyFanInfo(state,
719 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
720 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilTypeCh,
721 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
722 : DataAirSystems::StructArrayLegacyFanModels,
723 60 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index);
724 : }
725 : }
726 14 : }
727 :
728 638739 : void InitUnitHeater(EnergyPlusData &state,
729 : int const UnitHeatNum, // index for the current unit heater
730 : int const ZoneNum, // number of zone being served
731 : [[maybe_unused]] bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
732 : )
733 : {
734 :
735 : // SUBROUTINE INFORMATION:
736 : // AUTHOR Rick Strand
737 : // DATE WRITTEN May 2000
738 : // MODIFIED Chandan Sharma, FSEC, March 2011: Added ZoneHVAC sys avail manager
739 : // RE-ENGINEERED na
740 :
741 : // PURPOSE OF THIS SUBROUTINE:
742 : // This subroutine initializes all of the data elements which are necessary
743 : // to simulate a unit heater.
744 :
745 : // METHODOLOGY EMPLOYED:
746 : // Uses the status flags to trigger initializations.
747 :
748 : // Using/Aliasing
749 : using DataHVACGlobals::FanType_SimpleOnOff;
750 638739 : auto &ZoneComp = state.dataHVACGlobal->ZoneComp;
751 638739 : auto &ZoneCompTurnFansOff = state.dataHVACGlobal->ZoneCompTurnFansOff;
752 638739 : auto &ZoneCompTurnFansOn = state.dataHVACGlobal->ZoneCompTurnFansOn;
753 :
754 : using DataZoneEquipment::CheckZoneEquipmentList;
755 : using FluidProperties::GetDensityGlycol;
756 : using PlantUtilities::InitComponentNodes;
757 : using PlantUtilities::ScanPlantLoopsForObject;
758 : using PlantUtilities::SetComponentFlowRate;
759 : using namespace DataZoneEnergyDemands;
760 : using WaterCoils::SimulateWaterCoilComponents;
761 :
762 : // SUBROUTINE PARAMETER DEFINITIONS:
763 : static constexpr std::string_view RoutineName("InitUnitHeater");
764 :
765 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
766 : int Loop;
767 : int HotConNode; // hot water control node number in unit heater loop
768 : int InNode; // inlet node number in unit heater loop
769 : int OutNode; // outlet node number in unit heater loop
770 : Real64 RhoAir; // air density at InNode
771 : Real64 TempSteamIn;
772 : Real64 SteamDensity;
773 : Real64 rho; // local fluid density
774 : bool errFlag;
775 :
776 : // Do the one time initializations
777 638739 : if (state.dataUnitHeaters->InitUnitHeaterOneTimeFlag) {
778 :
779 14 : state.dataUnitHeaters->MyEnvrnFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
780 14 : state.dataUnitHeaters->MySizeFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
781 14 : state.dataUnitHeaters->MyPlantScanFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
782 14 : state.dataUnitHeaters->MyZoneEqFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
783 14 : state.dataUnitHeaters->MyEnvrnFlag = true;
784 14 : state.dataUnitHeaters->MySizeFlag = true;
785 14 : state.dataUnitHeaters->MyPlantScanFlag = true;
786 14 : state.dataUnitHeaters->MyZoneEqFlag = true;
787 14 : state.dataUnitHeaters->InitUnitHeaterOneTimeFlag = false;
788 : }
789 :
790 638739 : if (allocated(ZoneComp)) {
791 638735 : if (state.dataUnitHeaters->MyZoneEqFlag(UnitHeatNum)) { // initialize the name of each availability manager list and zone number
792 126 : ZoneComp(DataZoneEquipment::ZoneEquip::UnitHeater).ZoneCompAvailMgrs(UnitHeatNum).AvailManagerListName =
793 126 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).AvailManagerListName;
794 63 : ZoneComp(DataZoneEquipment::ZoneEquip::UnitHeater).ZoneCompAvailMgrs(UnitHeatNum).ZoneNum = ZoneNum;
795 63 : state.dataUnitHeaters->MyZoneEqFlag(UnitHeatNum) = false;
796 : }
797 638735 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).AvailStatus =
798 638735 : ZoneComp(DataZoneEquipment::ZoneEquip::UnitHeater).ZoneCompAvailMgrs(UnitHeatNum).AvailStatus;
799 : }
800 :
801 638739 : if (state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) && allocated(state.dataPlnt->PlantLoop)) {
802 120 : if ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType == DataPlant::PlantEquipmentType::CoilWaterSimpleHeating) ||
803 57 : (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType == DataPlant::PlantEquipmentType::CoilSteamAirHeating)) {
804 6 : errFlag = false;
805 24 : ScanPlantLoopsForObject(state,
806 6 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
807 6 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType,
808 6 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc,
809 : errFlag,
810 : _,
811 : _,
812 : _,
813 : _,
814 : _);
815 6 : if (errFlag) {
816 0 : ShowContinueError(state,
817 0 : "Reference Unit=\"" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name + "\", type=ZoneHVAC:UnitHeater");
818 0 : ShowFatalError(state, "InitUnitHeater: Program terminated due to previous condition(s).");
819 : }
820 :
821 6 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum =
822 6 : DataPlant::CompData::getPlantComponent(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc).NodeNumOut;
823 : }
824 63 : state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) = false;
825 638676 : } else if (state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) && !state.dataGlobal->AnyPlantInModel) {
826 0 : state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) = false;
827 : }
828 : // need to check all units to see if they are on Zone Equipment List or issue warning
829 638739 : if (!state.dataUnitHeaters->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
830 14 : state.dataUnitHeaters->ZoneEquipmentListChecked = true;
831 77 : for (Loop = 1; Loop <= state.dataUnitHeaters->NumOfUnitHeats; ++Loop) {
832 63 : if (CheckZoneEquipmentList(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(Loop).Name)) continue;
833 0 : ShowSevereError(state,
834 0 : "InitUnitHeater: Unit=[UNIT HEATER," + state.dataUnitHeaters->UnitHeat(Loop).Name +
835 : "] is not on any ZoneHVAC:EquipmentList. It will not be simulated.");
836 : }
837 : }
838 :
839 638802 : if (!state.dataGlobal->SysSizingCalc && state.dataUnitHeaters->MySizeFlag(UnitHeatNum) &&
840 63 : !state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum)) {
841 :
842 63 : SizeUnitHeater(state, UnitHeatNum);
843 :
844 63 : state.dataUnitHeaters->MySizeFlag(UnitHeatNum) = false;
845 : } // Do the one time initializations
846 :
847 639080 : if (state.dataGlobal->BeginEnvrnFlag && state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) &&
848 341 : !state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum)) {
849 341 : InNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
850 341 : OutNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
851 341 : HotConNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode;
852 341 : RhoAir = state.dataEnvrn->StdRhoAir;
853 :
854 : // set the mass flow rates from the input volume flow rates
855 341 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow = RhoAir * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow;
856 :
857 : // set the node max and min mass flow rates
858 341 : state.dataLoopNodes->Node(OutNode).MassFlowRateMax = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
859 341 : state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
860 :
861 341 : state.dataLoopNodes->Node(InNode).MassFlowRateMax = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
862 341 : state.dataLoopNodes->Node(InNode).MassFlowRateMin = 0.0;
863 :
864 341 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
865 60 : rho = GetDensityGlycol(state,
866 30 : state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum).FluidName,
867 : DataGlobalConstants::HWInitConvTemp,
868 30 : state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum).FluidIndex,
869 : RoutineName);
870 :
871 30 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow = rho * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
872 30 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow = rho * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotWaterFlow;
873 120 : InitComponentNodes(state,
874 30 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow,
875 30 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow,
876 30 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
877 30 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum);
878 : }
879 341 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
880 0 : TempSteamIn = 100.00;
881 0 : SteamDensity = GetSatDensityRefrig(
882 0 : state, fluidNameSteam, TempSteamIn, 1.0, state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_FluidIndex, RoutineName);
883 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow =
884 0 : SteamDensity * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow;
885 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotSteamFlow =
886 0 : SteamDensity * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotSteamFlow;
887 :
888 0 : InitComponentNodes(state,
889 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotSteamFlow,
890 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow,
891 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
892 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum);
893 : }
894 :
895 341 : state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) = false;
896 : } // ...end start of environment inits
897 :
898 638739 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) = true;
899 :
900 : // These initializations are done every iteration...
901 638739 : InNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
902 638739 : OutNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
903 :
904 638739 : state.dataUnitHeaters->QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // zone load needed
905 638739 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanSchedPtr > 0) {
906 13320 : if (GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanSchedPtr) == 0.0 &&
907 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num == FanType_SimpleOnOff) {
908 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).OpMode = CycFanCycCoil;
909 : } else {
910 13320 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).OpMode = ContFanCycCoil;
911 : }
912 13320 : if ((state.dataUnitHeaters->QZnReq < SmallLoad) || state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) {
913 : // Unit is available, but there is no load on it or we are in setback/deadband
914 12122 : if (!state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating &&
915 4846 : GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanSchedPtr) > 0.0) {
916 4846 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).OpMode = ContFanCycCoil;
917 : }
918 : }
919 : }
920 :
921 638739 : state.dataUnitHeaters->SetMassFlowRateToZero = false;
922 638739 : if (GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).SchedPtr) > 0) {
923 1178194 : if ((GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanAvailSchedPtr) > 0 || ZoneCompTurnFansOn) &&
924 583363 : !ZoneCompTurnFansOff) {
925 1247985 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating &&
926 575180 : ((state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP < SmallLoad) ||
927 161988 : (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)))) {
928 251430 : state.dataUnitHeaters->SetMassFlowRateToZero = true;
929 : }
930 : } else {
931 11468 : state.dataUnitHeaters->SetMassFlowRateToZero = true;
932 : }
933 : } else {
934 43908 : state.dataUnitHeaters->SetMassFlowRateToZero = true;
935 : }
936 :
937 638739 : if (state.dataUnitHeaters->SetMassFlowRateToZero) {
938 306806 : state.dataLoopNodes->Node(InNode).MassFlowRate = 0.0;
939 306806 : state.dataLoopNodes->Node(InNode).MassFlowRateMaxAvail = 0.0;
940 306806 : state.dataLoopNodes->Node(InNode).MassFlowRateMinAvail = 0.0;
941 306806 : state.dataLoopNodes->Node(OutNode).MassFlowRate = 0.0;
942 306806 : state.dataLoopNodes->Node(OutNode).MassFlowRateMaxAvail = 0.0;
943 306806 : state.dataLoopNodes->Node(OutNode).MassFlowRateMinAvail = 0.0;
944 : } else {
945 331933 : state.dataLoopNodes->Node(InNode).MassFlowRate = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
946 331933 : state.dataLoopNodes->Node(InNode).MassFlowRateMaxAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
947 331933 : state.dataLoopNodes->Node(InNode).MassFlowRateMinAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
948 331933 : state.dataLoopNodes->Node(OutNode).MassFlowRate = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
949 331933 : state.dataLoopNodes->Node(OutNode).MassFlowRateMaxAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
950 331933 : state.dataLoopNodes->Node(OutNode).MassFlowRateMinAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
951 : }
952 :
953 : // Just in case the unit is off and conditions do not get sent through
954 : // the unit for some reason, set the outlet conditions equal to the inlet
955 : // conditions of the unit heater
956 638739 : state.dataLoopNodes->Node(OutNode).Temp = state.dataLoopNodes->Node(InNode).Temp;
957 638739 : state.dataLoopNodes->Node(OutNode).Press = state.dataLoopNodes->Node(InNode).Press;
958 638739 : state.dataLoopNodes->Node(OutNode).HumRat = state.dataLoopNodes->Node(InNode).HumRat;
959 638739 : state.dataLoopNodes->Node(OutNode).Enthalpy = state.dataLoopNodes->Node(InNode).Enthalpy;
960 638739 : }
961 :
962 63 : void SizeUnitHeater(EnergyPlusData &state, int const UnitHeatNum)
963 : {
964 :
965 : // SUBROUTINE INFORMATION:
966 : // AUTHOR Fred Buhl
967 : // DATE WRITTEN February 2002
968 : // MODIFIED August 2013 Daeho Kang, add component sizing table entries
969 : // July 2014, B. Nigusse, added scalable sizing
970 : // RE-ENGINEERED na
971 :
972 : // PURPOSE OF THIS SUBROUTINE:
973 : // This subroutine is for sizing Unit Heater components for which flow rates have not been
974 : // specified in the input.
975 :
976 : // METHODOLOGY EMPLOYED:
977 : // Obtains flow rates from the zone sizing arrays and plant sizing data.
978 :
979 : // Using/Aliasing
980 : using namespace DataSizing;
981 : using DataHVACGlobals::HeatingAirflowSizing;
982 : using DataHVACGlobals::HeatingCapacitySizing;
983 : using PlantUtilities::MyPlantSizingIndex;
984 : using Psychrometrics::CPHW;
985 : using SteamCoils::GetCoilSteamInletNode;
986 : using SteamCoils::GetCoilSteamOutletNode;
987 :
988 : // SUBROUTINE PARAMETER DEFINITIONS:
989 : static constexpr std::string_view RoutineName("SizeUnitHeater");
990 :
991 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
992 : int PltSizHeatNum; // index of plant sizing object for 1st heating loop
993 : bool ErrorsFound;
994 : Real64 DesCoilLoad;
995 : Real64 TempSteamIn;
996 : Real64 EnthSteamInDry;
997 : Real64 EnthSteamOutWet;
998 : Real64 LatentHeatSteam;
999 : Real64 SteamDensity;
1000 63 : int CoilWaterInletNode(0);
1001 63 : int CoilWaterOutletNode(0);
1002 63 : int CoilSteamInletNode(0);
1003 63 : int CoilSteamOutletNode(0);
1004 : Real64 Cp; // local temporary for fluid specific heat
1005 : Real64 rho; // local temporary for fluid density
1006 : bool IsAutoSize; // Indicator to autosize
1007 : Real64 MaxAirVolFlowDes; // Autosized maximum air flow for reporting
1008 : Real64 MaxAirVolFlowUser; // Hardsized maximum air flow for reporting
1009 : Real64 MaxVolHotWaterFlowDes; // Autosized maximum hot water flow for reporting
1010 : Real64 MaxVolHotWaterFlowUser; // Hardsized maximum hot water flow for reporting
1011 : Real64 MaxVolHotSteamFlowDes; // Autosized maximum hot steam flow for reporting
1012 : Real64 MaxVolHotSteamFlowUser; // Hardsized maximum hot steam flow for reporting
1013 126 : std::string CompName; // component name
1014 126 : std::string CompType; // component type
1015 126 : std::string SizingString; // input field sizing description (e.g., Nominal Capacity)
1016 : Real64 TempSize; // autosized value of coil input field
1017 63 : int FieldNum = 1; // IDD numeric field number where input field description is found
1018 : int SizingMethod; // Integer representation of sizing method name (e.g., CoolingAirflowSizing, HeatingAirflowSizing, CoolingCapacitySizing,
1019 : // HeatingCapacitySizing, etc.)
1020 : bool PrintFlag; // TRUE when sizing information is reported in the eio file
1021 : int zoneHVACIndex; // index of zoneHVAC equipment sizing specification
1022 63 : int SAFMethod(0); // supply air flow rate sizing method (SupplyAirFlowRate, FlowPerFloorArea, FractionOfAutosizedCoolingAirflow,
1023 : // FractionOfAutosizedHeatingAirflow ...)
1024 63 : int CapSizingMethod(0); // capacity sizing methods (HeatingDesignCapacity, CapacityPerFloorArea, FractionOfAutosizedCoolingCapacity, and
1025 : // FractionOfAutosizedHeatingCapacity )
1026 63 : bool DoWaterCoilSizing = false; // if TRUE do water coil sizing calculation
1027 : Real64 WaterCoilSizDeltaT; // water coil deltaT for design water flow rate autosizing
1028 : int CoilNum; // index of water coil object
1029 :
1030 63 : auto &ZoneEqSizing(state.dataSize->ZoneEqSizing);
1031 63 : auto &CurZoneEqNum(state.dataSize->CurZoneEqNum);
1032 :
1033 63 : PltSizHeatNum = 0;
1034 63 : ErrorsFound = false;
1035 63 : IsAutoSize = false;
1036 63 : MaxAirVolFlowDes = 0.0;
1037 63 : MaxAirVolFlowUser = 0.0;
1038 63 : MaxVolHotWaterFlowDes = 0.0;
1039 63 : MaxVolHotWaterFlowUser = 0.0;
1040 63 : MaxVolHotSteamFlowDes = 0.0;
1041 63 : MaxVolHotSteamFlowUser = 0.0;
1042 :
1043 63 : state.dataSize->DataScalableSizingON = false;
1044 63 : state.dataSize->DataScalableCapSizingON = false;
1045 63 : state.dataSize->ZoneHeatingOnlyFan = true;
1046 63 : CompType = "ZoneHVAC:UnitHeater";
1047 63 : CompName = state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name;
1048 63 : state.dataSize->DataZoneNumber = state.dataUnitHeaters->UnitHeat(UnitHeatNum).ZonePtr;
1049 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
1050 3 : state.dataSize->DataFanEnumType = DataAirSystems::ObjectVectorOOFanSystemModel;
1051 : } else {
1052 60 : state.dataSize->DataFanEnumType = DataAirSystems::StructArrayLegacyFanModels;
1053 : }
1054 63 : state.dataSize->DataFanIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index;
1055 : // unit heater is always blow thru
1056 63 : state.dataSize->DataFanPlacement = DataSizing::ZoneFanPlacement::BlowThru;
1057 :
1058 63 : if (CurZoneEqNum > 0) {
1059 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
1060 0 : zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
1061 0 : SizingMethod = HeatingAirflowSizing;
1062 0 : FieldNum = 1; // N1 , \field Maximum Supply Air Flow Rate
1063 0 : PrintFlag = true;
1064 0 : SizingString = state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames(FieldNum) + " [m3/s]";
1065 0 : SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod;
1066 0 : ZoneEqSizing(CurZoneEqNum).SizingMethod(SizingMethod) = SAFMethod;
1067 0 : if (SAFMethod == None || SAFMethod == SupplyAirFlowRate || SAFMethod == FlowPerFloorArea ||
1068 : SAFMethod == FractionOfAutosizedHeatingAirflow) {
1069 0 : if (SAFMethod == SupplyAirFlowRate) {
1070 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow > 0.0) {
1071 0 : ZoneEqSizing(CurZoneEqNum).AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1072 0 : ZoneEqSizing(CurZoneEqNum).SystemAirFlow = true;
1073 : }
1074 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1075 0 : } else if (SAFMethod == FlowPerFloorArea) {
1076 0 : ZoneEqSizing(CurZoneEqNum).SystemAirFlow = true;
1077 0 : ZoneEqSizing(CurZoneEqNum).AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow *
1078 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1079 0 : TempSize = ZoneEqSizing(CurZoneEqNum).AirVolFlow;
1080 0 : state.dataSize->DataScalableSizingON = true;
1081 0 : } else if (SAFMethod == FractionOfAutosizedHeatingAirflow) {
1082 0 : state.dataSize->DataFracOfAutosizedCoolingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1083 0 : TempSize = AutoSize;
1084 0 : state.dataSize->DataScalableSizingON = true;
1085 : } else {
1086 0 : TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1087 : }
1088 0 : bool errorsFound = false;
1089 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1090 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1091 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1092 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1093 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1094 :
1095 0 : } else if (SAFMethod == FlowPerHeatingCapacity) {
1096 0 : SizingMethod = HeatingCapacitySizing;
1097 0 : TempSize = AutoSize;
1098 0 : PrintFlag = false;
1099 0 : state.dataSize->DataScalableSizingON = true;
1100 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatVolFlow;
1101 0 : bool errorsFound = false;
1102 0 : HeatingCapacitySizer sizerHeatingCapacity;
1103 0 : sizerHeatingCapacity.overrideSizingString(SizingString);
1104 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1105 0 : TempSize = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1106 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod == FractionOfAutosizedHeatingCapacity) {
1107 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1108 : }
1109 0 : state.dataSize->DataAutosizedHeatingCapacity = TempSize;
1110 0 : state.dataSize->DataFlowPerHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
1111 0 : SizingMethod = HeatingAirflowSizing;
1112 0 : PrintFlag = true;
1113 0 : TempSize = AutoSize;
1114 0 : errorsFound = false;
1115 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1116 0 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1117 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1118 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1119 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1120 : }
1121 0 : state.dataSize->DataScalableSizingON = false;
1122 : } else {
1123 : // no scalble sizing method has been specified. Sizing proceeds using the method
1124 : // specified in the zoneHVAC object
1125 63 : SizingMethod = HeatingAirflowSizing;
1126 63 : FieldNum = 1; // N1 , \field Maximum Supply Air Flow Rate
1127 63 : PrintFlag = true;
1128 63 : SizingString = state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames(FieldNum) + " [m3/s]";
1129 63 : TempSize = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow;
1130 63 : bool errorsFound = false;
1131 126 : HeatingAirFlowSizer sizingHeatingAirFlow;
1132 63 : sizingHeatingAirFlow.overrideSizingString(SizingString);
1133 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1134 63 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1135 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1136 : }
1137 : }
1138 :
1139 63 : IsAutoSize = false;
1140 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow == AutoSize) {
1141 43 : IsAutoSize = true;
1142 : }
1143 :
1144 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
1145 :
1146 6 : if (CurZoneEqNum > 0) {
1147 6 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1148 3 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow > 0.0) {
1149 12 : BaseSizer::reportSizerOutput(state,
1150 : "ZoneHVAC:UnitHeater",
1151 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
1152 : "User-Specified Maximum Hot Water Flow [m3/s]",
1153 9 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow);
1154 : }
1155 : } else {
1156 3 : CheckZoneSizing(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
1157 :
1158 6 : CoilWaterInletNode = WaterCoils::GetCoilWaterInletNode(
1159 6 : state, "Coil:Heating:Water", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
1160 6 : CoilWaterOutletNode = WaterCoils::GetCoilWaterOutletNode(
1161 6 : state, "Coil:Heating:Water", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
1162 3 : if (IsAutoSize) {
1163 6 : PltSizHeatNum = MyPlantSizingIndex(state,
1164 : "Coil:Heating:Water",
1165 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1166 : CoilWaterInletNode,
1167 : CoilWaterOutletNode,
1168 : ErrorsFound);
1169 6 : CoilNum = WaterCoils::GetWaterCoilIndex(
1170 6 : state, "COIL:HEATING:WATER", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
1171 3 : if (state.dataWaterCoils->WaterCoil(CoilNum).UseDesignWaterDeltaTemp) {
1172 0 : WaterCoilSizDeltaT = state.dataWaterCoils->WaterCoil(CoilNum).DesignWaterDeltaTemp;
1173 0 : DoWaterCoilSizing = true;
1174 : } else {
1175 3 : if (PltSizHeatNum > 0) {
1176 3 : WaterCoilSizDeltaT = state.dataSize->PlantSizData(PltSizHeatNum).DeltaT;
1177 3 : DoWaterCoilSizing = true;
1178 : } else {
1179 0 : DoWaterCoilSizing = false;
1180 : // If there is no heating Plant Sizing object and autosizing was requested, issue fatal error message
1181 0 : ShowSevereError(state, "Autosizing of water coil requires a heating loop Sizing:Plant object");
1182 0 : ShowContinueError(state, "Occurs in ZoneHVAC:UnitHeater Object=" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
1183 0 : ErrorsFound = true;
1184 : }
1185 : }
1186 :
1187 3 : if (DoWaterCoilSizing) {
1188 3 : SizingMethod = HeatingCapacitySizing;
1189 3 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
1190 0 : zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
1191 0 : CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
1192 0 : ZoneEqSizing(CurZoneEqNum).SizingMethod(SizingMethod) = CapSizingMethod;
1193 0 : if (CapSizingMethod == HeatingDesignCapacity || CapSizingMethod == CapacityPerFloorArea ||
1194 : CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
1195 0 : if (CapSizingMethod == HeatingDesignCapacity) {
1196 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity == AutoSize) {
1197 0 : ZoneEqSizing(CurZoneEqNum).DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
1198 : } else {
1199 0 : ZoneEqSizing(CurZoneEqNum).DesHeatingLoad =
1200 0 : state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1201 : }
1202 0 : ZoneEqSizing(CurZoneEqNum).HeatingCapacity = true;
1203 0 : TempSize = AutoSize;
1204 0 : } else if (CapSizingMethod == CapacityPerFloorArea) {
1205 0 : ZoneEqSizing(CurZoneEqNum).HeatingCapacity = true;
1206 0 : ZoneEqSizing(CurZoneEqNum).DesHeatingLoad =
1207 0 : state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
1208 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1209 0 : state.dataSize->DataScalableCapSizingON = true;
1210 0 : } else if (CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
1211 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity =
1212 0 : state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1213 0 : state.dataSize->DataScalableCapSizingON = true;
1214 0 : TempSize = AutoSize;
1215 : }
1216 : }
1217 0 : PrintFlag = false;
1218 0 : bool errorsFound = false;
1219 0 : HeatingCapacitySizer sizerHeatingCapacity;
1220 0 : sizerHeatingCapacity.overrideSizingString(SizingString);
1221 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1222 0 : DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1223 0 : state.dataSize->DataScalableCapSizingON = false;
1224 : } else {
1225 3 : SizingString = "";
1226 3 : PrintFlag = false;
1227 3 : TempSize = AutoSize;
1228 3 : ZoneEqSizing(CurZoneEqNum).HeatingCapacity = true;
1229 3 : ZoneEqSizing(CurZoneEqNum).DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
1230 3 : bool errorsFound = false;
1231 6 : HeatingCapacitySizer sizerHeatingCapacity;
1232 3 : sizerHeatingCapacity.overrideSizingString(SizingString);
1233 3 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1234 3 : DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1235 : }
1236 :
1237 3 : if (DesCoilLoad >= SmallLoad) {
1238 6 : rho = GetDensityGlycol(
1239 : state,
1240 3 : state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum).FluidName,
1241 : DataGlobalConstants::HWInitConvTemp,
1242 3 : state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum).FluidIndex,
1243 : RoutineName);
1244 6 : Cp = GetSpecificHeatGlycol(
1245 : state,
1246 3 : state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum).FluidName,
1247 : DataGlobalConstants::HWInitConvTemp,
1248 3 : state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum).FluidIndex,
1249 : RoutineName);
1250 3 : MaxVolHotWaterFlowDes = DesCoilLoad / (WaterCoilSizDeltaT * Cp * rho);
1251 : } else {
1252 0 : MaxVolHotWaterFlowDes = 0.0;
1253 : }
1254 : }
1255 : }
1256 3 : if (IsAutoSize) {
1257 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow = MaxVolHotWaterFlowDes;
1258 9 : BaseSizer::reportSizerOutput(state,
1259 : "ZoneHVAC:UnitHeater",
1260 3 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
1261 : "Design Size Maximum Hot Water Flow [m3/s]",
1262 6 : MaxVolHotWaterFlowDes);
1263 : } else {
1264 0 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow > 0.0 && MaxVolHotWaterFlowDes > 0.0) {
1265 0 : MaxVolHotWaterFlowUser = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
1266 0 : BaseSizer::reportSizerOutput(state,
1267 : "ZoneHVAC:UnitHeater",
1268 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
1269 : "Design Size Maximum Hot Water Flow [m3/s]",
1270 : MaxVolHotWaterFlowDes,
1271 : "User-Specified Maximum Hot Water Flow [m3/s]",
1272 0 : MaxVolHotWaterFlowUser);
1273 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1274 0 : if ((std::abs(MaxVolHotWaterFlowDes - MaxVolHotWaterFlowUser) / MaxVolHotWaterFlowUser) >
1275 0 : state.dataSize->AutoVsHardSizingThreshold) {
1276 0 : ShowMessage(state,
1277 0 : "SizeUnitHeater: Potential issue with equipment sizing for ZoneHVAC:UnitHeater " +
1278 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
1279 0 : ShowContinueError(state,
1280 0 : format("User-Specified Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowUser));
1281 0 : ShowContinueError(
1282 0 : state, format("differs from Design Size Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowDes));
1283 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1284 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1285 : }
1286 : }
1287 : }
1288 : }
1289 : }
1290 : }
1291 : } else {
1292 57 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow = 0.0;
1293 : }
1294 :
1295 63 : IsAutoSize = false;
1296 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow == AutoSize) {
1297 43 : IsAutoSize = true;
1298 : }
1299 :
1300 63 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
1301 :
1302 0 : if (CurZoneEqNum > 0) {
1303 0 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1304 0 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow > 0.0) {
1305 0 : BaseSizer::reportSizerOutput(state,
1306 : "ZoneHVAC:UnitHeater",
1307 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
1308 : "User-Specified Maximum Steam Flow [m3/s]",
1309 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow);
1310 : }
1311 : } else {
1312 0 : CheckZoneSizing(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
1313 :
1314 0 : CoilSteamInletNode =
1315 0 : GetCoilSteamInletNode(state, "Coil:Heating:Steam", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
1316 0 : CoilSteamOutletNode =
1317 0 : GetCoilSteamInletNode(state, "Coil:Heating:Steam", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
1318 0 : if (IsAutoSize) {
1319 0 : PltSizHeatNum = MyPlantSizingIndex(state,
1320 : "Coil:Heating:Steam",
1321 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1322 : CoilSteamInletNode,
1323 : CoilSteamOutletNode,
1324 : ErrorsFound);
1325 0 : if (PltSizHeatNum > 0) {
1326 0 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
1327 0 : zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
1328 0 : SizingMethod = HeatingCapacitySizing;
1329 0 : CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
1330 0 : ZoneEqSizing(CurZoneEqNum).SizingMethod(SizingMethod) = CapSizingMethod;
1331 0 : if (CapSizingMethod == HeatingDesignCapacity || CapSizingMethod == CapacityPerFloorArea ||
1332 : CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
1333 0 : if (CapSizingMethod == HeatingDesignCapacity) {
1334 0 : if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity == AutoSize) {
1335 0 : ZoneEqSizing(CurZoneEqNum).DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
1336 : } else {
1337 0 : ZoneEqSizing(CurZoneEqNum).DesHeatingLoad =
1338 0 : state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1339 : }
1340 0 : ZoneEqSizing(CurZoneEqNum).HeatingCapacity = true;
1341 0 : TempSize = AutoSize;
1342 0 : } else if (CapSizingMethod == CapacityPerFloorArea) {
1343 0 : ZoneEqSizing(CurZoneEqNum).HeatingCapacity = true;
1344 0 : ZoneEqSizing(CurZoneEqNum).DesHeatingLoad =
1345 0 : state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
1346 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1347 0 : state.dataSize->DataScalableCapSizingON = true;
1348 0 : } else if (CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
1349 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity =
1350 0 : state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
1351 0 : TempSize = AutoSize;
1352 0 : state.dataSize->DataScalableCapSizingON = true;
1353 : }
1354 : }
1355 0 : SizingMethod = HeatingCapacitySizing;
1356 0 : PrintFlag = false;
1357 0 : bool errorsFound = false;
1358 0 : HeatingCapacitySizer sizerHeatingCapacity;
1359 0 : sizerHeatingCapacity.overrideSizingString(SizingString);
1360 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1361 0 : DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1362 0 : state.dataSize->DataScalableCapSizingON = false;
1363 : } else {
1364 0 : DesCoilLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
1365 : }
1366 0 : if (DesCoilLoad >= SmallLoad) {
1367 0 : TempSteamIn = 100.00;
1368 0 : EnthSteamInDry =
1369 0 : GetSatEnthalpyRefrig(state, fluidNameSteam, TempSteamIn, 1.0, state.dataUnitHeaters->RefrigIndex, RoutineName);
1370 0 : EnthSteamOutWet =
1371 0 : GetSatEnthalpyRefrig(state, fluidNameSteam, TempSteamIn, 0.0, state.dataUnitHeaters->RefrigIndex, RoutineName);
1372 0 : LatentHeatSteam = EnthSteamInDry - EnthSteamOutWet;
1373 0 : SteamDensity =
1374 0 : GetSatDensityRefrig(state, fluidNameSteam, TempSteamIn, 1.0, state.dataUnitHeaters->RefrigIndex, RoutineName);
1375 0 : MaxVolHotSteamFlowDes =
1376 0 : DesCoilLoad / (SteamDensity * (LatentHeatSteam + state.dataSize->PlantSizData(PltSizHeatNum).DeltaT *
1377 0 : CPHW(state.dataSize->PlantSizData(PltSizHeatNum).ExitTemp)));
1378 : } else {
1379 0 : MaxVolHotSteamFlowDes = 0.0;
1380 : }
1381 : } else {
1382 0 : ShowSevereError(state, "Autosizing of Steam flow requires a heating loop Sizing:Plant object");
1383 0 : ShowContinueError(state, "Occurs in ZoneHVAC:UnitHeater Object=" + state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
1384 0 : ErrorsFound = true;
1385 : }
1386 : }
1387 0 : if (IsAutoSize) {
1388 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow = MaxVolHotSteamFlowDes;
1389 0 : BaseSizer::reportSizerOutput(state,
1390 : "ZoneHVAC:UnitHeater",
1391 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
1392 : "Design Size Maximum Steam Flow [m3/s]",
1393 0 : MaxVolHotSteamFlowDes);
1394 : } else {
1395 0 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow > 0.0 && MaxVolHotSteamFlowDes > 0.0) {
1396 0 : MaxVolHotSteamFlowUser = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow;
1397 0 : BaseSizer::reportSizerOutput(state,
1398 : "ZoneHVAC:UnitHeater",
1399 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
1400 : "Design Size Maximum Steam Flow [m3/s]",
1401 : MaxVolHotSteamFlowDes,
1402 : "User-Specified Maximum Steam Flow [m3/s]",
1403 0 : MaxVolHotSteamFlowUser);
1404 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1405 0 : if ((std::abs(MaxVolHotSteamFlowDes - MaxVolHotSteamFlowUser) / MaxVolHotSteamFlowUser) >
1406 0 : state.dataSize->AutoVsHardSizingThreshold) {
1407 0 : ShowMessage(state,
1408 0 : "SizeUnitHeater: Potential issue with equipment sizing for ZoneHVAC:UnitHeater " +
1409 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
1410 0 : ShowContinueError(state, format("User-Specified Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowUser));
1411 0 : ShowContinueError(state,
1412 0 : format("differs from Design Size Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowDes));
1413 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1414 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1415 : }
1416 : }
1417 : }
1418 : }
1419 : }
1420 : }
1421 : } else {
1422 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow = 0.0;
1423 : }
1424 :
1425 : // set the design air flow rate for the heating coil
1426 :
1427 189 : WaterCoils::SetCoilDesFlow(state,
1428 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilTypeCh,
1429 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1430 63 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow,
1431 : ErrorsFound);
1432 63 : if (CurZoneEqNum > 0) {
1433 63 : ZoneEqSizing(CurZoneEqNum).MaxHWVolFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
1434 : }
1435 :
1436 63 : if (ErrorsFound) {
1437 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
1438 : }
1439 63 : }
1440 :
1441 638739 : void CalcUnitHeater(EnergyPlusData &state,
1442 : int &UnitHeatNum, // number of the current fan coil unit being simulated
1443 : int const ZoneNum, // number of zone being served
1444 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
1445 : Real64 &PowerMet, // Sensible power supplied (W)
1446 : Real64 &LatOutputProvided // Latent power supplied (kg/s), negative = dehumidification
1447 : )
1448 : {
1449 :
1450 : // SUBROUTINE INFORMATION:
1451 : // AUTHOR Rick Strand
1452 : // DATE WRITTEN May 2000
1453 : // MODIFIED Don Shirey, Aug 2009 (LatOutputProvided)
1454 : // July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
1455 : // RE-ENGINEERED na
1456 :
1457 : // PURPOSE OF THIS SUBROUTINE:
1458 : // This subroutine mainly controls the action of the unit heater
1459 : // based on the user input for controls and the defined controls
1460 : // algorithms. There are currently (at the initial creation of this
1461 : // subroutine) two control methods: on-off fan operation or continuous
1462 : // fan operation.
1463 :
1464 : // METHODOLOGY EMPLOYED:
1465 : // Unit is controlled based on user input and what is happening in the
1466 : // simulation. There are various cases to consider:
1467 : // 1. OFF: Unit is schedule off. All flow rates are set to zero and
1468 : // the temperatures are set to zone conditions.
1469 : // 2. NO LOAD OR COOLING/ON-OFF FAN CONTROL: Unit is available, but
1470 : // there is no heating load. All flow rates are set to zero and
1471 : // the temperatures are set to zone conditions.
1472 : // 3. NO LOAD OR COOLING/CONTINUOUS FAN CONTROL: Unit is available and
1473 : // the fan is running (if it is scheduled to be available also).
1474 : // No heating is provided, only circulation via the fan running.
1475 : // 4. HEATING: The unit is on/available and there is a heating load.
1476 : // The heating coil is modulated (constant fan speed) to meet the
1477 : // heating load.
1478 :
1479 : // REFERENCES:
1480 : // ASHRAE Systems and Equipment Handbook (SI), 1996. page 31.7
1481 :
1482 : // Using/Aliasing
1483 : using namespace DataZoneEnergyDemands;
1484 : using DataHVACGlobals::FanType_SimpleOnOff;
1485 638739 : auto &ZoneCompTurnFansOff = state.dataHVACGlobal->ZoneCompTurnFansOff;
1486 638739 : auto &ZoneCompTurnFansOn = state.dataHVACGlobal->ZoneCompTurnFansOn;
1487 : using General::SolveRoot;
1488 : using PlantUtilities::SetComponentFlowRate;
1489 :
1490 : // SUBROUTINE PARAMETER DEFINITIONS:
1491 638739 : int constexpr MaxIter(100); // maximum number of iterations
1492 :
1493 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1494 : int ControlNode; // the hot water inlet node
1495 : int InletNode; // unit air inlet node
1496 : int OutletNode; // unit air outlet node
1497 : Real64 ControlOffset; // tolerance for output control
1498 : Real64 MaxWaterFlow; // maximum water flow for heating or cooling [kg/sec]
1499 : Real64 MinWaterFlow; // minimum water flow for heating or cooling [kg/sec]
1500 : Real64 QUnitOut; // heating or sens. cooling provided by fan coil unit [watts]
1501 : Real64 LatentOutput; // Latent (moisture) add/removal rate, negative is dehumidification [kg/s]
1502 : Real64 SpecHumOut; // Specific humidity ratio of outlet air (kg moisture / kg moist air)
1503 : Real64 SpecHumIn; // Specific humidity ratio of inlet air (kg moisture / kg moist air)
1504 : Real64 mdot; // local temporary for fluid mass flow rate
1505 : int OpMode;
1506 : Real64 PartLoadFrac;
1507 : Real64 NoOutput;
1508 : Real64 FullOutput;
1509 : int SolFlag; // return flag from RegulaFalsi for sensible load
1510 : bool UnitOn;
1511 :
1512 : // initialize local variables
1513 638739 : QUnitOut = 0.0;
1514 638739 : NoOutput = 0.0;
1515 638739 : FullOutput = 0.0;
1516 638739 : LatentOutput = 0.0;
1517 638739 : MaxWaterFlow = 0.0;
1518 638739 : MinWaterFlow = 0.0;
1519 638739 : PartLoadFrac = 0.0;
1520 638739 : SolFlag = 0; // # of iterations IF positive, -1 means failed to converge, -2 means bounds are incorrect
1521 638739 : InletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
1522 638739 : OutletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
1523 638739 : ControlNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode;
1524 638739 : ControlOffset = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlOffset;
1525 638739 : UnitOn = false;
1526 638739 : OpMode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).OpMode;
1527 :
1528 638739 : if (OpMode != CycFanCycCoil) {
1529 :
1530 1288946 : if (GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).SchedPtr) <= 0 ||
1531 1189662 : ((GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanAvailSchedPtr) <= 0 && !ZoneCompTurnFansOn) ||
1532 583363 : ZoneCompTurnFansOff)) {
1533 : // Case 1: OFF-->unit schedule says that it it not available
1534 : // OR child fan in not available OR child fan not being cycled ON by sys avail manager
1535 : // OR child fan being forced OFF by sys avail manager
1536 55376 : state.dataUnitHeaters->HCoilOn = false;
1537 55376 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
1538 4585 : mdot = 0.0; // try to turn off
1539 :
1540 13755 : SetComponentFlowRate(state,
1541 : mdot,
1542 4585 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1543 4585 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1544 4585 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1545 : }
1546 55376 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
1547 0 : mdot = 0.0; // try to turn off
1548 :
1549 0 : SetComponentFlowRate(state,
1550 : mdot,
1551 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1552 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1553 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1554 : }
1555 55376 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
1556 :
1557 583363 : } else if ((state.dataUnitHeaters->QZnReq < SmallLoad) || state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) {
1558 : // Unit is available, but there is no load on it or we are in setback/deadband
1559 377103 : if (!state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating) {
1560 :
1561 : // Case 2: NO LOAD OR COOLING/ON-OFF FAN CONTROL-->turn everything off
1562 : // because there is no load on the unit heater
1563 125673 : state.dataUnitHeaters->HCoilOn = false;
1564 125673 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
1565 9403 : mdot = 0.0; // try to turn off
1566 :
1567 28209 : SetComponentFlowRate(state,
1568 : mdot,
1569 9403 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1570 9403 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1571 9403 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1572 : }
1573 125673 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
1574 0 : mdot = 0.0; // try to turn off
1575 :
1576 0 : SetComponentFlowRate(state,
1577 : mdot,
1578 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1579 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1580 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1581 : }
1582 125673 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
1583 :
1584 : } else {
1585 : // Case 3: NO LOAD OR COOLING/CONTINUOUS FAN CONTROL-->let the fan
1586 : // continue to run even though there is no load (air circulation)
1587 : // Note that the flow rates were already set in the initialization routine
1588 : // so there is really nothing else left to do except call the components.
1589 :
1590 251430 : state.dataUnitHeaters->HCoilOn = false;
1591 251430 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
1592 5751 : mdot = 0.0; // try to turn off
1593 :
1594 5751 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum > 0) {
1595 17253 : SetComponentFlowRate(state,
1596 : mdot,
1597 5751 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1598 5751 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1599 5751 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1600 : }
1601 : }
1602 251430 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
1603 0 : mdot = 0.0; // try to turn off
1604 0 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum > 0) {
1605 0 : SetComponentFlowRate(state,
1606 : mdot,
1607 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1608 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1609 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1610 : }
1611 : }
1612 :
1613 251430 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
1614 : }
1615 :
1616 : } else { // Case 4: HEATING-->unit is available and there is a heating load
1617 :
1618 206260 : switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
1619 :
1620 13684 : case HCoilType::WaterHeatingCoil: {
1621 :
1622 : // On the first HVAC iteration the system values are given to the controller, but after that
1623 : // the demand limits are in place and there needs to be feedback to the Zone Equipment
1624 13684 : if (FirstHVACIteration) {
1625 6842 : MaxWaterFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow;
1626 6842 : MinWaterFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow;
1627 : } else {
1628 6842 : MaxWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMaxAvail;
1629 6842 : MinWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMinAvail;
1630 : }
1631 : // control water flow to obtain output matching QZnReq
1632 95788 : ControlCompOutput(state,
1633 13684 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
1634 13684 : state.dataUnitHeaters->cMO_UnitHeater,
1635 : UnitHeatNum,
1636 : FirstHVACIteration,
1637 13684 : state.dataUnitHeaters->QZnReq,
1638 : ControlNode,
1639 : MaxWaterFlow,
1640 : MinWaterFlow,
1641 : ControlOffset,
1642 13684 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ControlCompTypeNum,
1643 13684 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).CompErrIndex,
1644 : _,
1645 : _,
1646 : _,
1647 : _,
1648 : _,
1649 13684 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1650 13684 : break;
1651 : }
1652 192576 : case HCoilType::Electric:
1653 : case HCoilType::Gas:
1654 : case HCoilType::SteamCoil: {
1655 192576 : state.dataUnitHeaters->HCoilOn = true;
1656 192576 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
1657 192576 : break;
1658 : }
1659 0 : default:
1660 0 : break;
1661 : }
1662 : }
1663 1277478 : QUnitOut = state.dataLoopNodes->Node(OutletNode).MassFlowRate *
1664 1277478 : (PsyHFnTdbW(state.dataLoopNodes->Node(OutletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat) -
1665 638739 : PsyHFnTdbW(state.dataLoopNodes->Node(InletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat));
1666 638739 : if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax > 0.0) {
1667 561150 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio =
1668 561150 : state.dataLoopNodes->Node(InletNode).MassFlowRate / state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
1669 : }
1670 : } else { // OnOff fan and cycling
1671 0 : if ((state.dataUnitHeaters->QZnReq < SmallLoad) || (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) ||
1672 0 : GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).SchedPtr) <= 0 ||
1673 0 : ((GetCurrentScheduleValue(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanAvailSchedPtr) <= 0 && !ZoneCompTurnFansOn) ||
1674 0 : ZoneCompTurnFansOff)) {
1675 : // Case 1: OFF-->unit schedule says that it it not available
1676 : // OR child fan in not available OR child fan not being cycled ON by sys avail manager
1677 : // OR child fan being forced OFF by sys avail manager
1678 0 : PartLoadFrac = 0.0;
1679 0 : state.dataUnitHeaters->HCoilOn = false;
1680 0 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, OpMode, PartLoadFrac);
1681 :
1682 0 : if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax > 0.0) {
1683 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio =
1684 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate / state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
1685 : }
1686 :
1687 : } else { // Case 4: HEATING-->unit is available and there is a heating load
1688 :
1689 0 : state.dataUnitHeaters->HCoilOn = true;
1690 0 : UnitOn = true;
1691 :
1692 : // Find part load ratio of unit heater coils
1693 0 : PartLoadFrac = 0.0;
1694 0 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, NoOutput, OpMode, PartLoadFrac);
1695 0 : if ((NoOutput - state.dataUnitHeaters->QZnReq) < SmallLoad) {
1696 : // Unit heater is unable to meet the load with coil off, set PLR = 1
1697 0 : PartLoadFrac = 1.0;
1698 0 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, FullOutput, OpMode, PartLoadFrac);
1699 0 : if ((FullOutput - state.dataUnitHeaters->QZnReq) > SmallLoad) {
1700 : // Unit heater full load capacity is able to meet the load, Find PLR
1701 0 : auto opMode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).OpMode;
1702 :
1703 0 : auto f = [&state, UnitHeatNum, FirstHVACIteration, opMode](Real64 const PartLoadRatio) {
1704 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1705 : Real64 QUnitOut; // heating provided by unit heater [watts]
1706 :
1707 0 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, opMode, PartLoadRatio);
1708 :
1709 : // Calculate residual based on output calculation flag
1710 0 : if (state.dataUnitHeaters->QZnReq != 0.0) {
1711 0 : return (QUnitOut - state.dataUnitHeaters->QZnReq) / state.dataUnitHeaters->QZnReq;
1712 : } else
1713 0 : return 0.0;
1714 0 : };
1715 :
1716 : // Tolerance is in fraction of load, MaxIter = 30, SolFalg = # of iterations or error as appropriate
1717 0 : SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadFrac, f, 0.0, 1.0);
1718 : }
1719 : }
1720 :
1721 0 : CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, OpMode, PartLoadFrac);
1722 :
1723 : } // ...end of unit ON/OFF IF-THEN block
1724 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).PartLoadFrac = PartLoadFrac;
1725 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio = PartLoadFrac;
1726 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
1727 : }
1728 :
1729 : // CR9155 Remove specific humidity calculations
1730 638739 : SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
1731 638739 : SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
1732 638739 : LatentOutput = state.dataLoopNodes->Node(OutletNode).MassFlowRate * (SpecHumOut - SpecHumIn); // Latent rate (kg/s), dehumid = negative
1733 :
1734 1277478 : QUnitOut = state.dataLoopNodes->Node(OutletNode).MassFlowRate *
1735 1277478 : (PsyHFnTdbW(state.dataLoopNodes->Node(OutletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat) -
1736 638739 : PsyHFnTdbW(state.dataLoopNodes->Node(InletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat));
1737 :
1738 : // Report variables...
1739 638739 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatPower = max(0.0, QUnitOut);
1740 638739 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num != DataHVACGlobals::FanType_SystemModelObject) {
1741 625419 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower = Fans::GetFanPower(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index);
1742 : } else {
1743 13320 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower =
1744 13320 : state.dataHVACFan->fanObjs[state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index]->fanPower();
1745 : }
1746 :
1747 638739 : PowerMet = QUnitOut;
1748 638739 : LatOutputProvided = LatentOutput;
1749 638739 : }
1750 :
1751 791702 : void CalcUnitHeaterComponents(EnergyPlusData &state,
1752 : int const UnitHeatNum, // Unit index in unit heater array
1753 : bool const FirstHVACIteration, // flag for 1st HVAV iteration in the time step
1754 : Real64 &LoadMet, // load met by unit (watts)
1755 : Optional_int_const OpMode, // fan operating mode
1756 : Optional<Real64 const> PartLoadRatio // part-load ratio
1757 : )
1758 : {
1759 :
1760 : // SUBROUTINE INFORMATION:
1761 : // AUTHOR Rick Strand
1762 : // DATE WRITTEN May 2000
1763 : // MODIFIED July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
1764 : // RE-ENGINEERED na
1765 :
1766 : // PURPOSE OF THIS SUBROUTINE:
1767 : // This subroutine launches the individual component simulations.
1768 : // This is called either when the unit is off to carry null conditions
1769 : // through the unit or during control iterations to continue updating
1770 : // what is going on within the unit.
1771 :
1772 : // METHODOLOGY EMPLOYED:
1773 : // Simply calls the different components in order.
1774 :
1775 : // Using/Aliasing
1776 : using DataHVACGlobals::FanType_SimpleOnOff;
1777 791702 : auto &ZoneCompTurnFansOff = state.dataHVACGlobal->ZoneCompTurnFansOff;
1778 791702 : auto &ZoneCompTurnFansOn = state.dataHVACGlobal->ZoneCompTurnFansOn;
1779 : using HeatingCoils::SimulateHeatingCoilComponents;
1780 : using PlantUtilities::SetComponentFlowRate;
1781 : using SteamCoils::SimulateSteamCoilComponents;
1782 : using WaterCoils::SimulateWaterCoilComponents;
1783 :
1784 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1785 : Real64 AirMassFlow; // total mass flow through the unit
1786 : Real64 CpAirZn; // specific heat of dry air at zone conditions (zone conditions same as unit inlet)
1787 : int HCoilInAirNode; // inlet node number for fan exit/coil inlet
1788 : int InletNode; // unit air inlet node
1789 : int OutletNode; // unit air outlet node
1790 : Real64 QCoilReq; // Heat addition required from an electric/gas heating coil
1791 : Real64 mdot; // local temporary for fluid mass flow rate
1792 : int FanOpMode; // Fan operting mode or fan type
1793 : Real64 PartLoadFrac; // part-load ratio
1794 :
1795 791702 : InletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
1796 791702 : OutletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
1797 791702 : QCoilReq = 0.0;
1798 :
1799 791702 : if (present(PartLoadRatio)) {
1800 0 : PartLoadFrac = PartLoadRatio;
1801 : } else {
1802 791702 : PartLoadFrac = 1.0;
1803 : }
1804 791702 : if (present(OpMode)) {
1805 0 : FanOpMode = OpMode;
1806 : } else {
1807 791702 : FanOpMode = ContFanCycCoil;
1808 : }
1809 791702 : if (FanOpMode != CycFanCycCoil) {
1810 791702 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num != DataHVACGlobals::FanType_SystemModelObject) {
1811 2140944 : Fans::SimulateFanComponents(state,
1812 713648 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
1813 : FirstHVACIteration,
1814 713648 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index,
1815 : _,
1816 : ZoneCompTurnFansOn,
1817 : ZoneCompTurnFansOff);
1818 : } else {
1819 78054 : state.dataHVACFan->fanObjs[state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index]->simulate(
1820 : state, _, ZoneCompTurnFansOn, ZoneCompTurnFansOff, _);
1821 : }
1822 :
1823 791702 : switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
1824 :
1825 186386 : case HCoilType::WaterHeatingCoil: {
1826 :
1827 559158 : SimulateWaterCoilComponents(state,
1828 186386 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1829 : FirstHVACIteration,
1830 186386 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index);
1831 186386 : break;
1832 : }
1833 0 : case HCoilType::SteamCoil: {
1834 :
1835 0 : if (!state.dataUnitHeaters->HCoilOn) {
1836 0 : QCoilReq = 0.0;
1837 : } else {
1838 0 : HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
1839 0 : CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
1840 0 : QCoilReq =
1841 0 : state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
1842 0 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
1843 0 : state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
1844 : }
1845 0 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
1846 0 : SimulateSteamCoilComponents(state,
1847 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1848 : FirstHVACIteration,
1849 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
1850 : QCoilReq);
1851 0 : break;
1852 : }
1853 605316 : case HCoilType::Electric:
1854 : case HCoilType::Gas: {
1855 :
1856 605316 : if (!state.dataUnitHeaters->HCoilOn) {
1857 412740 : QCoilReq = 0.0;
1858 : } else {
1859 192576 : HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
1860 192576 : CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
1861 192576 : QCoilReq =
1862 385152 : state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
1863 385152 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
1864 192576 : state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
1865 : }
1866 605316 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
1867 1815948 : SimulateHeatingCoilComponents(state,
1868 605316 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1869 : FirstHVACIteration,
1870 : QCoilReq,
1871 605316 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index);
1872 605316 : break;
1873 : }
1874 0 : default:
1875 0 : break;
1876 : }
1877 :
1878 791702 : AirMassFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
1879 :
1880 791702 : state.dataLoopNodes->Node(InletNode).MassFlowRate =
1881 791702 : state.dataLoopNodes->Node(OutletNode).MassFlowRate; // maintain continuity through unit heater
1882 :
1883 : } else { // OnOff fan cycling
1884 :
1885 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRateMax * PartLoadFrac;
1886 0 : AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
1887 : // Set the fan inlet node maximum available mass flow rates for cycling fans
1888 0 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
1889 :
1890 0 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
1891 0 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanType_Num != DataHVACGlobals::FanType_SystemModelObject) {
1892 0 : Fans::SimulateFanComponents(state,
1893 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanName,
1894 : FirstHVACIteration,
1895 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index,
1896 : _,
1897 : ZoneCompTurnFansOn,
1898 : ZoneCompTurnFansOff);
1899 : } else {
1900 0 : state.dataHVACFan->fanObjs[state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index]->simulate(
1901 : state, _, ZoneCompTurnFansOn, ZoneCompTurnFansOff, _);
1902 : }
1903 0 : switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
1904 :
1905 0 : case HCoilType::WaterHeatingCoil: {
1906 :
1907 0 : if (!state.dataUnitHeaters->HCoilOn) {
1908 0 : mdot = 0.0;
1909 0 : QCoilReq = 0.0;
1910 : } else {
1911 0 : HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
1912 0 : CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
1913 0 : QCoilReq =
1914 0 : state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
1915 0 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
1916 0 : state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
1917 0 : mdot = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow * PartLoadFrac;
1918 : }
1919 0 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
1920 0 : SetComponentFlowRate(state,
1921 : mdot,
1922 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1923 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1924 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1925 0 : SimulateWaterCoilComponents(state,
1926 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1927 : FirstHVACIteration,
1928 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
1929 : QCoilReq,
1930 : FanOpMode,
1931 : PartLoadFrac);
1932 0 : break;
1933 : }
1934 0 : case HCoilType::SteamCoil: {
1935 0 : if (!state.dataUnitHeaters->HCoilOn) {
1936 0 : mdot = 0.0;
1937 0 : QCoilReq = 0.0;
1938 : } else {
1939 0 : HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
1940 0 : CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
1941 0 : QCoilReq =
1942 0 : state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
1943 0 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
1944 0 : state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
1945 0 : mdot = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow * PartLoadFrac;
1946 : }
1947 0 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
1948 0 : SetComponentFlowRate(state,
1949 : mdot,
1950 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
1951 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
1952 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
1953 0 : SimulateSteamCoilComponents(state,
1954 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1955 : FirstHVACIteration,
1956 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
1957 : QCoilReq,
1958 : _,
1959 : FanOpMode,
1960 : PartLoadFrac);
1961 0 : break;
1962 : }
1963 0 : case HCoilType::Electric:
1964 : case HCoilType::Gas: {
1965 :
1966 0 : if (!state.dataUnitHeaters->HCoilOn) {
1967 0 : QCoilReq = 0.0;
1968 : } else {
1969 0 : HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
1970 0 : CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
1971 0 : QCoilReq =
1972 0 : state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
1973 0 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
1974 0 : state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
1975 : }
1976 0 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
1977 0 : SimulateHeatingCoilComponents(state,
1978 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
1979 : FirstHVACIteration,
1980 : QCoilReq,
1981 0 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
1982 : _,
1983 : _,
1984 : FanOpMode,
1985 : PartLoadFrac);
1986 0 : break;
1987 : }
1988 0 : default:
1989 0 : break;
1990 : }
1991 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
1992 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate; // maintain continuity through unit heater
1993 : }
1994 1583404 : LoadMet = AirMassFlow * (PsyHFnTdbW(state.dataLoopNodes->Node(OutletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat) -
1995 791702 : PsyHFnTdbW(state.dataLoopNodes->Node(InletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat));
1996 791702 : }
1997 :
1998 : // SUBROUTINE UpdateUnitHeater
1999 :
2000 : // No update routine needed in this module since all of the updates happen on
2001 : // the Node derived type directly and these updates are done by other routines.
2002 :
2003 : // END SUBROUTINE UpdateUnitHeater
2004 :
2005 638739 : void ReportUnitHeater(EnergyPlusData &state, int const UnitHeatNum) // Unit index in unit heater array
2006 : {
2007 :
2008 : // SUBROUTINE INFORMATION:
2009 : // AUTHOR Rick Strand
2010 : // DATE WRITTEN May 2000
2011 : // MODIFIED na
2012 : // RE-ENGINEERED na
2013 :
2014 : // PURPOSE OF THIS SUBROUTINE:
2015 : // This subroutine needs a description.
2016 :
2017 : // METHODOLOGY EMPLOYED:
2018 : // Needs description, as appropriate.
2019 :
2020 : // Using/Aliasing
2021 638739 : auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
2022 :
2023 638739 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatEnergy =
2024 638739 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatPower * TimeStepSys * DataGlobalConstants::SecInHour;
2025 638739 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecEnergy =
2026 638739 : state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower * TimeStepSys * DataGlobalConstants::SecInHour;
2027 :
2028 638739 : if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FirstPass) { // reset sizing flags so other zone equipment can size normally
2029 116 : if (!state.dataGlobal->SysSizingCalc) {
2030 63 : DataSizing::resetHVACSizingGlobals(state, state.dataSize->CurZoneEqNum, 0, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FirstPass);
2031 : }
2032 : }
2033 638739 : }
2034 :
2035 : } // namespace UnitHeater
2036 :
2037 2313 : } // namespace EnergyPlus
|