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