Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // ObjexxFCL Headers
49 : #include <ObjexxFCL/Array.functions.hh>
50 : #include <ObjexxFCL/Optional.hh>
51 : #include <ObjexxFCL/floops.hh>
52 : #include <ObjexxFCL/member.functions.hh>
53 :
54 : // EnergyPlus Headers
55 : #include <EnergyPlus/Autosizing/Base.hh>
56 : #include <EnergyPlus/BranchNodeConnections.hh>
57 : #include <EnergyPlus/Coils/CoilCoolingDX.hh>
58 : #include <EnergyPlus/CurveManager.hh>
59 : #include <EnergyPlus/DXCoils.hh>
60 : #include <EnergyPlus/Data/EnergyPlusData.hh>
61 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
62 : #include <EnergyPlus/DataHVACGlobals.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataLoopNode.hh>
66 : #include <EnergyPlus/DataSizing.hh>
67 : #include <EnergyPlus/DataZoneEquipment.hh>
68 : #include <EnergyPlus/Fans.hh>
69 : #include <EnergyPlus/FluidProperties.hh>
70 : #include <EnergyPlus/General.hh>
71 : #include <EnergyPlus/GeneralRoutines.hh>
72 : #include <EnergyPlus/GlobalNames.hh>
73 : #include <EnergyPlus/HVACFan.hh>
74 : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
75 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
76 : #include <EnergyPlus/IntegratedHeatPump.hh>
77 : #include <EnergyPlus/NodeInputManager.hh>
78 : #include <EnergyPlus/OutAirNodeManager.hh>
79 : #include <EnergyPlus/OutputProcessor.hh>
80 : #include <EnergyPlus/OutputReportPredefined.hh>
81 : #include <EnergyPlus/Plant/DataPlant.hh>
82 : #include <EnergyPlus/Plant/PlantLocation.hh>
83 : #include <EnergyPlus/PlantUtilities.hh>
84 : #include <EnergyPlus/Psychrometrics.hh>
85 : #include <EnergyPlus/RefrigeratedCase.hh>
86 : #include <EnergyPlus/ScheduleManager.hh>
87 : #include <EnergyPlus/SolarCollectors.hh>
88 : #include <EnergyPlus/VariableSpeedCoils.hh>
89 : #include <EnergyPlus/WaterThermalTanks.hh>
90 : #include <EnergyPlus/WaterToAirHeatPumpSimple.hh>
91 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
92 :
93 : namespace EnergyPlus::WaterThermalTanks {
94 :
95 : // MODULE INFORMATION:
96 : // AUTHOR Brandon Anderson
97 : // DATE WRITTEN May 2000
98 : // MODIFIED Feb 2005, PGE; July 2005, FSEC - added HPWH's and desuperheater water heating coils
99 : // Jan 2007, PGE - added stratified water heater
100 : // Oct 2007, BTG - extended for indirect water heater
101 : // May 2008, Stovall - added desup from condenser and removed double counting
102 : // (includes "d0"s from revision 145)
103 : // Nov 2011, BAN; corrected use and source outlet temp. calculation of stratified tank
104 : // RE-ENGINEERED Feb 2004, PGE
105 : // Sep 2008, BTG - refactored, was PlantWaterHeater.cc is now PlantWaterThermalTank.cc
106 : // reuse water heater code for chilled water storage
107 :
108 : // PURPOSE OF THIS MODULE:
109 : // This module simulates water thermal storage tanks heaters in the plant loop. Tanks can
110 : // be positioned as supply side equipment or demand side equipment. Water heater versions can be stand-alone as
111 : // non-zone equipment.
112 :
113 : // METHODOLOGY EMPLOYED:
114 : // Two water thermal tank models are implemented, MIXED and STRATIFIED with hot and cold versions of each:
115 : // WaterHeater:Mixed simulates a well-mixed, single-node tank for hot water applications. Source (e.g. heat recovery) and
116 : // use plant connections are allowed. A scheduled domestic hot water demand can also be specified
117 : // to directly utilize the hot water without use side connections.
118 : // WaterHeater:Stratified simulates a stratified, multi-node tank for hot water applicatons.
119 : // The model shares most of the same capabilities as WaterHeater:Mixed
120 : // but also has up to two heating elements which can be operated in
121 : // a master-slave mode or simultaneous mode.
122 :
123 : // ThermalStorage:ChilledWater:Mixed simulates a well-mixed, single-node tank for chilled water applications
124 :
125 : // ThermalStorage:ChilledWater:Stratified simulates a stratified, multi-node tank for chilled water applications.
126 :
127 771 : std::string const cMixedWHModuleObj = "WaterHeater:Mixed";
128 771 : std::string const cStratifiedWHModuleObj = "WaterHeater:Stratified";
129 771 : std::string const cMixedCWTankModuleObj = "ThermalStorage:ChilledWater:Mixed";
130 771 : std::string const cStratifiedCWTankModuleObj = "ThermalStorage:ChilledWater:Stratified";
131 771 : std::string const cHPWHPumpedCondenser = "WaterHeater:HeatPump:PumpedCondenser";
132 771 : std::string const cHPWHWrappedCondenser = "WaterHeater:HeatPump:WrappedCondenser";
133 771 : std::string const cCoilDesuperheater = "Coil:WaterHeating:Desuperheater";
134 771 : std::string const fluidNameWater = "WATER";
135 :
136 136 : PlantComponent *WaterThermalTankData::factory(EnergyPlusData &state, std::string const &objectName)
137 : {
138 : // Process the input data
139 136 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
140 10 : GetWaterThermalTankInput(state);
141 10 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
142 : }
143 :
144 : // Now look for this object in the list
145 156 : for (auto &tank : state.dataWaterThermalTanks->WaterThermalTank) {
146 156 : if (tank.Name == objectName) {
147 136 : return &tank;
148 : }
149 : }
150 : // If we didn't find it, fatal
151 : ShowFatalError(state, "LocalWaterTankFactory: Error getting inputs for tank named: " + objectName); // LCOV_EXCL_LINE
152 : // Shut up the compiler
153 : return nullptr; // LCOV_EXCL_LINE
154 : }
155 :
156 736 : void WaterThermalTankData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
157 : {
158 736 : this->initialize(state, true);
159 736 : this->MinePlantStructForInfo(state);
160 736 : if (calledFromLocation.loopNum > 0) {
161 736 : if ((this->SrcSidePlantLoc.loopNum == calledFromLocation.loopNum) || (this->UseSidePlantLoc.loopNum == calledFromLocation.loopNum)) {
162 731 : this->SizeTankForDemandSide(state);
163 731 : this->SizeDemandSidePlantConnections(state);
164 731 : this->SizeSupplySidePlantConnections(state, calledFromLocation.loopNum);
165 731 : this->SizeTankForSupplySide(state);
166 : } else {
167 5 : return;
168 : }
169 : } else {
170 0 : this->SizeTankForDemandSide(state);
171 0 : this->SizeDemandSidePlantConnections(state);
172 0 : this->SizeSupplySidePlantConnections(state);
173 0 : this->SizeTankForSupplySide(state);
174 : }
175 :
176 731 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
177 159 : if (!this->IsChilledWaterTank) {
178 150 : this->CalcStandardRatings(state);
179 : } else {
180 9 : this->ReportCWTankInits(state);
181 : }
182 : }
183 : }
184 :
185 686 : void WaterThermalTankData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
186 : [[maybe_unused]] const PlantLocation &calledFromLocation,
187 : Real64 &MaxLoad,
188 : Real64 &MinLoad,
189 : Real64 &OptLoad)
190 : {
191 686 : MinLoad = 0.0;
192 686 : MaxLoad = this->MaxCapacity;
193 686 : OptLoad = this->MaxCapacity;
194 686 : }
195 :
196 0 : int getTankIDX(EnergyPlusData &state, std::string_view CompName, int &CompIndex)
197 : {
198 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
199 0 : GetWaterThermalTankInput(state);
200 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
201 : }
202 :
203 : int CompNum;
204 :
205 0 : if (CompIndex == 0) {
206 0 : CompNum = UtilityRoutines::FindItem(CompName, state.dataWaterThermalTanks->WaterThermalTank);
207 0 : if (CompNum == 0) {
208 0 : ShowFatalError(state, "SimWaterThermalTank_WaterTank: Unit not found=" + std::string{CompName});
209 : }
210 0 : CompIndex = CompNum;
211 : } else {
212 0 : CompNum = CompIndex;
213 0 : if (CompNum > state.dataWaterThermalTanks->numWaterThermalTank || CompNum < 1) {
214 0 : ShowFatalError(state,
215 0 : format("SimWaterThermalTank_WaterTank: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
216 : CompNum,
217 0 : state.dataWaterThermalTanks->numWaterThermalTank,
218 0 : CompName));
219 : }
220 0 : if (state.dataWaterThermalTanks->WaterThermalTank(CompNum).CheckWTTEquipName) {
221 0 : if (CompName != state.dataWaterThermalTanks->WaterThermalTank(CompNum).Name) {
222 0 : ShowFatalError(state,
223 0 : format("SimWaterThermalTank_WaterTank: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
224 : CompNum,
225 : CompName,
226 0 : state.dataWaterThermalTanks->WaterThermalTank(CompNum).Name));
227 : }
228 0 : state.dataWaterThermalTanks->WaterThermalTank(CompNum).CheckWTTEquipName = false;
229 : }
230 : }
231 :
232 0 : return CompNum;
233 : }
234 :
235 5048 : int getHPTankIDX(EnergyPlusData &state, std::string_view CompName, int &CompIndex)
236 : {
237 5048 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
238 0 : GetWaterThermalTankInput(state);
239 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
240 : }
241 :
242 : int CompNum;
243 :
244 5048 : if (CompIndex == 0) {
245 0 : CompNum = UtilityRoutines::FindItem(CompName, state.dataWaterThermalTanks->HPWaterHeater);
246 0 : if (CompNum == 0) {
247 0 : ShowFatalError(state, "SimWaterThermalTank_HeatPump: Unit not found=" + std::string{CompName});
248 : }
249 0 : CompIndex = CompNum;
250 : } else {
251 5048 : CompNum = CompIndex;
252 5048 : if (CompNum > state.dataWaterThermalTanks->numWaterThermalTank || CompNum < 1) {
253 0 : ShowFatalError(state,
254 0 : format("SimWaterThermalTank_HeatPump: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
255 : CompNum,
256 0 : state.dataWaterThermalTanks->numHeatPumpWaterHeater,
257 0 : CompName));
258 : }
259 5048 : if (state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName) {
260 1 : if (CompName != state.dataWaterThermalTanks->HPWaterHeater(CompNum).Name) {
261 0 : ShowFatalError(state,
262 0 : format("SimWaterThermalTank_HeatPump: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
263 : CompNum,
264 : CompName,
265 0 : state.dataWaterThermalTanks->HPWaterHeater(CompNum).Name));
266 : }
267 1 : state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName = false;
268 : }
269 : }
270 :
271 5048 : return CompNum;
272 : }
273 :
274 5468492 : void WaterThermalTankData::simulate(
275 : EnergyPlusData &state, const PlantLocation &calledFromLocation, bool FirstHVACIteration, Real64 &CurLoad, [[maybe_unused]] bool RunFlag)
276 : {
277 : // SUBROUTINE INFORMATION:
278 : // AUTHOR Brandon Anderson
279 : // DATE WRITTEN May 2000
280 : // MODIFIED FSEC, July 2005
281 : // RE-ENGINEERED na
282 :
283 : // set the caller loop num to mimic what was happening in plant loop equip
284 5468492 : this->callerLoopNum = calledFromLocation.loopNum;
285 :
286 5468492 : this->oneTimeInit(state);
287 :
288 5468492 : if (this->MyOneTimeFlagWH) {
289 168 : this->MyOneTimeFlagWH = false;
290 : } else {
291 5468324 : if (this->MyTwoTimeFlagWH) {
292 168 : this->MinePlantStructForInfo(state); // call it again to get control types filled out
293 168 : this->MyTwoTimeFlagWH = false;
294 : }
295 : }
296 5468492 : this->UseSideLoadRequested = std::abs(CurLoad);
297 10599928 : if (this->UseSidePlantLoc.loopNum > 0 && this->UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
298 5131436 : !state.dataGlobal->KickOffSimulation) {
299 5111902 : this->UseCurrentFlowLock = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).LoopSide(this->UseSidePlantLoc.loopSideNum).FlowLock;
300 : } else {
301 356590 : this->UseCurrentFlowLock = DataPlant::FlowLock::Locked;
302 : }
303 5468492 : this->initialize(state, FirstHVACIteration);
304 : // Plant connected water heaters may have a desuperheater heating coil attached
305 5468492 : if (this->DesuperheaterNum == 0) {
306 5998913 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) ||
307 560471 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed)) {
308 5193004 : this->CalcWaterThermalTankMixed(state);
309 418411 : } else if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
310 172973 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
311 245438 : this->CalcWaterThermalTankStratified(state);
312 : }
313 30050 : } else if (this->DesuperheaterNum > 0) {
314 30050 : this->CalcDesuperheaterWaterHeater(state, FirstHVACIteration);
315 : }
316 5468492 : this->UpdateWaterThermalTank(state);
317 5468492 : this->ReportWaterThermalTank(state);
318 : // reset the caller loop num to mimic what was happening in PlantLoopEquip
319 5468492 : this->callerLoopNum = 0;
320 5468492 : }
321 :
322 10 : PlantComponent *HeatPumpWaterHeaterData::factory(EnergyPlusData &state, std::string const &objectName)
323 : {
324 : // Process the input data
325 10 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
326 3 : GetWaterThermalTankInput(state);
327 3 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
328 : }
329 :
330 : // Now look for this object in the list
331 28 : for (auto &HPWH : state.dataWaterThermalTanks->HPWaterHeater) {
332 28 : if (HPWH.Name == objectName) {
333 10 : return &HPWH;
334 : }
335 : }
336 : // If we didn't find it, fatal
337 : ShowFatalError(state, "LocalHeatPumpWaterHeaterFactory: Error getting inputs for object named: " + objectName); // LCOV_EXCL_LINE
338 : // Shut up the compiler
339 : return nullptr; // LCOV_EXCL_LINE
340 : }
341 :
342 50 : void HeatPumpWaterHeaterData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
343 : {
344 50 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
345 50 : Tank.onInitLoopEquip(state, calledFromLocation);
346 50 : }
347 :
348 50 : void HeatPumpWaterHeaterData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
349 : [[maybe_unused]] const PlantLocation &calledFromLocation,
350 : Real64 &MaxLoad,
351 : Real64 &MinLoad,
352 : Real64 &OptLoad)
353 : {
354 50 : MinLoad = 0.0;
355 50 : MaxLoad = this->Capacity;
356 50 : OptLoad = this->Capacity;
357 50 : }
358 :
359 644606 : void HeatPumpWaterHeaterData::simulate(
360 : EnergyPlusData &state, const PlantLocation &calledFromLocation, bool FirstHVACIteration, Real64 &CurLoad, [[maybe_unused]] bool RunFlag)
361 : {
362 : // SUBROUTINE INFORMATION:
363 : // AUTHOR Brandon Anderson
364 : // DATE WRITTEN May 2000
365 : // MODIFIED FSEC, July 2005
366 : // RE-ENGINEERED na
367 :
368 644606 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
369 :
370 : // set caller loop num to mimic what plantloopequip was doing
371 644606 : Tank.callerLoopNum = calledFromLocation.loopNum;
372 :
373 644606 : if (this->myOneTimeInitFlag) {
374 23 : if (Tank.myOneTimeInitFlag) {
375 23 : Tank.setupOutputVars(state);
376 23 : Tank.myOneTimeInitFlag = false;
377 : }
378 23 : this->myOneTimeInitFlag = false;
379 : }
380 :
381 644606 : if (this->MyOneTimeFlagHP) {
382 23 : this->MyOneTimeFlagHP = false;
383 : } else {
384 644583 : if (this->MyTwoTimeFlagHP) {
385 23 : Tank.MinePlantStructForInfo(state); // call it again to get control types filled out
386 23 : this->MyTwoTimeFlagHP = false;
387 : }
388 : }
389 644606 : Tank.UseSideLoadRequested = std::abs(CurLoad);
390 1179616 : if (Tank.UseSidePlantLoc.loopNum > 0 && Tank.UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
391 535010 : !state.dataGlobal->KickOffSimulation) {
392 533630 : Tank.UseCurrentFlowLock = state.dataPlnt->PlantLoop(Tank.UseSidePlantLoc.loopNum).LoopSide(Tank.UseSidePlantLoc.loopSideNum).FlowLock;
393 : } else {
394 110976 : Tank.UseCurrentFlowLock = DataPlant::FlowLock::Locked;
395 : }
396 :
397 644606 : Tank.initialize(state, FirstHVACIteration);
398 :
399 644606 : int InletNodeSav = this->HeatPumpAirInletNode;
400 644606 : int OutletNodeSav = this->HeatPumpAirOutletNode;
401 644606 : int DXINletNodeSav = this->DXCoilAirInletNode;
402 644606 : int IHPFanIndexSav = this->FanNum;
403 1289212 : std::string IHPFanNameSave = this->FanName;
404 644606 : int IHPFanplaceSav = this->FanPlacement;
405 :
406 644606 : if (this->bIsIHP) // pass the tank indexes to the IHP object
407 : {
408 10098 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankType = this->HPWHType;
409 10098 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankName = this->Name;
410 10098 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankID = this->WaterHeaterTankNum;
411 10098 : IntegratedHeatPump::IHPOperationMode IHPMode = IntegratedHeatPump::GetCurWorkMode(state, this->DXCoilNum);
412 :
413 10098 : if ((IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) ||
414 7695 : (IntegratedHeatPump::IHPOperationMode::SpaceClgDedicatedWaterHtg == IHPMode) ||
415 7695 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) ||
416 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOn == IHPMode)) { // default is to specify the air nodes for SCWH mode
417 2403 : bool bDWHCoilReading = false;
418 2403 : this->HeatPumpAirInletNode =
419 4806 : VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state,
420 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
421 2403 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
422 : bDWHCoilReading);
423 2403 : this->HeatPumpAirOutletNode =
424 4806 : VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state,
425 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
426 2403 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
427 : bDWHCoilReading);
428 2403 : this->DXCoilAirInletNode = this->HeatPumpAirInletNode;
429 : } else // default is to input outdoor fan to the the this
430 : {
431 7695 : this->FanNum = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanID;
432 7695 : this->FanName = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanName;
433 7695 : this->FanPlacement = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanPlace;
434 : }
435 : }
436 :
437 644606 : Tank.CalcHeatPumpWaterHeater(state, FirstHVACIteration);
438 644606 : Tank.UpdateWaterThermalTank(state);
439 644606 : Tank.ReportWaterThermalTank(state);
440 :
441 644606 : this->HeatPumpAirInletNode = InletNodeSav;
442 644606 : this->HeatPumpAirOutletNode = OutletNodeSav;
443 644606 : this->DXCoilAirInletNode = DXINletNodeSav;
444 644606 : this->FanNum = IHPFanIndexSav;
445 644606 : this->FanName = IHPFanNameSave;
446 644606 : this->FanPlacement = IHPFanplaceSav;
447 : // reset caller loop num to 0 to mimic what plantloopequip was doing
448 644606 : Tank.callerLoopNum = 0;
449 644606 : }
450 0 : void HeatPumpWaterHeaterData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
451 : {
452 0 : }
453 :
454 1420050 : void SimulateWaterHeaterStandAlone(EnergyPlusData &state, int const WaterHeaterNum, bool const FirstHVACIteration)
455 : {
456 :
457 : // SUBROUTINE INFORMATION:
458 : // AUTHOR Peter Graham Ellis
459 : // DATE WRITTEN January 2004
460 : // MODIFIED July 2005, FSEC - added HPWHs and desuperheater water heating coils
461 : // RE-ENGINEERED na
462 :
463 : // PURPOSE OF THIS SUBROUTINE:
464 : // This subroutine acts an interface to SimWaterHeater for stand-alone water heaters with no plant connections,
465 : // HPWHs not defined as zone equipment with no plant connections, and stand-alone water heaters with
466 : // desuperheater heating coils with no plant connections.
467 :
468 : // METHODOLOGY EMPLOYED:
469 : // The necessary control flags and dummy variables are set and passed into SimWaterHeater. This subroutine is
470 : // called from NonZoneEquipmentManager.
471 :
472 : Real64 MyLoad;
473 :
474 1420050 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
475 7 : GetWaterThermalTankInput(state);
476 7 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
477 : }
478 :
479 1420050 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterHeaterNum);
480 :
481 : // Only simulate stand-alone water heaters here. Plant connected water heaters are called by the PlantLoopEquipments.
482 1420050 : if (Tank.StandAlone) {
483 293710 : bool localRunFlag = true;
484 293710 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
485 293710 : Tank.simulate(state, A, FirstHVACIteration, MyLoad, localRunFlag);
486 :
487 : // HPWHs with inlet air from a zone and not connected to a plant loop are simulated through a CALL from ZoneEquipmentManager.
488 : // HPWHs that are plant connected are always simulated through a CALL from PlantLoopEquipments directly to SimWaterThermalTank.
489 :
490 : // NOTE: HPWHs with inlet air from a zone AND plant connected are not stand alone and are simulated in PlantLoopEquipments
491 1126340 : } else if (Tank.HeatPumpNum > 0) {
492 : // Only HPWHs with inlet air from outdoors or scheduled HPWHs (not connected to a plant loop) are simulated here.
493 :
494 192042 : auto &HPWaterHtr = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum);
495 :
496 293644 : if (HPWaterHtr.StandAlone &&
497 174016 : (HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::Schedule)) {
498 53326 : bool LocalRunFlag = true;
499 53326 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
500 53326 : HPWaterHtr.simulate(state, A, FirstHVACIteration, MyLoad, LocalRunFlag);
501 : }
502 :
503 : // Only simulate stand-alone water heaters with desuperheater water heating coils here. Plant connected water heaters
504 : // with desuperheater water heating coils are called by PlantLoopEquipments.
505 934298 : } else if (Tank.DesuperheaterNum > 0) {
506 30050 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).StandAlone) {
507 30050 : bool localRunFlag = true;
508 30050 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
509 30050 : Tank.simulate(state, A, FirstHVACIteration, MyLoad, localRunFlag);
510 : }
511 : }
512 1420050 : }
513 :
514 256753 : void SimHeatPumpWaterHeater(EnergyPlusData &state,
515 : std::string_view CompName,
516 : bool const FirstHVACIteration,
517 : Real64 &SensLoadMet, // sensible load met by this equipment and sent to zone, W
518 : Real64 &LatLoadMet, // net latent load met and sent to zone (kg/s), dehumid = negative
519 : int &CompIndex)
520 : {
521 : // SUBROUTINE INFORMATION:
522 : // AUTHOR Richard Raustad
523 : // DATE WRITTEN April 2005
524 : // MODIFIED Don Shirey, Aug 2009 (LatLoadMet)
525 : // RE-ENGINEERED na
526 :
527 : // PURPOSE OF THIS SUBROUTINE:
528 : // This subroutine acts as an interface to SimWaterHeater.
529 : // HPWHs defined as zone equipment and not connected to a plant loop are called here by ZoneEquipmentManager
530 :
531 : // METHODOLOGY EMPLOYED:
532 : // The necessary control flags and dummy variables are set and passed into SimWaterHeater.
533 :
534 256753 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
535 0 : GetWaterThermalTankInput(state);
536 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
537 : }
538 :
539 : // Find the correct Heat Pump Water Heater
540 : int HeatPumpNum;
541 256753 : if (CompIndex == 0) {
542 10 : HeatPumpNum = UtilityRoutines::FindItemInList(CompName, state.dataWaterThermalTanks->HPWaterHeater);
543 10 : if (HeatPumpNum == 0) {
544 0 : ShowFatalError(state, "SimHeatPumpWaterHeater: Unit not found=" + std::string{CompName});
545 : }
546 10 : CompIndex = HeatPumpNum;
547 : } else {
548 256743 : HeatPumpNum = CompIndex;
549 256743 : if (HeatPumpNum > state.dataWaterThermalTanks->numHeatPumpWaterHeater || HeatPumpNum < 1) {
550 0 : ShowFatalError(state,
551 0 : format("SimHeatPumpWaterHeater: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
552 : HeatPumpNum,
553 0 : state.dataWaterThermalTanks->numHeatPumpWaterHeater,
554 0 : CompName));
555 : }
556 : }
557 :
558 : // Only simulate HPWHs specified as zone equipment and not connected to a plant loop.
559 : // HPWHs not defined as zone equipment with no plant connections are simulated in NonZoneEquipmentManager.
560 : // Plant connected HPWHs are called by PlantLoopEquipments (but only those on supply side ).
561 : // HPWH will not be included in sizing calculations, fan is initialized only during BeginEnvrnFlag (FALSE during sizing)
562 : // (fan will be turned off during Standard Ratings procedure yielding incorrect results)
563 256753 : if (state.dataGlobal->DoingSizing) return;
564 :
565 : // For HPWHs, StandAlone means not connected to a plant loop (use nodes are not used, source nodes are connected to a HPWH)
566 256743 : if (state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).StandAlone) {
567 51222 : bool LocalRunFlag = true;
568 : Real64 MyLoad;
569 :
570 51222 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
571 51222 : state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).simulate(state, A, FirstHVACIteration, MyLoad, LocalRunFlag);
572 :
573 51222 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
574 51222 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
575 : } else {
576 : // HPWH is plant connected and will get simulated when called from plant SimWaterThermalTank, but need to update loads met here
577 205521 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
578 205521 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
579 : }
580 : }
581 :
582 2568509 : void CalcWaterThermalTankZoneGains(EnergyPlusData &state)
583 : {
584 :
585 : // SUBROUTINE INFORMATION:
586 : // AUTHOR Peter Graham Ellis
587 : // DATE WRITTEN March 2005
588 : // MODIFIED B. Griffith November 2011, new internal gains structure
589 : // RE-ENGINEERED na
590 :
591 : // PURPOSE OF THIS SUBROUTINE:
592 : // Calculates the zone internal gains due to water heater skin losses during sizing.
593 : // initializes gains to zone at begin environment.
594 :
595 : // METHODOLOGY EMPLOYED:
596 : // Sums the tank losses from all of the water heaters in the zone to add as a gain to the zone.
597 : // Now used to determine tank losses during sizing. Internal gains are summed in a centralized way now
598 :
599 2568509 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) {
600 :
601 2063949 : if (!state.dataGlobal->DoingSizing) {
602 1490778 : return;
603 : } else {
604 573171 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
605 406 : GetWaterThermalTankInput(state);
606 406 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
607 : }
608 573171 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) return;
609 : }
610 : }
611 :
612 504668 : if (state.dataGlobal->BeginEnvrnFlag && state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag) {
613 3220 : for (auto &e : state.dataWaterThermalTanks->WaterThermalTank) {
614 1936 : e.AmbientZoneGain = 0.0;
615 1936 : e.FuelEnergy = 0.0;
616 1936 : e.OffCycParaFuelEnergy = 0.0;
617 1936 : e.OnCycParaFuelEnergy = 0.0;
618 : }
619 1284 : state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = false;
620 : }
621 :
622 504668 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = true;
623 :
624 1200536 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
625 695868 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
626 695868 : if (Tank.AmbientTempZone == 0) continue;
627 241812 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(Tank.AmbientTempZone);
628 241812 : if (state.dataGlobal->DoingSizing) {
629 : // Initialize tank temperature to setpoint
630 : // (use HPWH or Desuperheater heating coil set point if applicable)
631 : int SchIndex;
632 105084 : if (Tank.HeatPumpNum > 0) {
633 14844 : SchIndex = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum).SetPointTempSchedule;
634 90240 : } else if (Tank.DesuperheaterNum > 0) {
635 1350 : SchIndex = state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).SetPointTempSchedule;
636 : } else {
637 88890 : SchIndex = Tank.SetPointTempSchedule;
638 : }
639 :
640 : Real64 TankTemp;
641 105084 : Real64 QLossToZone = 0.0;
642 105084 : if (SchIndex > 0) {
643 105084 : TankTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
644 : } else {
645 0 : TankTemp = 20.0;
646 : }
647 105084 : switch (Tank.WaterThermalTankType) {
648 88896 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
649 177792 : QLossToZone = max(Tank.OnCycLossCoeff * Tank.OnCycLossFracToZone, Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone) *
650 88896 : (TankTemp - thisZoneHB.MAT);
651 88896 : break;
652 : }
653 8094 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
654 16188 : QLossToZone = max(Tank.Node(1).OnCycLossCoeff * Tank.SkinLossFracToZone, Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone) *
655 8094 : (TankTemp - thisZoneHB.MAT);
656 8094 : break;
657 : }
658 4050 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
659 4050 : QLossToZone = Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone * (TankTemp - thisZoneHB.MAT);
660 4050 : break;
661 : }
662 4044 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
663 4044 : QLossToZone = Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone * (TankTemp - thisZoneHB.MAT);
664 4044 : break;
665 : }
666 0 : default:
667 0 : break;
668 : }
669 105084 : Tank.AmbientZoneGain = QLossToZone;
670 : }
671 : }
672 : }
673 :
674 6 : bool getDesuperHtrInput(EnergyPlusData &state)
675 : {
676 6 : bool ErrorsFound = false;
677 : static constexpr std::string_view RoutineName = "getDesuperHtrInput";
678 : // Make local copies of IPShortCut because other getinputs might overwrite the ones in state
679 12 : auto cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
680 12 : auto cAlphaArgs = state.dataIPShortCut->cAlphaArgs;
681 12 : auto rNumericArgs = state.dataIPShortCut->rNumericArgs;
682 12 : auto lNumericFieldBlanks = state.dataIPShortCut->lNumericFieldBlanks;
683 12 : auto lAlphaFieldBlanks = state.dataIPShortCut->lAlphaFieldBlanks;
684 12 : auto cAlphaFieldNames = state.dataIPShortCut->cAlphaFieldNames;
685 12 : auto cNumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
686 :
687 6 : cCurrentModuleObject = cCoilDesuperheater;
688 12 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
689 : int NumAlphas;
690 : int NumNums;
691 : int IOStat;
692 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
693 : cCurrentModuleObject,
694 : DesuperheaterNum,
695 : cAlphaArgs,
696 : NumAlphas,
697 : rNumericArgs,
698 : NumNums,
699 : IOStat,
700 : lNumericFieldBlanks,
701 : lAlphaFieldBlanks,
702 : cAlphaFieldNames,
703 : cNumericFieldNames);
704 6 : UtilityRoutines::IsNameEmpty(state, cAlphaArgs(1), cCurrentModuleObject, ErrorsFound);
705 :
706 : // ErrorsFound will be set to True if problem was found, left untouched otherwise
707 6 : GlobalNames::VerifyUniqueCoilName(state, cCurrentModuleObject, cAlphaArgs(1), ErrorsFound, cCurrentModuleObject + " Name");
708 :
709 6 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
710 :
711 6 : DesupHtr.Name = cAlphaArgs(1);
712 6 : DesupHtr.Type = cCurrentModuleObject;
713 :
714 : // convert availability schedule name to pointer
715 6 : if (!lAlphaFieldBlanks(2)) {
716 6 : DesupHtr.AvailSchedPtr = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(2));
717 6 : if (DesupHtr.AvailSchedPtr == 0) {
718 0 : ShowSevereError(state, "Invalid, " + cAlphaFieldNames(2) + " = " + cAlphaArgs(2));
719 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + cAlphaArgs(1));
720 0 : ErrorsFound = true;
721 : }
722 : } else {
723 0 : DesupHtr.AvailSchedPtr = DataGlobalConstants::ScheduleAlwaysOn;
724 : }
725 :
726 : // convert schedule name to pointer
727 6 : DesupHtr.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(3));
728 6 : if (DesupHtr.SetPointTempSchedule == 0) {
729 0 : ShowSevereError(state, "Invalid, " + cAlphaFieldNames(3) + " = " + cAlphaArgs(3));
730 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + cAlphaArgs(1));
731 0 : ErrorsFound = true;
732 : }
733 :
734 6 : DesupHtr.DeadBandTempDiff = rNumericArgs(1);
735 6 : if (DesupHtr.DeadBandTempDiff <= 0.0 || DesupHtr.DeadBandTempDiff > 20.0) {
736 0 : ShowSevereError(state,
737 0 : format("{} = {}: {} must be > 0 and <= 20. {} = {:.1T}",
738 : cCurrentModuleObject,
739 : DesupHtr.Name,
740 : cNumericFieldNames(1),
741 : cNumericFieldNames(1),
742 0 : rNumericArgs(1)));
743 0 : ErrorsFound = true;
744 : }
745 :
746 : // Error limits on heat reclaim efficiency applied after source type identified
747 :
748 6 : DesupHtr.RatedInletWaterTemp = rNumericArgs(3);
749 6 : DesupHtr.RatedOutdoorAirTemp = rNumericArgs(4);
750 6 : DesupHtr.MaxInletWaterTemp = rNumericArgs(5);
751 :
752 6 : if (!lAlphaFieldBlanks(4)) {
753 6 : DesupHtr.HEffFTemp = Curve::GetCurveIndex(state, cAlphaArgs(4));
754 6 : if (DesupHtr.HEffFTemp == 0) {
755 0 : ShowSevereError(state, cCurrentModuleObject + " = " + DesupHtr.Name + ": " + cAlphaFieldNames(4) + " not found = " + cAlphaArgs(4));
756 0 : ErrorsFound = true;
757 : } else {
758 6 : ErrorsFound |= Curve::CheckCurveDims(state,
759 : DesupHtr.HEffFTemp, // Curve index
760 : {2}, // Valid dimensions
761 : RoutineName, // Routine name
762 : cCurrentModuleObject, // Object Type
763 : DesupHtr.Name, // Object Name
764 6 : cAlphaFieldNames(4)); // Field Name
765 6 : if (!ErrorsFound) {
766 6 : if (DesupHtr.HEffFTemp > 0) {
767 12 : Real64 HEffFTemp = min(
768 6 : 1.0, max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, DesupHtr.RatedInletWaterTemp, DesupHtr.RatedOutdoorAirTemp)));
769 6 : if (std::abs(HEffFTemp - 1.0) > 0.05) {
770 0 : ShowWarningError(state, cCurrentModuleObject + ", \"" + DesupHtr.Name + "\":");
771 0 : ShowContinueError(state, "The " + cAlphaFieldNames(4) + " should be normalized ");
772 0 : ShowContinueError(state, format(" to 1.0 at the rating point. Curve output at the rating point = {:.3T}", HEffFTemp));
773 0 : ShowContinueError(state, " The simulation continues using the user-specified curve.");
774 : }
775 : }
776 : }
777 : }
778 : }
779 :
780 6 : DesupHtr.WaterInletNode = NodeInputManager::GetOnlySingleNode(state,
781 6 : cAlphaArgs(5),
782 : ErrorsFound,
783 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
784 6 : cAlphaArgs(1),
785 : DataLoopNode::NodeFluidType::Water,
786 : DataLoopNode::ConnectionType::Inlet,
787 : NodeInputManager::CompFluidStream::Primary,
788 6 : DataLoopNode::ObjectIsParent);
789 :
790 6 : DesupHtr.WaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
791 6 : cAlphaArgs(6),
792 : ErrorsFound,
793 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
794 6 : cAlphaArgs(1),
795 : DataLoopNode::NodeFluidType::Water,
796 : DataLoopNode::ConnectionType::Outlet,
797 : NodeInputManager::CompFluidStream::Primary,
798 6 : DataLoopNode::ObjectIsParent);
799 :
800 6 : DesupHtr.InletNodeName1 = cAlphaArgs(5);
801 6 : DesupHtr.OutletNodeName1 = cAlphaArgs(6);
802 :
803 6 : DesupHtr.TankType = cAlphaArgs(7);
804 :
805 7 : if (!UtilityRoutines::SameString(DesupHtr.TankType, cMixedWHModuleObj) &&
806 1 : !UtilityRoutines::SameString(DesupHtr.TankType, cStratifiedWHModuleObj)) {
807 :
808 0 : ShowSevereError(state, cCurrentModuleObject + " = " + state.dataWaterThermalTanks->HPWaterHeater(DesuperheaterNum).Name + ":");
809 0 : ShowContinueError(state, "Desuperheater can only be used with " + cMixedWHModuleObj + " or " + cStratifiedWHModuleObj + ".");
810 0 : ErrorsFound = true;
811 : }
812 :
813 6 : DesupHtr.TankName = cAlphaArgs(8);
814 :
815 : // Set up comp set for water side nodes (reverse inlet/outlet for water heater)
816 6 : BranchNodeConnections::SetUpCompSets(state, DesupHtr.Type, DesupHtr.Name, DesupHtr.TankType, DesupHtr.TankName, cAlphaArgs(6), cAlphaArgs(5));
817 :
818 12 : std::string const heatSourceObjType = cAlphaArgs(9);
819 :
820 17 : if ((UtilityRoutines::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
821 17 : (UtilityRoutines::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
822 11 : (UtilityRoutines::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
823 1 : if (lNumericFieldBlanks(2)) {
824 0 : DesupHtr.HeatReclaimRecoveryEff = 0.8;
825 : } else {
826 1 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
827 1 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.9) {
828 0 : ShowSevereError(state,
829 0 : format("{} = {}: {} must be > 0.0 and <= 0.9, Efficiency = {:.3T}",
830 : cCurrentModuleObject,
831 : DesupHtr.Name,
832 : cNumericFieldNames(2),
833 0 : DesupHtr.HeatReclaimRecoveryEff));
834 0 : ErrorsFound = true;
835 : }
836 : } // Blank Num(2)
837 : } else { // max is 0.3 for all other sources
838 5 : if (lNumericFieldBlanks(2)) {
839 0 : DesupHtr.HeatReclaimRecoveryEff = 0.25;
840 : } else {
841 5 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
842 5 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.3) {
843 0 : ShowSevereError(state,
844 0 : format("{} = {}: {} must be > 0.0 and <= 0.3, {} = {:.3T}",
845 : cCurrentModuleObject,
846 : DesupHtr.Name,
847 : cNumericFieldNames(2),
848 : cNumericFieldNames(2),
849 0 : DesupHtr.HeatReclaimRecoveryEff));
850 0 : ErrorsFound = true;
851 : }
852 : } // Blank Num(2)
853 : } // setting limits on heat recovery efficiency
854 :
855 : // Find the Refrigeration equipment index associated with the desuperheater heating coil.
856 6 : bool errFlag = false;
857 6 : DesupHtr.HeatingSourceType = heatSourceObjType;
858 6 : DesupHtr.HeatingSourceName = cAlphaArgs(10);
859 6 : if (UtilityRoutines::SameString(heatSourceObjType, "Refrigeration:CompressorRack")) {
860 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CompressorRackRefrigeratedCase;
861 0 : for (int RackNum = 1; RackNum <= state.dataRefrigCase->NumRefrigeratedRacks; ++RackNum) {
862 0 : if (!UtilityRoutines::SameString(state.dataHeatBal->HeatReclaimRefrigeratedRack(RackNum).Name, cAlphaArgs(10))) continue;
863 0 : DesupHtr.ReclaimHeatingSourceIndexNum = RackNum;
864 0 : if (allocated(state.dataHeatBal->HeatReclaimRefrigeratedRack)) {
865 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
866 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(DesupHtr.ReclaimHeatingSourceIndexNum);
867 0 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
868 0 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
869 0 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
870 0 : num = 0.0;
871 : }
872 0 : DesupHtr.ValidSourceType = true;
873 0 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
874 0 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
875 0 : ShowSevereError(state,
876 0 : cCurrentModuleObject + " = " + DesupHtr.Name +
877 : ": "
878 0 : " sum of heat reclaim recovery efficiencies from the same source coil: \"" +
879 0 : DesupHtr.HeatingSourceName + "\" cannot be over 0.3");
880 0 : ErrorsFound = true;
881 : }
882 : }
883 0 : break;
884 : }
885 17 : } else if ((UtilityRoutines::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
886 17 : (UtilityRoutines::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
887 11 : (UtilityRoutines::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
888 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CondenserRefrigeration;
889 2 : for (int CondNum = 1; CondNum <= state.dataRefrigCase->NumRefrigCondensers; ++CondNum) {
890 2 : if (!UtilityRoutines::SameString(state.dataHeatBal->HeatReclaimRefrigCondenser(CondNum).Name, cAlphaArgs(10))) continue;
891 1 : DesupHtr.ReclaimHeatingSourceIndexNum = CondNum;
892 1 : if (allocated(state.dataHeatBal->HeatReclaimRefrigCondenser)) {
893 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
894 1 : state.dataHeatBal->HeatReclaimRefrigCondenser(DesupHtr.ReclaimHeatingSourceIndexNum);
895 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
896 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
897 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
898 1 : num = 0.0;
899 : }
900 1 : DesupHtr.ValidSourceType = true;
901 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
902 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.9) {
903 0 : ShowSevereError(state,
904 0 : cCurrentModuleObject + " = " + DesupHtr.Name +
905 : ": "
906 0 : " sum of heat reclaim recovery efficiencies from the same source coil: \"" +
907 0 : DesupHtr.HeatingSourceName + "\" cannot be over 0.9");
908 0 : ErrorsFound = true;
909 : }
910 : }
911 1 : break;
912 : }
913 13 : } else if (UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed") ||
914 11 : UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoSpeed") ||
915 16 : UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX:MultiSpeed") ||
916 8 : UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
917 :
918 2 : if (UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed")) {
919 2 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXCooling;
920 0 : } else if (UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
921 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiMode;
922 : } else {
923 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiSpeed;
924 : }
925 2 : DXCoils::GetDXCoilIndex(state, DesupHtr.HeatingSourceName, DesupHtr.ReclaimHeatingSourceIndexNum, errFlag, cCurrentModuleObject);
926 2 : if (allocated(state.dataHeatBal->HeatReclaimDXCoil)) {
927 2 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimDXCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
928 2 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
929 2 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
930 4 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
931 2 : num = 0.0;
932 : }
933 2 : DesupHtr.ValidSourceType = true;
934 2 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
935 2 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
936 0 : ShowSevereError(state,
937 0 : cCurrentModuleObject + " = " + DesupHtr.Name +
938 : ": "
939 0 : " sum of heat reclaim recovery efficiencies from the same source coil: \"" +
940 0 : DesupHtr.HeatingSourceName + "\" cannot be over 0.3");
941 0 : ErrorsFound = true;
942 : }
943 : }
944 3 : } else if (UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX:VariableSpeed")) {
945 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXVariableCooling;
946 1 : DesupHtr.ReclaimHeatingSourceIndexNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(state, heatSourceObjType, cAlphaArgs(10), errFlag);
947 1 : if (allocated(state.dataHeatBal->HeatReclaimVS_DXCoil)) {
948 1 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimVS_DXCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
949 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
950 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
951 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
952 1 : num = 0.0;
953 : }
954 1 : DesupHtr.ValidSourceType = true;
955 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
956 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
957 0 : ShowSevereError(state,
958 0 : cCurrentModuleObject + " = " + DesupHtr.Name +
959 : ": "
960 0 : " sum of heat reclaim recovery efficiencies from the same source coil: \"" +
961 0 : DesupHtr.HeatingSourceName + "\" cannot be over 0.3");
962 0 : ErrorsFound = true;
963 : }
964 : }
965 2 : } else if (UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:WaterToAirHeatPump:EquationFit")) {
966 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::AirWaterHeatPumpEQ;
967 1 : DesupHtr.ReclaimHeatingSourceIndexNum = WaterToAirHeatPumpSimple::GetCoilIndex(state, heatSourceObjType, cAlphaArgs(10), errFlag);
968 1 : if (allocated(state.dataHeatBal->HeatReclaimSimple_WAHPCoil)) {
969 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
970 1 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
971 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
972 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
973 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
974 1 : num = 0.0;
975 : }
976 1 : DesupHtr.ValidSourceType = true;
977 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
978 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
979 0 : ShowSevereError(state,
980 0 : cCurrentModuleObject + " = " + DesupHtr.Name +
981 : ": "
982 0 : " sum of heat reclaim recovery efficiencies from the same source coil: \"" +
983 0 : DesupHtr.HeatingSourceName + "\" cannot be over 0.3");
984 0 : ErrorsFound = true;
985 : }
986 : }
987 1 : } else if (UtilityRoutines::SameString(heatSourceObjType, "Coil:Cooling:DX")) {
988 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CoilCoolingDX;
989 1 : DesupHtr.ReclaimHeatingSourceIndexNum = CoilCoolingDX::factory(state, cAlphaArgs(10));
990 1 : if (DesupHtr.ReclaimHeatingSourceIndexNum < 0) {
991 0 : ShowSevereError(
992 : state,
993 0 : format("{}={}, could not find desuperheater coil {}={}", cCurrentModuleObject, DesupHtr.Name, cAlphaArgs(9), cAlphaArgs(10)));
994 0 : ErrorsFound = true;
995 : } else {
996 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
997 1 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat;
998 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
999 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
1000 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
1001 1 : num = 0.0;
1002 : }
1003 1 : DesupHtr.ValidSourceType = true;
1004 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
1005 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
1006 0 : ShowSevereError(state,
1007 0 : cCurrentModuleObject + ", \"" + DesupHtr.Name +
1008 0 : "\" sum of heat reclaim recovery efficiencies from the same source coil: \"" + DesupHtr.HeatingSourceName +
1009 : "\" cannot be over 0.3");
1010 0 : ErrorsFound = true;
1011 : }
1012 : }
1013 : } else {
1014 0 : ShowSevereError(state, cCurrentModuleObject + " = " + DesupHtr.Name + ':');
1015 0 : ShowContinueError(state, " desuperheater can only be used with Coil:Cooling:DX:SingleSpeed, ");
1016 0 : ShowContinueError(state,
1017 : " Coil:Cooling:DX:TwoSpeed, Coil:Cooling:DX:MultiSpeed, Coil:Cooling:DX:TwoStageWithHumidityControlMode, "
1018 : "Coil:Cooling:DX:VariableSpeed, "
1019 : "Coil:Cooling:WaterToAirHeatPump:EquationFit, Refrigeration:CompressorRack,");
1020 0 : ShowContinueError(state, " Refrigeration:Condenser:AirCooled ,Refrigeration:Condenser:EvaporativeCooled, ");
1021 0 : ShowContinueError(state, " or Refrigeration:Condenser:WaterCooled.");
1022 0 : ShowContinueError(state, " Invalid desuperheater heat source object: " + heatSourceObjType + " \"" + cAlphaArgs(10) + "\"");
1023 0 : ErrorsFound = true;
1024 : }
1025 6 : if (errFlag) {
1026 0 : ShowContinueError(state, "...occurs in " + cCurrentModuleObject + '=' + DesupHtr.Name);
1027 0 : ErrorsFound = true;
1028 : }
1029 :
1030 6 : if (DesupHtr.ReclaimHeatingSourceIndexNum == 0 && DesupHtr.ReclaimHeatingSource != ReclaimHeatObjectType::CoilCoolingDX) {
1031 0 : ShowSevereError(state,
1032 0 : cCurrentModuleObject + ", \"" + DesupHtr.Name + "\" desuperheater heat source object not found: " + heatSourceObjType +
1033 0 : " \"" + cAlphaArgs(10) + "\"");
1034 0 : ErrorsFound = true;
1035 : }
1036 :
1037 6 : DesupHtr.OperatingWaterFlowRate = rNumericArgs(6);
1038 6 : if (DesupHtr.OperatingWaterFlowRate <= 0.0) {
1039 0 : ShowSevereError(state,
1040 0 : format("{} = {}: {} must be greater than 0. {} = {:.6T}",
1041 : cCurrentModuleObject,
1042 : DesupHtr.Name,
1043 : cNumericFieldNames(6),
1044 : cNumericFieldNames(6),
1045 0 : rNumericArgs(6)));
1046 0 : ErrorsFound = true;
1047 : }
1048 :
1049 6 : DesupHtr.PumpElecPower = rNumericArgs(7);
1050 6 : if (DesupHtr.PumpElecPower < 0.0) {
1051 0 : ShowSevereError(state,
1052 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1053 : cCurrentModuleObject,
1054 : DesupHtr.Name,
1055 : cNumericFieldNames(7),
1056 : cNumericFieldNames(7),
1057 0 : rNumericArgs(7)));
1058 0 : ErrorsFound = true;
1059 : }
1060 :
1061 6 : if ((DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate) > 7.9264e6) {
1062 0 : ShowWarningError(state,
1063 0 : format("{} = {}: {} to {} ratio > 7.9264E6. {} to {} = {:.3T}",
1064 : cCurrentModuleObject,
1065 : DesupHtr.Name,
1066 : cNumericFieldNames(7),
1067 : cNumericFieldNames(6),
1068 : cNumericFieldNames(7),
1069 : cNumericFieldNames(6),
1070 0 : (DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate)));
1071 0 : ShowContinueError(state, " Suggest reducing " + cNumericFieldNames(7) + " or increasing " + cNumericFieldNames(6) + '.');
1072 0 : ShowContinueError(state, " The simulation will continue using the user defined values.");
1073 : }
1074 :
1075 6 : DesupHtr.PumpFracToWater = rNumericArgs(8);
1076 6 : if (DesupHtr.PumpFracToWater < 0.0 || DesupHtr.PumpFracToWater > 1.0) {
1077 0 : ShowSevereError(state,
1078 0 : format("{} = {}: {} must be >= 0 or <= 1. {} = {:.3T}",
1079 : cCurrentModuleObject,
1080 : DesupHtr.Name,
1081 : cNumericFieldNames(8),
1082 : cNumericFieldNames(8),
1083 0 : rNumericArgs(8)));
1084 0 : ErrorsFound = true;
1085 : }
1086 :
1087 6 : DesupHtr.OnCycParaLoad = rNumericArgs(9);
1088 6 : if (DesupHtr.OnCycParaLoad < 0.0) {
1089 0 : ShowSevereError(state,
1090 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1091 : cCurrentModuleObject,
1092 : DesupHtr.Name,
1093 : cNumericFieldNames(9),
1094 : cNumericFieldNames(9),
1095 0 : rNumericArgs(9)));
1096 0 : ErrorsFound = true;
1097 : }
1098 :
1099 6 : DesupHtr.OffCycParaLoad = rNumericArgs(10);
1100 6 : if (DesupHtr.OffCycParaLoad < 0.0) {
1101 0 : ShowSevereError(state,
1102 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1103 : cCurrentModuleObject,
1104 : DesupHtr.Name,
1105 : cNumericFieldNames(10),
1106 : cNumericFieldNames(10),
1107 0 : rNumericArgs(10)));
1108 0 : ErrorsFound = true;
1109 : }
1110 : }
1111 :
1112 6 : if (ErrorsFound) {
1113 0 : ShowFatalError(state, "Errors found in getting " + cCurrentModuleObject + " input. Preceding condition causes termination.");
1114 : }
1115 :
1116 12 : return ErrorsFound;
1117 :
1118 : } // namespace WaterThermalTanks
1119 :
1120 9 : bool getHPWaterHeaterInput(EnergyPlusData &state)
1121 : {
1122 9 : bool ErrorsFound = false;
1123 :
1124 9 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
1125 9 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
1126 : int nAlphaOffset; // the difference of array location between alpha items between pumped and wrapped condensers
1127 : int nNumericOffset; // the difference of array location between numeric items between pumped and wrapped condensers
1128 : int nNumPossibleNumericArgs; // the number of possible numeric arguments in the idd
1129 : int nNumPossibleAlphaArgs; // the number of possible numeric arguments in the idd
1130 :
1131 : // For looking up in IDF/epJSON, you need the index that corresponds to the actual object type (Pumped or Wrapped)
1132 : int HPWaterHeaterNumOfSpecificType;
1133 :
1134 32 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
1135 :
1136 : // Create reference to current HPWH object in array.
1137 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
1138 :
1139 : // Initialize the offsets to zero
1140 23 : nAlphaOffset = 0;
1141 23 : nNumericOffset = 0;
1142 :
1143 : DataLoopNode::ConnectionObjectType objType;
1144 :
1145 23 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
1146 : // Pumped Condenser
1147 20 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
1148 20 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpPumpedCondenser;
1149 20 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped;
1150 20 : nNumPossibleAlphaArgs = 29;
1151 20 : nNumPossibleNumericArgs = 9;
1152 : // Actual index of Pumped type
1153 20 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum;
1154 : } else {
1155 : // Wrapped Condenser
1156 3 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
1157 3 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpWrappedCondenser;
1158 3 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped;
1159 3 : nNumPossibleAlphaArgs = 27;
1160 3 : nNumPossibleNumericArgs = 10;
1161 : // Actual index of Wrapped type
1162 3 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum - NumPumpedCondenser;
1163 : }
1164 :
1165 : int NumAlphas;
1166 : int NumNums;
1167 : int IOStat;
1168 184 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1169 23 : state.dataIPShortCut->cCurrentModuleObject,
1170 : HPWaterHeaterNumOfSpecificType,
1171 23 : state.dataIPShortCut->cAlphaArgs,
1172 : NumAlphas,
1173 23 : state.dataIPShortCut->rNumericArgs,
1174 : NumNums,
1175 : IOStat,
1176 23 : state.dataIPShortCut->lNumericFieldBlanks,
1177 23 : state.dataIPShortCut->lAlphaFieldBlanks,
1178 23 : state.dataIPShortCut->cAlphaFieldNames,
1179 23 : state.dataIPShortCut->cNumericFieldNames);
1180 :
1181 : // Copy those lists into C++ std::maps
1182 46 : std::map<int, std::string> hpwhAlpha;
1183 46 : std::map<int, Real64> hpwhNumeric;
1184 46 : std::map<int, bool> hpwhAlphaBlank;
1185 46 : std::map<int, bool> hpwhNumericBlank;
1186 46 : std::map<int, std::string> hpwhAlphaFieldNames;
1187 46 : std::map<int, std::string> hpwhNumericFieldNames;
1188 204 : for (int i = 1; i <= NumNums; ++i) {
1189 181 : hpwhNumeric[i] = state.dataIPShortCut->rNumericArgs(i);
1190 181 : hpwhNumericBlank[i] = state.dataIPShortCut->lNumericFieldBlanks(i);
1191 181 : hpwhNumericFieldNames[i] = state.dataIPShortCut->cNumericFieldNames(i);
1192 : }
1193 55 : for (int i = NumNums + 1; i <= nNumPossibleNumericArgs; ++i) {
1194 32 : hpwhNumericBlank[i] = true;
1195 : }
1196 642 : for (int i = 1; i <= NumAlphas; ++i) {
1197 619 : hpwhAlpha[i] = state.dataIPShortCut->cAlphaArgs(i);
1198 619 : hpwhAlphaBlank[i] = state.dataIPShortCut->lAlphaFieldBlanks(i);
1199 619 : hpwhAlphaFieldNames[i] = state.dataIPShortCut->cAlphaFieldNames(i);
1200 : }
1201 65 : for (int i = NumAlphas + 1; i <= nNumPossibleAlphaArgs; ++i) {
1202 42 : hpwhAlphaBlank[i] = true;
1203 : }
1204 23 : UtilityRoutines::IsNameEmpty(state, hpwhAlpha[1], state.dataIPShortCut->cCurrentModuleObject, ErrorsFound);
1205 :
1206 : // Name and type
1207 23 : HPWH.Name = hpwhAlpha[1];
1208 23 : HPWH.Type = state.dataIPShortCut->cCurrentModuleObject;
1209 :
1210 : // Availability Schedule
1211 : // convert schedule name to pointer
1212 23 : if (!hpwhAlphaBlank[2]) {
1213 23 : HPWH.AvailSchedPtr = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[2]);
1214 23 : if (HPWH.AvailSchedPtr == 0) {
1215 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
1216 0 : ShowContinueError(state, hpwhAlphaFieldNames[2] + "=\"" + hpwhAlpha[2] + "\".");
1217 0 : ErrorsFound = true;
1218 : }
1219 : } else {
1220 0 : HPWH.AvailSchedPtr = DataGlobalConstants::ScheduleAlwaysOn;
1221 : }
1222 :
1223 : // Compressor Setpoint Temperature Schedule
1224 : // convert schedule name to pointer
1225 23 : if (!hpwhAlphaBlank[3]) {
1226 23 : HPWH.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[3]);
1227 23 : if (HPWH.SetPointTempSchedule == 0) {
1228 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
1229 0 : ShowContinueError(state, hpwhAlphaFieldNames[3] + "=\"" + hpwhAlpha[3] + "\".");
1230 0 : ErrorsFound = true;
1231 : }
1232 : } else {
1233 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1234 0 : ShowContinueError(state, "required " + hpwhAlphaFieldNames[3] + " is blank.");
1235 0 : ErrorsFound = true;
1236 : }
1237 :
1238 : // Dead Band Temperature Difference
1239 23 : HPWH.DeadBandTempDiff = hpwhNumeric[1 + nNumericOffset];
1240 23 : if (HPWH.DeadBandTempDiff <= 0.0 || HPWH.DeadBandTempDiff > 20.0) {
1241 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1242 0 : ShowContinueError(state,
1243 0 : hpwhNumericFieldNames[1 + nNumericOffset] +
1244 0 : format(" difference must be > 0 and <= 20. Dead band = {:.1T}", hpwhNumeric[1 + nNumericOffset]));
1245 0 : ErrorsFound = true;
1246 : }
1247 :
1248 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1249 :
1250 : // Condenser Inlet/Outlet Nodes
1251 60 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1252 40 : hpwhAlpha[4],
1253 : ErrorsFound,
1254 : objType,
1255 : HPWH.Name,
1256 : DataLoopNode::NodeFluidType::Water,
1257 : DataLoopNode::ConnectionType::Inlet,
1258 : NodeInputManager::CompFluidStream::Secondary,
1259 20 : DataLoopNode::ObjectIsParent);
1260 20 : HPWH.InletNodeName1 = hpwhAlpha[4];
1261 60 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1262 40 : hpwhAlpha[5],
1263 : ErrorsFound,
1264 : objType,
1265 : HPWH.Name,
1266 : DataLoopNode::NodeFluidType::Water,
1267 : DataLoopNode::ConnectionType::Outlet,
1268 : NodeInputManager::CompFluidStream::Secondary,
1269 20 : DataLoopNode::ObjectIsParent);
1270 20 : HPWH.OutletNodeName1 = hpwhAlpha[5];
1271 :
1272 : // Condenser Water Flow Rate
1273 20 : HPWH.OperatingWaterFlowRate = hpwhNumeric[2];
1274 20 : if (HPWH.OperatingWaterFlowRate <= 0.0 && hpwhNumeric[2] != DataGlobalConstants::AutoCalculate) {
1275 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1276 0 : ShowContinueError(state,
1277 0 : format("{} must be greater than 0. Condenser water flow rate = {:.6T}", hpwhNumericFieldNames[2], hpwhNumeric[2]));
1278 0 : ErrorsFound = true;
1279 : }
1280 :
1281 3 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1282 :
1283 : // Wrapped Condenser Location
1284 3 : HPWH.WrappedCondenserBottomLocation = hpwhNumeric[2 + nNumericOffset];
1285 3 : HPWH.WrappedCondenserTopLocation = hpwhNumeric[3 + nNumericOffset];
1286 :
1287 3 : if (HPWH.WrappedCondenserBottomLocation < 0.0) {
1288 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1289 0 : ShowContinueError(state,
1290 0 : format("{} must be greater than 0. Condenser bottom location = {:.6T}",
1291 0 : hpwhNumericFieldNames[2],
1292 0 : HPWH.WrappedCondenserBottomLocation));
1293 0 : ErrorsFound = true;
1294 : }
1295 :
1296 3 : if (HPWH.WrappedCondenserBottomLocation >= HPWH.WrappedCondenserTopLocation) {
1297 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1298 0 : ShowContinueError(state,
1299 0 : format("{} ({:.6T}) must be greater than {} ({:.6T}).",
1300 : HPWH.WrappedCondenserTopLocation,
1301 0 : hpwhNumericFieldNames[2],
1302 0 : hpwhNumericFieldNames[3],
1303 0 : HPWH.WrappedCondenserBottomLocation));
1304 0 : ErrorsFound = true;
1305 : }
1306 :
1307 : // Reset the offset
1308 3 : nAlphaOffset = -2;
1309 3 : nNumericOffset = 1;
1310 :
1311 : } else {
1312 0 : assert(0);
1313 : }
1314 :
1315 : // Evaporator Air Flow Rate
1316 23 : HPWH.OperatingAirFlowRate = hpwhNumeric[3 + nNumericOffset];
1317 23 : if (HPWH.OperatingAirFlowRate <= 0.0 && hpwhNumeric[3 + nNumericOffset] != DataGlobalConstants::AutoCalculate) {
1318 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1319 0 : ShowContinueError(state,
1320 0 : hpwhNumericFieldNames[3 + nNumericOffset] +
1321 0 : format(" must be greater than 0. Evaporator air flow rate = {:.6T}", hpwhNumeric[3 + nNumericOffset]));
1322 0 : ErrorsFound = true;
1323 : }
1324 :
1325 : // Inlet Air Configuration
1326 23 : HPWH.InletAirConfiguration =
1327 46 : static_cast<WTTAmbientTemp>(getEnumerationValue(HPWHAmbientTempNamesUC, UtilityRoutines::MakeUPPERCase(hpwhAlpha[6 + nAlphaOffset])));
1328 23 : switch (HPWH.InletAirConfiguration) {
1329 3 : case WTTAmbientTemp::Schedule: {
1330 :
1331 : // Inlet Air Temperature Schedule
1332 3 : if (!hpwhAlphaBlank[11 + nAlphaOffset]) {
1333 3 : HPWH.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[11 + nAlphaOffset]);
1334 3 : if (HPWH.AmbientTempSchedule == 0) {
1335 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
1336 0 : ShowContinueError(state, hpwhAlphaFieldNames[11 + nAlphaOffset] + "=\"" + hpwhAlpha[11 + nAlphaOffset] + "\".");
1337 0 : ErrorsFound = true;
1338 : }
1339 : } else {
1340 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1341 0 : ShowContinueError(state, "required " + hpwhAlphaFieldNames[11 + nAlphaOffset] + " is blank.");
1342 0 : ErrorsFound = true;
1343 : }
1344 :
1345 : // Inlet Air Humidity Schedule
1346 3 : if (!hpwhAlphaBlank[12 + nAlphaOffset]) {
1347 3 : HPWH.AmbientRHSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[12 + nAlphaOffset]);
1348 3 : if (HPWH.AmbientRHSchedule == 0) {
1349 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
1350 0 : ShowContinueError(state, hpwhAlphaFieldNames[12 + nAlphaOffset] + "=\"" + hpwhAlpha[12 + nAlphaOffset] + "\".");
1351 0 : ErrorsFound = true;
1352 : } else {
1353 3 : if (!ScheduleManager::CheckScheduleValueMinMax(state, HPWH.AmbientRHSchedule, ">=", 0.0, "<=", 1.0)) {
1354 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", invalid values");
1355 0 : ShowContinueError(state,
1356 0 : hpwhAlphaFieldNames[12 + nAlphaOffset] + "=\"" + hpwhAlpha[12 + nAlphaOffset] +
1357 : "\", schedule values must be (>=0., <=1.)");
1358 0 : ErrorsFound = true;
1359 : }
1360 : }
1361 : } else {
1362 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1363 0 : ShowContinueError(state, "required " + hpwhAlphaFieldNames[12 + nAlphaOffset] + " is blank.");
1364 0 : ErrorsFound = true;
1365 : }
1366 :
1367 3 : break;
1368 : }
1369 10 : case WTTAmbientTemp::ZoneAndOA:
1370 : case WTTAmbientTemp::TempZone: {
1371 :
1372 : // Inlet Air Zone
1373 10 : if (!hpwhAlphaBlank[13 + nAlphaOffset]) {
1374 10 : HPWH.AmbientTempZone = UtilityRoutines::FindItemInList(hpwhAlpha[13 + nAlphaOffset], state.dataHeatBal->Zone);
1375 10 : if (HPWH.AmbientTempZone == 0) {
1376 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
1377 0 : ShowContinueError(state, hpwhAlphaFieldNames[13 + nAlphaOffset] + "=\"" + hpwhAlpha[13 + nAlphaOffset] + "\".");
1378 0 : ErrorsFound = true;
1379 : }
1380 : } else {
1381 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1382 0 : ShowContinueError(state, "required " + hpwhAlphaFieldNames[13 + nAlphaOffset] + " is blank.");
1383 0 : ErrorsFound = true;
1384 : }
1385 10 : break;
1386 : }
1387 10 : default:
1388 : case WTTAmbientTemp::OutsideAir:
1389 10 : break;
1390 : }
1391 :
1392 : // Read air inlet nodes after mixer/splitter nodes have been read in (state.dataIPShortCut->cAlphaArgs 7-10),
1393 : // Node_ConnectionType differs for inlet node if mixer/splitter node exists
1394 :
1395 : // Tank Name
1396 : // We will verify this exists and is the right kind of tank later when the tanks are all loaded.
1397 23 : HPWH.TankName = hpwhAlpha[15 + nAlphaOffset];
1398 23 : HPWH.TankType = hpwhAlpha[14 + nAlphaOffset];
1399 :
1400 : // Use Side Inlet/Outlet
1401 : // Get the water heater tank use side inlet node names for HPWHs connected to a plant loop
1402 : // Save the name of the node for use with set up comp sets
1403 23 : HPWH.InletNodeName2 = hpwhAlpha[16 + nAlphaOffset];
1404 23 : HPWH.OutletNodeName2 = hpwhAlpha[17 + nAlphaOffset];
1405 :
1406 23 : if (!hpwhAlphaBlank[16 + nAlphaOffset] && !hpwhAlphaBlank[17 + nAlphaOffset]) {
1407 10 : HPWH.WHUseInletNode = NodeInputManager::GetOnlySingleNode(state,
1408 : HPWH.InletNodeName2,
1409 : ErrorsFound,
1410 : objType,
1411 : HPWH.Name,
1412 : DataLoopNode::NodeFluidType::Water,
1413 : DataLoopNode::ConnectionType::Inlet,
1414 : NodeInputManager::CompFluidStream::Primary,
1415 10 : DataLoopNode::ObjectIsParent);
1416 10 : HPWH.WHUseOutletNode = NodeInputManager::GetOnlySingleNode(state,
1417 : HPWH.OutletNodeName2,
1418 : ErrorsFound,
1419 : objType,
1420 : HPWH.Name,
1421 : DataLoopNode::NodeFluidType::Water,
1422 : DataLoopNode::ConnectionType::Outlet,
1423 : NodeInputManager::CompFluidStream::Primary,
1424 10 : DataLoopNode::ObjectIsParent);
1425 : }
1426 :
1427 : // DX Coil
1428 : // get Coil:DX:HeatPumpWaterHeater object
1429 23 : HPWH.DXCoilName = hpwhAlpha[19 + nAlphaOffset];
1430 23 : HPWH.DXCoilType = hpwhAlpha[18 + nAlphaOffset];
1431 :
1432 : // check that the DX Coil exists
1433 23 : bool DXCoilErrFlag = false;
1434 23 : bool bIsVScoil = false;
1435 23 : DXCoils::GetDXCoilIndex(state, HPWH.DXCoilName, HPWH.DXCoilNum, DXCoilErrFlag, state.dataIPShortCut->cCurrentModuleObject, true);
1436 23 : if (DXCoilErrFlag) {
1437 : // This could be a variable speed heat pump water heater
1438 7 : bool bVSCoilErrFlag = false;
1439 :
1440 7 : bool checkIHPFirst = IntegratedHeatPump::IHPInModel(state);
1441 7 : if (checkIHPFirst) {
1442 1 : HPWH.DXCoilNum =
1443 2 : IntegratedHeatPump::GetCoilIndexIHP(state, "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE", HPWH.DXCoilName, bVSCoilErrFlag);
1444 :
1445 1 : if (!bVSCoilErrFlag) {
1446 1 : HPWH.bIsIHP = true;
1447 : }
1448 : }
1449 :
1450 7 : if (bVSCoilErrFlag || !checkIHPFirst) {
1451 6 : bVSCoilErrFlag = false;
1452 6 : HPWH.DXCoilNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(
1453 : state, "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed", HPWH.DXCoilName, bVSCoilErrFlag);
1454 :
1455 6 : if (bVSCoilErrFlag) {
1456 0 : ShowContinueError(state, "...occurs in " + state.dataIPShortCut->cCurrentModuleObject + " =" + HPWH.Name);
1457 0 : ShowContinueError(state, "...could not find either DXCoils::DXCoil or Variable Speed Coil " + HPWH.DXCoilName);
1458 0 : ErrorsFound = true;
1459 : }
1460 : }
1461 :
1462 7 : bIsVScoil = true;
1463 7 : HPWH.DXCoilTypeNum = 0;
1464 7 : if (HPWH.bIsIHP) {
1465 1 : HPWH.DXCoilType = "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE";
1466 : } else {
1467 6 : HPWH.DXCoilType = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum).VarSpeedCoilType;
1468 : }
1469 : } else {
1470 : // this is a single speed coil
1471 16 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1472 16 : if (!UtilityRoutines::SameString(HPWH.DXCoilType, Coil.DXCoilType)) {
1473 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1474 0 : ShowContinueError(state, "specifies the coil " + HPWH.DXCoilType + "=\"" + HPWH.DXCoilName + "\".");
1475 0 : ShowContinueError(state, "However, " + HPWH.DXCoilName + " is a coil of type " + Coil.DXCoilType + ".");
1476 0 : ErrorsFound = true;
1477 : }
1478 16 : HPWH.DXCoilTypeNum = Coil.DXCoilType_Num;
1479 : }
1480 :
1481 : // Make sure that the coil and tank are compatible.
1482 23 : if (bIsVScoil) {
1483 7 : if (HPWH.HPWHType != DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1484 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1485 0 : ShowContinueError(state,
1486 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed can only be used with a pumped condenser heat pump "
1487 : "water heater.");
1488 0 : ErrorsFound = true;
1489 : }
1490 : } else {
1491 32 : if (!((HPWH.DXCoilTypeNum == DataHVACGlobals::CoilDX_HeatPumpWaterHeaterPumped &&
1492 13 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) ||
1493 6 : (HPWH.DXCoilTypeNum == DataHVACGlobals::CoilDX_HeatPumpWaterHeaterWrapped &&
1494 3 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped))) {
1495 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1496 0 : std::string ExpectedCoilType;
1497 0 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1498 0 : ExpectedCoilType = DataHVACGlobals::cAllCoilTypes(DataHVACGlobals::CoilDX_HeatPumpWaterHeaterPumped);
1499 0 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1500 0 : ExpectedCoilType = DataHVACGlobals::cAllCoilTypes(DataHVACGlobals::CoilDX_HeatPumpWaterHeaterWrapped);
1501 : } else {
1502 0 : assert(0);
1503 : }
1504 0 : ShowContinueError(state, "can only be used with " + ExpectedCoilType);
1505 0 : ErrorsFound = true;
1506 : }
1507 : }
1508 :
1509 : // Dummy condenser Inlet/Outlet Nodes for wrapped tanks
1510 23 : if (HPWH.DXCoilTypeNum == DataHVACGlobals::CoilDX_HeatPumpWaterHeaterWrapped) {
1511 3 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1512 :
1513 3 : HPWH.InletNodeName1 = "DUMMY CONDENSER INLET " + Coil.Name;
1514 3 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1515 : HPWH.InletNodeName1,
1516 : ErrorsFound,
1517 : objType,
1518 : HPWH.Name,
1519 : DataLoopNode::NodeFluidType::Water,
1520 : DataLoopNode::ConnectionType::Inlet,
1521 : NodeInputManager::CompFluidStream::Secondary,
1522 3 : DataLoopNode::ObjectIsParent);
1523 3 : HPWH.OutletNodeName1 = "DUMMY CONDENSER OUTLET " + Coil.Name;
1524 3 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1525 : HPWH.OutletNodeName1,
1526 : ErrorsFound,
1527 : objType,
1528 : HPWH.Name,
1529 : DataLoopNode::NodeFluidType::Water,
1530 : DataLoopNode::ConnectionType::Outlet,
1531 : NodeInputManager::CompFluidStream::Secondary,
1532 3 : DataLoopNode::ObjectIsParent);
1533 : }
1534 :
1535 : // Minimum Inlet Air Temperature for Compressor Operation
1536 23 : HPWH.MinAirTempForHPOperation = hpwhNumeric[4 + nNumericOffset];
1537 :
1538 : // Maximum Inlet Air Temperature for Compressor Operation
1539 23 : HPWH.MaxAirTempForHPOperation = hpwhNumeric[5 + nNumericOffset];
1540 23 : if (HPWH.MaxAirTempForHPOperation <= HPWH.MinAirTempForHPOperation) {
1541 0 : ShowWarningError(state,
1542 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name +
1543 : "\": maximum inlet air temperature for heat pump compressor operation");
1544 0 : ShowContinueError(state, "must be greater than the minimum inlet air temperature for heat pump compressor operation.");
1545 0 : ShowContinueError(state, format("...Minimum inlet air temperature = {:.1T}", HPWH.MinAirTempForHPOperation));
1546 0 : ShowContinueError(state, format("...Maximum inlet air temperature = {:.1T}", HPWH.MaxAirTempForHPOperation));
1547 : }
1548 :
1549 : // Compressor Location
1550 23 : HPWH.CrankcaseTempIndicator = static_cast<CrankcaseHeaterControlTemp>(
1551 46 : getEnumerationValue(CrankcaseHeaterControlTempNamesUC, UtilityRoutines::MakeUPPERCase(hpwhAlpha[20 + nAlphaOffset])));
1552 23 : switch (HPWH.CrankcaseTempIndicator) {
1553 3 : case CrankcaseHeaterControlTemp::Schedule: {
1554 3 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1555 : // Compressor Ambient Temperature Schedule
1556 3 : HPWH.CrankcaseTempSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[21 + nAlphaOffset]);
1557 3 : if (HPWH.CrankcaseTempSchedule == 0) {
1558 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
1559 0 : ShowContinueError(state, hpwhAlphaFieldNames[21 + nAlphaOffset] + "=\"" + hpwhAlpha[21 + nAlphaOffset] + "\".");
1560 0 : ErrorsFound = true;
1561 : }
1562 : } else {
1563 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", ");
1564 0 : ShowContinueError(state, "required " + hpwhAlphaFieldNames[21 + nAlphaOffset] + " is blank.");
1565 0 : ErrorsFound = true;
1566 : }
1567 :
1568 3 : break;
1569 : }
1570 10 : case CrankcaseHeaterControlTemp::Zone: {
1571 10 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1572 0 : ShowSevereError(state,
1573 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name +
1574 : "\": Inlet Air Configuration must be Zone Air Only or Zone And");
1575 0 : ShowContinueError(state, " Outdoor Air when compressor location equals ZONE.");
1576 0 : ErrorsFound = true;
1577 : }
1578 :
1579 10 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1580 0 : ShowWarningError(state,
1581 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\" " + hpwhAlphaFieldNames[21 + nAlphaOffset] +
1582 0 : " was provided but will not be used based on compressor location input=\"" + hpwhAlpha[20 + nAlphaOffset] +
1583 : "\".");
1584 : }
1585 10 : break;
1586 : }
1587 10 : case CrankcaseHeaterControlTemp::Outdoors: {
1588 10 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1589 0 : ShowWarningError(state,
1590 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\" " + hpwhAlphaFieldNames[21 + nAlphaOffset] +
1591 0 : " was provided but will not be used based on " + hpwhAlphaFieldNames[21 + nAlphaOffset] + "=\"" +
1592 0 : hpwhAlpha[20 + nAlphaOffset] + "\".");
1593 : }
1594 10 : break;
1595 : }
1596 0 : default:
1597 0 : break;
1598 : }
1599 :
1600 : // Fan Name
1601 23 : HPWH.FanName = hpwhAlpha[23 + nAlphaOffset];
1602 23 : HPWH.FanType = hpwhAlpha[22 + nAlphaOffset];
1603 :
1604 : // check that the fan exists
1605 23 : bool errFlag = false;
1606 23 : ValidateComponent(state, HPWH.FanType, HPWH.FanName, errFlag, state.dataIPShortCut->cCurrentModuleObject);
1607 :
1608 23 : Real64 FanVolFlow = 0.0;
1609 23 : if (errFlag) {
1610 0 : ShowContinueError(state, "...occurs in " + state.dataIPShortCut->cCurrentModuleObject + ", unit=\"" + HPWH.Name + "\".");
1611 0 : ErrorsFound = true;
1612 : } else {
1613 23 : if (UtilityRoutines::SameString(HPWH.FanType, "Fan:SystemModel")) {
1614 13 : HPWH.FanType_Num = DataHVACGlobals::FanType_SystemModelObject;
1615 13 : state.dataHVACFan->fanObjs.emplace_back(new HVACFan::FanSystem(state, HPWH.FanName)); // call constructor
1616 13 : HPWH.FanNum = HVACFan::getFanObjectVectorIndex(state, HPWH.FanName);
1617 13 : FanVolFlow = state.dataHVACFan->fanObjs[HPWH.FanNum]->designAirVolFlowRate;
1618 :
1619 : } else {
1620 10 : Fans::GetFanType(state, HPWH.FanName, HPWH.FanType_Num, errFlag, state.dataIPShortCut->cCurrentModuleObject, HPWH.Name);
1621 10 : Fans::GetFanIndex(state, HPWH.FanName, HPWH.FanNum, errFlag, state.dataIPShortCut->cCurrentModuleObject);
1622 10 : Fans::GetFanVolFlow(state, HPWH.FanNum, FanVolFlow);
1623 : }
1624 : }
1625 : // issue #5630, set fan info in coils.
1626 23 : if (bIsVScoil) {
1627 7 : VariableSpeedCoils::setVarSpeedHPWHFanTypeNum(state, HPWH.DXCoilNum, HPWH.FanType_Num);
1628 7 : VariableSpeedCoils::setVarSpeedHPWHFanIndex(state, HPWH.DXCoilNum, HPWH.FanNum);
1629 : } else {
1630 16 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanName);
1631 16 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanNum);
1632 16 : DXCoils::SetDXCoolingCoilData(
1633 : state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanType_Num);
1634 : }
1635 :
1636 23 : if (errFlag) {
1637 0 : ErrorsFound = true;
1638 23 : } else if (HPWH.FanType_Num != DataHVACGlobals::FanType_SimpleOnOff && HPWH.FanType_Num != DataHVACGlobals::FanType_SystemModelObject) {
1639 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\": illegal fan type specified.");
1640 0 : ShowContinueError(
1641 0 : state, " The fan object (" + HPWH.FanName + ") type must be Fan:SystemModel or Fan:OnOff when used with a heat pump water heater.");
1642 0 : ErrorsFound = true;
1643 23 : } else if (!UtilityRoutines::SameString(HPWH.FanType, "Fan:OnOff") && !UtilityRoutines::SameString(HPWH.FanType, "Fan:SystemModel")) {
1644 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\": illegal fan type specified.");
1645 0 : ShowContinueError(state, " The " + state.dataIPShortCut->cCurrentModuleObject + " must specify that the fan object");
1646 0 : ShowContinueError(state,
1647 : " is of type FanSystemModel or Fan:OnOff in addition to the fan actually being of that type and defined elsewhere.");
1648 : }
1649 :
1650 23 : if (FanVolFlow != DataSizing::AutoSize && !errFlag) {
1651 21 : if (FanVolFlow < HPWH.OperatingAirFlowRate) {
1652 0 : ShowSevereError(state,
1653 0 : format("{} - air flow rate = {:.7T} in fan object {} is less than the HPWHs evaporator air flow rate.",
1654 0 : state.dataIPShortCut->cCurrentModuleObject,
1655 : FanVolFlow,
1656 0 : HPWH.FanName));
1657 0 : ShowContinueError(state, " The fan flow rate must be >= to the HPWHs evaporator volumetric air flow rate.");
1658 0 : ShowContinueError(state, " Occurs in unit = " + HPWH.Name);
1659 0 : ErrorsFound = true;
1660 : }
1661 : }
1662 :
1663 : // Fan Placement
1664 23 : if (UtilityRoutines::SameString(hpwhAlpha[24 + nAlphaOffset], "BlowThrough")) {
1665 12 : HPWH.FanPlacement = DataHVACGlobals::BlowThru;
1666 :
1667 11 : } else if (UtilityRoutines::SameString(hpwhAlpha[24 + nAlphaOffset], "DrawThrough")) {
1668 11 : HPWH.FanPlacement = DataHVACGlobals::DrawThru;
1669 :
1670 : } else {
1671 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", invalid ");
1672 0 : ShowContinueError(state, hpwhAlphaFieldNames[24 + nAlphaOffset] + "=\"" + hpwhAlpha[24 + nAlphaOffset] + "\".");
1673 0 : ErrorsFound = true;
1674 : }
1675 :
1676 23 : if (HPWH.DXCoilNum > 0 && !bIsVScoil) {
1677 : // get HPWH capacity, air inlet node, and PLF curve info from DX coil object
1678 16 : HPWH.Capacity = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2;
1679 16 : HPWH.DXCoilAirInletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
1680 16 : HPWH.DXCoilPLFFPLR = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).PLFFPLR(1);
1681 : // check the range of condenser pump power to be <= 5 gpm/ton
1682 32 : if (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower / state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2 >
1683 : 0.1422) {
1684 0 : ShowWarningError(
1685 : state,
1686 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).DXCoilType + "= " + state.dataDXCoils->DXCoil(HPWH.DXCoilNum).Name +
1687 0 : format(": Rated condenser pump power per watt of rated heating capacity has exceeded the recommended maximum of 0.1422 "
1688 : "W/W (41.67 watt/MBH). Condenser pump power per watt = {:.4T}",
1689 0 : (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower /
1690 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2)));
1691 : }
1692 7 : } else if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
1693 :
1694 7 : if (HPWH.bIsIHP) {
1695 1 : HPWH.Capacity =
1696 1 : GetDWHCoilCapacityIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1697 1 : HPWH.DXCoilAirInletNode = IntegratedHeatPump::GetCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1698 1 : HPWH.DXCoilPLFFPLR =
1699 1 : GetIHPDWHCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1700 : } else {
1701 6 : HPWH.Capacity = VariableSpeedCoils::GetCoilCapacityVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1702 6 : HPWH.DXCoilAirInletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1703 6 : HPWH.DXCoilPLFFPLR = VariableSpeedCoils::GetVSCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1704 : }
1705 : // check the range of condenser pump power to be <= 5 gpm/ton, will be checked in the coil object
1706 : }
1707 :
1708 23 : if (HPWH.OperatingWaterFlowRate == DataGlobalConstants::AutoCalculate) {
1709 8 : HPWH.OperatingWaterFlowRate = 0.00000004487 * HPWH.Capacity;
1710 8 : HPWH.WaterFlowRateAutoSized = true;
1711 : }
1712 :
1713 23 : if (HPWH.OperatingAirFlowRate == DataGlobalConstants::AutoCalculate) {
1714 10 : HPWH.OperatingAirFlowRate = 0.00005035 * HPWH.Capacity;
1715 10 : HPWH.AirFlowRateAutoSized = true;
1716 : }
1717 :
1718 : // On Cycle Parasitic Electric Load
1719 23 : HPWH.OnCycParaLoad = hpwhNumeric[6 + nNumericOffset];
1720 23 : if (HPWH.OnCycParaLoad < 0.0) {
1721 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\",");
1722 0 : ShowContinueError(state,
1723 0 : hpwhNumericFieldNames[6 + nNumericOffset] + " must be >= 0. " + hpwhNumericFieldNames[6 + nNumericOffset] +
1724 0 : format(" = {:.2T}", hpwhNumeric[6 + nNumericOffset]));
1725 0 : ErrorsFound = true;
1726 : }
1727 :
1728 : // Off Cycle Parasitic Electric Load
1729 23 : HPWH.OffCycParaLoad = hpwhNumeric[7 + nNumericOffset];
1730 23 : if (HPWH.OffCycParaLoad < 0.0) {
1731 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\",");
1732 0 : ShowContinueError(state,
1733 0 : hpwhNumericFieldNames[7 + nNumericOffset] + " must be >= 0. " + hpwhNumericFieldNames[2 + nNumericOffset] +
1734 0 : format(" = {:.2T}", hpwhNumeric[7 + nNumericOffset]));
1735 0 : ErrorsFound = true;
1736 : }
1737 :
1738 : // Parasitic Heat Rejection Location
1739 23 : if (UtilityRoutines::SameString(hpwhAlpha[25 + nAlphaOffset], "Zone")) {
1740 8 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::TempZone;
1741 8 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1742 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\",");
1743 0 : ShowContinueError(state, hpwhAlphaFieldNames[25 + nAlphaOffset] + " must be ZoneAirOnly or ZoneAndOutdoorAir");
1744 0 : ShowContinueError(state, " when parasitic heat rejection location equals Zone.");
1745 0 : ErrorsFound = true;
1746 : }
1747 15 : } else if (UtilityRoutines::SameString(hpwhAlpha[25 + nAlphaOffset], "Outdoors")) {
1748 15 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::OutsideAir;
1749 : } else {
1750 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1751 0 : ShowContinueError(state, " parasitic heat rejection location must be either Zone or Outdoors.");
1752 0 : ErrorsFound = true;
1753 : }
1754 :
1755 : // Inlet Air Mixer Node
1756 : // get mixer/splitter nodes only when Inlet Air Configuration is ZoneAndOutdoorAir
1757 23 : if (!hpwhAlphaBlank[26 + nAlphaOffset]) {
1758 : // For the inlet air mixer node, NodeConnectionType is outlet from the HPWH inlet air node
1759 3 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1760 6 : HPWH.InletAirMixerNode = NodeInputManager::GetOnlySingleNode(state,
1761 6 : hpwhAlpha[26 + nAlphaOffset],
1762 : ErrorsFound,
1763 : objType,
1764 6 : HPWH.Name + "-INLET AIR MIXER",
1765 : DataLoopNode::NodeFluidType::Air,
1766 : DataLoopNode::ConnectionType::Outlet,
1767 : NodeInputManager::CompFluidStream::Primary,
1768 3 : DataLoopNode::ObjectIsNotParent);
1769 : } else {
1770 0 : ShowWarningError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1771 0 : ShowContinueError(state,
1772 : "Inlet air mixer node name specified but only required when Inlet Air Configuration is selected as "
1773 : "Zone and OutdoorAir. Node name disregarded and simulation continues.");
1774 : }
1775 20 : } else if (hpwhAlphaBlank[26 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1776 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1777 0 : ShowContinueError(state, "Inlet air mixer node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1778 0 : ErrorsFound = true;
1779 : }
1780 :
1781 : // Outlet Air Splitter Node
1782 23 : if (!hpwhAlphaBlank[27 + nAlphaOffset]) {
1783 : // For the outlet air splitter node, NodeConnectionType is inlet to the HPWH outlet air node
1784 3 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1785 6 : HPWH.OutletAirSplitterNode = NodeInputManager::GetOnlySingleNode(state,
1786 6 : hpwhAlpha[27 + nAlphaOffset],
1787 : ErrorsFound,
1788 : objType,
1789 6 : HPWH.Name + "-OUTLET AIR SPLITTER",
1790 : DataLoopNode::NodeFluidType::Air,
1791 : DataLoopNode::ConnectionType::Inlet,
1792 : NodeInputManager::CompFluidStream::Primary,
1793 3 : DataLoopNode::ObjectIsNotParent);
1794 : } else {
1795 0 : ShowWarningError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1796 0 : ShowContinueError(state,
1797 : "Outlet air splitter node name specified but only required when Inlet Air Configuration is selected as "
1798 : "ZoneAndOutdoorAir. Node name disregarded and simulation continues.");
1799 : }
1800 20 : } else if (hpwhAlphaBlank[27 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1801 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1802 0 : ShowContinueError(state, "Outlet air splitter node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1803 0 : ErrorsFound = true;
1804 : }
1805 :
1806 : // get node data for HPWH
1807 23 : if (HPWH.InletAirMixerNode != 0) {
1808 : // when mixer/splitter nodes are used the HPWH's inlet/outlet node are set up as DataLoopNode::ObjectIsNotParent
1809 :
1810 6 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1811 6 : hpwhAlpha[7 + nAlphaOffset],
1812 : ErrorsFound,
1813 : objType,
1814 6 : HPWH.Name + "-INLET AIR MIXER",
1815 : DataLoopNode::NodeFluidType::Air,
1816 : DataLoopNode::ConnectionType::Inlet,
1817 : NodeInputManager::CompFluidStream::Primary,
1818 3 : DataLoopNode::ObjectIsNotParent);
1819 :
1820 6 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1821 6 : hpwhAlpha[8 + nAlphaOffset],
1822 : ErrorsFound,
1823 : objType,
1824 6 : HPWH.Name + "-OUTLET AIR SPLITTER",
1825 : DataLoopNode::NodeFluidType::Air,
1826 : DataLoopNode::ConnectionType::Outlet,
1827 : NodeInputManager::CompFluidStream::Primary,
1828 3 : DataLoopNode::ObjectIsNotParent);
1829 :
1830 9 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1831 6 : hpwhAlpha[9 + nAlphaOffset],
1832 : ErrorsFound,
1833 : objType,
1834 : HPWH.Name,
1835 : DataLoopNode::NodeFluidType::Air,
1836 : DataLoopNode::ConnectionType::OutsideAirReference,
1837 : NodeInputManager::CompFluidStream::Primary,
1838 3 : DataLoopNode::ObjectIsParent);
1839 3 : if (!hpwhAlpha[9 + nAlphaOffset].empty()) {
1840 : bool Okay;
1841 3 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, HPWH.OutsideAirNode, Okay);
1842 3 : if (!Okay) {
1843 0 : ShowWarningError(state,
1844 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name +
1845 0 : "\": Adding outdoor air node=" + hpwhAlpha[9 + nAlphaOffset]);
1846 : }
1847 : }
1848 :
1849 9 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1850 6 : hpwhAlpha[10 + nAlphaOffset],
1851 : ErrorsFound,
1852 : objType,
1853 : HPWH.Name,
1854 : DataLoopNode::NodeFluidType::Air,
1855 : DataLoopNode::ConnectionType::ReliefAir,
1856 : NodeInputManager::CompFluidStream::Primary,
1857 3 : DataLoopNode::ObjectIsParent);
1858 :
1859 : } else {
1860 : // when mixer/splitter nodes are NOT used the HPWH's inlet/outlet nodes are set up as DataLoopNode::ObjectIsParent
1861 20 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1862 : // for scheduled HPWH's the inlet node is not on any branch or parent object, make it an outlet node
1863 : // to avoid node connection errors
1864 9 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1865 6 : hpwhAlpha[7 + nAlphaOffset],
1866 : ErrorsFound,
1867 : objType,
1868 : HPWH.Name,
1869 : DataLoopNode::NodeFluidType::Air,
1870 : DataLoopNode::ConnectionType::Outlet,
1871 : NodeInputManager::CompFluidStream::Primary,
1872 3 : DataLoopNode::ObjectIsParent);
1873 :
1874 9 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1875 6 : hpwhAlpha[8 + nAlphaOffset],
1876 : ErrorsFound,
1877 : objType,
1878 : HPWH.Name,
1879 : DataLoopNode::NodeFluidType::Air,
1880 : DataLoopNode::ConnectionType::Outlet,
1881 : NodeInputManager::CompFluidStream::Primary,
1882 3 : DataLoopNode::ObjectIsParent);
1883 :
1884 : } else { // HPWH is connected to a zone with no mixer/splitter nodes
1885 17 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1886 21 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1887 14 : hpwhAlpha[7 + nAlphaOffset],
1888 : ErrorsFound,
1889 : objType,
1890 : HPWH.Name,
1891 : DataLoopNode::NodeFluidType::Air,
1892 : DataLoopNode::ConnectionType::Inlet,
1893 : NodeInputManager::CompFluidStream::Primary,
1894 7 : DataLoopNode::ObjectIsParent);
1895 :
1896 21 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1897 14 : hpwhAlpha[8 + nAlphaOffset],
1898 : ErrorsFound,
1899 : objType,
1900 : HPWH.Name,
1901 : DataLoopNode::NodeFluidType::Air,
1902 : DataLoopNode::ConnectionType::Outlet,
1903 : NodeInputManager::CompFluidStream::Primary,
1904 7 : DataLoopNode::ObjectIsParent);
1905 : } else { // HPWH is located outdoors
1906 30 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1907 20 : hpwhAlpha[9 + nAlphaOffset],
1908 : ErrorsFound,
1909 : objType,
1910 : HPWH.Name,
1911 : DataLoopNode::NodeFluidType::Air,
1912 : DataLoopNode::ConnectionType::OutsideAirReference,
1913 : NodeInputManager::CompFluidStream::Primary,
1914 10 : DataLoopNode::ObjectIsParent);
1915 10 : if (!hpwhAlphaBlank[9 + nAlphaOffset]) {
1916 : bool Okay;
1917 10 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, HPWH.OutsideAirNode, Okay);
1918 10 : if (!Okay) {
1919 0 : ShowWarningError(state,
1920 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name +
1921 0 : "\": Adding outdoor air node =" + hpwhAlpha[9 + nAlphaOffset]);
1922 : }
1923 : }
1924 :
1925 30 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1926 20 : hpwhAlpha[10 + nAlphaOffset],
1927 : ErrorsFound,
1928 : objType,
1929 : HPWH.Name,
1930 : DataLoopNode::NodeFluidType::Air,
1931 : DataLoopNode::ConnectionType::ReliefAir,
1932 : NodeInputManager::CompFluidStream::Primary,
1933 10 : DataLoopNode::ObjectIsParent);
1934 : }
1935 : }
1936 : }
1937 : // check that required node names are present
1938 23 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule || HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1939 20 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1940 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1941 0 : ShowContinueError(state, "When " + hpwhAlphaFieldNames[6 + nAlphaOffset] + "=\"" + hpwhAlpha[6 + nAlphaOffset] + "\".");
1942 0 : ShowContinueError(state,
1943 0 : hpwhAlphaFieldNames[7 + nAlphaOffset] + " and " + hpwhAlphaFieldNames[8 + nAlphaOffset] + " must be specified.");
1944 0 : ErrorsFound = true;
1945 : }
1946 13 : } else if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
1947 10 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1948 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1949 0 : ShowContinueError(state, "When " + hpwhAlphaFieldNames[6 + nAlphaOffset] + "=\"" + hpwhAlpha[6 + nAlphaOffset] + "\".");
1950 0 : ShowContinueError(state,
1951 0 : hpwhAlphaFieldNames[9 + nAlphaOffset] + " and " + hpwhAlphaFieldNames[10 + nAlphaOffset] + " must be specified.");
1952 0 : ErrorsFound = true;
1953 : }
1954 3 : } else if (HPWH.InletAirMixerNode > 0 && HPWH.OutletAirSplitterNode > 0 && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1955 3 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0 || HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1956 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1957 0 : ShowContinueError(state, "When " + hpwhAlphaFieldNames[6 + nAlphaOffset] + "=\"" + hpwhAlpha[6 + nAlphaOffset] + "\".");
1958 0 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1959 0 : ShowContinueError(
1960 0 : state, hpwhAlphaFieldNames[7 + nAlphaOffset] + " and " + hpwhAlphaFieldNames[8 + nAlphaOffset] + " must be specified.");
1961 : }
1962 0 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1963 0 : ShowContinueError(
1964 0 : state, hpwhAlphaFieldNames[9 + nAlphaOffset] + " and " + hpwhAlphaFieldNames[10 + nAlphaOffset] + " must be specified.");
1965 : }
1966 0 : ErrorsFound = true;
1967 : }
1968 : }
1969 :
1970 : // check that the HPWH inlet and outlet nodes are in the same zone (ZoneHVAC:EquipmentConnections) when
1971 : // Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
1972 33 : if ((HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) &&
1973 10 : HPWH.AmbientTempZone > 0) {
1974 10 : if (!state.dataZoneEquip->ZoneEquipInputsFilled) {
1975 0 : DataZoneEquipment::GetZoneEquipmentData(state);
1976 0 : state.dataZoneEquip->ZoneEquipInputsFilled = true;
1977 : }
1978 10 : if (allocated(state.dataZoneEquip->ZoneEquipConfig)) {
1979 10 : bool FoundInletNode = false;
1980 10 : bool FoundOutletNode = false;
1981 10 : int ZoneNum = HPWH.AmbientTempZone;
1982 10 : if (ZoneNum <= state.dataGlobal->NumOfZones) {
1983 37 : for (int SupAirIn = 1; SupAirIn <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumInletNodes; ++SupAirIn) {
1984 27 : if (HPWH.HeatPumpAirOutletNode != state.dataZoneEquip->ZoneEquipConfig(ZoneNum).InletNode(SupAirIn)) continue;
1985 10 : FoundOutletNode = true;
1986 : }
1987 27 : for (int ExhAirOut = 1; ExhAirOut <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumExhaustNodes; ++ExhAirOut) {
1988 17 : if (HPWH.HeatPumpAirInletNode != state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ExhaustNode(ExhAirOut)) continue;
1989 10 : FoundInletNode = true;
1990 : }
1991 10 : if (!FoundInletNode) {
1992 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
1993 0 : ShowContinueError(state, "The HPWH's air inlet node name = " + hpwhAlpha[7 + nAlphaOffset] + " was not properly specified ");
1994 0 : ShowContinueError(state,
1995 0 : "as an exhaust air node for zone = " + hpwhAlpha[13 + nAlphaOffset] +
1996 : " in a ZoneHVAC:EquipmentConnections object.");
1997 0 : ErrorsFound = true;
1998 : }
1999 10 : if (!FoundOutletNode) {
2000 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
2001 0 : ShowContinueError(state, "The HPWH's air outlet node name = " + hpwhAlpha[8 + nAlphaOffset] + " was not properly specified ");
2002 0 : ShowContinueError(
2003 0 : state, "as an inlet air node for zone = " + hpwhAlpha[13 + nAlphaOffset] + " in a ZoneHVAC:EquipmentConnections object.");
2004 0 : ErrorsFound = true;
2005 : }
2006 : }
2007 : } else {
2008 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
2009 0 : ShowContinueError(state,
2010 : "Heat pump water heater air inlet node name and air outlet node name must be listed in a "
2011 : "ZoneHVAC:EquipmentConnections object when Inlet Air Configuration is equal to ZoneAirOnly or "
2012 : "ZoneAndOutdoorAir.");
2013 0 : ErrorsFound = true;
2014 : }
2015 : }
2016 :
2017 : // only get the inlet air mixer schedule if the inlet air configuration is zone and outdoor air
2018 23 : if (!hpwhAlphaBlank[28 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
2019 3 : HPWH.InletAirMixerSchPtr = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[28 + nAlphaOffset]);
2020 3 : if (HPWH.InletAirMixerSchPtr == 0) {
2021 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
2022 0 : ShowContinueError(state, hpwhAlphaFieldNames[28 + nAlphaOffset] + "=\"" + hpwhAlpha[28 + nAlphaOffset] + "\",");
2023 0 : ErrorsFound = true;
2024 : } else {
2025 3 : bool ValidScheduleValue = ScheduleManager::CheckScheduleValueMinMax(state, HPWH.InletAirMixerSchPtr, ">=", 0.0, "<=", 1.0);
2026 3 : if (!ValidScheduleValue) {
2027 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\", not found");
2028 0 : ShowContinueError(state,
2029 0 : hpwhAlphaFieldNames[28 + nAlphaOffset] + " values out of range of 0 to 1, Schedule=\"" +
2030 0 : hpwhAlpha[28 + nAlphaOffset] + "\".");
2031 0 : ErrorsFound = true;
2032 : }
2033 : // set outlet air splitter schedule index equal to inlet air mixer schedule index
2034 : // (place holder for when zone pressurization/depressurization is allowed and different schedules can be used)
2035 3 : HPWH.OutletAirSplitterSchPtr = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[28 + nAlphaOffset]);
2036 : }
2037 : }
2038 :
2039 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2040 23 : if (HPWH.FanPlacement == DataHVACGlobals::DrawThru) {
2041 11 : if (HPWH.OutletAirSplitterNode != 0) {
2042 3 : HPWH.FanOutletNode = HPWH.OutletAirSplitterNode;
2043 : } else {
2044 8 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2045 1 : HPWH.FanOutletNode = HPWH.ExhaustAirNode;
2046 : } else {
2047 7 : HPWH.FanOutletNode = HPWH.HeatPumpAirOutletNode;
2048 : }
2049 : }
2050 12 : } else if (HPWH.FanPlacement == DataHVACGlobals::BlowThru) {
2051 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2052 12 : if (bIsVScoil) {
2053 5 : if (HPWH.bIsIHP) {
2054 1 : HPWH.FanOutletNode = IntegratedHeatPump::GetDWHCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2055 : } else {
2056 4 : HPWH.FanOutletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2057 : }
2058 : } else {
2059 7 : HPWH.FanOutletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
2060 : }
2061 : }
2062 :
2063 : // check that fan outlet node is indeed correct
2064 23 : int FanOutletNodeNum(0);
2065 23 : if (HPWH.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
2066 13 : FanOutletNodeNum = state.dataHVACFan->fanObjs[HPWH.FanNum]->outletNodeNum;
2067 : } else {
2068 10 : errFlag = false;
2069 10 : FanOutletNodeNum = Fans::GetFanOutletNode(state, HPWH.FanType, HPWH.FanName, errFlag);
2070 10 : if (errFlag) {
2071 0 : ShowContinueError(state, "...occurs in unit=\"" + HPWH.Name + "\".");
2072 0 : ErrorsFound = true;
2073 : }
2074 : }
2075 23 : if (FanOutletNodeNum != HPWH.FanOutletNode) {
2076 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
2077 0 : ShowContinueError(state, "Heat pump water heater fan outlet node name does not match next connected component.");
2078 0 : if (FanOutletNodeNum != 0) {
2079 0 : ShowContinueError(state, "Fan outlet node name = " + state.dataLoopNodes->NodeID(FanOutletNodeNum));
2080 : }
2081 0 : if (HPWH.FanOutletNode != 0) {
2082 0 : ShowContinueError(state, "Expected fan outlet node name = " + state.dataLoopNodes->NodeID(HPWH.FanOutletNode));
2083 : }
2084 0 : ErrorsFound = true;
2085 : }
2086 23 : int FanInletNodeNum(0);
2087 23 : if (HPWH.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
2088 13 : FanInletNodeNum = state.dataHVACFan->fanObjs[HPWH.FanNum]->inletNodeNum;
2089 : } else {
2090 10 : errFlag = false;
2091 10 : FanInletNodeNum = Fans::GetFanInletNode(state, HPWH.FanType, HPWH.FanName, errFlag);
2092 10 : if (errFlag) {
2093 0 : ShowContinueError(state, "...occurs in unit=\"" + HPWH.Name + "\".");
2094 0 : ErrorsFound = true;
2095 : }
2096 : }
2097 23 : int HPWHFanInletNodeNum(0);
2098 23 : if (HPWH.InletAirMixerNode != 0) {
2099 3 : HPWHFanInletNodeNum = HPWH.InletAirMixerNode;
2100 : } else {
2101 20 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2102 10 : HPWHFanInletNodeNum = HPWH.OutsideAirNode;
2103 : } else {
2104 10 : HPWHFanInletNodeNum = HPWH.HeatPumpAirInletNode;
2105 : }
2106 : }
2107 23 : if (HPWH.FanPlacement == DataHVACGlobals::BlowThru) {
2108 12 : if (FanInletNodeNum != HPWHFanInletNodeNum) {
2109 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
2110 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2111 0 : if (FanOutletNodeNum != 0) {
2112 0 : ShowContinueError(state, "Fan inlet node name = " + state.dataLoopNodes->NodeID(FanInletNodeNum));
2113 : }
2114 0 : if (HPWH.FanOutletNode != 0) {
2115 0 : ShowContinueError(state, "Expected fan inlet node name = " + state.dataLoopNodes->NodeID(HPWHFanInletNodeNum));
2116 : }
2117 0 : ErrorsFound = true;
2118 : }
2119 : }
2120 :
2121 23 : int DXCoilAirOutletNodeNum(0);
2122 23 : if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
2123 14 : if (HPWH.bIsIHP) {
2124 1 : DXCoilAirOutletNodeNum = IntegratedHeatPump::GetDWHCoilOutletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2125 : } else {
2126 6 : DXCoilAirOutletNodeNum = VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2127 : }
2128 :
2129 16 : } else if (HPWH.DXCoilNum > 0) {
2130 16 : DXCoilAirOutletNodeNum = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirOutNode;
2131 : }
2132 23 : if (HPWH.FanPlacement == DataHVACGlobals::DrawThru) {
2133 11 : if (FanInletNodeNum != DXCoilAirOutletNodeNum) {
2134 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
2135 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2136 0 : if (FanInletNodeNum != 0) {
2137 0 : ShowContinueError(state, "Fan inlet node name = " + state.dataLoopNodes->NodeID(FanInletNodeNum));
2138 : }
2139 0 : if (DXCoilAirOutletNodeNum != 0) {
2140 0 : ShowContinueError(state, "Expected fan inlet node name = " + state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum));
2141 : }
2142 0 : ErrorsFound = true;
2143 : }
2144 12 : } else if (HPWH.FanPlacement == DataHVACGlobals::BlowThru) {
2145 12 : int HPWHCoilOutletNodeNum(0);
2146 12 : if (HPWH.OutletAirSplitterNode != 0) {
2147 0 : HPWHCoilOutletNodeNum = HPWH.OutletAirSplitterNode;
2148 : } else {
2149 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2150 9 : HPWHCoilOutletNodeNum = HPWH.ExhaustAirNode;
2151 : } else {
2152 3 : HPWHCoilOutletNodeNum = HPWH.HeatPumpAirOutletNode;
2153 : }
2154 : }
2155 12 : if (DXCoilAirOutletNodeNum != HPWHCoilOutletNodeNum) {
2156 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
2157 0 : ShowContinueError(state, "Heat pump water heater coil outlet node name does not match next connected component.");
2158 0 : if (DXCoilAirOutletNodeNum != 0) {
2159 0 : ShowContinueError(state, "Coil outlet node name = " + state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum));
2160 : }
2161 0 : if (HPWHCoilOutletNodeNum != 0) {
2162 0 : ShowContinueError(state, "Expected coil outlet node name = " + state.dataLoopNodes->NodeID(HPWHCoilOutletNodeNum));
2163 : }
2164 0 : ErrorsFound = true;
2165 : }
2166 : }
2167 :
2168 : // set the max mass flow rate for outdoor fans
2169 23 : if (HPWH.FanOutletNode > 0)
2170 23 : state.dataLoopNodes->Node(HPWH.FanOutletNode).MassFlowRateMax =
2171 46 : HPWH.OperatingAirFlowRate * Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, 20.0, 0.0);
2172 :
2173 23 : if (HPWH.FanPlacement == DataHVACGlobals::BlowThru) {
2174 12 : if (HPWH.InletAirMixerNode > 0) {
2175 0 : HPWH.FanInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2176 0 : HPWH.FanOutletNode_str = "UNDEFINED";
2177 : } else {
2178 12 : if (HPWH.OutsideAirNode == 0) {
2179 3 : HPWH.FanInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2180 3 : HPWH.FanOutletNode_str = "UNDEFINED";
2181 : } else {
2182 9 : HPWH.FanInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2183 9 : HPWH.FanOutletNode_str = "UNDEFINED";
2184 : }
2185 : }
2186 12 : if (HPWH.OutletAirSplitterNode > 0) {
2187 0 : HPWH.CoilInletNode_str = "UNDEFINED";
2188 0 : HPWH.CoilOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2189 : } else {
2190 12 : if (HPWH.OutsideAirNode == 0) {
2191 3 : HPWH.CoilInletNode_str = "UNDEFINED";
2192 3 : HPWH.CoilOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2193 : } else {
2194 9 : HPWH.CoilInletNode_str = "UNDEFINED";
2195 9 : HPWH.CoilOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2196 : }
2197 : }
2198 : } else {
2199 11 : if (HPWH.InletAirMixerNode > 0) {
2200 3 : HPWH.CoilInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2201 3 : HPWH.CoilOutletNode_str = "UNDEFINED";
2202 : } else {
2203 8 : if (HPWH.OutsideAirNode == 0) {
2204 7 : HPWH.CoilInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2205 7 : HPWH.CoilOutletNode_str = "UNDEFINED";
2206 : } else {
2207 1 : HPWH.CoilInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2208 1 : HPWH.CoilOutletNode_str = "UNDEFINED";
2209 : }
2210 : }
2211 11 : if (HPWH.OutletAirSplitterNode > 0) {
2212 3 : HPWH.FanInletNode_str = "UNDEFINED";
2213 3 : HPWH.FanOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2214 : } else {
2215 8 : if (HPWH.OutsideAirNode == 0) {
2216 7 : HPWH.FanInletNode_str = "UNDEFINED";
2217 7 : HPWH.FanOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2218 : } else {
2219 1 : HPWH.FanInletNode_str = "UNDEFINED";
2220 1 : HPWH.FanOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2221 : }
2222 : }
2223 : }
2224 :
2225 : // set up comp set for air side nodes (can be blow thru or draw thru, may or may not have damper nodes)
2226 23 : if (HPWH.bIsIHP) {
2227 3 : BranchNodeConnections::SetUpCompSets(
2228 3 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName + " Outdoor Coil", HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2229 : } else {
2230 22 : BranchNodeConnections::SetUpCompSets(
2231 22 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2232 : }
2233 :
2234 23 : BranchNodeConnections::SetUpCompSets(state, HPWH.Type, HPWH.Name, HPWH.FanType, HPWH.FanName, HPWH.FanInletNode_str, HPWH.FanOutletNode_str);
2235 :
2236 : // Control Logic Flag
2237 46 : std::string CtrlLogicFlag = hpwhAlphaBlank[29 + nAlphaOffset] ? "SIMULTANEOUS" : hpwhAlpha[29 + nAlphaOffset];
2238 23 : if (UtilityRoutines::SameString(CtrlLogicFlag, "SIMULTANEOUS")) {
2239 20 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = true;
2240 3 : } else if (UtilityRoutines::SameString(CtrlLogicFlag, "MUTUALLYEXCLUSIVE")) {
2241 3 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = false;
2242 : } else {
2243 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + "=\"" + HPWH.Name + "\":");
2244 0 : ShowContinueError(state, CtrlLogicFlag + " is not a valid value for field Tank Element Control Logic.");
2245 0 : ErrorsFound = true;
2246 : }
2247 :
2248 : // Control Sensor 1 Location In Stratified Tank
2249 23 : if (!hpwhNumericBlank[8 + nNumericOffset]) {
2250 11 : HPWH.ControlSensor1Height = hpwhNumeric[8 + nNumericOffset];
2251 : } else {
2252 : // use heater1 location, which we don't know right now
2253 12 : HPWH.ControlSensor1Height = -1.0;
2254 : }
2255 :
2256 : // Control Sensor 1 Weight
2257 23 : HPWH.ControlSensor1Weight = hpwhNumericBlank[9 + nNumericOffset] ? 1.0 : hpwhNumeric[9 + nNumericOffset];
2258 :
2259 : // Control Sensor 2 Location In Stratified Tank
2260 23 : if (!hpwhNumericBlank[10 + nNumericOffset]) {
2261 23 : HPWH.ControlSensor2Height = hpwhNumeric[10 + nNumericOffset];
2262 : } else {
2263 0 : HPWH.ControlSensor2Height = -1.0;
2264 : }
2265 :
2266 : // Control Sensor 2 Weight
2267 23 : HPWH.ControlSensor2Weight = 1.0 - HPWH.ControlSensor1Weight;
2268 : }
2269 :
2270 9 : return ErrorsFound;
2271 : }
2272 :
2273 119 : bool getWaterHeaterMixedInputs(EnergyPlusData &state)
2274 : {
2275 119 : bool ErrorsFound = false;
2276 119 : state.dataIPShortCut->cCurrentModuleObject = cMixedWHModuleObj;
2277 : static constexpr std::string_view RoutineName = "getWaterHeaterMixedInputs";
2278 :
2279 291 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed; ++WaterThermalTankNum) {
2280 : int NumAlphas;
2281 : int NumNums;
2282 : int IOStat;
2283 1376 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2284 172 : state.dataIPShortCut->cCurrentModuleObject,
2285 : WaterThermalTankNum,
2286 172 : state.dataIPShortCut->cAlphaArgs,
2287 : NumAlphas,
2288 172 : state.dataIPShortCut->rNumericArgs,
2289 : NumNums,
2290 : IOStat,
2291 172 : state.dataIPShortCut->lNumericFieldBlanks,
2292 172 : state.dataIPShortCut->lAlphaFieldBlanks,
2293 172 : state.dataIPShortCut->cAlphaFieldNames,
2294 172 : state.dataIPShortCut->cNumericFieldNames);
2295 516 : GlobalNames::VerifyUniqueInterObjectName(state,
2296 172 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2297 172 : state.dataIPShortCut->cAlphaArgs(1),
2298 172 : state.dataIPShortCut->cCurrentModuleObject,
2299 172 : state.dataIPShortCut->cAlphaFieldNames(1),
2300 : ErrorsFound);
2301 :
2302 172 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2303 :
2304 172 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2305 172 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2306 172 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterMixed;
2307 172 : Tank.FluidIndex = Tank.waterIndex;
2308 :
2309 : // default to always on
2310 172 : Tank.SourceSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
2311 172 : Tank.UseSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
2312 :
2313 : // A user field will be added in a later release
2314 172 : Tank.EndUseSubcategoryName = "Water Heater";
2315 :
2316 172 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2317 172 : if (Tank.Volume == DataSizing::AutoSize) {
2318 2 : Tank.VolumeWasAutoSized = true;
2319 : }
2320 172 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
2321 : // Set volume to a really small number to simulate a tankless/instantaneous water heater
2322 0 : Tank.Volume = 0.000001; // = 1 cm3
2323 : }
2324 :
2325 172 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(2));
2326 172 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
2327 0 : ShowSevereError(state,
2328 0 : std::string{RoutineName} + state.dataIPShortCut->cCurrentModuleObject + "=\"" + state.dataIPShortCut->cAlphaArgs(1) +
2329 : "\", missing data.");
2330 0 : ShowContinueError(state, "blank field, missing " + state.dataIPShortCut->cAlphaFieldNames(2) + " is required");
2331 0 : ErrorsFound = true;
2332 172 : } else if (Tank.SetPointTempSchedule == 0) {
2333 0 : ShowSevereError(state,
2334 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) + ": " +
2335 0 : state.dataIPShortCut->cAlphaFieldNames(2) + " not found = " + state.dataIPShortCut->cAlphaArgs(2));
2336 0 : ErrorsFound = true;
2337 : }
2338 :
2339 172 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
2340 166 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
2341 : } else {
2342 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2343 6 : Tank.DeadBandDeltaTemp = 0.5;
2344 : }
2345 :
2346 172 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2347 172 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
2348 : } else {
2349 : // Default to very large number
2350 : // BG comment why a large number here why not boilng point of water?
2351 0 : Tank.TankTempLimit = 100.0; // 1.0E9
2352 : }
2353 :
2354 172 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
2355 172 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2356 0 : Tank.MaxCapacityWasAutoSized = true;
2357 : }
2358 :
2359 172 : if ((state.dataIPShortCut->rNumericArgs(5) > Tank.MaxCapacity) && (!Tank.MaxCapacityWasAutoSized)) {
2360 0 : ShowSevereError(state,
2361 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2362 : ": Heater Minimum Capacity cannot be greater than Heater Maximum Capacity");
2363 0 : ErrorsFound = true;
2364 : } else {
2365 172 : Tank.MinCapacity = state.dataIPShortCut->rNumericArgs(5);
2366 : }
2367 :
2368 : // Validate Heater Control Type
2369 172 : Tank.ControlType = static_cast<HeaterControlMode>(
2370 344 : getEnumerationValue(HeaterControlModeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(3))));
2371 172 : switch (Tank.ControlType) {
2372 167 : case HeaterControlMode::Cycle: {
2373 167 : Tank.MinCapacity = Tank.MaxCapacity;
2374 167 : break;
2375 : }
2376 5 : case HeaterControlMode::Modulate: {
2377 :
2378 : // CASE ('MODULATE WITH OVERHEAT') ! Not yet implemented
2379 :
2380 : // CASE ('MODULATE WITH UNDERHEAT') ! Not yet implemented
2381 :
2382 5 : break;
2383 : }
2384 0 : default: {
2385 0 : ShowSevereError(state,
2386 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2387 0 : ": Invalid Control Type entered=" + state.dataIPShortCut->cAlphaArgs(3));
2388 0 : ErrorsFound = true;
2389 0 : break;
2390 : }
2391 : }
2392 :
2393 172 : Tank.VolFlowRateMin = state.dataIPShortCut->rNumericArgs(6);
2394 172 : Tank.VolFlowRateMin = max(0.0, Tank.VolFlowRateMin);
2395 172 : Tank.IgnitionDelay = state.dataIPShortCut->rNumericArgs(7); // Not yet implemented
2396 :
2397 : // Validate Heater Fuel Type
2398 172 : Tank.FuelType = static_cast<Fuel>(getEnumerationValue(FuelTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(4))));
2399 172 : switch (Tank.FuelType) {
2400 0 : case Fuel::Invalid: {
2401 0 : ShowSevereError(state,
2402 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2403 0 : ": Invalid Heater Fuel Type entered=" + state.dataIPShortCut->cAlphaArgs(4));
2404 : // Set to Electric to avoid errors when setting up output variables
2405 0 : Tank.FuelType = Fuel::Electricity;
2406 0 : ErrorsFound = true;
2407 0 : break;
2408 : }
2409 172 : default:
2410 172 : break;
2411 : }
2412 :
2413 172 : if (state.dataIPShortCut->rNumericArgs(8) > 0.0) {
2414 172 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(8);
2415 : } else {
2416 0 : ShowSevereError(state,
2417 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2418 : ": Heater Thermal Efficiency must be greater than zero");
2419 0 : ErrorsFound = true;
2420 : }
2421 :
2422 172 : if (!state.dataIPShortCut->cAlphaArgs(5).empty()) {
2423 0 : Tank.PLFCurve = Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(5));
2424 0 : if (Tank.PLFCurve == 0) {
2425 0 : ShowSevereError(state,
2426 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2427 0 : ": Part Load Factor curve not found = " + state.dataIPShortCut->cAlphaArgs(5));
2428 0 : ErrorsFound = true;
2429 : } else {
2430 : bool IsValid;
2431 0 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::ValidatePLFCurve(state, Tank.PLFCurve, IsValid);
2432 :
2433 0 : if (!IsValid) {
2434 0 : ShowSevereError(state,
2435 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2436 : ": Part Load Factor curve failed to evaluate to greater than zero for all numbers in the domain of 0 to 1");
2437 0 : ErrorsFound = true;
2438 : }
2439 :
2440 0 : ErrorsFound |= Curve::CheckCurveDims(state,
2441 : Tank.PLFCurve, // Curve index
2442 : {1}, // Valid dimensions
2443 : RoutineName, // Routine name
2444 0 : state.dataIPShortCut->cCurrentModuleObject, // Object Type
2445 : Tank.Name, // Object Name
2446 0 : state.dataIPShortCut->cAlphaFieldNames(5)); // Field Name
2447 : }
2448 : }
2449 :
2450 172 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(9);
2451 :
2452 : // Validate Off-Cycle Parasitic Fuel Type
2453 172 : Tank.OffCycParaFuelType =
2454 344 : static_cast<Fuel>(getEnumerationValue(FuelTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(6))));
2455 172 : switch (Tank.OffCycParaFuelType) {
2456 15 : case Fuel::Invalid:
2457 15 : if (state.dataIPShortCut->cAlphaArgs(6).empty()) { // If blank, default to Fuel Type for heater
2458 15 : Tank.OffCycParaFuelType = Tank.FuelType;
2459 : } else { // could have been an unsupported value
2460 0 : ShowSevereError(state,
2461 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2462 0 : ": Invalid Off-Cycle Parasitic Fuel Type entered=" + state.dataIPShortCut->cAlphaArgs(6));
2463 : // Set to Electric to avoid errors when setting up output variables
2464 0 : Tank.OffCycParaFuelType = Fuel::Electricity;
2465 0 : ErrorsFound = true;
2466 : }
2467 15 : break;
2468 157 : default:
2469 157 : break;
2470 : }
2471 :
2472 172 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(10);
2473 :
2474 172 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(11);
2475 :
2476 : // Validate On-Cycle Parasitic Fuel Type
2477 172 : Tank.OnCycParaFuelType =
2478 344 : static_cast<Fuel>(getEnumerationValue(FuelTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(7))));
2479 172 : switch (Tank.OnCycParaFuelType) {
2480 55 : case Fuel::Invalid:
2481 55 : if (state.dataIPShortCut->cAlphaArgs(7).empty()) { // If blank, default to Fuel Type for heater
2482 55 : Tank.OnCycParaFuelType = Tank.FuelType;
2483 : } else { // could have been an unsupported value
2484 0 : ShowSevereError(state,
2485 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2486 0 : ": Invalid On-Cycle Parasitic Fuel Type entered=" + state.dataIPShortCut->cAlphaArgs(7));
2487 : // Set to Electric to avoid errors when setting up output variables
2488 0 : Tank.OnCycParaFuelType = Fuel::Electricity;
2489 0 : ErrorsFound = true;
2490 : }
2491 55 : break;
2492 117 : default:
2493 117 : break;
2494 : }
2495 :
2496 172 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(12);
2497 :
2498 172 : Tank.AmbientTempIndicator = static_cast<WTTAmbientTemp>(
2499 344 : getEnumerationValue(TankAmbientTempNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(8))));
2500 172 : switch (Tank.AmbientTempIndicator) {
2501 96 : case WTTAmbientTemp::Schedule: {
2502 96 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(9));
2503 96 : if (Tank.AmbientTempSchedule == 0) {
2504 0 : ShowSevereError(state,
2505 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2506 0 : ": Ambient Temperature Schedule not found = " + state.dataIPShortCut->cAlphaArgs(9));
2507 0 : ErrorsFound = true;
2508 : }
2509 :
2510 96 : break;
2511 : }
2512 69 : case WTTAmbientTemp::TempZone: {
2513 69 : Tank.AmbientTempZone = UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(10), state.dataHeatBal->Zone);
2514 69 : if (Tank.AmbientTempZone == 0) {
2515 0 : ShowSevereError(state,
2516 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2517 0 : ": Ambient Temperature Zone not found = " + state.dataIPShortCut->cAlphaArgs(10));
2518 0 : ErrorsFound = true;
2519 : }
2520 :
2521 69 : break;
2522 : }
2523 7 : case WTTAmbientTemp::OutsideAir: {
2524 7 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
2525 7 : state.dataIPShortCut->cAlphaArgs(11),
2526 : ErrorsFound,
2527 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2528 7 : state.dataIPShortCut->cAlphaArgs(1),
2529 : DataLoopNode::NodeFluidType::Air,
2530 : DataLoopNode::ConnectionType::OutsideAirReference,
2531 : NodeInputManager::CompFluidStream::Primary,
2532 7 : DataLoopNode::ObjectIsNotParent);
2533 7 : if (!state.dataIPShortCut->cAlphaArgs(11).empty()) {
2534 7 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
2535 0 : ShowSevereError(state,
2536 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2537 : ": Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
2538 0 : ShowContinueError(state, "...Referenced Node Name=" + state.dataIPShortCut->cAlphaArgs(11));
2539 0 : ErrorsFound = true;
2540 : }
2541 : } else {
2542 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
2543 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
2544 0 : ErrorsFound = true;
2545 : }
2546 :
2547 7 : break;
2548 : }
2549 0 : default: {
2550 0 : ShowSevereError(state,
2551 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2552 0 : ": Invalid Ambient Temperature Indicator entered=" + state.dataIPShortCut->cAlphaArgs(8));
2553 0 : ShowContinueError(state, " Valid entries are SCHEDULE, ZONE, and OUTDOORS.");
2554 0 : ErrorsFound = true;
2555 0 : break;
2556 : }
2557 : }
2558 :
2559 172 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(13);
2560 172 : Tank.OffCycLossFracToZone = state.dataIPShortCut->rNumericArgs(14);
2561 :
2562 172 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(15);
2563 172 : Tank.OnCycLossFracToZone = state.dataIPShortCut->rNumericArgs(16);
2564 172 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, Tank.FluidIndex, RoutineName);
2565 172 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(17) * rho;
2566 :
2567 172 : if ((state.dataIPShortCut->cAlphaArgs(14).empty()) && (state.dataIPShortCut->cAlphaArgs(15).empty())) {
2568 61 : if (!state.dataIPShortCut->cAlphaArgs(12).empty()) {
2569 61 : Tank.FlowRateSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(12));
2570 61 : if (Tank.FlowRateSchedule == 0) {
2571 0 : ShowSevereError(state,
2572 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2573 0 : ": Flow Rate Schedule not found = " + state.dataIPShortCut->cAlphaArgs(12));
2574 0 : ErrorsFound = true;
2575 : }
2576 : }
2577 : }
2578 :
2579 172 : if (!state.dataIPShortCut->cAlphaArgs(13).empty()) {
2580 9 : Tank.UseInletTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(13));
2581 9 : if (Tank.UseInletTempSchedule == 0) {
2582 0 : ShowSevereError(state,
2583 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2584 0 : ": Cold Water Supply Temperature Schedule not found = " + state.dataIPShortCut->cAlphaArgs(13));
2585 0 : ErrorsFound = true;
2586 : }
2587 : }
2588 :
2589 172 : if (NumNums > 17) {
2590 127 : if ((state.dataIPShortCut->rNumericArgs(18) > 1) || (state.dataIPShortCut->rNumericArgs(18) < 0)) {
2591 0 : ShowSevereError(state,
2592 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2593 : ": Use Side Effectiveness is out of bounds (0 to 1)");
2594 0 : ErrorsFound = true;
2595 : }
2596 127 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(18);
2597 : } else {
2598 45 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
2599 : }
2600 :
2601 172 : if (NumNums > 18) {
2602 127 : if ((state.dataIPShortCut->rNumericArgs(19) > 1) || (state.dataIPShortCut->rNumericArgs(19) <= 0)) {
2603 0 : ShowSevereError(state,
2604 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2605 : ": Source Side Effectiveness is out of bounds (>0 to 1)");
2606 0 : ErrorsFound = true;
2607 : }
2608 127 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(19);
2609 : } else {
2610 45 : Tank.SourceEffectiveness = 1.0;
2611 : }
2612 :
2613 : // If no plant nodes are connected, simulate in stand-alone mode.
2614 466 : if (state.dataIPShortCut->cAlphaArgs(14).empty() && state.dataIPShortCut->cAlphaArgs(15).empty() &&
2615 280 : state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty()) {
2616 47 : Tank.StandAlone = true;
2617 : }
2618 :
2619 172 : if (!state.dataIPShortCut->lNumericFieldBlanks(20)) {
2620 111 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(20);
2621 111 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
2622 105 : Tank.UseDesignVolFlowRateWasAutoSized = true;
2623 : }
2624 : } else {
2625 61 : Tank.UseDesignVolFlowRate = 0.0;
2626 : }
2627 172 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2628 :
2629 172 : if (!state.dataIPShortCut->lNumericFieldBlanks(21)) {
2630 103 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(21);
2631 103 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
2632 94 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
2633 : }
2634 : } else {
2635 69 : Tank.SourceDesignVolFlowRate = 0.0;
2636 : }
2637 172 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2638 :
2639 172 : if (!state.dataIPShortCut->lNumericFieldBlanks(22)) {
2640 90 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(22);
2641 : } else {
2642 82 : Tank.SizingRecoveryTime = 1.5;
2643 : }
2644 :
2645 172 : if ((!state.dataIPShortCut->cAlphaArgs(14).empty()) || (!state.dataIPShortCut->cAlphaArgs(15).empty())) {
2646 111 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
2647 111 : state.dataIPShortCut->cAlphaArgs(14),
2648 : ErrorsFound,
2649 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2650 111 : state.dataIPShortCut->cAlphaArgs(1),
2651 : DataLoopNode::NodeFluidType::Water,
2652 : DataLoopNode::ConnectionType::Inlet,
2653 : NodeInputManager::CompFluidStream::Primary,
2654 111 : DataLoopNode::ObjectIsNotParent);
2655 111 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(14);
2656 111 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
2657 111 : state.dataIPShortCut->cAlphaArgs(15),
2658 : ErrorsFound,
2659 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2660 111 : state.dataIPShortCut->cAlphaArgs(1),
2661 : DataLoopNode::NodeFluidType::Water,
2662 : DataLoopNode::ConnectionType::Outlet,
2663 : NodeInputManager::CompFluidStream::Primary,
2664 111 : DataLoopNode::ObjectIsNotParent);
2665 111 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(15);
2666 :
2667 111 : if (state.dataIPShortCut->rNumericArgs(17) > 0) {
2668 0 : ShowWarningError(state,
2669 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2670 : ": Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used");
2671 : }
2672 :
2673 111 : if (Tank.FlowRateSchedule > 0) {
2674 0 : ShowWarningError(state,
2675 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2676 : ": Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used");
2677 : }
2678 :
2679 111 : if (Tank.UseInletTempSchedule > 0) {
2680 0 : ShowWarningError(state,
2681 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2682 : ": Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used");
2683 : }
2684 : }
2685 :
2686 172 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
2687 32 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
2688 32 : state.dataIPShortCut->cAlphaArgs(16),
2689 : ErrorsFound,
2690 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2691 32 : state.dataIPShortCut->cAlphaArgs(1),
2692 : DataLoopNode::NodeFluidType::Water,
2693 : DataLoopNode::ConnectionType::Inlet,
2694 : NodeInputManager::CompFluidStream::Secondary,
2695 32 : DataLoopNode::ObjectIsNotParent);
2696 32 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(16);
2697 32 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
2698 32 : state.dataIPShortCut->cAlphaArgs(17),
2699 : ErrorsFound,
2700 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2701 32 : state.dataIPShortCut->cAlphaArgs(1),
2702 : DataLoopNode::NodeFluidType::Water,
2703 : DataLoopNode::ConnectionType::Outlet,
2704 : NodeInputManager::CompFluidStream::Secondary,
2705 32 : DataLoopNode::ObjectIsNotParent);
2706 32 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(17);
2707 : }
2708 :
2709 172 : if (!state.dataIPShortCut->lAlphaFieldBlanks(18)) {
2710 1 : Tank.SourceSideControlMode = static_cast<SourceSideControl>(
2711 2 : getEnumerationValue(SourceSideControlNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(18))));
2712 1 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
2713 0 : ShowSevereError(state,
2714 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2715 0 : ": Invalid Control Mode entered=" + state.dataIPShortCut->cAlphaArgs(18));
2716 0 : ErrorsFound = true;
2717 : }
2718 : } else {
2719 171 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
2720 : }
2721 :
2722 172 : if (!state.dataIPShortCut->lAlphaFieldBlanks(19)) {
2723 0 : Tank.SourceSideAltSetpointSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(19));
2724 0 : if (Tank.SourceSideAltSetpointSchedNum == 0) {
2725 0 : ShowSevereError(state,
2726 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) + ": " +
2727 0 : state.dataIPShortCut->cAlphaFieldNames(19) + " not found = " + state.dataIPShortCut->cAlphaArgs(19));
2728 0 : ErrorsFound = true;
2729 : }
2730 : }
2731 172 : if (NumAlphas > 19) {
2732 1 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(20);
2733 : }
2734 :
2735 : } // WaterThermalTankNum
2736 :
2737 119 : return ErrorsFound;
2738 : }
2739 :
2740 10 : bool getWaterHeaterStratifiedInput(EnergyPlusData &state)
2741 : {
2742 10 : bool ErrorsFound = false;
2743 : static constexpr std::string_view RoutineName = "getWaterHeaterStratifiedInput";
2744 :
2745 10 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedWHModuleObj; //'WaterHeater:Stratified'
2746 :
2747 26 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + 1;
2748 26 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified;
2749 : ++WaterThermalTankNum) {
2750 : int NumAlphas;
2751 : int NumNums;
2752 : int IOStat;
2753 144 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2754 16 : state.dataIPShortCut->cCurrentModuleObject,
2755 16 : WaterThermalTankNum - state.dataWaterThermalTanks->numWaterHeaterMixed,
2756 16 : state.dataIPShortCut->cAlphaArgs,
2757 : NumAlphas,
2758 16 : state.dataIPShortCut->rNumericArgs,
2759 : NumNums,
2760 : IOStat,
2761 16 : state.dataIPShortCut->lNumericFieldBlanks,
2762 16 : state.dataIPShortCut->lAlphaFieldBlanks,
2763 16 : state.dataIPShortCut->cAlphaFieldNames,
2764 16 : state.dataIPShortCut->cNumericFieldNames);
2765 48 : GlobalNames::VerifyUniqueInterObjectName(state,
2766 16 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2767 16 : state.dataIPShortCut->cAlphaArgs(1),
2768 16 : state.dataIPShortCut->cCurrentModuleObject,
2769 16 : state.dataIPShortCut->cAlphaFieldNames(1),
2770 : ErrorsFound);
2771 :
2772 16 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2773 :
2774 16 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2775 16 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2776 16 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterStratified;
2777 16 : Tank.FluidIndex = Tank.waterIndex;
2778 :
2779 : // default to always on
2780 16 : Tank.SourceSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
2781 16 : Tank.UseSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
2782 :
2783 16 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(2);
2784 :
2785 16 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2786 16 : if (Tank.Volume == DataSizing::AutoSize) {
2787 0 : Tank.VolumeWasAutoSized = true;
2788 : }
2789 16 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, Tank.FluidIndex, RoutineName);
2790 16 : Tank.Mass = Tank.Volume * rho;
2791 16 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
2792 16 : if (Tank.Height == DataSizing::AutoSize) {
2793 0 : Tank.HeightWasAutoSized = true;
2794 : }
2795 :
2796 16 : Tank.Shape =
2797 32 : static_cast<TankShape>(getEnumerationValue(TankShapeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(3))));
2798 16 : switch (Tank.Shape) {
2799 16 : case TankShape::HorizCylinder:
2800 : case TankShape::VertCylinder: {
2801 16 : break;
2802 : }
2803 0 : case TankShape::Other: {
2804 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2805 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
2806 : } else {
2807 0 : ShowSevereError(state,
2808 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2809 : ": Tank Perimeter must be greater than zero for Tank Shape=OTHER");
2810 0 : ErrorsFound = true;
2811 : }
2812 :
2813 0 : break;
2814 : }
2815 0 : default: {
2816 0 : ShowSevereError(state,
2817 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2818 0 : ": Invalid Tank Shape entered=" + state.dataIPShortCut->cAlphaArgs(3));
2819 0 : Tank.Shape = TankShape::VertCylinder;
2820 0 : ErrorsFound = true;
2821 0 : break;
2822 : }
2823 : }
2824 :
2825 16 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
2826 16 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(4);
2827 : } else {
2828 : // Default to very large number
2829 0 : Tank.TankTempLimit = 1.0e9;
2830 : }
2831 :
2832 : // Validate Heater Priority Control
2833 16 : Tank.StratifiedControlMode = static_cast<PriorityControlMode>(
2834 32 : getEnumerationValue(PriorityControlModeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(4))));
2835 16 : if (Tank.StratifiedControlMode == PriorityControlMode::Invalid) {
2836 0 : ShowSevereError(state,
2837 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2838 0 : ": Invalid Heater Priority Control entered=" + state.dataIPShortCut->cAlphaArgs(4));
2839 0 : ErrorsFound = true;
2840 : }
2841 :
2842 16 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(5));
2843 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(5)) {
2844 0 : ShowSevereError(state,
2845 0 : std::string{RoutineName} + state.dataIPShortCut->cCurrentModuleObject + "=\"" + state.dataIPShortCut->cAlphaArgs(1) +
2846 : "\", missing data.");
2847 0 : ShowContinueError(state, "blank field, missing " + state.dataIPShortCut->cAlphaFieldNames(5) + " is required");
2848 0 : ErrorsFound = true;
2849 16 : } else if (Tank.SetPointTempSchedule == 0) {
2850 0 : ShowSevereError(state,
2851 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) + ": " +
2852 0 : state.dataIPShortCut->cAlphaFieldNames(5) + " not found = " + state.dataIPShortCut->cAlphaArgs(5));
2853 0 : ErrorsFound = true;
2854 : }
2855 :
2856 16 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2857 16 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(5);
2858 : } else {
2859 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2860 0 : Tank.DeadBandDeltaTemp = 0.0001;
2861 : }
2862 :
2863 16 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(6);
2864 16 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2865 2 : Tank.MaxCapacityWasAutoSized = true;
2866 : }
2867 :
2868 16 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(7);
2869 :
2870 : // adjust tank height used in these calculations for testing heater height
2871 : Real64 tankHeightForTesting;
2872 16 : if (Tank.Shape == TankShape::HorizCylinder) {
2873 0 : tankHeightForTesting = 2.0 * sqrt((Tank.Volume / Tank.Height) / DataGlobalConstants::Pi);
2874 : } else {
2875 16 : tankHeightForTesting = Tank.Height;
2876 : }
2877 :
2878 : // Test if Heater height is within range
2879 16 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight1 > tankHeightForTesting)) {
2880 0 : ShowSevereError(state,
2881 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2882 : ": Heater 1 is located higher than overall tank height.");
2883 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2884 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(7), state.dataIPShortCut->rNumericArgs(7)));
2885 0 : ErrorsFound = true;
2886 : }
2887 :
2888 16 : Tank.SetPointTempSchedule2 = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(6));
2889 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(6)) {
2890 0 : ShowSevereError(state,
2891 0 : std::string{RoutineName} + state.dataIPShortCut->cCurrentModuleObject + "=\"" + state.dataIPShortCut->cAlphaArgs(1) +
2892 : "\", missing data.");
2893 0 : ShowContinueError(state, "blank field, missing " + state.dataIPShortCut->cAlphaFieldNames(6) + " is required");
2894 0 : ErrorsFound = true;
2895 16 : } else if (Tank.SetPointTempSchedule2 == 0) {
2896 0 : ShowSevereError(state,
2897 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) + ": " +
2898 0 : state.dataIPShortCut->cAlphaFieldNames(6) + " not found = " + state.dataIPShortCut->cAlphaArgs(6));
2899 0 : ErrorsFound = true;
2900 : }
2901 :
2902 16 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2903 16 : Tank.DeadBandDeltaTemp2 = state.dataIPShortCut->rNumericArgs(8);
2904 : } else {
2905 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2906 0 : Tank.DeadBandDeltaTemp2 = 0.0001;
2907 : }
2908 :
2909 16 : Tank.MaxCapacity2 = state.dataIPShortCut->rNumericArgs(9);
2910 16 : Tank.HeaterHeight2 = state.dataIPShortCut->rNumericArgs(10);
2911 :
2912 : // Test if Heater height is within range
2913 16 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight2 > tankHeightForTesting)) {
2914 0 : ShowSevereError(state,
2915 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2916 : ": Heater 2 is located higher than overall tank height.");
2917 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2918 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
2919 0 : ErrorsFound = true;
2920 : }
2921 :
2922 : // Validate Heater Fuel Type
2923 16 : Tank.FuelType = static_cast<Fuel>(
2924 48 : getEnumerationValue(FuelTypeNamesUC,
2925 48 : UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(
2926 32 : 7)))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
2927 16 : if (Tank.FuelType == Fuel::Invalid) {
2928 0 : ShowSevereError(state,
2929 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2930 0 : ": Invalid Heater Fuel Type entered=" + state.dataIPShortCut->cAlphaArgs(7));
2931 : // Set to Electric to avoid errors when setting up output variables
2932 0 : Tank.FuelType = Fuel::Electricity;
2933 0 : ErrorsFound = true;
2934 : }
2935 :
2936 16 : if (state.dataIPShortCut->rNumericArgs(11) > 0.0) {
2937 16 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(11);
2938 : } else {
2939 0 : ShowSevereError(state,
2940 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2941 : ": Heater Thermal Efficiency must be greater than zero");
2942 0 : ErrorsFound = true;
2943 : }
2944 :
2945 16 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(12);
2946 :
2947 : // Validate Off-Cycle Parasitic Fuel Type
2948 16 : Tank.OffCycParaFuelType = static_cast<Fuel>(
2949 48 : getEnumerationValue(FuelTypeNamesUC,
2950 48 : UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(
2951 32 : 8)))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
2952 16 : if (Tank.OffCycParaFuelType == Fuel::Invalid) {
2953 0 : if (state.dataIPShortCut->cAlphaArgs(8).empty()) {
2954 0 : Tank.OffCycParaFuelType = Tank.FuelType;
2955 : } else {
2956 0 : ShowSevereError(state,
2957 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2958 0 : ": Invalid Off-Cycle Parasitic Fuel Type entered=" + state.dataIPShortCut->cAlphaArgs(8));
2959 : // Set to Electric to avoid errors when setting up output variables
2960 0 : Tank.OffCycParaFuelType = Fuel::Electricity;
2961 0 : ErrorsFound = true;
2962 : }
2963 : }
2964 :
2965 16 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(13);
2966 16 : Tank.OffCycParaHeight = state.dataIPShortCut->rNumericArgs(14);
2967 :
2968 16 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(15);
2969 :
2970 : // Validate On-Cycle Parasitic Fuel Type
2971 32 : Tank.OnCycParaFuelType = static_cast<Fuel>(getEnumerationValue(
2972 : FuelTypeNamesUC,
2973 48 : UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(
2974 32 : 9)))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported/empty
2975 16 : if (Tank.OnCycParaFuelType == Fuel::Invalid) {
2976 0 : if (state.dataIPShortCut->cAlphaArgs(9).empty()) {
2977 0 : Tank.OnCycParaFuelType = Tank.FuelType;
2978 : } else {
2979 0 : ShowSevereError(state,
2980 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
2981 0 : ": Invalid On-Cycle Parasitic Fuel Type entered=" + state.dataIPShortCut->cAlphaArgs(9));
2982 : // Set to Electric to avoid errors when setting up output variables
2983 0 : Tank.OnCycParaFuelType = Fuel::Electricity;
2984 0 : ErrorsFound = true;
2985 : }
2986 : }
2987 :
2988 16 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(16);
2989 16 : Tank.OnCycParaHeight = state.dataIPShortCut->rNumericArgs(17);
2990 :
2991 16 : Tank.AmbientTempIndicator = static_cast<WTTAmbientTemp>(
2992 32 : getEnumerationValue(TankAmbientTempNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(10))));
2993 16 : switch (Tank.AmbientTempIndicator) {
2994 :
2995 8 : case WTTAmbientTemp::Schedule: {
2996 8 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(11));
2997 8 : if (Tank.AmbientTempSchedule == 0) {
2998 0 : ShowSevereError(state,
2999 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3000 0 : ": Ambient Temperature Schedule not found = " + state.dataIPShortCut->cAlphaArgs(11));
3001 0 : ErrorsFound = true;
3002 : }
3003 :
3004 8 : break;
3005 : }
3006 6 : case WTTAmbientTemp::TempZone: {
3007 6 : Tank.AmbientTempZone = UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(12), state.dataHeatBal->Zone);
3008 6 : if (Tank.AmbientTempZone == 0) {
3009 0 : ShowSevereError(state,
3010 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3011 0 : ": Ambient Temperature Zone not found = " + state.dataIPShortCut->cAlphaArgs(12));
3012 0 : ErrorsFound = true;
3013 : }
3014 :
3015 6 : break;
3016 : }
3017 2 : case WTTAmbientTemp::OutsideAir: {
3018 2 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3019 2 : state.dataIPShortCut->cAlphaArgs(13),
3020 : ErrorsFound,
3021 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3022 2 : state.dataIPShortCut->cAlphaArgs(1),
3023 : DataLoopNode::NodeFluidType::Air,
3024 : DataLoopNode::ConnectionType::Inlet,
3025 : NodeInputManager::CompFluidStream::Primary,
3026 2 : DataLoopNode::ObjectIsNotParent);
3027 2 : if (!state.dataIPShortCut->cAlphaArgs(13).empty()) {
3028 2 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3029 0 : ShowSevereError(state,
3030 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3031 : ": Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3032 0 : ShowContinueError(state, "...Referenced Node Name=" + state.dataIPShortCut->cAlphaArgs(13));
3033 0 : ErrorsFound = true;
3034 : }
3035 : } else {
3036 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3037 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3038 0 : ErrorsFound = true;
3039 : }
3040 :
3041 2 : break;
3042 : }
3043 0 : default: {
3044 0 : ShowSevereError(state,
3045 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3046 0 : ": Invalid Ambient Temperature Indicator entered=" + state.dataIPShortCut->cAlphaArgs(10));
3047 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3048 0 : ErrorsFound = true;
3049 0 : break;
3050 : }
3051 : }
3052 :
3053 16 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(18);
3054 16 : Tank.SkinLossFracToZone = state.dataIPShortCut->rNumericArgs(19);
3055 16 : Tank.OffCycFlueLossCoeff = state.dataIPShortCut->rNumericArgs(20);
3056 16 : Tank.OffCycFlueLossFracToZone = state.dataIPShortCut->rNumericArgs(21);
3057 :
3058 : // this is temporary until we know fluid type
3059 16 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, Tank.FluidIndex, RoutineName);
3060 16 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(22) * rho;
3061 :
3062 16 : if ((state.dataIPShortCut->cAlphaArgs(16).empty()) && (state.dataIPShortCut->cAlphaArgs(17).empty())) {
3063 7 : if (!state.dataIPShortCut->cAlphaArgs(14).empty()) {
3064 7 : Tank.FlowRateSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(14));
3065 7 : if (Tank.FlowRateSchedule == 0) {
3066 0 : ShowSevereError(state,
3067 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3068 0 : ": Flow Rate Schedule not found = " + state.dataIPShortCut->cAlphaArgs(14));
3069 0 : ErrorsFound = true;
3070 : }
3071 : }
3072 : }
3073 :
3074 16 : if (!state.dataIPShortCut->cAlphaArgs(15).empty()) {
3075 0 : Tank.UseInletTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(15));
3076 0 : if (Tank.UseInletTempSchedule == 0) {
3077 0 : ShowSevereError(state,
3078 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3079 0 : ": Cold Water Supply Temperature Schedule not found = " + state.dataIPShortCut->cAlphaArgs(15));
3080 0 : ErrorsFound = true;
3081 : }
3082 : }
3083 :
3084 16 : if (NumNums > 22) {
3085 16 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(23);
3086 : } else {
3087 0 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
3088 : }
3089 :
3090 16 : if (NumNums > 23) {
3091 16 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(24);
3092 : } else {
3093 : // Defaults to bottom of tank
3094 0 : Tank.UseInletHeight = 0.0;
3095 : }
3096 16 : if ((!Tank.HeightWasAutoSized) && (Tank.UseInletHeight > Tank.Height)) {
3097 0 : ShowSevereError(state,
3098 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3099 : ": Use inlet is located higher than overall tank height.");
3100 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3101 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(24), state.dataIPShortCut->rNumericArgs(24)));
3102 0 : ErrorsFound = true;
3103 : }
3104 :
3105 16 : if ((NumNums > 24) && (state.dataIPShortCut->rNumericArgs(25) != DataGlobalConstants::AutoCalculate)) {
3106 1 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(25);
3107 : } else {
3108 : // Defaults to top of tank
3109 15 : Tank.UseOutletHeight = Tank.Height;
3110 : }
3111 16 : if (Tank.UseOutletHeight == DataSizing::AutoSize) {
3112 0 : Tank.UseOutletHeightWasAutoSized = true;
3113 : }
3114 16 : if ((!Tank.HeightWasAutoSized) && (Tank.UseOutletHeight > Tank.Height)) {
3115 0 : ShowSevereError(state,
3116 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3117 : ": Use outlet is located higher than overall tank height.");
3118 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3119 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(25), state.dataIPShortCut->rNumericArgs(25)));
3120 0 : ErrorsFound = true;
3121 : }
3122 :
3123 16 : if (NumNums > 25) {
3124 16 : if ((state.dataIPShortCut->rNumericArgs(26) > 1) || (state.dataIPShortCut->rNumericArgs(26) <= 0)) {
3125 0 : ShowSevereError(state,
3126 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3127 : ": Source Side Effectiveness is out of bounds (>0 to 1)");
3128 0 : ErrorsFound = true;
3129 : }
3130 16 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(26);
3131 : } else {
3132 0 : Tank.SourceEffectiveness = 1.0;
3133 : }
3134 :
3135 16 : if ((NumNums > 26) && (state.dataIPShortCut->rNumericArgs(27) != DataGlobalConstants::AutoCalculate)) {
3136 11 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(27);
3137 : } else {
3138 : // Defaults to top of tank
3139 5 : Tank.SourceInletHeight = Tank.Height;
3140 : }
3141 16 : if (Tank.SourceInletHeight == DataSizing::AutoSize) {
3142 0 : Tank.SourceInletHeightWasAutoSized = true;
3143 : }
3144 16 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceInletHeight > Tank.Height)) {
3145 0 : ShowSevereError(state,
3146 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3147 : ": Source inlet is located higher than overall tank height.");
3148 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3149 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(27), state.dataIPShortCut->rNumericArgs(27)));
3150 0 : ErrorsFound = true;
3151 : }
3152 :
3153 16 : if ((NumNums > 27) && (state.dataIPShortCut->rNumericArgs(28) != DataGlobalConstants::AutoCalculate)) {
3154 16 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(28);
3155 : } else {
3156 : // Defaults to bottom of tank
3157 0 : Tank.SourceOutletHeight = 0.0;
3158 : }
3159 16 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceOutletHeight > Tank.Height)) {
3160 0 : ShowSevereError(state,
3161 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3162 : ": Source outlet is located higher than overall tank height.");
3163 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3164 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(28), state.dataIPShortCut->rNumericArgs(28)));
3165 0 : ErrorsFound = true;
3166 : }
3167 :
3168 : // If no plant nodes are connected, simulate in stand-alone mode.
3169 46 : if (state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty() &&
3170 25 : state.dataIPShortCut->cAlphaArgs(18).empty() && state.dataIPShortCut->cAlphaArgs(19).empty())
3171 2 : Tank.StandAlone = true;
3172 :
3173 16 : if (!state.dataIPShortCut->lNumericFieldBlanks(29)) {
3174 9 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(29);
3175 9 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3176 9 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3177 : }
3178 : } else {
3179 7 : Tank.UseDesignVolFlowRate = 0.0;
3180 : }
3181 :
3182 16 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3183 :
3184 16 : if (!state.dataIPShortCut->lNumericFieldBlanks(30)) {
3185 6 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(30);
3186 6 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3187 4 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3188 : }
3189 : } else {
3190 10 : Tank.SourceDesignVolFlowRate = 0.0;
3191 : }
3192 :
3193 16 : if (NumNums > 30) {
3194 16 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(31);
3195 : } else {
3196 0 : Tank.SizingRecoveryTime = 1.5;
3197 : }
3198 :
3199 16 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3200 :
3201 16 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
3202 9 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3203 9 : state.dataIPShortCut->cAlphaArgs(16),
3204 : ErrorsFound,
3205 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3206 9 : state.dataIPShortCut->cAlphaArgs(1),
3207 : DataLoopNode::NodeFluidType::Water,
3208 : DataLoopNode::ConnectionType::Inlet,
3209 : NodeInputManager::CompFluidStream::Primary,
3210 9 : DataLoopNode::ObjectIsNotParent);
3211 9 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(16);
3212 9 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3213 9 : state.dataIPShortCut->cAlphaArgs(17),
3214 : ErrorsFound,
3215 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3216 9 : state.dataIPShortCut->cAlphaArgs(1),
3217 : DataLoopNode::NodeFluidType::Water,
3218 : DataLoopNode::ConnectionType::Outlet,
3219 : NodeInputManager::CompFluidStream::Primary,
3220 9 : DataLoopNode::ObjectIsNotParent);
3221 9 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(17);
3222 :
3223 9 : if (state.dataIPShortCut->rNumericArgs(22) > 0) {
3224 0 : ShowWarningError(state,
3225 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3226 : ": Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used");
3227 : }
3228 :
3229 9 : if (Tank.FlowRateSchedule > 0) {
3230 0 : ShowWarningError(state,
3231 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3232 : ": Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used");
3233 : }
3234 :
3235 9 : if (Tank.UseInletTempSchedule > 0) {
3236 0 : ShowWarningError(state,
3237 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3238 : ": Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used");
3239 : }
3240 : }
3241 :
3242 16 : if ((!state.dataIPShortCut->cAlphaArgs(18).empty()) || (!state.dataIPShortCut->cAlphaArgs(19).empty())) {
3243 10 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3244 10 : state.dataIPShortCut->cAlphaArgs(18),
3245 : ErrorsFound,
3246 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3247 10 : state.dataIPShortCut->cAlphaArgs(1),
3248 : DataLoopNode::NodeFluidType::Water,
3249 : DataLoopNode::ConnectionType::Inlet,
3250 : NodeInputManager::CompFluidStream::Secondary,
3251 10 : DataLoopNode::ObjectIsNotParent);
3252 10 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(18);
3253 10 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3254 10 : state.dataIPShortCut->cAlphaArgs(19),
3255 : ErrorsFound,
3256 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3257 10 : state.dataIPShortCut->cAlphaArgs(1),
3258 : DataLoopNode::NodeFluidType::Water,
3259 : DataLoopNode::ConnectionType::Outlet,
3260 : NodeInputManager::CompFluidStream::Secondary,
3261 10 : DataLoopNode::ObjectIsNotParent);
3262 10 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(19);
3263 : }
3264 :
3265 : // Validate inlet mode
3266 16 : Tank.InletMode = static_cast<InletPositionMode>(
3267 32 : getEnumerationValue(InletPositionModeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(20))));
3268 :
3269 16 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(32);
3270 16 : int specifiedNodes = 0;
3271 16 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(33);
3272 :
3273 16 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
3274 16 : Tank.AdditionalLossCoeff = 0.0;
3275 44 : for (int NodeNum = 1; NodeNum <= 12; ++NodeNum) {
3276 44 : int index = 33 + NodeNum;
3277 44 : if (NumNums >= index) {
3278 28 : if (NodeNum <= Tank.Nodes) {
3279 28 : ++specifiedNodes;
3280 28 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(index);
3281 0 : } else if (!state.dataIPShortCut->lNumericFieldBlanks(index) && (state.dataIPShortCut->rNumericArgs(index) != 0)) {
3282 : // If either blank, or zero (default), then do not warn
3283 0 : ++specifiedNodes;
3284 : }
3285 : } else {
3286 16 : break;
3287 : }
3288 : }
3289 :
3290 16 : if (specifiedNodes > Tank.Nodes) {
3291 0 : ShowWarningError(state,
3292 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3293 : ": More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used");
3294 : }
3295 :
3296 16 : Tank.SetupStratifiedNodes(state);
3297 :
3298 16 : if (!state.dataIPShortCut->lAlphaFieldBlanks(21)) {
3299 0 : Tank.SourceSideControlMode = static_cast<SourceSideControl>(
3300 0 : getEnumerationValue(SourceSideControlNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(21))));
3301 0 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
3302 0 : ShowSevereError(state,
3303 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3304 0 : ": Invalid Control Mode entered=" + state.dataIPShortCut->cAlphaArgs(21));
3305 0 : ErrorsFound = true;
3306 : }
3307 : } else {
3308 16 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
3309 : }
3310 :
3311 16 : if (!state.dataIPShortCut->lAlphaFieldBlanks(22)) {
3312 0 : Tank.SourceSideAltSetpointSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(22));
3313 0 : if (Tank.SourceSideAltSetpointSchedNum == 0) {
3314 0 : ShowSevereError(state,
3315 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) + ": " +
3316 0 : state.dataIPShortCut->cAlphaFieldNames(22) + " not found = " + state.dataIPShortCut->cAlphaArgs(22));
3317 0 : ErrorsFound = true;
3318 : }
3319 : }
3320 : }
3321 :
3322 10 : return ErrorsFound;
3323 : }
3324 :
3325 3 : bool getWaterTankMixedInput(EnergyPlusData &state)
3326 : {
3327 3 : bool ErrorsFound = false;
3328 :
3329 3 : state.dataIPShortCut->cCurrentModuleObject = cMixedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Mixed'
3330 6 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified + 1;
3331 12 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3332 6 : state.dataWaterThermalTanks->numChilledWaterMixed;
3333 : ++WaterThermalTankNum) {
3334 : int NumAlphas;
3335 : int NumNums;
3336 : int IOStat;
3337 27 : state.dataInputProcessing->inputProcessor->getObjectItem(
3338 : state,
3339 3 : state.dataIPShortCut->cCurrentModuleObject,
3340 3 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified),
3341 3 : state.dataIPShortCut->cAlphaArgs,
3342 : NumAlphas,
3343 3 : state.dataIPShortCut->rNumericArgs,
3344 : NumNums,
3345 : IOStat,
3346 3 : state.dataIPShortCut->lNumericFieldBlanks,
3347 3 : state.dataIPShortCut->lAlphaFieldBlanks,
3348 3 : state.dataIPShortCut->cAlphaFieldNames,
3349 3 : state.dataIPShortCut->cNumericFieldNames);
3350 9 : GlobalNames::VerifyUniqueInterObjectName(state,
3351 3 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3352 3 : state.dataIPShortCut->cAlphaArgs(1),
3353 3 : state.dataIPShortCut->cCurrentModuleObject,
3354 3 : state.dataIPShortCut->cAlphaFieldNames(1),
3355 : ErrorsFound);
3356 :
3357 3 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3358 :
3359 3 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3360 3 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3361 3 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankMixed;
3362 3 : Tank.FluidIndex = Tank.waterIndex;
3363 3 : Tank.IsChilledWaterTank = true;
3364 3 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3365 :
3366 3 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3367 3 : if (Tank.Volume == DataSizing::AutoSize) {
3368 0 : Tank.VolumeWasAutoSized = true;
3369 : }
3370 :
3371 3 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
3372 : // Set volume to a really small number to continue simulation
3373 0 : Tank.Volume = 0.000001; // = 1 cm3
3374 : }
3375 :
3376 3 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(2));
3377 3 : if (Tank.SetPointTempSchedule == 0) {
3378 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(2) + " = " + state.dataIPShortCut->cAlphaArgs(2));
3379 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
3380 :
3381 0 : ErrorsFound = true;
3382 : }
3383 :
3384 3 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
3385 3 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
3386 : } else {
3387 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3388 0 : Tank.DeadBandDeltaTemp = 0.5;
3389 : }
3390 :
3391 3 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3392 3 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
3393 : } else {
3394 : // default to just above freezing
3395 0 : Tank.TankTempLimit = 1.0;
3396 : }
3397 :
3398 3 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
3399 3 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3400 0 : Tank.MaxCapacityWasAutoSized = true;
3401 : }
3402 :
3403 3 : Tank.MinCapacity = 0.0;
3404 3 : Tank.ControlType = HeaterControlMode::Cycle;
3405 :
3406 3 : Tank.MassFlowRateMin = 0.0;
3407 3 : Tank.IgnitionDelay = 0.0;
3408 3 : Tank.FuelType = Fuel::Electricity;
3409 3 : Tank.Efficiency = 1.0;
3410 3 : Tank.PLFCurve = 0;
3411 3 : Tank.OffCycParaLoad = 0.0;
3412 3 : Tank.OffCycParaFuelType = Fuel::Electricity;
3413 3 : Tank.OffCycParaFracToTank = 0.0;
3414 3 : Tank.OnCycParaLoad = 0.0;
3415 3 : Tank.OnCycParaFuelType = Fuel::Electricity;
3416 3 : Tank.OnCycParaFracToTank = 0.0;
3417 :
3418 3 : Tank.AmbientTempIndicator = static_cast<WTTAmbientTemp>(
3419 6 : getEnumerationValue(TankAmbientTempNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(3))));
3420 3 : switch (Tank.AmbientTempIndicator) {
3421 :
3422 0 : case WTTAmbientTemp::Schedule: {
3423 0 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(4));
3424 0 : if (Tank.AmbientTempSchedule == 0) {
3425 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(4) + " = " + state.dataIPShortCut->cAlphaArgs(4));
3426 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3427 0 : ShowContinueError(state, "Schedule was not found.");
3428 0 : ErrorsFound = true;
3429 : }
3430 :
3431 0 : break;
3432 : }
3433 3 : case WTTAmbientTemp::TempZone: {
3434 3 : Tank.AmbientTempZone = UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(5), state.dataHeatBal->Zone);
3435 3 : if (Tank.AmbientTempZone == 0) {
3436 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(5) + " = " + state.dataIPShortCut->cAlphaArgs(5));
3437 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3438 0 : ShowContinueError(state, "Zone was not found.");
3439 0 : ErrorsFound = true;
3440 : }
3441 :
3442 3 : break;
3443 : }
3444 0 : case WTTAmbientTemp::OutsideAir: {
3445 0 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3446 0 : state.dataIPShortCut->cAlphaArgs(6),
3447 : ErrorsFound,
3448 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3449 0 : state.dataIPShortCut->cAlphaArgs(1),
3450 : DataLoopNode::NodeFluidType::Air,
3451 : DataLoopNode::ConnectionType::OutsideAirReference,
3452 : NodeInputManager::CompFluidStream::Primary,
3453 0 : DataLoopNode::ObjectIsNotParent);
3454 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(6)) {
3455 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3456 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(6) + " = " + state.dataIPShortCut->cAlphaArgs(6));
3457 0 : ShowContinueError(state,
3458 0 : "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3459 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3460 0 : ErrorsFound = true;
3461 : }
3462 : } else {
3463 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3464 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3465 0 : ErrorsFound = true;
3466 : }
3467 :
3468 0 : break;
3469 : }
3470 0 : default: {
3471 0 : ShowSevereError(state,
3472 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3473 0 : ": Invalid Ambient Temperature Indicator entered=" + state.dataIPShortCut->cAlphaArgs(3));
3474 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3475 0 : ErrorsFound = true;
3476 0 : break;
3477 : }
3478 : }
3479 :
3480 3 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3481 3 : Tank.OffCycLossFracToZone = 1.0;
3482 :
3483 3 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3484 3 : Tank.OnCycLossFracToZone = 1.0;
3485 :
3486 3 : Tank.MassFlowRateMax = 0.0;
3487 3 : Tank.FlowRateSchedule = 0;
3488 3 : Tank.UseInletTempSchedule = 0;
3489 :
3490 : // default to always on
3491 3 : Tank.SourceSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3492 3 : Tank.UseSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3493 :
3494 3 : if ((state.dataIPShortCut->rNumericArgs(6) > 1) || (state.dataIPShortCut->rNumericArgs(6) < 0)) {
3495 0 : ShowSevereError(state,
3496 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3497 : ": Use Side Effectiveness is out of bounds (0 to 1)");
3498 0 : ErrorsFound = true;
3499 : }
3500 3 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(6);
3501 :
3502 3 : if ((state.dataIPShortCut->rNumericArgs(8) > 1) || (state.dataIPShortCut->rNumericArgs(8) <= 0)) {
3503 0 : ShowSevereError(state,
3504 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3505 : ": Source Side Effectiveness is out of bounds (>0 to 1)");
3506 0 : ErrorsFound = true;
3507 : }
3508 3 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(8);
3509 :
3510 3 : if (state.dataIPShortCut->lNumericFieldBlanks(7)) {
3511 0 : Tank.UseDesignVolFlowRate = 0.0;
3512 : } else {
3513 3 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(7);
3514 3 : if (Tank.UseDesignVolFlowRate) {
3515 3 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3516 : }
3517 : }
3518 :
3519 3 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3520 :
3521 3 : if (state.dataIPShortCut->lAlphaFieldBlanks(9)) {
3522 0 : Tank.UseSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3523 : } else {
3524 3 : Tank.UseSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(9));
3525 3 : if (Tank.UseSideAvailSchedNum == 0) {
3526 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(9) + " = " + state.dataIPShortCut->cAlphaArgs(9));
3527 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3528 0 : ShowContinueError(state, "Schedule was not found.");
3529 0 : ErrorsFound = true;
3530 : }
3531 : }
3532 :
3533 3 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3534 :
3535 3 : if (state.dataIPShortCut->lNumericFieldBlanks(9)) {
3536 0 : Tank.SourceDesignVolFlowRate = 0.0;
3537 : } else {
3538 3 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(9);
3539 3 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3540 3 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3541 : }
3542 : }
3543 :
3544 3 : if (state.dataIPShortCut->lAlphaFieldBlanks(12)) {
3545 0 : Tank.SourceSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3546 : } else {
3547 3 : Tank.SourceSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(12));
3548 3 : if (Tank.SourceSideAvailSchedNum == 0) {
3549 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(12) + " = " + state.dataIPShortCut->cAlphaArgs(12));
3550 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3551 0 : ShowContinueError(state, "Schedule was not found.");
3552 0 : ErrorsFound = true;
3553 : }
3554 : }
3555 3 : if (state.dataIPShortCut->lNumericFieldBlanks(10)) {
3556 0 : Tank.SizingRecoveryTime = 4.0;
3557 : } else {
3558 3 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(10);
3559 : }
3560 :
3561 3 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(7)) || (!state.dataIPShortCut->lAlphaFieldBlanks(8))) {
3562 3 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3563 3 : state.dataIPShortCut->cAlphaArgs(7),
3564 : ErrorsFound,
3565 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3566 3 : state.dataIPShortCut->cAlphaArgs(1),
3567 : DataLoopNode::NodeFluidType::Water,
3568 : DataLoopNode::ConnectionType::Inlet,
3569 : NodeInputManager::CompFluidStream::Primary,
3570 3 : DataLoopNode::ObjectIsNotParent);
3571 3 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(7);
3572 3 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3573 3 : state.dataIPShortCut->cAlphaArgs(8),
3574 : ErrorsFound,
3575 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3576 3 : state.dataIPShortCut->cAlphaArgs(1),
3577 : DataLoopNode::NodeFluidType::Water,
3578 : DataLoopNode::ConnectionType::Outlet,
3579 : NodeInputManager::CompFluidStream::Primary,
3580 3 : DataLoopNode::ObjectIsNotParent);
3581 3 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
3582 : }
3583 :
3584 3 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(10)) || (!state.dataIPShortCut->lAlphaFieldBlanks(11))) {
3585 3 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3586 3 : state.dataIPShortCut->cAlphaArgs(10),
3587 : ErrorsFound,
3588 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3589 3 : state.dataIPShortCut->cAlphaArgs(1),
3590 : DataLoopNode::NodeFluidType::Water,
3591 : DataLoopNode::ConnectionType::Inlet,
3592 : NodeInputManager::CompFluidStream::Secondary,
3593 3 : DataLoopNode::ObjectIsNotParent);
3594 3 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(10);
3595 3 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3596 3 : state.dataIPShortCut->cAlphaArgs(11),
3597 : ErrorsFound,
3598 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3599 3 : state.dataIPShortCut->cAlphaArgs(1),
3600 : DataLoopNode::NodeFluidType::Water,
3601 : DataLoopNode::ConnectionType::Outlet,
3602 : NodeInputManager::CompFluidStream::Secondary,
3603 3 : DataLoopNode::ObjectIsNotParent);
3604 3 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
3605 : }
3606 :
3607 3 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
3608 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
3609 : }
3610 :
3611 : } // WaterThermalTankNum
3612 :
3613 3 : return ErrorsFound;
3614 : }
3615 :
3616 2 : bool getWaterTankStratifiedInput(EnergyPlusData &state)
3617 : {
3618 2 : bool ErrorsFound = false;
3619 : static constexpr std::string_view RoutineName = "getWaterTankStratifiedInput";
3620 :
3621 2 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Stratified'
3622 :
3623 6 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3624 4 : state.dataWaterThermalTanks->numChilledWaterMixed + 1;
3625 12 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3626 8 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
3627 : ++WaterThermalTankNum) {
3628 : int NumNums;
3629 : int NumAlphas;
3630 : int IOStat;
3631 18 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3632 2 : state.dataIPShortCut->cCurrentModuleObject,
3633 4 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed +
3634 4 : state.dataWaterThermalTanks->numWaterHeaterStratified +
3635 2 : state.dataWaterThermalTanks->numChilledWaterMixed),
3636 2 : state.dataIPShortCut->cAlphaArgs,
3637 : NumAlphas,
3638 2 : state.dataIPShortCut->rNumericArgs,
3639 : NumNums,
3640 : IOStat,
3641 2 : state.dataIPShortCut->lNumericFieldBlanks,
3642 2 : state.dataIPShortCut->lAlphaFieldBlanks,
3643 2 : state.dataIPShortCut->cAlphaFieldNames,
3644 2 : state.dataIPShortCut->cNumericFieldNames);
3645 6 : GlobalNames::VerifyUniqueInterObjectName(state,
3646 2 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3647 2 : state.dataIPShortCut->cAlphaArgs(1),
3648 2 : state.dataIPShortCut->cCurrentModuleObject,
3649 2 : state.dataIPShortCut->cAlphaFieldNames(1),
3650 : ErrorsFound);
3651 :
3652 2 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3653 :
3654 2 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3655 2 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3656 2 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankStratified;
3657 2 : Tank.FluidIndex = Tank.waterIndex;
3658 2 : Tank.IsChilledWaterTank = true;
3659 2 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3660 :
3661 2 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3662 2 : if (Tank.Volume == DataSizing::AutoSize) {
3663 0 : Tank.VolumeWasAutoSized = true;
3664 : }
3665 2 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, Tank.FluidIndex, RoutineName);
3666 2 : Tank.Mass = Tank.Volume * rho;
3667 2 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
3668 2 : if (Tank.Height == DataSizing::AutoSize) {
3669 0 : Tank.HeightWasAutoSized = true;
3670 : }
3671 :
3672 2 : Tank.Shape =
3673 4 : static_cast<TankShape>(getEnumerationValue(TankShapeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(2))));
3674 2 : switch (Tank.Shape) {
3675 2 : case TankShape::HorizCylinder:
3676 : case TankShape::VertCylinder: {
3677 2 : break;
3678 : }
3679 0 : case TankShape::Other: {
3680 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3681 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
3682 : } else {
3683 0 : ShowSevereError(state,
3684 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3685 : ": Tank Perimeter must be greater than zero for Tank Shape=OTHER");
3686 0 : ErrorsFound = true;
3687 : }
3688 0 : break;
3689 : }
3690 0 : default: {
3691 0 : ShowSevereError(state,
3692 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3693 0 : ": Invalid Tank Shape entered=" + state.dataIPShortCut->cAlphaArgs(2));
3694 0 : Tank.Shape = TankShape::VertCylinder;
3695 0 : ErrorsFound = true;
3696 0 : break;
3697 : }
3698 : }
3699 :
3700 2 : if (state.dataIPShortCut->rNumericArgs(6) > 0.0) {
3701 2 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(6);
3702 : } else {
3703 : // default to just above freezing
3704 0 : Tank.TankTempLimit = 1.0;
3705 : }
3706 :
3707 2 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(3));
3708 2 : if (Tank.SetPointTempSchedule == 0) {
3709 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(3) + " = " + state.dataIPShortCut->cAlphaArgs(3));
3710 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3711 0 : ShowContinueError(state, "Schedule was not found.");
3712 0 : ErrorsFound = true;
3713 : }
3714 :
3715 2 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
3716 2 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(4);
3717 : } else {
3718 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3719 0 : Tank.DeadBandDeltaTemp = 0.0001;
3720 : }
3721 :
3722 2 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(5);
3723 2 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(7);
3724 2 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3725 0 : Tank.MaxCapacityWasAutoSized = true;
3726 : }
3727 :
3728 2 : Tank.Efficiency = 1.0;
3729 2 : Tank.SetPointTempSchedule2 = 0;
3730 2 : Tank.MaxCapacity2 = 0.0;
3731 2 : Tank.HeaterHeight2 = 0.0;
3732 2 : Tank.FuelType = Fuel::Electricity;
3733 :
3734 2 : Tank.OffCycParaLoad = 0.0;
3735 2 : Tank.OffCycParaFuelType = Fuel::Electricity;
3736 2 : Tank.OffCycParaFracToTank = 0.0;
3737 2 : Tank.OffCycParaHeight = 0.0;
3738 2 : Tank.OnCycParaLoad = 0.0;
3739 2 : Tank.OnCycParaFuelType = Fuel::Electricity;
3740 2 : Tank.OnCycParaFracToTank = 0.0;
3741 2 : Tank.OnCycParaHeight = 0.0;
3742 :
3743 2 : Tank.AmbientTempIndicator = static_cast<WTTAmbientTemp>(
3744 4 : getEnumerationValue(TankAmbientTempNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(4))));
3745 2 : switch (Tank.AmbientTempIndicator) {
3746 :
3747 0 : case WTTAmbientTemp::Schedule: {
3748 0 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(5));
3749 0 : if (Tank.AmbientTempSchedule == 0) {
3750 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(5) + " = " + state.dataIPShortCut->cAlphaArgs(5));
3751 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3752 0 : ShowContinueError(state, "Schedule was not found.");
3753 0 : ErrorsFound = true;
3754 : }
3755 :
3756 0 : break;
3757 : }
3758 2 : case WTTAmbientTemp::TempZone: {
3759 2 : Tank.AmbientTempZone = UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(6), state.dataHeatBal->Zone);
3760 2 : if (Tank.AmbientTempZone == 0) {
3761 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(6) + " = " + state.dataIPShortCut->cAlphaArgs(6));
3762 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3763 0 : ShowContinueError(state, "Zone was not found.");
3764 0 : ErrorsFound = true;
3765 : }
3766 2 : Tank.OffCycLossFracToZone = 1.0;
3767 :
3768 2 : break;
3769 : }
3770 0 : case WTTAmbientTemp::OutsideAir: {
3771 0 : Tank.AmbientTempOutsideAirNode =
3772 0 : NodeInputManager::GetOnlySingleNode(state,
3773 0 : state.dataIPShortCut->cAlphaArgs(7),
3774 : ErrorsFound,
3775 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3776 0 : state.dataIPShortCut->cAlphaArgs(1),
3777 : DataLoopNode::NodeFluidType::Air,
3778 : DataLoopNode::ConnectionType::Inlet,
3779 : NodeInputManager::CompFluidStream::Primary,
3780 0 : DataLoopNode::ObjectIsNotParent);
3781 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(7)) {
3782 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3783 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(7) + " = " + state.dataIPShortCut->cAlphaArgs(7));
3784 0 : ShowContinueError(state,
3785 0 : "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3786 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3787 0 : ErrorsFound = true;
3788 : }
3789 : } else {
3790 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3791 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3792 0 : ErrorsFound = true;
3793 : }
3794 :
3795 0 : break;
3796 : }
3797 0 : default: {
3798 0 : ShowSevereError(state,
3799 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3800 0 : ": Invalid Ambient Temperature Indicator entered=" + state.dataIPShortCut->cAlphaArgs(4));
3801 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3802 0 : ErrorsFound = true;
3803 0 : break;
3804 : }
3805 : }
3806 :
3807 2 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(8);
3808 2 : Tank.SkinLossFracToZone = 1.0;
3809 2 : Tank.OffCycFlueLossCoeff = 0.0;
3810 2 : Tank.OffCycFlueLossFracToZone = 0.0;
3811 :
3812 2 : Tank.MassFlowRateMax = 0.0;
3813 2 : Tank.FlowRateSchedule = 0;
3814 2 : Tank.UseInletTempSchedule = 0;
3815 2 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(9);
3816 2 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(10);
3817 :
3818 : // default to always on
3819 2 : Tank.SourceSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3820 2 : Tank.UseSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3821 :
3822 2 : if (state.dataIPShortCut->rNumericArgs(10) == DataGlobalConstants::AutoCalculate) {
3823 0 : Tank.UseInletHeight = Tank.Height; // top of tank
3824 : }
3825 2 : if (Tank.UseInletHeight > Tank.Height) {
3826 0 : ShowSevereError(state,
3827 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3828 : ": Use inlet is located higher than overall tank height.");
3829 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3830 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
3831 0 : ErrorsFound = true;
3832 : }
3833 :
3834 2 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(11);
3835 2 : if (Tank.UseOutletHeight > Tank.Height) {
3836 0 : ShowSevereError(state,
3837 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3838 : ": Use outlet is located higher than overall tank height.");
3839 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3840 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(11), state.dataIPShortCut->rNumericArgs(11)));
3841 0 : ErrorsFound = true;
3842 : }
3843 :
3844 2 : if ((state.dataIPShortCut->rNumericArgs(13) > 1) || (state.dataIPShortCut->rNumericArgs(13) <= 0)) {
3845 0 : ShowSevereError(state,
3846 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3847 : ": Source Side Effectiveness is out of bounds (>0 to 1)");
3848 0 : ErrorsFound = true;
3849 : }
3850 2 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(13);
3851 :
3852 2 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(14);
3853 2 : if (Tank.SourceInletHeight > Tank.Height) {
3854 0 : ShowSevereError(state,
3855 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3856 : ": Source inlet is located higher than overall tank height.");
3857 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3858 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(14), state.dataIPShortCut->rNumericArgs(14)));
3859 0 : ErrorsFound = true;
3860 : }
3861 :
3862 2 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(15);
3863 2 : if (state.dataIPShortCut->rNumericArgs(15) == DataGlobalConstants::AutoCalculate) {
3864 0 : Tank.SourceOutletHeight = Tank.Height; // top of tank
3865 : }
3866 2 : if (Tank.SourceOutletHeight > Tank.Height) {
3867 0 : ShowSevereError(state,
3868 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3869 : ": Source outlet is located higher than overall tank height.");
3870 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3871 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(15), state.dataIPShortCut->rNumericArgs(15)));
3872 0 : ErrorsFound = true;
3873 : }
3874 :
3875 2 : Tank.StandAlone = false;
3876 :
3877 2 : if (state.dataIPShortCut->lNumericFieldBlanks(12)) {
3878 0 : Tank.UseDesignVolFlowRate = 0.0;
3879 : } else {
3880 2 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(12);
3881 2 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3882 2 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3883 : }
3884 : }
3885 :
3886 2 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3887 :
3888 2 : if (state.dataIPShortCut->lNumericFieldBlanks(16)) {
3889 0 : Tank.SourceDesignVolFlowRate = 0.0;
3890 : } else {
3891 2 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(16);
3892 2 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3893 0 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3894 : }
3895 : }
3896 :
3897 2 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(17);
3898 :
3899 2 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3900 :
3901 2 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(8)) || (!state.dataIPShortCut->lAlphaFieldBlanks(9))) {
3902 2 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3903 2 : state.dataIPShortCut->cAlphaArgs(8),
3904 : ErrorsFound,
3905 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3906 2 : state.dataIPShortCut->cAlphaArgs(1),
3907 : DataLoopNode::NodeFluidType::Water,
3908 : DataLoopNode::ConnectionType::Inlet,
3909 : NodeInputManager::CompFluidStream::Primary,
3910 2 : DataLoopNode::ObjectIsNotParent);
3911 2 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
3912 2 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3913 2 : state.dataIPShortCut->cAlphaArgs(9),
3914 : ErrorsFound,
3915 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3916 2 : state.dataIPShortCut->cAlphaArgs(1),
3917 : DataLoopNode::NodeFluidType::Water,
3918 : DataLoopNode::ConnectionType::Outlet,
3919 : NodeInputManager::CompFluidStream::Primary,
3920 2 : DataLoopNode::ObjectIsNotParent);
3921 2 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(9);
3922 : }
3923 :
3924 2 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(11)) || (!state.dataIPShortCut->lAlphaFieldBlanks(12))) {
3925 1 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3926 1 : state.dataIPShortCut->cAlphaArgs(11),
3927 : ErrorsFound,
3928 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3929 1 : state.dataIPShortCut->cAlphaArgs(1),
3930 : DataLoopNode::NodeFluidType::Water,
3931 : DataLoopNode::ConnectionType::Inlet,
3932 : NodeInputManager::CompFluidStream::Secondary,
3933 1 : DataLoopNode::ObjectIsNotParent);
3934 1 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
3935 1 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3936 1 : state.dataIPShortCut->cAlphaArgs(12),
3937 : ErrorsFound,
3938 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3939 1 : state.dataIPShortCut->cAlphaArgs(1),
3940 : DataLoopNode::NodeFluidType::Water,
3941 : DataLoopNode::ConnectionType::Outlet,
3942 : NodeInputManager::CompFluidStream::Secondary,
3943 1 : DataLoopNode::ObjectIsNotParent);
3944 1 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(12);
3945 : }
3946 :
3947 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(10)) {
3948 0 : Tank.UseSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3949 : } else {
3950 2 : Tank.UseSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(10));
3951 2 : if (Tank.UseSideAvailSchedNum == 0) {
3952 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(10) + " = " + state.dataIPShortCut->cAlphaArgs(10));
3953 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3954 0 : ShowContinueError(state, "Schedule was not found.");
3955 0 : ErrorsFound = true;
3956 : }
3957 : }
3958 :
3959 2 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
3960 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
3961 : }
3962 :
3963 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(13)) {
3964 0 : Tank.SourceSideAvailSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
3965 : } else {
3966 2 : Tank.SourceSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(13));
3967 2 : if (Tank.SourceSideAvailSchedNum == 0) {
3968 0 : ShowSevereError(state, "Invalid, " + state.dataIPShortCut->cAlphaFieldNames(13) + " = " + state.dataIPShortCut->cAlphaArgs(13));
3969 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
3970 0 : ShowContinueError(state, "Schedule was not found.");
3971 0 : ErrorsFound = true;
3972 : }
3973 : }
3974 :
3975 : // Validate inlet mode
3976 2 : Tank.InletMode = static_cast<InletPositionMode>(
3977 4 : getEnumerationValue(InletPositionModeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataIPShortCut->cAlphaArgs(14))));
3978 :
3979 2 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(18);
3980 2 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(19);
3981 :
3982 2 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
3983 2 : Tank.AdditionalLossCoeff = 0.0;
3984 2 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
3985 2 : if (NumNums > 19 + NodeNum) {
3986 0 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(19 + NodeNum);
3987 : } else {
3988 2 : break;
3989 : }
3990 : }
3991 :
3992 2 : if (NumNums > 19 + Tank.Nodes) {
3993 0 : ShowWarningError(state,
3994 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1) +
3995 : ": More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used");
3996 : }
3997 :
3998 2 : Tank.SetupStratifiedNodes(state);
3999 : }
4000 :
4001 2 : return ErrorsFound;
4002 : }
4003 :
4004 426 : bool GetWaterThermalTankInput(EnergyPlusData &state)
4005 : {
4006 :
4007 : // SUBROUTINE INFORMATION:
4008 : // AUTHOR Dan Fisher and Brandon Anderson
4009 : // DATE WRITTEN May 2000
4010 : // MODIFIED R. Raustad, June 2005, added HPWH and desuperheater water heating coils
4011 : // B. Griffith, Oct. 2007 extensions for indirect water heaters
4012 : // B. Griffith, Feb. 2008 extensions for autosizing water heaters
4013 : // BG Mar 2009. Trap for bad heater height input for stratified water heater CR7718
4014 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
4015 :
4016 : // PURPOSE OF THIS SUBROUTINE:
4017 : // Gets the water heater, HPWH, and/or desuperheater heating coil input from the input file.
4018 :
4019 426 : bool ErrorsFound = false;
4020 :
4021 : // Make sure refrigeration input is gotten before this input
4022 426 : RefrigeratedCase::CheckRefrigerationInput(state);
4023 :
4024 426 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
4025 426 : state.dataWaterThermalTanks->numWaterHeaterMixed = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedWHModuleObj);
4026 426 : state.dataWaterThermalTanks->numWaterHeaterStratified =
4027 426 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedWHModuleObj);
4028 426 : state.dataWaterThermalTanks->numChilledWaterMixed =
4029 426 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedCWTankModuleObj);
4030 426 : state.dataWaterThermalTanks->numChilledWaterStratified =
4031 426 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedCWTankModuleObj);
4032 426 : state.dataWaterThermalTanks->numWaterThermalTank =
4033 852 : state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
4034 852 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
4035 426 : state.dataWaterThermalTanks->numHeatPumpWaterHeater =
4036 852 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHPumpedCondenser) +
4037 426 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHWrappedCondenser);
4038 426 : state.dataWaterThermalTanks->numWaterHeaterDesuperheater =
4039 426 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCoilDesuperheater);
4040 :
4041 426 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4042 : static constexpr std::string_view Format_720(
4043 : "! <Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery Efficiency, "
4044 : "Standard Rated Energy Factor\n");
4045 : static constexpr std::string_view Format_721(
4046 : "! <Heat Pump Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery "
4047 : "Efficiency,Standard Rated Energy Factor,\"DX Coil Total Cooling Rate {{W, HPWH Only}}\"\n");
4048 : static constexpr std::string_view Format_722(
4049 : "! <Water Heater Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},Maximum Capacity "
4050 : "{{W}},Off-Cycle UA {{W/K}},On-Cycle UA {{W/K}},Number Of Inlets,Number Of Outlets\n");
4051 : static constexpr std::string_view Format_725(
4052 : "! <Chilled Water Tank Information>,Type,Name,Volume {{m3}},Use Side Design Flow Rate {{m3/s}}, "
4053 : "Source Side Design Flow Rate {{m3/s}}\n");
4054 : static constexpr std::string_view Format_726(
4055 : "! <Chilled Water Tank Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},UA {{W/K}},Number Of "
4056 : "Inlets,Number Of Outlets\n");
4057 :
4058 : // Write water heater header for EIO
4059 128 : if ((state.dataWaterThermalTanks->numWaterHeaterMixed > 0) || (state.dataWaterThermalTanks->numWaterHeaterStratified > 0))
4060 128 : print(state.files.eio, Format_720);
4061 128 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) print(state.files.eio, Format_721);
4062 128 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) print(state.files.eio, Format_722);
4063 128 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) print(state.files.eio, Format_725);
4064 128 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) print(state.files.eio, Format_726);
4065 : }
4066 :
4067 426 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4068 128 : state.dataWaterThermalTanks->WaterThermalTank.allocate(state.dataWaterThermalTanks->numWaterThermalTank);
4069 128 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames.reserve(static_cast<unsigned>(state.dataWaterThermalTanks->numWaterThermalTank));
4070 : }
4071 426 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4072 9 : state.dataWaterThermalTanks->HPWaterHeater.allocate(state.dataWaterThermalTanks->numHeatPumpWaterHeater);
4073 : }
4074 :
4075 426 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4076 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
4077 : }
4078 :
4079 : // ======= Get Coil:WaterHeating:Desuperheater ======================================================================
4080 426 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4081 6 : ErrorsFound |= getDesuperHtrInput(state);
4082 : }
4083 :
4084 : // ======= Get HEAT PUMP:WATER HEATER ===============================================================================
4085 426 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4086 9 : ErrorsFound |= getHPWaterHeaterInput(state);
4087 : }
4088 :
4089 : // ======= Get WATER HEATER:MIXED ===================================================================================
4090 426 : if (state.dataWaterThermalTanks->numWaterHeaterMixed > 0) {
4091 119 : ErrorsFound |= getWaterHeaterMixedInputs(state);
4092 : }
4093 :
4094 : // ======= Get WATER HEATER:STRATIFIED ==============================================================================
4095 426 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) {
4096 10 : ErrorsFound |= getWaterHeaterStratifiedInput(state);
4097 : }
4098 :
4099 : // ======= Get Chilled Water :MIXED ===================================================================================
4100 426 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) {
4101 3 : ErrorsFound |= getWaterTankMixedInput(state);
4102 : }
4103 :
4104 : // ======= Get 'ThermalStorage:ChilledWater:Stratified' =======================================================
4105 426 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) {
4106 2 : ErrorsFound |= getWaterTankStratifiedInput(state);
4107 : }
4108 :
4109 : // Loop through all desuperheating coils and then search all water heaters for the tank connected to the desuperheating coil
4110 426 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4111 6 : state.dataIPShortCut->cCurrentModuleObject = cCoilDesuperheater;
4112 12 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
4113 6 : auto &DesuperHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
4114 12 : for (int WtrHtrNum = 1; WtrHtrNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WtrHtrNum) {
4115 6 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WtrHtrNum);
4116 6 : if (!UtilityRoutines::SameString(DesuperHtr.TankName, Tank.Name) || !UtilityRoutines::SameString(DesuperHtr.TankType, Tank.Type))
4117 0 : continue;
4118 6 : Tank.DesuperheaterNum = DesuperheaterNum;
4119 6 : DesuperHtr.WaterHeaterTankNum = WtrHtrNum;
4120 6 : DesuperHtr.TankTypeNum = Tank.WaterThermalTankType;
4121 6 : DesuperHtr.BackupElementCapacity = Tank.MaxCapacity;
4122 6 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) DesuperHtr.StandAlone = true;
4123 :
4124 : // verify Desuperheater/tank source node connections
4125 6 : if (DesuperHtr.WaterInletNode != Tank.SourceOutletNode) {
4126 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + DesuperHtr.Name + ':');
4127 0 : ShowContinueError(state, "Desuperheater inlet node name does not match thermal tank source outlet node name.");
4128 0 : ShowContinueError(state,
4129 0 : "Desuperheater water inlet and outlet node names = " + DesuperHtr.InletNodeName1 + " and " +
4130 : DesuperHtr.OutletNodeName1);
4131 0 : ShowContinueError(state,
4132 0 : "Thermal tank source side inlet and outlet node names = " + Tank.InletNodeName2 + " and " +
4133 : Tank.OutletNodeName2);
4134 0 : ErrorsFound = true;
4135 : }
4136 :
4137 6 : if (DesuperHtr.WaterOutletNode != Tank.SourceInletNode) {
4138 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + DesuperHtr.Name + ':');
4139 0 : ShowContinueError(state, "Desuperheater water outlet node name does not match thermal tank source inlet node name.");
4140 0 : ShowContinueError(state,
4141 0 : "Desuperheater water inlet and outlet node names = " + DesuperHtr.InletNodeName1 + " and " +
4142 : DesuperHtr.OutletNodeName1);
4143 0 : ShowContinueError(state,
4144 0 : "Thermal tank source side inlet and outlet node names = " + Tank.InletNodeName2 + " and " +
4145 : Tank.OutletNodeName2);
4146 0 : ErrorsFound = true;
4147 : }
4148 : }
4149 :
4150 6 : if (DesuperHtr.WaterHeaterTankNum == 0) {
4151 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + DesuperHtr.Name + ':');
4152 0 : ShowContinueError(state, " Water heater tank = " + DesuperHtr.TankName + " not found.");
4153 0 : ErrorsFound = true;
4154 : }
4155 : }
4156 : }
4157 :
4158 : // Loop through HPWH's and then search all water heaters for the tank connected to the HPWH
4159 426 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4160 :
4161 9 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
4162 9 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
4163 32 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
4164 :
4165 : // Create reference to current HPWH object in array.
4166 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
4167 23 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
4168 : // Pumped Condenser
4169 20 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
4170 : } else {
4171 : // Wrapped Condenser
4172 3 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
4173 : }
4174 :
4175 : // find the tank associated with the heat pump water heater and change its %TYPE to HEAT PUMP:WATER HEATER
4176 143 : for (int CheckWaterHeaterNum = 1; CheckWaterHeaterNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++CheckWaterHeaterNum) {
4177 :
4178 120 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(CheckWaterHeaterNum);
4179 :
4180 120 : if (!(UtilityRoutines::SameString(HPWH.TankName, Tank.Name) && UtilityRoutines::SameString(HPWH.TankType, Tank.Type))) continue;
4181 :
4182 : // save backup element and on/off-cycle parasitic properties for use during standard rating procedure
4183 23 : HPWH.BackupElementCapacity = Tank.MaxCapacity;
4184 23 : HPWH.BackupElementEfficiency = Tank.Efficiency;
4185 23 : HPWH.WHOnCycParaLoad = Tank.OnCycParaLoad;
4186 23 : HPWH.WHOffCycParaLoad = Tank.OffCycParaLoad;
4187 23 : HPWH.WHOnCycParaFracToTank = Tank.OnCycParaFracToTank;
4188 23 : HPWH.WHOffCycParaFracToTank = Tank.OffCycParaFracToTank;
4189 23 : HPWH.WHPLFCurve = Tank.PLFCurve;
4190 :
4191 35 : if (((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4192 23 : (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) ||
4193 11 : (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified)) {
4194 23 : HPWH.TankType = Tank.Type;
4195 23 : HPWH.HPWHTankType = Tank.WaterThermalTankType;
4196 : } else {
4197 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4198 0 : ShowContinueError(state, "Invalid water heater tank type = " + Tank.Type);
4199 0 : ErrorsFound = true;
4200 : }
4201 :
4202 : // Set up comp set for condenser water side nodes (reverse inlet/outlet for water heater)
4203 23 : if (HPWH.bIsIHP) {
4204 3 : BranchNodeConnections::SetUpCompSets(state,
4205 : HPWH.Type,
4206 : HPWH.Name,
4207 : HPWH.DXCoilType,
4208 2 : HPWH.DXCoilName + " Water Coil",
4209 : HPWH.InletNodeName1,
4210 : HPWH.OutletNodeName1,
4211 1 : "HPWH To Coil");
4212 : } else {
4213 22 : BranchNodeConnections::SetUpCompSets(
4214 22 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.InletNodeName1, HPWH.OutletNodeName1, "HPWH To Coil");
4215 : }
4216 23 : BranchNodeConnections::SetUpCompSets(
4217 23 : state, HPWH.Type, HPWH.Name, HPWH.TankType, HPWH.TankName, HPWH.OutletNodeName1, HPWH.InletNodeName1, "HPWH To Tank");
4218 :
4219 : // If WaterHeaterMixed: do not allow modulating control for HPWH's (i.e. modulating control usually used for tankless WH's)
4220 35 : if ((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4221 12 : (Tank.ControlType == HeaterControlMode::Modulate)) {
4222 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4223 0 : ShowContinueError(state, "Heater Control Type for " + Tank.Type + " = " + Tank.Name + " must be CYCLE.");
4224 0 : ErrorsFound = true;
4225 : }
4226 :
4227 23 : Tank.HeatPumpNum = HPWaterHeaterNum;
4228 23 : HPWH.WaterHeaterTankNum = CheckWaterHeaterNum;
4229 23 : HPWH.FoundTank = true;
4230 :
4231 23 : if (Tank.DesuperheaterNum > 0) {
4232 0 : ShowSevereError(state,
4233 0 : state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + "and Coil:WaterHeating:Desuperheater = " +
4234 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(CheckWaterHeaterNum).Name +
4235 0 : ": cannot be connected to the same water heater tank = " + Tank.Name);
4236 : }
4237 :
4238 : // check that water heater source side effectiveness is greater than 0
4239 23 : if (Tank.SourceEffectiveness <= 0.0) {
4240 0 : ShowSevereError(state,
4241 0 : format("{} = {}: Invalid source side effectiveness for heat pump water heater = {:.3T}",
4242 0 : state.dataIPShortCut->cCurrentModuleObject,
4243 : HPWH.Name,
4244 0 : Tank.SourceEffectiveness));
4245 0 : ShowContinueError(state, " water heater source effectiveness will default to 1.0 and simulation continues.");
4246 0 : Tank.SourceEffectiveness = 1.0;
4247 : }
4248 :
4249 : // Set up the source side nodes for wrapped condensers
4250 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4251 3 : if (Tank.SourceInletNode > 0 || Tank.SourceOutletNode > 0) {
4252 0 : ShowSevereError(state, Tank.Type + " = " + Tank.Name + " has a source inlet or outlet node specified,");
4253 0 : ShowContinueError(
4254 0 : state, "but it is attached to " + HPWH.Type + " = " + HPWH.Name + ", which doesn't permit source side connections.");
4255 0 : ShowContinueError(state, "Please leave the source side inlet and outlet fields blank.");
4256 0 : ErrorsFound = true;
4257 : } else {
4258 :
4259 6 : auto objType = (DataLoopNode::ConnectionObjectType)getEnumerationValue(BranchNodeConnections::ConnectionObjectTypeNamesUC,
4260 9 : UtilityRoutines::MakeUPPERCase(Tank.Type));
4261 :
4262 3 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
4263 : HPWH.OutletNodeName1,
4264 : ErrorsFound,
4265 : objType,
4266 : Tank.Name,
4267 : DataLoopNode::NodeFluidType::Water,
4268 : DataLoopNode::ConnectionType::Inlet,
4269 : NodeInputManager::CompFluidStream::Secondary,
4270 3 : DataLoopNode::ObjectIsNotParent);
4271 3 : Tank.InletNodeName2 = HPWH.OutletNodeName1;
4272 3 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
4273 : HPWH.InletNodeName1,
4274 : ErrorsFound,
4275 : objType,
4276 : Tank.Name,
4277 : DataLoopNode::NodeFluidType::Water,
4278 : DataLoopNode::ConnectionType::Outlet,
4279 : NodeInputManager::CompFluidStream::Secondary,
4280 3 : DataLoopNode::ObjectIsNotParent);
4281 3 : Tank.OutletNodeName2 = HPWH.InletNodeName1;
4282 : }
4283 :
4284 : // Mark the tank as not stand alone because it is connected now.
4285 3 : Tank.StandAlone = false;
4286 : }
4287 :
4288 : // Set HPWH structure variable StandAlone to TRUE if use nodes are not connected
4289 23 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) HPWH.StandAlone = true;
4290 :
4291 23 : if (HPWH.WHUseInletNode != Tank.UseInletNode || HPWH.WHUseOutletNode != Tank.UseOutletNode) {
4292 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4293 0 : ShowContinueError(state,
4294 : "Heat pump water heater tank use side inlet and outlet node names must match the use side inlet and "
4295 0 : "outlet node names for water heater tank = " +
4296 0 : HPWH.TankType + ": " + HPWH.TankName);
4297 0 : ShowContinueError(state,
4298 0 : "Heat pump water heater use side inlet and outlet node names = " + HPWH.InletNodeName2 + " and " +
4299 : HPWH.OutletNodeName2);
4300 0 : ShowContinueError(state,
4301 0 : "Water heater tank use side inlet and outlet node names = " + Tank.InletNodeName1 + " and " +
4302 : Tank.OutletNodeName1);
4303 0 : ErrorsFound = true;
4304 : } else {
4305 23 : if (!HPWH.StandAlone) {
4306 10 : BranchNodeConnections::TestCompSet(state, HPWH.Type, HPWH.Name, Tank.InletNodeName1, Tank.OutletNodeName1, "Water Nodes");
4307 : }
4308 : }
4309 :
4310 : // verify HP/tank source node connections
4311 23 : if (HPWH.CondWaterInletNode != Tank.SourceOutletNode) {
4312 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4313 0 : ShowContinueError(state,
4314 : "Heat Pump condenser water inlet node name does not match water heater tank source outlet node name.");
4315 0 : ShowContinueError(
4316 0 : state, "Heat pump condenser water inlet and outlet node names = " + HPWH.InletNodeName1 + " and " + HPWH.OutletNodeName1);
4317 0 : ShowContinueError(state,
4318 0 : "Water heater tank source side inlet and outlet node names = " + Tank.InletNodeName2 + " and " +
4319 : Tank.OutletNodeName2);
4320 0 : ErrorsFound = true;
4321 : }
4322 :
4323 23 : if (HPWH.CondWaterOutletNode != Tank.SourceInletNode) {
4324 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4325 0 : ShowContinueError(state,
4326 : "Heat Pump condenser water outlet node name does not match water heater tank source inlet node name.");
4327 0 : ShowContinueError(
4328 0 : state, "Heat pump condenser water inlet and outlet node names = " + HPWH.InletNodeName1 + " and " + HPWH.OutletNodeName1);
4329 0 : ShowContinueError(state,
4330 0 : "Water heater tank source side inlet and outlet node names = " + Tank.InletNodeName2 + " and " +
4331 : Tank.OutletNodeName2);
4332 0 : ErrorsFound = true;
4333 : }
4334 :
4335 : // verify wrapped condenser location
4336 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4337 : // make sure the top of the condenser is not above the tank height.
4338 3 : if (HPWH.WrappedCondenserTopLocation > Tank.Height) {
4339 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4340 0 : ShowContinueError(state, "The height of the top of the wrapped condenser is greater than the height of the tank.");
4341 0 : ErrorsFound = true;
4342 : }
4343 : }
4344 :
4345 : // Verify tank name is in a zone equipment list if HPWH Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
4346 23 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
4347 10 : if (allocated(state.dataZoneEquip->ZoneEquipConfig) && allocated(state.dataZoneEquip->ZoneEquipList)) {
4348 10 : bool FoundTankInList = false;
4349 10 : bool TankNotLowestPriority = false;
4350 10 : int ZoneEquipConfigNum = HPWH.AmbientTempZone;
4351 10 : int ZoneEquipListNum = state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigNum).EquipListIndex;
4352 10 : int TankCoolingPriority = 0;
4353 10 : int TankHeatingPriority = 0;
4354 19 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).NumOfEquipTypes;
4355 : ++EquipmentTypeNum) {
4356 19 : if (state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).EquipName(EquipmentTypeNum) != HPWH.Name) continue;
4357 10 : FoundTankInList = true;
4358 10 : TankCoolingPriority = state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).CoolingPriority(EquipmentTypeNum);
4359 10 : TankHeatingPriority = state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).HeatingPriority(EquipmentTypeNum);
4360 10 : break;
4361 : } // EquipmentTypeNum
4362 10 : if (!FoundTankInList) {
4363 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4364 0 : ShowContinueError(state,
4365 : "Heat pump water heater type and name must be listed in the correct "
4366 : "ZoneHVAC:EquipmentList object when Inlet Air Configuration is equal to "
4367 : "ZoneAirOnly or ZoneAndOutdoorAir.");
4368 0 : ErrorsFound = true;
4369 : }
4370 : // check that tank has lower priority than all other non-HPWH objects in Zone
4371 : // Equipment List
4372 39 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).NumOfEquipTypes;
4373 : ++EquipmentTypeNum) {
4374 29 : if (UtilityRoutines::SameString(state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).EquipType(EquipmentTypeNum),
4375 29 : state.dataIPShortCut->cCurrentModuleObject))
4376 14 : continue;
4377 30 : if (TankCoolingPriority > state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).CoolingPriority(EquipmentTypeNum) ||
4378 15 : TankHeatingPriority > state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum).HeatingPriority(EquipmentTypeNum)) {
4379 0 : TankNotLowestPriority = true;
4380 : }
4381 : } // EquipmentTypeNum
4382 10 : if (TankNotLowestPriority && FoundTankInList) {
4383 0 : ShowWarningError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4384 0 : ShowContinueError(state,
4385 : "Heat pump water heaters should be simulated first, before other space "
4386 : "conditioning equipment.");
4387 0 : ShowContinueError(state,
4388 : "Poor temperature control may result if the Heating/Cooling sequence number is "
4389 : "not 1 in the ZoneHVAC:EquipmentList.");
4390 : }
4391 : } else {
4392 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4393 0 : ShowContinueError(state,
4394 : "ZoneHVAC:EquipmentList and ZoneHVAC:EquipmentConnections objects are required when Inlet Air "
4395 : "Configuration is either ZoneAirOnly or ZoneAndOutdoorAir.");
4396 0 : ErrorsFound = true;
4397 : } // ALLOCATED
4398 : } // InletAirConfiguration
4399 :
4400 23 : if (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
4401 :
4402 : // Nodal heat distribution fraction for stratified tank wrapped condensers
4403 11 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4404 3 : if (Tank.Shape == TankShape::HorizCylinder) {
4405 0 : ShowWarningError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ":");
4406 0 : ShowContinueError(state, "A wrapped condenser HPWH model should not be used with a horizontal stratified tank.");
4407 0 : ShowContinueError(
4408 : state, "Ignoring condenser location and distributing heat evenly throughout the tank. Simulation continues.");
4409 0 : Real64 const SameFrac = 1.0 / Tank.Nodes;
4410 0 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4411 0 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac = SameFrac;
4412 : }
4413 : } else {
4414 3 : Real64 H0 = Tank.Height; // height of top of node
4415 : Real64 H; // height of bottom of node
4416 3 : Real64 SumFrac(0.0);
4417 : // Get the fraction of each stratified node that is wrapped by the condenser
4418 17 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4419 14 : StratifiedNodeData &CurNode = Tank.Node(NodeNum);
4420 14 : if (NodeNum == Tank.Nodes) {
4421 3 : H = 0.0;
4422 : } else {
4423 11 : H = H0 - CurNode.Height;
4424 : }
4425 14 : if (H < HPWH.WrappedCondenserBottomLocation && H0 > HPWH.WrappedCondenserBottomLocation) {
4426 : // The bottom of the condenser starts partway through this node.
4427 3 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0 - (HPWH.WrappedCondenserBottomLocation - H) / CurNode.Height;
4428 11 : } else if (H >= HPWH.WrappedCondenserBottomLocation && H <= HPWH.WrappedCondenserTopLocation) {
4429 12 : if (H0 > HPWH.WrappedCondenserTopLocation) {
4430 : // the top of the condenser ends partway through this node.
4431 1 : CurNode.HPWHWrappedCondenserHeatingFrac = (HPWH.WrappedCondenserTopLocation - H) / CurNode.Height;
4432 : } else {
4433 : // the entire node is wrapped by the condenser
4434 5 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0;
4435 : }
4436 : } else {
4437 5 : CurNode.HPWHWrappedCondenserHeatingFrac = 0.0;
4438 : }
4439 14 : SumFrac += CurNode.HPWHWrappedCondenserHeatingFrac;
4440 14 : H0 = H;
4441 : }
4442 : // Normalize the fractions so they sum to 1.
4443 17 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4444 14 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac /= SumFrac;
4445 : }
4446 : }
4447 : }
4448 :
4449 : // Stratified Tank HPWH control sensor node locations
4450 11 : if (HPWH.ControlSensor1Height < 0.0) {
4451 : // default to heater 1
4452 0 : HPWH.ControlSensor1Height = Tank.HeaterHeight1;
4453 : }
4454 11 : if (HPWH.ControlSensor2Height < 0.0) {
4455 : // default to heater 2
4456 0 : HPWH.ControlSensor2Height = Tank.HeaterHeight2;
4457 : }
4458 :
4459 : // Get the vertical tank height depending on the type of tank
4460 : Real64 TankHeight;
4461 11 : if (Tank.Shape == TankShape::VertCylinder || Tank.Shape == TankShape::Other) {
4462 11 : TankHeight = Tank.Height;
4463 : } else {
4464 0 : assert(Tank.Shape == TankShape::HorizCylinder);
4465 : // For horizontal cylinders, the tank "height" is actually the length.
4466 : // We need to calculate the height.
4467 0 : Real64 EndArea = Tank.Volume / Tank.Height;
4468 0 : Real64 Radius = std::sqrt(EndArea / DataGlobalConstants::Pi);
4469 0 : TankHeight = 2.0 * Radius; // actual vertical height
4470 : }
4471 :
4472 : // Make sure the control sensor locations are in the tank
4473 11 : if (HPWH.ControlSensor1Height < 0.0 || HPWH.ControlSensor1Height > TankHeight) {
4474 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4475 0 : ShowContinueError(state, "Control Sensor 1 is located outside the tank.");
4476 0 : ErrorsFound = true;
4477 : }
4478 11 : if (HPWH.ControlSensor2Height < 0.0 || HPWH.ControlSensor2Height > TankHeight) {
4479 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4480 0 : ShowContinueError(state, "Control Sensor 2 is located outside the tank.");
4481 0 : ErrorsFound = true;
4482 : }
4483 :
4484 : // Assign the control sensors to the appropriate nodes
4485 11 : Real64 H0 = TankHeight;
4486 : Real64 H;
4487 97 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4488 86 : StratifiedNodeData const &TankNode = Tank.Node(NodeNum);
4489 86 : if (NodeNum == Tank.Nodes) {
4490 11 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
4491 : } else {
4492 75 : H = H0 - TankNode.Height;
4493 : }
4494 :
4495 : // Control Sensor 1 Node
4496 86 : if (HPWH.ControlSensor1Height <= H0 && HPWH.ControlSensor1Height > H) {
4497 11 : HPWH.ControlSensor1Node = NodeNum;
4498 : }
4499 :
4500 : // Control Sensor 2 Node
4501 86 : if (HPWH.ControlSensor2Height <= H0 && HPWH.ControlSensor2Height > H) {
4502 11 : HPWH.ControlSensor2Node = NodeNum;
4503 : }
4504 :
4505 86 : H0 = H;
4506 : }
4507 : }
4508 :
4509 : } // DO CheckWaterHeaterNum = 1, NumWaterHeater
4510 :
4511 23 : if (!HPWH.FoundTank) {
4512 0 : ShowSevereError(state, state.dataIPShortCut->cCurrentModuleObject + " = " + HPWH.Name + ':');
4513 0 : ShowContinueError(state, "Water heater tank object not found = " + HPWH.TankType + ", " + HPWH.TankName);
4514 0 : ErrorsFound = true;
4515 : }
4516 :
4517 : } // DO HPWaterHeaterNum = 1, NumHeatPumpWaterHeater
4518 : }
4519 :
4520 : // Get water heater sizing input.
4521 426 : state.dataIPShortCut->cCurrentModuleObject = "WaterHeater:Sizing";
4522 426 : state.dataWaterThermalTanks->numWaterHeaterSizing =
4523 426 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
4524 :
4525 426 : if (state.dataWaterThermalTanks->numWaterHeaterSizing > 0) {
4526 :
4527 8 : for (int WHsizingNum = 1; WHsizingNum <= state.dataWaterThermalTanks->numWaterHeaterSizing; ++WHsizingNum) {
4528 : int NumAlphas;
4529 : int NumNums;
4530 : int IOStat;
4531 16 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4532 4 : state.dataIPShortCut->cCurrentModuleObject,
4533 : WHsizingNum,
4534 4 : state.dataIPShortCut->cAlphaArgs,
4535 : NumAlphas,
4536 4 : state.dataIPShortCut->rNumericArgs,
4537 : NumNums,
4538 : IOStat);
4539 :
4540 : // find which water heater this object is for
4541 : int WaterThermalTankNum =
4542 4 : UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(1), state.dataWaterThermalTanks->WaterThermalTank);
4543 4 : if (WaterThermalTankNum == 0) {
4544 : // did not match name throw warning.
4545 0 : ShowSevereError(state,
4546 0 : state.dataIPShortCut->cCurrentModuleObject + " object name: " + state.dataIPShortCut->cAlphaArgs(1) +
4547 : " does not match any of the water heaters defined in the file");
4548 0 : ErrorsFound = true;
4549 0 : continue;
4550 : } else { // we have a match
4551 : // store the sizing data in "sizing" nested derived type for the correct water heater
4552 :
4553 4 : if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "PeakDraw")) {
4554 2 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PeakDraw;
4555 2 : } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "ResidentialHUD-FHAMinimum")) {
4556 2 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::ResidentialMin;
4557 0 : } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerPerson")) {
4558 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerPerson;
4559 0 : } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerFloorArea")) {
4560 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerFloorArea;
4561 0 : } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerUnit")) {
4562 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerUnit;
4563 0 : } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerSolarCollectorArea")) {
4564 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerSolarColArea;
4565 : } else {
4566 : // wrong design mode entered, throw error
4567 0 : ShowSevereError(state,
4568 0 : state.dataIPShortCut->cCurrentModuleObject + " object named: " + state.dataIPShortCut->cAlphaArgs(1) +
4569 0 : " contains an incorrect Design Mode of: " + state.dataIPShortCut->cAlphaArgs(2));
4570 0 : ErrorsFound = true;
4571 : }
4572 :
4573 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime = state.dataIPShortCut->rNumericArgs(1);
4574 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryTime = state.dataIPShortCut->rNumericArgs(2);
4575 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow =
4576 4 : state.dataIPShortCut->rNumericArgs(3);
4577 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms =
4578 4 : int(state.dataIPShortCut->rNumericArgs(4));
4579 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBathrooms =
4580 4 : int(state.dataIPShortCut->rNumericArgs(5));
4581 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson =
4582 4 : state.dataIPShortCut->rNumericArgs(6);
4583 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson =
4584 4 : state.dataIPShortCut->rNumericArgs(7);
4585 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea =
4586 4 : state.dataIPShortCut->rNumericArgs(8);
4587 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea =
4588 4 : state.dataIPShortCut->rNumericArgs(9);
4589 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits = state.dataIPShortCut->rNumericArgs(10);
4590 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit =
4591 4 : state.dataIPShortCut->rNumericArgs(11);
4592 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit =
4593 4 : state.dataIPShortCut->rNumericArgs(12);
4594 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea =
4595 4 : state.dataIPShortCut->rNumericArgs(13);
4596 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.HeightAspectRatio =
4597 4 : state.dataIPShortCut->rNumericArgs(14);
4598 :
4599 4 : switch (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode) {
4600 :
4601 0 : case SizingMode::Invalid: {
4602 : // do nothing, error thrown if design mode not found
4603 0 : break;
4604 : }
4605 2 : case SizingMode::PeakDraw: { // need to have entered a reasonable value for TankDrawTime
4606 2 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime <= 0.0) {
4607 0 : ShowSevereError(state,
4608 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4609 : ", design mode set to Peak Draw but needs a positive value for tank draw time");
4610 0 : ErrorsFound = true;
4611 : }
4612 : // constrain crazy sizes by limiting to 10 years or 8760*10
4613 2 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime > 87600.0) {
4614 0 : ShowWarningError(state,
4615 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4616 : ", has input with an unreasonably large Tank Draw Time, more than 10 years");
4617 0 : ErrorsFound = true;
4618 : }
4619 : // if both volume and demand side flow connections are autosized, must be a good NominalVolForSizingDemandSideFlow
4620 4 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseSidePlantLoc.loopSideNum ==
4621 2 : DataPlant::LoopSideLocation::Demand) &&
4622 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseDesignVolFlowRateWasAutoSized)) {
4623 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4624 0 : ShowWarningError(state,
4625 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4626 : " needs a value for Nominal Tank Volume for Autosizing Plant Connections");
4627 0 : ErrorsFound = true;
4628 : }
4629 : }
4630 4 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SrcSidePlantLoc.loopSideNum ==
4631 2 : DataPlant::LoopSideLocation::Demand) &&
4632 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceDesignVolFlowRateWasAutoSized)) {
4633 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4634 0 : ShowWarningError(state,
4635 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4636 : " needs a value for Nominal Tank Volume for Autosizing Plant Connections");
4637 0 : ErrorsFound = true;
4638 : }
4639 : }
4640 :
4641 2 : break;
4642 : }
4643 2 : case SizingMode::ResidentialMin: {
4644 : // it would have to have at least on bedroom and any more than 10 is crazy for this mode
4645 2 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms < 1) {
4646 0 : ShowSevereError(state,
4647 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4648 : ", mode needs at least one bedroom");
4649 0 : ErrorsFound = true;
4650 : }
4651 2 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms > 10) {
4652 0 : ShowWarningError(state,
4653 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4654 : ", probably has too many bedrooms for the selected design mode");
4655 : }
4656 :
4657 2 : break;
4658 : }
4659 0 : case SizingMode::PerPerson: {
4660 :
4661 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4662 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson <= 0.0)) {
4663 0 : ShowSevereError(state,
4664 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4665 : ", PerPerson mode needs positive value input for storage capacity per person");
4666 0 : ErrorsFound = true;
4667 : }
4668 :
4669 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4670 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson <= 0.0)) {
4671 0 : ShowSevereError(state,
4672 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4673 : ", PerPerson mode needs positive value input for recovery capacity per person");
4674 0 : ErrorsFound = true;
4675 : }
4676 :
4677 0 : break;
4678 : }
4679 0 : case SizingMode::PerFloorArea: {
4680 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4681 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea <= 0.0)) {
4682 0 : ShowSevereError(state,
4683 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4684 : ", PerArea mode needs positive value input for storage capacity per floor area");
4685 0 : ErrorsFound = true;
4686 : }
4687 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4688 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea <= 0.0)) {
4689 0 : ShowSevereError(state,
4690 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4691 : ", PerArea mode needs positive value input for recovery capacity per floor area");
4692 0 : ErrorsFound = true;
4693 : }
4694 :
4695 0 : break;
4696 : }
4697 0 : case SizingMode::PerUnit: {
4698 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4699 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit <= 0.0)) {
4700 0 : ShowSevereError(state,
4701 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4702 : ", PerUnit mode needs positive value input for storage capacity per unit");
4703 0 : ErrorsFound = true;
4704 : }
4705 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4706 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4707 0 : ShowSevereError(state,
4708 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4709 : ", PerUnit mode needs positive value input for number of units");
4710 0 : ErrorsFound = true;
4711 : }
4712 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4713 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit <= 0.0)) {
4714 0 : ShowSevereError(state,
4715 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4716 : ", PerUnit mode needs positive value input for recovery capacity per unit");
4717 0 : ErrorsFound = true;
4718 : }
4719 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4720 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4721 0 : ShowSevereError(state,
4722 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4723 : ", PerUnit mode needs positive value input for number of units");
4724 0 : ErrorsFound = true;
4725 : }
4726 0 : break;
4727 : }
4728 0 : case SizingMode::PerSolarColArea: {
4729 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4730 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea <= 0.0)) {
4731 0 : ShowSevereError(state,
4732 0 : state.dataIPShortCut->cCurrentModuleObject + ", named " + state.dataIPShortCut->cAlphaArgs(1) +
4733 : ", PerSolarCollectorArea mode needs positive value input for storage capacity per collector area");
4734 0 : ErrorsFound = true;
4735 : }
4736 0 : break;
4737 : }
4738 0 : default:
4739 0 : break;
4740 : }
4741 :
4742 : } // found water heater num okay
4743 : } // loop over sizing objects
4744 :
4745 : } // any water heater sizing objects
4746 :
4747 : // now check that if water heater fields were autosized, that there was also a sizing object for that water heater
4748 426 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4749 321 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4750 :
4751 195 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4752 2 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4753 0 : ShowWarningError(state,
4754 0 : "Water heater named " + state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name +
4755 : "has tank volume set to AUTOSIZE but it is missing associated WaterHeater:Sizing object");
4756 0 : ErrorsFound = true;
4757 : }
4758 195 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4759 2 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4760 0 : ShowWarningError(state,
4761 0 : "Water heater named " + state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name +
4762 : "has heater capacity set to AUTOSIZE but it is missing associated WaterHeater:Sizing object");
4763 0 : ErrorsFound = true;
4764 : }
4765 193 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeightWasAutoSized) &&
4766 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4767 0 : ShowWarningError(state,
4768 0 : "Water heater named " + state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name +
4769 : "has tank height set to AUTOSIZE but it is missing associated WaterHeater:Sizing object");
4770 0 : ErrorsFound = true;
4771 : }
4772 : }
4773 : }
4774 :
4775 : // now do calls to TestCompSet for tanks, depending on nodes and heat pump water heater
4776 426 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4777 321 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4778 318 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseInletNode > 0 &&
4779 125 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseOutletNode > 0) {
4780 125 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeatPumpNum > 0) {
4781 : // do nothing, Use nodes are tested for HeatPump:WaterHeater not tank
4782 : } else {
4783 575 : BranchNodeConnections::TestCompSet(state,
4784 115 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4785 115 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4786 115 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName1,
4787 115 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName1,
4788 : "Use Side Water Nodes");
4789 : }
4790 : }
4791 242 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceInletNode > 0 &&
4792 49 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceOutletNode > 0) {
4793 :
4794 245 : BranchNodeConnections::TestCompSet(state,
4795 49 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4796 49 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4797 49 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName2,
4798 49 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName2,
4799 : "Source Side Water Nodes");
4800 : }
4801 : }
4802 : }
4803 :
4804 426 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4805 321 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4806 :
4807 193 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).setupZoneInternalGains(state);
4808 :
4809 : } // WaterThermalTankNum
4810 : }
4811 : } // get input flag
4812 :
4813 426 : return ErrorsFound;
4814 : }
4815 :
4816 191 : void WaterThermalTankData::setupOutputVars(EnergyPlusData &state)
4817 : {
4818 379 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) ||
4819 188 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
4820 5 : this->setupChilledWaterTankOutputVars(state);
4821 : } else {
4822 : // moving setupWaterHeaterOutputVars to here causes big table diffs...
4823 186 : this->setupWaterHeaterOutputVars(state);
4824 : }
4825 : // moving setupZoneInternalGains to here causes math and table diffs...
4826 : // this->setupZoneInternalGains();
4827 191 : }
4828 :
4829 5 : void WaterThermalTankData::setupChilledWaterTankOutputVars(EnergyPlusData &state)
4830 : {
4831 :
4832 : // CurrentModuleObject='ThermalStorage:ChilledWater:Mixed/ThermalStorage:ChilledWater:Stratified'
4833 10 : SetupOutputVariable(state,
4834 : "Chilled Water Thermal Storage Tank Temperature",
4835 : OutputProcessor::Unit::C,
4836 : this->TankTempAvg,
4837 : OutputProcessor::SOVTimeStepType::System,
4838 : OutputProcessor::SOVStoreType::Average,
4839 5 : this->Name);
4840 :
4841 10 : SetupOutputVariable(state,
4842 : "Chilled Water Thermal Storage Final Tank Temperature",
4843 : OutputProcessor::Unit::C,
4844 : this->TankTemp,
4845 : OutputProcessor::SOVTimeStepType::System,
4846 : OutputProcessor::SOVStoreType::Average,
4847 5 : this->Name);
4848 :
4849 10 : SetupOutputVariable(state,
4850 : "Chilled Water Thermal Storage Tank Heat Gain Rate",
4851 : OutputProcessor::Unit::W,
4852 : this->LossRate,
4853 : OutputProcessor::SOVTimeStepType::System,
4854 : OutputProcessor::SOVStoreType::Average,
4855 5 : this->Name);
4856 10 : SetupOutputVariable(state,
4857 : "Chilled Water Thermal Storage Tank Heat Gain Energy",
4858 : OutputProcessor::Unit::J,
4859 : this->LossEnergy,
4860 : OutputProcessor::SOVTimeStepType::System,
4861 : OutputProcessor::SOVStoreType::Summed,
4862 5 : this->Name);
4863 :
4864 10 : SetupOutputVariable(state,
4865 : "Chilled Water Thermal Storage Use Side Mass Flow Rate",
4866 : OutputProcessor::Unit::kg_s,
4867 : this->UseMassFlowRate,
4868 : OutputProcessor::SOVTimeStepType::System,
4869 : OutputProcessor::SOVStoreType::Average,
4870 5 : this->Name);
4871 :
4872 10 : SetupOutputVariable(state,
4873 : "Chilled Water Thermal Storage Use Side Inlet Temperature",
4874 : OutputProcessor::Unit::C,
4875 : this->UseInletTemp,
4876 : OutputProcessor::SOVTimeStepType::System,
4877 : OutputProcessor::SOVStoreType::Average,
4878 5 : this->Name);
4879 :
4880 10 : SetupOutputVariable(state,
4881 : "Chilled Water Thermal Storage Use Side Outlet Temperature",
4882 : OutputProcessor::Unit::C,
4883 : this->UseOutletTemp,
4884 : OutputProcessor::SOVTimeStepType::System,
4885 : OutputProcessor::SOVStoreType::Average,
4886 5 : this->Name);
4887 :
4888 10 : SetupOutputVariable(state,
4889 : "Chilled Water Thermal Storage Use Side Heat Transfer Rate",
4890 : OutputProcessor::Unit::W,
4891 : this->UseRate,
4892 : OutputProcessor::SOVTimeStepType::System,
4893 : OutputProcessor::SOVStoreType::Average,
4894 5 : this->Name);
4895 10 : SetupOutputVariable(state,
4896 : "Chilled Water Thermal Storage Use Side Heat Transfer Energy",
4897 : OutputProcessor::Unit::J,
4898 : this->UseEnergy,
4899 : OutputProcessor::SOVTimeStepType::System,
4900 : OutputProcessor::SOVStoreType::Summed,
4901 5 : this->Name);
4902 :
4903 10 : SetupOutputVariable(state,
4904 : "Chilled Water Thermal Storage Source Side Mass Flow Rate",
4905 : OutputProcessor::Unit::kg_s,
4906 : this->SourceMassFlowRate,
4907 : OutputProcessor::SOVTimeStepType::System,
4908 : OutputProcessor::SOVStoreType::Average,
4909 5 : this->Name);
4910 :
4911 10 : SetupOutputVariable(state,
4912 : "Chilled Water Thermal Storage Source Side Inlet Temperature",
4913 : OutputProcessor::Unit::C,
4914 : this->SourceInletTemp,
4915 : OutputProcessor::SOVTimeStepType::System,
4916 : OutputProcessor::SOVStoreType::Average,
4917 5 : this->Name);
4918 :
4919 10 : SetupOutputVariable(state,
4920 : "Chilled Water Thermal Storage Source Side Outlet Temperature",
4921 : OutputProcessor::Unit::C,
4922 : this->SourceOutletTemp,
4923 : OutputProcessor::SOVTimeStepType::System,
4924 : OutputProcessor::SOVStoreType::Average,
4925 5 : this->Name);
4926 :
4927 10 : SetupOutputVariable(state,
4928 : "Chilled Water Thermal Storage Source Side Heat Transfer Rate",
4929 : OutputProcessor::Unit::W,
4930 : this->SourceRate,
4931 : OutputProcessor::SOVTimeStepType::System,
4932 : OutputProcessor::SOVStoreType::Average,
4933 5 : this->Name);
4934 10 : SetupOutputVariable(state,
4935 : "Chilled Water Thermal Storage Source Side Heat Transfer Energy",
4936 : OutputProcessor::Unit::J,
4937 : this->SourceEnergy,
4938 : OutputProcessor::SOVTimeStepType::System,
4939 : OutputProcessor::SOVStoreType::Summed,
4940 5 : this->Name);
4941 :
4942 5 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
4943 :
4944 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
4945 48 : SetupOutputVariable(state,
4946 24 : format("Chilled Water Thermal Storage Temperature Node {}", NodeNum),
4947 : OutputProcessor::Unit::C,
4948 12 : this->Node(NodeNum).TempAvg,
4949 : OutputProcessor::SOVTimeStepType::System,
4950 : OutputProcessor::SOVStoreType::Average,
4951 : this->Name);
4952 : }
4953 :
4954 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
4955 48 : SetupOutputVariable(state,
4956 24 : format("Chilled Water Thermal Storage Final Temperature Node {}", NodeNum),
4957 : OutputProcessor::Unit::C,
4958 12 : this->Node(NodeNum).Temp,
4959 : OutputProcessor::SOVTimeStepType::System,
4960 : OutputProcessor::SOVStoreType::Average,
4961 : this->Name);
4962 : }
4963 : }
4964 :
4965 5 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
4966 :
4967 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
4968 : static constexpr std::string_view Format_724("Chilled Water Tank Stratified Node Information,{},{:.4T},{:.4T},{:.4T},{},{}\n");
4969 :
4970 60 : print(state.files.eio,
4971 : Format_724,
4972 : NodeNum,
4973 12 : this->Node(NodeNum).Height,
4974 12 : this->Node(NodeNum).Volume,
4975 12 : this->Node(NodeNum).OffCycLossCoeff,
4976 12 : this->Node(NodeNum).Inlets,
4977 12 : this->Node(NodeNum).Outlets);
4978 : }
4979 : }
4980 5 : }
4981 :
4982 193 : void WaterThermalTankData::setupZoneInternalGains(EnergyPlusData &state)
4983 : {
4984 : // set up internal gains if tank is in a thermal zone
4985 193 : if (this->AmbientTempZone > 0) {
4986 80 : switch (this->WaterThermalTankType) {
4987 69 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
4988 69 : SetupZoneInternalGain(state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterMixed, &this->AmbientZoneGain);
4989 69 : break;
4990 : }
4991 6 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
4992 6 : SetupZoneInternalGain(
4993 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterStratified, &this->AmbientZoneGain);
4994 6 : break;
4995 : }
4996 3 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
4997 3 : SetupZoneInternalGain(
4998 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterMixed, &this->AmbientZoneGain);
4999 3 : break;
5000 : }
5001 2 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
5002 2 : SetupZoneInternalGain(
5003 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterStratified, &this->AmbientZoneGain);
5004 2 : break;
5005 : }
5006 0 : default:
5007 0 : break;
5008 : }
5009 : }
5010 193 : }
5011 :
5012 186 : void WaterThermalTankData::setupWaterHeaterOutputVars(EnergyPlusData &state)
5013 : {
5014 :
5015 : // Setup report variables for WaterHeater:Mixed
5016 : // CurrentModuleObject='WaterHeater:Mixed'
5017 372 : SetupOutputVariable(state,
5018 : "Water Heater Tank Temperature",
5019 : OutputProcessor::Unit::C,
5020 : this->TankTempAvg,
5021 : OutputProcessor::SOVTimeStepType::System,
5022 : OutputProcessor::SOVStoreType::Average,
5023 186 : this->Name);
5024 :
5025 372 : SetupOutputVariable(state,
5026 : "Water Heater Final Tank Temperature",
5027 : OutputProcessor::Unit::C,
5028 : this->TankTemp,
5029 : OutputProcessor::SOVTimeStepType::System,
5030 : OutputProcessor::SOVStoreType::Average,
5031 186 : this->Name);
5032 :
5033 372 : SetupOutputVariable(state,
5034 : "Water Heater Heat Loss Rate",
5035 : OutputProcessor::Unit::W,
5036 : this->LossRate,
5037 : OutputProcessor::SOVTimeStepType::System,
5038 : OutputProcessor::SOVStoreType::Average,
5039 186 : this->Name);
5040 372 : SetupOutputVariable(state,
5041 : "Water Heater Heat Loss Energy",
5042 : OutputProcessor::Unit::J,
5043 : this->LossEnergy,
5044 : OutputProcessor::SOVTimeStepType::System,
5045 : OutputProcessor::SOVStoreType::Summed,
5046 186 : this->Name);
5047 :
5048 372 : SetupOutputVariable(state,
5049 : "Water Heater Use Side Mass Flow Rate",
5050 : OutputProcessor::Unit::kg_s,
5051 : this->UseMassFlowRate,
5052 : OutputProcessor::SOVTimeStepType::System,
5053 : OutputProcessor::SOVStoreType::Average,
5054 186 : this->Name);
5055 :
5056 372 : SetupOutputVariable(state,
5057 : "Water Heater Use Side Inlet Temperature",
5058 : OutputProcessor::Unit::C,
5059 : this->UseInletTemp,
5060 : OutputProcessor::SOVTimeStepType::System,
5061 : OutputProcessor::SOVStoreType::Average,
5062 186 : this->Name);
5063 :
5064 372 : SetupOutputVariable(state,
5065 : "Water Heater Use Side Outlet Temperature",
5066 : OutputProcessor::Unit::C,
5067 : this->UseOutletTemp,
5068 : OutputProcessor::SOVTimeStepType::System,
5069 : OutputProcessor::SOVStoreType::Average,
5070 186 : this->Name);
5071 :
5072 372 : SetupOutputVariable(state,
5073 : "Water Heater Use Side Heat Transfer Rate",
5074 : OutputProcessor::Unit::W,
5075 : this->UseRate,
5076 : OutputProcessor::SOVTimeStepType::System,
5077 : OutputProcessor::SOVStoreType::Average,
5078 186 : this->Name);
5079 372 : SetupOutputVariable(state,
5080 : "Water Heater Use Side Heat Transfer Energy",
5081 : OutputProcessor::Unit::J,
5082 : this->UseEnergy,
5083 : OutputProcessor::SOVTimeStepType::System,
5084 : OutputProcessor::SOVStoreType::Summed,
5085 186 : this->Name);
5086 :
5087 372 : SetupOutputVariable(state,
5088 : "Water Heater Source Side Mass Flow Rate",
5089 : OutputProcessor::Unit::kg_s,
5090 : this->SourceMassFlowRate,
5091 : OutputProcessor::SOVTimeStepType::System,
5092 : OutputProcessor::SOVStoreType::Average,
5093 186 : this->Name);
5094 :
5095 372 : SetupOutputVariable(state,
5096 : "Water Heater Source Side Inlet Temperature",
5097 : OutputProcessor::Unit::C,
5098 : this->SourceInletTemp,
5099 : OutputProcessor::SOVTimeStepType::System,
5100 : OutputProcessor::SOVStoreType::Average,
5101 186 : this->Name);
5102 :
5103 372 : SetupOutputVariable(state,
5104 : "Water Heater Source Side Outlet Temperature",
5105 : OutputProcessor::Unit::C,
5106 : this->SourceOutletTemp,
5107 : OutputProcessor::SOVTimeStepType::System,
5108 : OutputProcessor::SOVStoreType::Average,
5109 186 : this->Name);
5110 :
5111 372 : SetupOutputVariable(state,
5112 : "Water Heater Source Side Heat Transfer Rate",
5113 : OutputProcessor::Unit::W,
5114 : this->SourceRate,
5115 : OutputProcessor::SOVTimeStepType::System,
5116 : OutputProcessor::SOVStoreType::Average,
5117 186 : this->Name);
5118 372 : SetupOutputVariable(state,
5119 : "Water Heater Source Side Heat Transfer Energy",
5120 : OutputProcessor::Unit::J,
5121 : this->SourceEnergy,
5122 : OutputProcessor::SOVTimeStepType::System,
5123 : OutputProcessor::SOVStoreType::Summed,
5124 : this->Name,
5125 : _,
5126 : "PLANTLOOPHEATINGDEMAND",
5127 : "DHW",
5128 : this->EndUseSubcategoryName,
5129 186 : "Plant");
5130 :
5131 372 : SetupOutputVariable(state,
5132 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Rate",
5133 : OutputProcessor::Unit::W,
5134 : this->OffCycParaRateToTank,
5135 : OutputProcessor::SOVTimeStepType::System,
5136 : OutputProcessor::SOVStoreType::Average,
5137 186 : this->Name);
5138 372 : SetupOutputVariable(state,
5139 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Energy",
5140 : OutputProcessor::Unit::J,
5141 : this->OffCycParaEnergyToTank,
5142 : OutputProcessor::SOVTimeStepType::System,
5143 : OutputProcessor::SOVStoreType::Summed,
5144 186 : this->Name);
5145 :
5146 372 : SetupOutputVariable(state,
5147 : "Water Heater On Cycle Parasitic Tank Heat Transfer Rate",
5148 : OutputProcessor::Unit::W,
5149 : this->OnCycParaRateToTank,
5150 : OutputProcessor::SOVTimeStepType::System,
5151 : OutputProcessor::SOVStoreType::Average,
5152 186 : this->Name);
5153 372 : SetupOutputVariable(state,
5154 : "Water Heater On Cycle Parasitic Tank Heat Transfer Energy",
5155 : OutputProcessor::Unit::J,
5156 : this->OnCycParaEnergyToTank,
5157 : OutputProcessor::SOVTimeStepType::System,
5158 : OutputProcessor::SOVStoreType::Summed,
5159 186 : this->Name);
5160 :
5161 372 : SetupOutputVariable(state,
5162 : "Water Heater Total Demand Heat Transfer Rate",
5163 : OutputProcessor::Unit::W,
5164 : this->TotalDemandRate,
5165 : OutputProcessor::SOVTimeStepType::System,
5166 : OutputProcessor::SOVStoreType::Average,
5167 186 : this->Name);
5168 372 : SetupOutputVariable(state,
5169 : "Water Heater Total Demand Heat Transfer Energy",
5170 : OutputProcessor::Unit::J,
5171 : this->TotalDemandEnergy,
5172 : OutputProcessor::SOVTimeStepType::System,
5173 : OutputProcessor::SOVStoreType::Summed,
5174 186 : this->Name);
5175 :
5176 372 : SetupOutputVariable(state,
5177 : "Water Heater Heating Rate",
5178 : OutputProcessor::Unit::W,
5179 : this->HeaterRate,
5180 : OutputProcessor::SOVTimeStepType::System,
5181 : OutputProcessor::SOVStoreType::Average,
5182 186 : this->Name);
5183 372 : SetupOutputVariable(state,
5184 : "Water Heater Heating Energy",
5185 : OutputProcessor::Unit::J,
5186 : this->HeaterEnergy,
5187 : OutputProcessor::SOVTimeStepType::System,
5188 : OutputProcessor::SOVStoreType::Summed,
5189 186 : this->Name);
5190 :
5191 372 : SetupOutputVariable(state,
5192 : "Water Heater Unmet Demand Heat Transfer Rate",
5193 : OutputProcessor::Unit::W,
5194 : this->UnmetRate,
5195 : OutputProcessor::SOVTimeStepType::System,
5196 : OutputProcessor::SOVStoreType::Average,
5197 186 : this->Name);
5198 372 : SetupOutputVariable(state,
5199 : "Water Heater Unmet Demand Heat Transfer Energy",
5200 : OutputProcessor::Unit::J,
5201 : this->UnmetEnergy,
5202 : OutputProcessor::SOVTimeStepType::System,
5203 : OutputProcessor::SOVStoreType::Summed,
5204 186 : this->Name);
5205 :
5206 372 : SetupOutputVariable(state,
5207 : "Water Heater Venting Heat Transfer Rate",
5208 : OutputProcessor::Unit::W,
5209 : this->VentRate,
5210 : OutputProcessor::SOVTimeStepType::System,
5211 : OutputProcessor::SOVStoreType::Average,
5212 186 : this->Name);
5213 372 : SetupOutputVariable(state,
5214 : "Water Heater Venting Heat Transfer Energy",
5215 : OutputProcessor::Unit::J,
5216 : this->VentEnergy,
5217 : OutputProcessor::SOVTimeStepType::System,
5218 : OutputProcessor::SOVStoreType::Summed,
5219 186 : this->Name);
5220 :
5221 372 : SetupOutputVariable(state,
5222 : "Water Heater Net Heat Transfer Rate",
5223 : OutputProcessor::Unit::W,
5224 : this->NetHeatTransferRate,
5225 : OutputProcessor::SOVTimeStepType::System,
5226 : OutputProcessor::SOVStoreType::Average,
5227 186 : this->Name);
5228 372 : SetupOutputVariable(state,
5229 : "Water Heater Net Heat Transfer Energy",
5230 : OutputProcessor::Unit::J,
5231 : this->NetHeatTransferEnergy,
5232 : OutputProcessor::SOVTimeStepType::System,
5233 : OutputProcessor::SOVStoreType::Summed,
5234 186 : this->Name);
5235 :
5236 372 : SetupOutputVariable(state,
5237 : "Water Heater Cycle On Count",
5238 : OutputProcessor::Unit::None,
5239 : this->CycleOnCount,
5240 : OutputProcessor::SOVTimeStepType::System,
5241 : OutputProcessor::SOVStoreType::Summed,
5242 186 : this->Name);
5243 372 : SetupOutputVariable(state,
5244 : "Water Heater Runtime Fraction",
5245 : OutputProcessor::Unit::None,
5246 : this->RuntimeFraction,
5247 : OutputProcessor::SOVTimeStepType::System,
5248 : OutputProcessor::SOVStoreType::Average,
5249 186 : this->Name);
5250 372 : SetupOutputVariable(state,
5251 : "Water Heater Part Load Ratio",
5252 : OutputProcessor::Unit::None,
5253 : this->PartLoadRatio,
5254 : OutputProcessor::SOVTimeStepType::System,
5255 : OutputProcessor::SOVStoreType::Average,
5256 186 : this->Name);
5257 :
5258 558 : SetupOutputVariable(state,
5259 372 : format("Water Heater {} Rate", FuelTypeNames[static_cast<int>(this->FuelType)]),
5260 : OutputProcessor::Unit::W,
5261 : this->FuelRate,
5262 : OutputProcessor::SOVTimeStepType::System,
5263 : OutputProcessor::SOVStoreType::Average,
5264 : this->Name);
5265 744 : SetupOutputVariable(state,
5266 372 : format("Water Heater {} Energy", FuelTypeNames[static_cast<int>(this->FuelType)]),
5267 : OutputProcessor::Unit::J,
5268 : this->FuelEnergy,
5269 : OutputProcessor::SOVTimeStepType::System,
5270 : OutputProcessor::SOVStoreType::Summed,
5271 : this->Name,
5272 : _,
5273 186 : FuelTypeNames[static_cast<int>(this->FuelType)],
5274 : "DHW",
5275 : this->EndUseSubcategoryName,
5276 : "Plant");
5277 :
5278 558 : SetupOutputVariable(state,
5279 372 : format("Water Heater Off Cycle Parasitic {} Rate", FuelTypeNames[static_cast<int>(this->OffCycParaFuelType)]),
5280 : OutputProcessor::Unit::W,
5281 : this->OffCycParaFuelRate,
5282 : OutputProcessor::SOVTimeStepType::System,
5283 : OutputProcessor::SOVStoreType::Average,
5284 : this->Name);
5285 744 : SetupOutputVariable(state,
5286 372 : format("Water Heater Off Cycle Parasitic {} Energy", FuelTypeNames[static_cast<int>(this->OffCycParaFuelType)]),
5287 : OutputProcessor::Unit::J,
5288 : this->OffCycParaFuelEnergy,
5289 : OutputProcessor::SOVTimeStepType::System,
5290 : OutputProcessor::SOVStoreType::Summed,
5291 : this->Name,
5292 : _,
5293 186 : FuelTypeNames[static_cast<int>(this->OffCycParaFuelType)],
5294 : "DHW",
5295 : this->EndUseSubcategoryName,
5296 : "Plant");
5297 :
5298 558 : SetupOutputVariable(state,
5299 372 : format("Water Heater On Cycle Parasitic {} Rate", FuelTypeNames[static_cast<int>(this->OnCycParaFuelType)]),
5300 : OutputProcessor::Unit::W,
5301 : this->OnCycParaFuelRate,
5302 : OutputProcessor::SOVTimeStepType::System,
5303 : OutputProcessor::SOVStoreType::Average,
5304 : this->Name);
5305 744 : SetupOutputVariable(state,
5306 372 : format("Water Heater On Cycle Parasitic {} Energy", FuelTypeNames[static_cast<int>(this->OnCycParaFuelType)]),
5307 : OutputProcessor::Unit::J,
5308 : this->OnCycParaFuelEnergy,
5309 : OutputProcessor::SOVTimeStepType::System,
5310 : OutputProcessor::SOVStoreType::Summed,
5311 : this->Name,
5312 : _,
5313 186 : FuelTypeNames[static_cast<int>(this->OnCycParaFuelType)],
5314 : "DHW",
5315 : this->EndUseSubcategoryName,
5316 : "Plant");
5317 :
5318 372 : SetupOutputVariable(state,
5319 : "Water Heater Water Volume Flow Rate",
5320 : OutputProcessor::Unit::m3_s,
5321 : this->VolFlowRate,
5322 : OutputProcessor::SOVTimeStepType::System,
5323 : OutputProcessor::SOVStoreType::Average,
5324 186 : this->Name);
5325 372 : SetupOutputVariable(state,
5326 : "Water Heater Water Volume",
5327 : OutputProcessor::Unit::m3,
5328 : this->VolumeConsumed,
5329 : OutputProcessor::SOVTimeStepType::System,
5330 : OutputProcessor::SOVStoreType::Summed,
5331 : this->Name,
5332 : _,
5333 : "Water",
5334 : "DHW",
5335 : this->EndUseSubcategoryName,
5336 186 : "Plant");
5337 372 : SetupOutputVariable(state,
5338 : "Water Heater Mains Water Volume",
5339 : OutputProcessor::Unit::m3,
5340 : this->VolumeConsumed,
5341 : OutputProcessor::SOVTimeStepType::System,
5342 : OutputProcessor::SOVStoreType::Summed,
5343 : this->Name,
5344 : _,
5345 : "MainsWater",
5346 : "DHW",
5347 : this->EndUseSubcategoryName,
5348 186 : "Plant");
5349 :
5350 186 : if (this->HeatPumpNum > 0) {
5351 : // CurrentModuleObject='WaterHeater:HeatPump:PumpedCondenser'
5352 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
5353 46 : SetupOutputVariable(state,
5354 : "Water Heater Compressor Part Load Ratio",
5355 : OutputProcessor::Unit::None,
5356 : HPWH.HeatingPLR,
5357 : OutputProcessor::SOVTimeStepType::System,
5358 : OutputProcessor::SOVStoreType::Average,
5359 23 : HPWH.Name);
5360 46 : SetupOutputVariable(state,
5361 : "Water Heater Off Cycle Ancillary Electricity Rate",
5362 : OutputProcessor::Unit::W,
5363 : HPWH.OffCycParaFuelRate,
5364 : OutputProcessor::SOVTimeStepType::System,
5365 : OutputProcessor::SOVStoreType::Average,
5366 23 : HPWH.Name);
5367 46 : SetupOutputVariable(state,
5368 : "Water Heater Off Cycle Ancillary Electricity Energy",
5369 : OutputProcessor::Unit::J,
5370 : HPWH.OffCycParaFuelEnergy,
5371 : OutputProcessor::SOVTimeStepType::System,
5372 : OutputProcessor::SOVStoreType::Summed,
5373 : HPWH.Name,
5374 : _,
5375 : "Electricity",
5376 : "DHW",
5377 : "Water Heater Parasitic",
5378 23 : "Plant");
5379 46 : SetupOutputVariable(state,
5380 : "Water Heater On Cycle Ancillary Electricity Rate",
5381 : OutputProcessor::Unit::W,
5382 : HPWH.OnCycParaFuelRate,
5383 : OutputProcessor::SOVTimeStepType::System,
5384 : OutputProcessor::SOVStoreType::Average,
5385 23 : HPWH.Name);
5386 46 : SetupOutputVariable(state,
5387 : "Water Heater On Cycle Ancillary Electricity Energy",
5388 : OutputProcessor::Unit::J,
5389 : HPWH.OnCycParaFuelEnergy,
5390 : OutputProcessor::SOVTimeStepType::System,
5391 : OutputProcessor::SOVStoreType::Summed,
5392 : HPWH.Name,
5393 : _,
5394 : "Electricity",
5395 : "DHW",
5396 : "Water Heater Parasitic",
5397 23 : "Plant");
5398 46 : SetupOutputVariable(state,
5399 : "Water Heater Heat Pump Control Tank Temperature",
5400 : OutputProcessor::Unit::C,
5401 : HPWH.ControlTempAvg,
5402 : OutputProcessor::SOVTimeStepType::System,
5403 : OutputProcessor::SOVStoreType::Average,
5404 23 : HPWH.Name);
5405 46 : SetupOutputVariable(state,
5406 : "Water Heater Heat Pump Control Tank Final Temperature",
5407 : OutputProcessor::Unit::C,
5408 : HPWH.ControlTempFinal,
5409 : OutputProcessor::SOVTimeStepType::System,
5410 : OutputProcessor::SOVStoreType::Average,
5411 23 : HPWH.Name);
5412 : }
5413 :
5414 186 : if (this->DesuperheaterNum > 0) {
5415 : // CurrentModuleObject='Coil:WaterHeating:Desuperheater'
5416 24 : SetupOutputVariable(state,
5417 : "Water Heater Part Load Ratio",
5418 : OutputProcessor::Unit::None,
5419 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR,
5420 : OutputProcessor::SOVTimeStepType::System,
5421 : OutputProcessor::SOVStoreType::Average,
5422 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5423 24 : SetupOutputVariable(state,
5424 : "Water Heater On Cycle Parasitic Electricity Rate",
5425 : OutputProcessor::Unit::W,
5426 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelRate,
5427 : OutputProcessor::SOVTimeStepType::System,
5428 : OutputProcessor::SOVStoreType::Average,
5429 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5430 24 : SetupOutputVariable(state,
5431 : "Water Heater On Cycle Parasitic Electricity Energy",
5432 : OutputProcessor::Unit::J,
5433 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelEnergy,
5434 : OutputProcessor::SOVTimeStepType::System,
5435 : OutputProcessor::SOVStoreType::Summed,
5436 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5437 : _,
5438 : "Electricity",
5439 : "DHW",
5440 : "Water Heater Parasitic",
5441 6 : "Plant");
5442 24 : SetupOutputVariable(state,
5443 : "Water Heater Off Cycle Parasitic Electricity Rate",
5444 : OutputProcessor::Unit::W,
5445 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelRate,
5446 : OutputProcessor::SOVTimeStepType::System,
5447 : OutputProcessor::SOVStoreType::Average,
5448 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5449 24 : SetupOutputVariable(state,
5450 : "Water Heater Off Cycle Parasitic Electricity Energy",
5451 : OutputProcessor::Unit::J,
5452 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelEnergy,
5453 : OutputProcessor::SOVTimeStepType::System,
5454 : OutputProcessor::SOVStoreType::Summed,
5455 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5456 : _,
5457 : "Electricity",
5458 : "DHW",
5459 : "Water Heater Parasitic",
5460 6 : "Plant");
5461 24 : SetupOutputVariable(state,
5462 : "Water Heater Heat Reclaim Efficiency Modifier Multiplier",
5463 : OutputProcessor::Unit::None,
5464 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HEffFTempOutput,
5465 : OutputProcessor::SOVTimeStepType::System,
5466 : OutputProcessor::SOVStoreType::Average,
5467 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5468 24 : SetupOutputVariable(state,
5469 : "Water Heater Pump Electricity Rate",
5470 : OutputProcessor::Unit::W,
5471 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpPower,
5472 : OutputProcessor::SOVTimeStepType::System,
5473 : OutputProcessor::SOVStoreType::Average,
5474 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5475 24 : SetupOutputVariable(state,
5476 : "Water Heater Pump Electricity Energy",
5477 : OutputProcessor::Unit::J,
5478 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpEnergy,
5479 : OutputProcessor::SOVTimeStepType::System,
5480 : OutputProcessor::SOVStoreType::Summed,
5481 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5482 : _,
5483 : "Electricity",
5484 : "DHW",
5485 : "Desuperheater Pump",
5486 6 : "Plant");
5487 24 : SetupOutputVariable(state,
5488 : "Water Heater Heating Rate",
5489 : OutputProcessor::Unit::W,
5490 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate,
5491 : OutputProcessor::SOVTimeStepType::System,
5492 : OutputProcessor::SOVStoreType::Average,
5493 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5494 24 : SetupOutputVariable(state,
5495 : "Water Heater Heating Energy",
5496 : OutputProcessor::Unit::J,
5497 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterEnergy,
5498 : OutputProcessor::SOVTimeStepType::System,
5499 : OutputProcessor::SOVStoreType::Summed,
5500 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5501 : _,
5502 : "EnergyTransfer",
5503 : "DHW",
5504 : "Water Heater",
5505 6 : "Plant");
5506 : }
5507 :
5508 : // Setup report variables for WaterHeater:Stratified
5509 : // CurrentModuleObject='WaterHeater:Stratified'
5510 186 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5511 :
5512 32 : SetupOutputVariable(state,
5513 : "Water Heater Heater 1 Heating Rate",
5514 : OutputProcessor::Unit::W,
5515 : this->HeaterRate1,
5516 : OutputProcessor::SOVTimeStepType::System,
5517 : OutputProcessor::SOVStoreType::Average,
5518 16 : this->Name);
5519 32 : SetupOutputVariable(state,
5520 : "Water Heater Heater 2 Heating Rate",
5521 : OutputProcessor::Unit::W,
5522 : this->HeaterRate2,
5523 : OutputProcessor::SOVTimeStepType::System,
5524 : OutputProcessor::SOVStoreType::Average,
5525 16 : this->Name);
5526 :
5527 32 : SetupOutputVariable(state,
5528 : "Water Heater Heater 1 Heating Energy",
5529 : OutputProcessor::Unit::J,
5530 : this->HeaterEnergy1,
5531 : OutputProcessor::SOVTimeStepType::System,
5532 : OutputProcessor::SOVStoreType::Summed,
5533 16 : this->Name);
5534 32 : SetupOutputVariable(state,
5535 : "Water Heater Heater 2 Heating Energy",
5536 : OutputProcessor::Unit::J,
5537 : this->HeaterEnergy2,
5538 : OutputProcessor::SOVTimeStepType::System,
5539 : OutputProcessor::SOVStoreType::Summed,
5540 16 : this->Name);
5541 :
5542 32 : SetupOutputVariable(state,
5543 : "Water Heater Heater 1 Cycle On Count",
5544 : OutputProcessor::Unit::None,
5545 : this->CycleOnCount1,
5546 : OutputProcessor::SOVTimeStepType::System,
5547 : OutputProcessor::SOVStoreType::Summed,
5548 16 : this->Name);
5549 32 : SetupOutputVariable(state,
5550 : "Water Heater Heater 2 Cycle On Count",
5551 : OutputProcessor::Unit::None,
5552 : this->CycleOnCount2,
5553 : OutputProcessor::SOVTimeStepType::System,
5554 : OutputProcessor::SOVStoreType::Summed,
5555 16 : this->Name);
5556 :
5557 32 : SetupOutputVariable(state,
5558 : "Water Heater Heater 1 Runtime Fraction",
5559 : OutputProcessor::Unit::None,
5560 : this->RuntimeFraction1,
5561 : OutputProcessor::SOVTimeStepType::System,
5562 : OutputProcessor::SOVStoreType::Average,
5563 16 : this->Name);
5564 32 : SetupOutputVariable(state,
5565 : "Water Heater Heater 2 Runtime Fraction",
5566 : OutputProcessor::Unit::None,
5567 : this->RuntimeFraction2,
5568 : OutputProcessor::SOVTimeStepType::System,
5569 : OutputProcessor::SOVStoreType::Average,
5570 16 : this->Name);
5571 :
5572 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5573 512 : SetupOutputVariable(state,
5574 256 : format("Water Heater Temperature Node {}", NodeNum),
5575 : OutputProcessor::Unit::C,
5576 128 : this->Node(NodeNum).TempAvg,
5577 : OutputProcessor::SOVTimeStepType::System,
5578 : OutputProcessor::SOVStoreType::Average,
5579 : this->Name);
5580 : }
5581 :
5582 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5583 512 : SetupOutputVariable(state,
5584 256 : format("Water Heater Final Temperature Node {}", NodeNum),
5585 : OutputProcessor::Unit::C,
5586 128 : this->Node(NodeNum).Temp,
5587 : OutputProcessor::SOVTimeStepType::System,
5588 : OutputProcessor::SOVStoreType::Average,
5589 : this->Name);
5590 : }
5591 : }
5592 :
5593 186 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5594 :
5595 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5596 : static constexpr std::string_view Format_723("Water Heater Stratified Node Information,{},{:.4T},{:.4T},{:.3T},{:.4T},{:.4T},{},{}\n");
5597 896 : print(state.files.eio,
5598 : Format_723,
5599 : NodeNum,
5600 128 : this->Node(NodeNum).Height,
5601 128 : this->Node(NodeNum).Volume,
5602 128 : this->Node(NodeNum).MaxCapacity,
5603 128 : this->Node(NodeNum).OffCycLossCoeff,
5604 128 : this->Node(NodeNum).OnCycLossCoeff,
5605 128 : this->Node(NodeNum).Inlets,
5606 128 : this->Node(NodeNum).Outlets);
5607 : }
5608 : }
5609 186 : }
5610 :
5611 0 : void WaterThermalTankData::ValidatePLFCurve(EnergyPlusData &state, int const CurveIndex, bool &IsValid)
5612 : {
5613 :
5614 : // SUBROUTINE INFORMATION:
5615 : // AUTHOR Peter Graham Ellis
5616 : // DATE WRITTEN February 2005
5617 : // MODIFIED na
5618 : // RE-ENGINEERED na
5619 :
5620 : // PURPOSE OF THIS SUBROUTINE:
5621 : // Validates the Part Load Factor curve by making sure it can never be less than or equal to zero
5622 : // over the domain of Part Load Ratio inputs from 0 to 1.
5623 :
5624 : // METHODOLOGY EMPLOYED:
5625 : // Currently can only check 0 and 1. Need changes in CurveManager to be able to check minimums and
5626 : // maximums.
5627 :
5628 0 : IsValid = true;
5629 :
5630 : // Check 0 and 1
5631 0 : if (Curve::CurveValue(state, CurveIndex, 0.0) <= 0) IsValid = false;
5632 0 : if (Curve::CurveValue(state, CurveIndex, 1.0) <= 0) IsValid = false;
5633 0 : }
5634 :
5635 18 : void WaterThermalTankData::SetupStratifiedNodes(EnergyPlusData &state)
5636 : {
5637 :
5638 : // SUBROUTINE INFORMATION:
5639 : // AUTHOR Peter Graham Ellis
5640 : // DATE WRITTEN January 2007
5641 : // MODIFIED na
5642 : // RE-ENGINEERED na
5643 :
5644 : // PURPOSE OF THIS SUBROUTINE:
5645 : // Sets up node properties based on the tank shape, i.e., vertical cylinder, horizontal cylinder, or other.
5646 : // Node height, skin area, vertical conduction area, and loss coefficients are calculated and assigned.
5647 : // Heating elements, parasitics, and fluid inlet and outlet flows are assigned according to node height.
5648 :
5649 : // METHODOLOGY EMPLOYED:
5650 : // Tank is divided into nodes of equal mass. For horizontal cylinders, node heights are calculated using
5651 : // the Newton-Raphson iterative method. For vertical cylinders and other shapes, the node heights are calculated
5652 : // using basic geometry.
5653 :
5654 : static constexpr std::string_view RoutineName("GetWaterThermalTankInput");
5655 :
5656 18 : constexpr Real64 Tolerance(1.0e-8); // Tolerance for Newton-Raphson solution
5657 18 : constexpr Real64 FluidCond(0.6); // Conductivity of water (W/m-K)
5658 :
5659 18 : int NumNodes = this->Nodes;
5660 18 : this->Node.allocate(NumNodes);
5661 : Real64 rho;
5662 18 : if ((this->UseSidePlantLoc.loopNum > 0) && allocated(state.dataPlnt->PlantLoop)) {
5663 0 : rho = FluidProperties::GetDensityGlycol(state,
5664 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
5665 : DataGlobalConstants::InitConvTemp,
5666 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
5667 : RoutineName);
5668 : } else {
5669 18 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, this->FluidIndex, RoutineName);
5670 : }
5671 :
5672 18 : Real64 NodeMass = this->Volume * rho / NumNodes;
5673 : Real64 TankHeight;
5674 :
5675 : // Mixing rate set to 50% of the max value for dt = 1.0
5676 18 : this->InversionMixingRate = NodeMass * 0.5 * 1.0;
5677 :
5678 18 : if ((this->Shape == TankShape::VertCylinder) || (this->Shape == TankShape::Other)) {
5679 18 : TankHeight = this->Height;
5680 18 : Real64 EndArea = this->Volume / TankHeight;
5681 18 : Real64 NodeHeight = TankHeight / NumNodes;
5682 18 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * EndArea / NodeHeight;
5683 :
5684 : Real64 Perimeter_loc;
5685 18 : if (this->Shape == TankShape::VertCylinder) {
5686 18 : Real64 Radius = std::sqrt(EndArea / DataGlobalConstants::Pi);
5687 18 : Perimeter_loc = 2.0 * DataGlobalConstants::Pi * Radius;
5688 : } else { // TankShapeOther
5689 0 : Perimeter_loc = this->Perimeter;
5690 : }
5691 :
5692 158 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5693 140 : this->Node(NodeNum).Mass = NodeMass;
5694 140 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5695 140 : this->Node(NodeNum).Height = NodeHeight;
5696 140 : this->Node(NodeNum).CondCoeffUp = CondCoeff;
5697 140 : this->Node(NodeNum).CondCoeffDn = CondCoeff;
5698 :
5699 : Real64 SkinArea;
5700 140 : if ((NodeNum == 1) || (NodeNum == NumNodes)) {
5701 34 : SkinArea = Perimeter_loc * NodeHeight + EndArea;
5702 : } else {
5703 106 : SkinArea = Perimeter_loc * NodeHeight;
5704 : }
5705 :
5706 140 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5707 :
5708 140 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5709 :
5710 : } // NodeNum
5711 :
5712 18 : this->Node(1).CondCoeffUp = 0.0;
5713 18 : this->Node(NumNodes).CondCoeffDn = 0.0;
5714 :
5715 : } else { // Tank%Shape == TankShapeHorizCylinder
5716 0 : Real64 TankLength = this->Height; // Height is the length in the axial direction
5717 0 : Real64 EndArea = this->Volume / TankLength;
5718 0 : Real64 Radius = std::sqrt(EndArea / DataGlobalConstants::Pi);
5719 0 : TankHeight = 2.0 * Radius; // Actual vertical height
5720 0 : Real64 NodeEndArea = EndArea / NumNodes;
5721 :
5722 0 : Real64 R = Radius;
5723 0 : Real64 H0 = 0.0;
5724 0 : Real64 ChordLength = 0.0;
5725 0 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5726 0 : this->Node(NodeNum).Mass = NodeMass;
5727 0 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5728 : Real64 H;
5729 0 : if (NodeNum == NumNodes) {
5730 0 : H = TankHeight;
5731 : } else {
5732 : // Use the Newton-Raphson method to solve the nonlinear algebraic equation for node height
5733 0 : H = H0 + TankHeight / NumNodes; // Initial guess
5734 :
5735 : while (true) {
5736 0 : Real64 a = std::sqrt(H);
5737 0 : Real64 b = std::sqrt(2.0 * R - H);
5738 0 : Real64 c = 2.0 * R * R * std::atan(a / b) - (2.0 * R * R - 3.0 * H * R + H * H) * (a / b);
5739 : Real64 c0;
5740 0 : if (H0 > 0.0) {
5741 0 : Real64 a0 = std::sqrt(H0);
5742 0 : Real64 b0 = std::sqrt(2.0 * R - H0);
5743 0 : c0 = 2.0 * R * R * std::atan(a0 / b0) - (2.0 * R * R - 3.0 * H0 * R + H0 * H0) * (a0 / b0);
5744 : } else {
5745 0 : c0 = 0.0;
5746 : }
5747 :
5748 0 : Real64 ApproxEndArea = c - c0; // Area approximated by iteration
5749 0 : Real64 G = ApproxEndArea - NodeEndArea; // G is the function that should converge to zero
5750 :
5751 0 : if (std::abs(G) < Tolerance) {
5752 0 : break; // Converged !!!
5753 : } else {
5754 0 : H -= G / (2.0 * a * b); // Calculate next guess: H = Hprev - G/G'
5755 : }
5756 0 : } // Newton-Raphson
5757 : }
5758 :
5759 0 : this->Node(NodeNum).Height = H - H0;
5760 :
5761 0 : if (NodeNum > 1) {
5762 0 : Real64 CrossArea = 2.0 * ChordLength * TankLength; // Use old ChordLength from previous node
5763 0 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * CrossArea / (0.5 * (H - H0) + 0.5 * this->Node(NodeNum - 1).Height);
5764 0 : this->Node(NodeNum - 1).CondCoeffUp = CondCoeff; // Set for previous node
5765 0 : this->Node(NodeNum).CondCoeffDn = CondCoeff; // Set for this node
5766 : }
5767 :
5768 0 : ChordLength = std::sqrt(2.0 * R * H - H * H); // Calc new ChordLength to be used with next node
5769 :
5770 0 : Real64 Perimeter_loc = 2.0 * R * (std::acos((R - H) / R) - std::acos((R - H0) / R)); // Segments of circular perimeter
5771 0 : Real64 SkinArea = Perimeter_loc * TankLength + 2.0 * NodeEndArea;
5772 :
5773 0 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5774 :
5775 0 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5776 : // Although it doesn't make much sense to have a flue in a horizontal tank, keep it in anyway
5777 :
5778 0 : H0 = H;
5779 : } // NodeNum
5780 :
5781 0 : this->Node(1).CondCoeffUp = 0.0;
5782 0 : this->Node(NumNodes).CondCoeffDn = 0.0;
5783 : }
5784 :
5785 : // Loop through nodes again (from top to bottom this time) and assign heating elements, parasitics, flow inlets/outlets
5786 : // according to their vertical heights in the tank
5787 18 : Real64 H0 = TankHeight;
5788 158 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5789 : Real64 H;
5790 140 : if (NodeNum == NumNodes) {
5791 18 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
5792 : } else {
5793 122 : H = H0 - this->Node(NodeNum).Height;
5794 : }
5795 :
5796 : // Assign heater elements to the nodes at the specified heights
5797 140 : if ((this->HeaterHeight1 <= H0) && (this->HeaterHeight1 > H)) {
5798 : // sensor node will not get set if user enters 0 for this heater capacity
5799 : // (Tank%MaxCapacity > 0.0d0)) THEN
5800 18 : this->HeaterNode1 = NodeNum;
5801 18 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity;
5802 : }
5803 :
5804 140 : if ((this->HeaterHeight2 <= H0) && (this->HeaterHeight2 > H)) {
5805 : // sensor node will not get set if user enters 0 for this heater capacity
5806 : // .AND. (Tank%MaxCapacity2 > 0.0d0)) THEN
5807 18 : this->HeaterNode2 = NodeNum;
5808 :
5809 18 : if ((NodeNum == this->HeaterNode1) && (this->StratifiedControlMode == PriorityControlMode::Simultaneous)) {
5810 0 : this->Node(NodeNum).MaxCapacity += this->MaxCapacity2;
5811 : } else {
5812 18 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity2;
5813 : }
5814 : }
5815 :
5816 : // Assign parasitic heat gains to the nodes at the specified heights
5817 140 : if ((this->OffCycParaHeight <= H0) && (this->OffCycParaHeight > H)) {
5818 18 : this->Node(NodeNum).OffCycParaLoad = this->OffCycParaFracToTank * this->OffCycParaLoad;
5819 : }
5820 :
5821 140 : if ((this->OnCycParaHeight <= H0) && (this->OnCycParaHeight > H)) {
5822 18 : this->Node(NodeNum).OnCycParaLoad = this->OnCycParaFracToTank * this->OnCycParaLoad;
5823 : }
5824 :
5825 : // Assign inlets and outlets to the nodes at the specified heights
5826 140 : if ((this->UseInletHeight <= H0) && (this->UseInletHeight > H)) {
5827 18 : this->UseInletStratNode = NodeNum;
5828 :
5829 18 : if ((this->UseInletNode > 0) || (this->MassFlowRateMax > 0.0)) ++this->Node(NodeNum).Inlets;
5830 : }
5831 :
5832 140 : if ((this->UseOutletHeight <= H0) && (this->UseOutletHeight > H)) {
5833 18 : this->UseOutletStratNode = NodeNum;
5834 :
5835 18 : if ((this->UseOutletNode > 0) || (this->MassFlowRateMax > 0.0)) ++this->Node(NodeNum).Outlets;
5836 : }
5837 :
5838 140 : if ((this->SourceInletHeight <= H0) && (this->SourceInletHeight > H) && (this->SourceInletNode > 0)) {
5839 :
5840 11 : this->SourceInletStratNode = NodeNum;
5841 11 : ++this->Node(NodeNum).Inlets;
5842 : }
5843 :
5844 140 : if ((this->SourceOutletHeight <= H0) && (this->SourceOutletHeight > H) && (this->SourceOutletNode > 0)) {
5845 :
5846 11 : this->SourceOutletStratNode = NodeNum;
5847 11 : ++this->Node(NodeNum).Outlets;
5848 : }
5849 :
5850 140 : H0 = H;
5851 : } // NodeNum
5852 18 : }
5853 :
5854 6113834 : void WaterThermalTankData::initialize(EnergyPlusData &state, bool const FirstHVACIteration)
5855 : {
5856 :
5857 : // SUBROUTINE INFORMATION:
5858 : // AUTHOR Peter Graham Ellis
5859 : // DATE WRITTEN February 2004
5860 : // MODIFIED FSEC, July 2005
5861 : // Brent Griffith, October 2007 indirect fired water heater
5862 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
5863 : // RE-ENGINEERED na
5864 :
5865 : // PURPOSE OF THIS SUBROUTINE:
5866 : // Initialize the water heater, heat pump water heater, or desuperheater heating coil objects during the simulation.
5867 : // determine flow rates thru use side and source side plant connections (if any)
5868 :
5869 : // METHODOLOGY EMPLOYED:
5870 : // Inlet and outlet nodes are initialized. Scheduled values are retrieved for the current timestep.
5871 :
5872 6113834 : auto &ZoneEqSizing(state.dataSize->ZoneEqSizing);
5873 :
5874 : static constexpr std::string_view RoutineName("InitWaterThermalTank");
5875 : static constexpr std::string_view GetWaterThermalTankInput("GetWaterThermalTankInput");
5876 : static constexpr std::string_view SizeTankForDemand("SizeTankForDemandSide");
5877 :
5878 6113834 : if (this->scanPlantLoopsFlag && allocated(state.dataPlnt->PlantLoop)) {
5879 191 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
5880 113 : bool errFlag = false;
5881 113 : PlantUtilities::ScanPlantLoopsForObject(
5882 : state, this->Name, this->WaterThermalTankType, this->UseSidePlantLoc, errFlag, _, _, _, this->UseInletNode, _);
5883 113 : if (errFlag) {
5884 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5885 : }
5886 : }
5887 191 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
5888 : // this is a heat pump water heater, need a separate block because TypeOf_HeatPumpWtrHeater shows up on Branch
5889 : // (input should probably have been the associated tank )
5890 10 : bool errFlag = false;
5891 30 : PlantUtilities::ScanPlantLoopsForObject(state,
5892 10 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
5893 10 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType,
5894 : this->UseSidePlantLoc,
5895 : errFlag,
5896 : _,
5897 : _,
5898 : _,
5899 : this->UseInletNode,
5900 : _);
5901 10 : if (errFlag) {
5902 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5903 : }
5904 : }
5905 191 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
5906 20 : bool errFlag = false;
5907 20 : PlantUtilities::ScanPlantLoopsForObject(
5908 : state, this->Name, this->WaterThermalTankType, this->SrcSidePlantLoc, errFlag, _, _, _, this->SourceInletNode, _);
5909 20 : if (this->UseInletNode > 0) {
5910 20 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->UseSidePlantLoc, this->SrcSidePlantLoc, this->WaterThermalTankType, true);
5911 : }
5912 20 : if (errFlag) {
5913 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5914 : }
5915 : }
5916 191 : this->scanPlantLoopsFlag = false;
5917 : }
5918 :
5919 6113834 : if (this->SetLoopIndexFlag && allocated(state.dataPlnt->PlantLoop)) {
5920 1366 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
5921 2576 : Real64 rho = FluidProperties::GetDensityGlycol(state,
5922 1288 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
5923 : DataGlobalConstants::InitConvTemp,
5924 1288 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
5925 1288 : GetWaterThermalTankInput);
5926 1288 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
5927 1288 : this->Mass = this->Volume * rho;
5928 1288 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
5929 1288 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
5930 0 : ShowSevereError(state, "InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = " + this->Name);
5931 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5932 : }
5933 : }
5934 1366 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
5935 20 : Real64 rho = FluidProperties::GetDensityGlycol(state,
5936 10 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
5937 : DataGlobalConstants::InitConvTemp,
5938 10 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
5939 10 : GetWaterThermalTankInput);
5940 10 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
5941 10 : this->Mass = this->Volume * rho;
5942 10 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
5943 10 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
5944 0 : ShowSevereError(state, "InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = " + this->Name);
5945 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5946 : }
5947 : }
5948 1366 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
5949 720 : Real64 rho = FluidProperties::GetDensityGlycol(state,
5950 360 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
5951 : DataGlobalConstants::InitConvTemp,
5952 360 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
5953 360 : GetWaterThermalTankInput);
5954 360 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
5955 360 : this->SourceSidePlantSizNum = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).PlantSizNum;
5956 360 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0)) {
5957 0 : ShowSevereError(state,
5958 0 : "InitWaterThermalTank: Did not find Sizing:Plant object for source side of plant thermal tank = " + this->Name);
5959 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5960 : }
5961 : }
5962 1366 : if (((this->SourceInletNode > 0) && (this->DesuperheaterNum > 0)) || (this->HeatPumpNum > 0)) {
5963 29 : this->SetLoopIndexFlag = false;
5964 : }
5965 1366 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) this->SetLoopIndexFlag = false;
5966 1366 : if (this->StandAlone) {
5967 49 : this->SizeStandAloneWaterHeater(state);
5968 49 : this->SetLoopIndexFlag = false;
5969 : }
5970 6112468 : } else if (this->SetLoopIndexFlag && !state.dataGlobal->AnyPlantInModel) {
5971 0 : if (this->StandAlone) {
5972 0 : this->SizeStandAloneWaterHeater(state);
5973 : }
5974 0 : this->SetLoopIndexFlag = false;
5975 : }
5976 :
5977 6113834 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag && !this->SetLoopIndexFlag) {
5978 :
5979 45861 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
5980 :
5981 44955 : if (this->ControlType == HeaterControlMode::Cycle) {
5982 44437 : this->MinCapacity = this->MaxCapacity;
5983 : }
5984 :
5985 : // check for sizing issues that model can not support
5986 :
5987 : // if stratified tank model, ensure that nominal change over rate is greater than one minute, avoid numerical problems.
5988 :
5989 87584 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
5990 42629 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
5991 3004 : Real64 MaxSideVolFlow = max(this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
5992 :
5993 3004 : if (MaxSideVolFlow > 0.0) { // protect div by zero
5994 2827 : Real64 TankChangeRateScale = this->Volume / MaxSideVolFlow;
5995 2827 : if (TankChangeRateScale < 60.0) { // nominal change over in less than one minute
5996 0 : ShowSevereError(state, "InitWaterThermalTank: Detected problem for stratified tank model. Model cannot be applied.");
5997 0 : ShowContinueError(state, "Occurs for stratified tank name = " + this->Name);
5998 0 : ShowContinueError(state, format("Tank volume = {:.4R} [m3]", this->Volume));
5999 0 : ShowContinueError(state, format("Tank use side volume flow rate = {:.4R} [m3/s]", this->UseDesignVolFlowRate));
6000 0 : ShowContinueError(state, format("Tank source side volume flow rate = {:.4R} [m3/s]", this->SourceDesignVolFlowRate));
6001 0 : ShowContinueError(state, format("Nominal tank change over rate = {:.2R} [s]", TankChangeRateScale));
6002 0 : ShowContinueError(
6003 : state, "Change over rate is too fast, increase tank volume, decrease connection flow rates or use mixed tank model");
6004 :
6005 0 : ShowFatalError(state, "InitWaterThermalTank: Simulation halted because of sizing problem in stratified tank model.");
6006 : }
6007 : }
6008 : }
6009 : }
6010 :
6011 : // Clear node initial conditions
6012 45861 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
6013 44366 : state.dataLoopNodes->Node(this->UseInletNode).Temp = 0.0;
6014 88732 : Real64 rho = FluidProperties::GetDensityGlycol(state,
6015 44366 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6016 : DataGlobalConstants::InitConvTemp,
6017 44366 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6018 44366 : GetWaterThermalTankInput);
6019 44366 : this->MassFlowRateMin = this->VolFlowRateMin * rho;
6020 44366 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6021 44366 : PlantUtilities::InitComponentNodes(state, this->MassFlowRateMin, this->PlantUseMassFlowRateMax, this->UseInletNode, this->UseOutletNode);
6022 44366 : this->UseOutletTemp = 0.0;
6023 44366 : this->UseMassFlowRate = 0.0;
6024 44366 : this->SavedUseOutletTemp = 0.0;
6025 :
6026 44366 : this->Mass = this->Volume * rho;
6027 44366 : this->UseBranchControlType = DataPlant::CompData::getPlantComponent(state, this->UseSidePlantLoc).FlowCtrl;
6028 : }
6029 :
6030 45861 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
6031 29320 : Real64 rho = FluidProperties::GetDensityGlycol(state,
6032 14660 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
6033 : DataGlobalConstants::InitConvTemp,
6034 14660 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
6035 14660 : GetWaterThermalTankInput);
6036 14660 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6037 14660 : PlantUtilities::InitComponentNodes(state, 0.0, this->PlantSourceMassFlowRateMax, this->SourceInletNode, this->SourceOutletNode);
6038 :
6039 14660 : this->SourceOutletTemp = 0.0;
6040 14660 : this->SourceMassFlowRate = 0.0;
6041 14660 : this->SavedSourceOutletTemp = 0.0;
6042 :
6043 14660 : this->SourceBranchControlType = DataPlant::CompData::getPlantComponent(state, this->SrcSidePlantLoc).FlowCtrl;
6044 : }
6045 :
6046 45861 : if ((this->SourceInletNode > 0) && ((this->DesuperheaterNum > 0) || (this->HeatPumpNum > 0))) {
6047 2412 : state.dataLoopNodes->Node(this->SourceInletNode).Temp = 0.0;
6048 2412 : this->SourceOutletTemp = 0.0;
6049 2412 : this->SourceMassFlowRate = 0.0;
6050 2412 : this->SavedSourceOutletTemp = 0.0;
6051 : Real64 rho =
6052 2412 : FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, this->FluidIndex, SizeTankForDemand);
6053 2412 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6054 : }
6055 :
6056 : // Initialize tank temperature to setpoint of first hour of warm up period
6057 : // (use HPWH or Desuperheater heating coil set point if applicable)
6058 : int SchIndex;
6059 45861 : if (this->HeatPumpNum > 0) {
6060 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6061 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveMode = TankOperatingMode::Floating;
6062 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveWHMode = TankOperatingMode::Floating;
6063 2278 : SchIndex = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTempSchedule;
6064 43583 : } else if (this->DesuperheaterNum > 0) {
6065 134 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6066 134 : SchIndex = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTempSchedule;
6067 : } else {
6068 43449 : SchIndex = this->SetPointTempSchedule;
6069 : }
6070 :
6071 45861 : if (SchIndex > 0) {
6072 45861 : this->TankTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6073 45861 : this->SavedTankTemp = this->TankTemp;
6074 :
6075 45861 : if (this->Nodes > 0) {
6076 27013 : for (auto &e : this->Node) {
6077 23926 : e.Temp = e.SavedTemp = this->TankTemp;
6078 : }
6079 : }
6080 : } else {
6081 0 : this->TankTemp = 20.0;
6082 0 : this->SavedTankTemp = this->TankTemp;
6083 :
6084 0 : if (this->Nodes > 0) {
6085 0 : for (auto &e : this->Node) {
6086 0 : e.Temp = e.SavedTemp = this->TankTemp;
6087 : }
6088 : }
6089 : }
6090 45861 : this->SourceOutletTemp = this->SavedTankTemp;
6091 45861 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6092 45861 : this->UseOutletTemp = this->SavedTankTemp;
6093 45861 : this->SavedUseOutletTemp = this->SavedTankTemp;
6094 45861 : this->TankTempAvg = this->SavedTankTemp;
6095 :
6096 45861 : this->SavedHeaterOn1 = false;
6097 45861 : this->SavedHeaterOn2 = false;
6098 45861 : this->Mode = TankOperatingMode::Floating;
6099 45861 : this->SavedMode = TankOperatingMode::Floating;
6100 45861 : this->FirstRecoveryDone = false;
6101 45861 : this->FirstRecoveryFuel = 0.0;
6102 45861 : this->UnmetEnergy = 0.0;
6103 45861 : this->LossEnergy = 0.0;
6104 45861 : this->FlueLossEnergy = 0.0;
6105 45861 : this->UseEnergy = 0.0;
6106 45861 : this->TotalDemandEnergy = 0.0;
6107 45861 : this->SourceEnergy = 0.0;
6108 45861 : this->HeaterEnergy = 0.0;
6109 45861 : this->HeaterEnergy1 = 0.0;
6110 45861 : this->HeaterEnergy2 = 0.0;
6111 45861 : this->FuelEnergy = 0.0;
6112 45861 : this->FuelEnergy1 = 0.0;
6113 45861 : this->FuelEnergy2 = 0.0;
6114 45861 : this->VentEnergy = 0.0;
6115 45861 : this->OffCycParaFuelEnergy = 0.0;
6116 45861 : this->OffCycParaEnergyToTank = 0.0;
6117 45861 : this->OnCycParaFuelEnergy = 0.0;
6118 45861 : this->OnCycParaEnergyToTank = 0.0;
6119 45861 : this->NetHeatTransferEnergy = 0.0;
6120 : }
6121 :
6122 6113834 : if (!state.dataGlobal->BeginEnvrnFlag) this->MyEnvrnFlag = true;
6123 :
6124 6113834 : if (this->WarmupFlag && (!state.dataGlobal->WarmupFlag)) {
6125 : // reInitialize tank temperature to setpoint of first hour (use HPWH or Desuperheater heating coil set point if applicable)
6126 : // BG's interpretation here is that its better to reset initial condition to setpoint once warm up is over.
6127 : // (otherwise with a dynamic storage model it is difficult for the user to see the initial performance if it isn't periodic.)
6128 : int SchIndex;
6129 427 : if (this->HeatPumpNum > 0) {
6130 46 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6131 46 : SchIndex = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTempSchedule;
6132 381 : } else if (this->DesuperheaterNum > 0) {
6133 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6134 12 : SchIndex = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTempSchedule;
6135 : } else {
6136 369 : SchIndex = this->SetPointTempSchedule;
6137 : }
6138 :
6139 427 : if (SchIndex > 0) {
6140 427 : this->TankTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6141 427 : this->SavedTankTemp = this->TankTemp;
6142 :
6143 427 : if (this->Nodes > 0) {
6144 316 : for (auto &e : this->Node) {
6145 280 : e.Temp = e.SavedTemp = this->TankTemp;
6146 : }
6147 : }
6148 : } else {
6149 0 : this->TankTemp = 20.0;
6150 0 : this->SavedTankTemp = this->TankTemp;
6151 :
6152 0 : if (this->Nodes > 0) {
6153 0 : for (auto &e : this->Node) {
6154 0 : e.Temp = e.SavedTemp = this->TankTemp;
6155 : }
6156 : }
6157 : }
6158 427 : this->SourceOutletTemp = this->SavedTankTemp;
6159 427 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6160 427 : this->UseOutletTemp = this->SavedTankTemp;
6161 427 : this->SavedUseOutletTemp = this->SavedTankTemp;
6162 427 : this->SavedHeaterOn1 = false;
6163 427 : this->SavedHeaterOn2 = false;
6164 427 : this->Mode = TankOperatingMode::Floating;
6165 427 : this->SavedMode = TankOperatingMode::Floating;
6166 427 : this->WarmupFlag = false;
6167 : }
6168 6113834 : if (state.dataGlobal->WarmupFlag) this->WarmupFlag = true;
6169 :
6170 6113834 : if (FirstHVACIteration) {
6171 : // Get all scheduled values
6172 2944790 : int SchIndex = this->SetPointTempSchedule;
6173 2944790 : this->SetPointTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6174 :
6175 2944790 : if (!this->IsChilledWaterTank) {
6176 2700717 : if (this->SetPointTemp > this->TankTempLimit) {
6177 : // Setpoint temperature scheduled higher than maximum tank temperature limit
6178 0 : this->SetPointTemp = this->TankTempLimit - 1.0;
6179 :
6180 0 : if (this->ShowSetPointWarning) {
6181 0 : ShowSevereError(state,
6182 0 : "Water heater = " + this->Name +
6183 : ": Water heater tank set point temperature is greater than the maximum tank temperature limit.");
6184 0 : ShowContinueErrorTimeStamp(state,
6185 0 : format("Water heater tank set point temperature is reset to Tank Temperature Limit minus 1 C "
6186 : "({:.2T}) and simulation continues.",
6187 0 : this->SetPointTemp));
6188 0 : this->ShowSetPointWarning = false;
6189 : }
6190 : }
6191 : } else {
6192 244073 : if (this->SetPointTemp < this->TankTempLimit) {
6193 : // Setpoint temperature scheduled lower than minimum tank temperature limit
6194 0 : this->SetPointTemp = this->TankTempLimit + 1.0;
6195 :
6196 0 : if (this->ShowSetPointWarning) {
6197 0 : ShowSevereError(state,
6198 0 : "Chilled Water Tank = " + this->Name +
6199 : ": Water heater tank set point temperature is lower than the minimum tank temperature limit.");
6200 0 : ShowContinueErrorTimeStamp(state,
6201 0 : format("Chilled water tank set point temperature is reset to Tank Temperature Limit plus 1 C "
6202 : "({:.2T}) and simulation continues.",
6203 0 : this->SetPointTemp));
6204 0 : this->ShowSetPointWarning = false;
6205 : }
6206 : }
6207 : }
6208 :
6209 2944790 : SchIndex = this->SetPointTempSchedule2;
6210 2944790 : if (SchIndex > 0) {
6211 191928 : this->SetPointTemp2 = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6212 : }
6213 :
6214 2944790 : switch (this->AmbientTempIndicator) {
6215 2135232 : case WTTAmbientTemp::Schedule: {
6216 2135232 : SchIndex = this->AmbientTempSchedule;
6217 2135232 : this->AmbientTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6218 :
6219 2135232 : break;
6220 : }
6221 674980 : case WTTAmbientTemp::TempZone: {
6222 674980 : this->AmbientTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->AmbientTempZone).MAT;
6223 :
6224 674980 : break;
6225 : }
6226 134578 : case WTTAmbientTemp::OutsideAir: {
6227 134578 : this->AmbientTemp = state.dataLoopNodes->Node(this->AmbientTempOutsideAirNode).Temp;
6228 134578 : break;
6229 : }
6230 0 : default:
6231 0 : break;
6232 : }
6233 :
6234 2944790 : if (this->UseInletNode == 0) { // Stand-alone operation
6235 :
6236 224357 : SchIndex = this->UseInletTempSchedule;
6237 224357 : if (SchIndex > 0) {
6238 80747 : this->UseInletTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6239 : } else {
6240 143610 : this->UseInletTemp = state.dataEnvrn->WaterMainsTemp;
6241 : }
6242 :
6243 224357 : SchIndex = this->FlowRateSchedule;
6244 224357 : if (SchIndex > 0) {
6245 224357 : this->UseMassFlowRate = ScheduleManager::GetCurrentScheduleValue(state, SchIndex) * this->MassFlowRateMax;
6246 :
6247 224357 : this->VolFlowRate = this->UseMassFlowRate / Psychrometrics::RhoH2O(DataGlobalConstants::InitConvTemp);
6248 : } else {
6249 0 : this->UseMassFlowRate = this->MassFlowRateMax;
6250 0 : this->VolFlowRate = this->UseMassFlowRate / Psychrometrics::RhoH2O(DataGlobalConstants::InitConvTemp);
6251 : }
6252 : }
6253 :
6254 2944790 : if (this->HeatPumpNum > 0) {
6255 323309 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp =
6256 323309 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTempSchedule);
6257 323309 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp >= this->TankTempLimit) {
6258 : // HP setpoint temperature scheduled equal to or higher than tank temperature limit
6259 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp = this->TankTempLimit - 1.0;
6260 :
6261 0 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning) {
6262 0 : ShowSevereError(
6263 : state,
6264 0 : "Heat Pump Water Heater = " + state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name +
6265 : ": Heat Pump water heater set point temperature is equal to or greater than the maximum tank temperature limit.");
6266 0 : ShowContinueErrorTimeStamp(state,
6267 0 : format("Heat Pump water heater tank set point temperature is reset to Tank Temperature Limit "
6268 : "minus 1 C ({:.2T}) and simulation continues.",
6269 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp));
6270 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning = false;
6271 : }
6272 : }
6273 : }
6274 :
6275 2944790 : if (this->DesuperheaterNum > 0) {
6276 15025 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTemp = ScheduleManager::GetCurrentScheduleValue(
6277 15025 : state, state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTempSchedule);
6278 : }
6279 :
6280 : } // first HVAC Iteration
6281 :
6282 6113834 : if (this->UseInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6283 : Real64 DeadBandTemp;
6284 5666002 : if (this->IsChilledWaterTank) {
6285 487974 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6286 : } else {
6287 5178028 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6288 : }
6289 :
6290 11332004 : Real64 mdotUse = this->PlantMassFlowRatesFunc(state,
6291 : this->UseInletNode,
6292 : FirstHVACIteration,
6293 : WaterHeaterSide::Use,
6294 : this->UseSidePlantLoc.loopSideNum,
6295 5666002 : this->UseSideSeries,
6296 : this->UseBranchControlType,
6297 : this->SavedUseOutletTemp,
6298 : DeadBandTemp,
6299 5666002 : this->SetPointTemp);
6300 5666002 : PlantUtilities::SetComponentFlowRate(state, mdotUse, this->UseInletNode, this->UseOutletNode, this->UseSidePlantLoc);
6301 :
6302 5666002 : this->UseInletTemp = state.dataLoopNodes->Node(this->UseInletNode).Temp;
6303 5666002 : this->UseMassFlowRate = mdotUse;
6304 : }
6305 :
6306 6113834 : if (this->SourceInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6307 : Real64 DeadBandTemp;
6308 1990982 : if (this->IsChilledWaterTank) {
6309 419296 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6310 : } else {
6311 1571686 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6312 : }
6313 :
6314 : Real64 sensedTemp;
6315 1990982 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
6316 104284 : int tmpNodeNum = this->HeaterNode1;
6317 104284 : sensedTemp = this->Node(tmpNodeNum).SavedTemp;
6318 : } else {
6319 1886698 : sensedTemp = this->SavedSourceOutletTemp;
6320 : }
6321 :
6322 3981964 : Real64 mdotSource = this->PlantMassFlowRatesFunc(state,
6323 : this->SourceInletNode,
6324 : FirstHVACIteration,
6325 : WaterHeaterSide::Source,
6326 : this->SrcSidePlantLoc.loopSideNum,
6327 1990982 : this->SourceSideSeries,
6328 : this->SourceBranchControlType,
6329 : sensedTemp,
6330 : DeadBandTemp,
6331 1990982 : this->SetPointTemp);
6332 1990982 : if (this->SrcSidePlantLoc.loopNum > 0) {
6333 1316276 : PlantUtilities::SetComponentFlowRate(state, mdotSource, this->SourceInletNode, this->SourceOutletNode, this->SrcSidePlantLoc);
6334 : } else { // not really plant connected (desuperheater or heat pump)
6335 674706 : state.dataLoopNodes->Node(this->SourceInletNode).MassFlowRate = mdotSource;
6336 674706 : state.dataLoopNodes->Node(this->SourceOutletNode).MassFlowRate = mdotSource;
6337 : }
6338 :
6339 1990982 : this->SourceInletTemp = state.dataLoopNodes->Node(this->SourceInletNode).Temp;
6340 1990982 : this->SourceMassFlowRate = mdotSource;
6341 : }
6342 :
6343 : // initialize HPWHs each iteration
6344 6113834 : if (this->HeatPumpNum > 0) {
6345 :
6346 644656 : int HPNum = this->HeatPumpNum;
6347 :
6348 644656 : if (this->MyHPSizeFlag) {
6349 : // autosize info must be calculated in GetWaterThermalTankInputFlag for use in StandardRating procedure
6350 : // (called at end of GetWaterThermalTankInputFlag)
6351 : // report autosizing information here (must be done after GetWaterThermalTankInputFlag is complete)
6352 69 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).WaterFlowRateAutoSized &&
6353 32 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6354 32 : BaseSizer::reportSizerOutput(state,
6355 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6356 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6357 : "Condenser water flow rate [m3/s]",
6358 16 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate);
6359 : }
6360 75 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).AirFlowRateAutoSized &&
6361 40 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6362 40 : BaseSizer::reportSizerOutput(state,
6363 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6364 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6365 : "Evaporator air flow rate [m3/s]",
6366 20 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate);
6367 : }
6368 45 : state.dataSize->DataNonZoneNonAirloopValue = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate;
6369 45 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate =
6370 45 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate * state.dataEnvrn->StdRhoAir;
6371 45 : if (state.dataSize->CurZoneEqNum > 0) {
6372 12 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirFlow = true;
6373 12 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirVolFlow = state.dataSize->DataNonZoneNonAirloopValue;
6374 : }
6375 45 : if (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated) this->MyHPSizeFlag = false;
6376 : }
6377 :
6378 644656 : int HPAirInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode;
6379 644656 : int HPAirOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirOutletNode;
6380 644656 : int OutdoorAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode;
6381 644656 : int ExhaustAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ExhaustAirNode;
6382 644656 : int HPWaterInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode;
6383 644656 : int HPWaterOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterOutletNode;
6384 644656 : int InletAirMixerNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode;
6385 644656 : int OutletAirSplitterNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutletAirSplitterNode;
6386 :
6387 644656 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).CrankcaseTempIndicator) {
6388 337490 : case CrankcaseHeaterControlTemp::Zone: {
6389 337490 : state.dataHVACGlobal->HPWHCrankcaseDBTemp =
6390 337490 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientTempZone).MAT;
6391 337490 : break;
6392 : }
6393 283028 : case CrankcaseHeaterControlTemp::Outdoors: {
6394 283028 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = state.dataEnvrn->OutDryBulbTemp;
6395 283028 : break;
6396 : }
6397 24138 : case CrankcaseHeaterControlTemp::Schedule: {
6398 24138 : state.dataHVACGlobal->HPWHCrankcaseDBTemp =
6399 24138 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).CrankcaseTempSchedule);
6400 24138 : break;
6401 : }
6402 0 : default:
6403 0 : break;
6404 : }
6405 :
6406 : // initialize HPWH report variables to 0 and set tank inlet node equal to outlet node
6407 644656 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterSensibleCapacity = 0.0;
6408 644656 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterLatentCapacity = 0.0;
6409 644656 : this->SourceMassFlowRate = 0.0;
6410 644656 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatingPLR = 0.0;
6411 644656 : this->SourceInletTemp = this->SourceOutletTemp;
6412 :
6413 : // determine HPWH inlet air conditions based on inlet air configuration (Zone, ZoneAndOA, OutdoorAir, or Schedule)
6414 644656 : Real64 HPInletDryBulbTemp = 0.0;
6415 644656 : Real64 HPInletHumRat = 0.0;
6416 : Real64 HPInletRelHum;
6417 644656 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirConfiguration) {
6418 311879 : case WTTAmbientTemp::TempZone: {
6419 311879 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6420 311879 : HPInletDryBulbTemp = state.dataLoopNodes->Node(HPAirInletNode).Temp;
6421 311879 : HPInletHumRat = state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6422 311879 : break;
6423 : }
6424 25611 : case WTTAmbientTemp::ZoneAndOA: {
6425 25611 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerSchPtr > 0) {
6426 : // schedule values are checked for boundary of 0 and 1 in GetWaterThermalTankInputFlag
6427 25611 : state.dataWaterThermalTanks->mixerInletAirSchedule =
6428 25611 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerSchPtr);
6429 : } else {
6430 0 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6431 : }
6432 51222 : HPInletDryBulbTemp = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).Temp +
6433 25611 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).Temp;
6434 51222 : HPInletHumRat = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).HumRat +
6435 25611 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6436 25611 : break;
6437 : }
6438 283028 : case WTTAmbientTemp::OutsideAir: {
6439 283028 : state.dataWaterThermalTanks->mixerInletAirSchedule = 1.0;
6440 283028 : HPInletDryBulbTemp = state.dataLoopNodes->Node(OutdoorAirNode).Temp;
6441 283028 : HPInletHumRat = state.dataLoopNodes->Node(OutdoorAirNode).HumRat;
6442 :
6443 283028 : break;
6444 : }
6445 24138 : case WTTAmbientTemp::Schedule: {
6446 24138 : HPInletDryBulbTemp =
6447 24138 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientTempSchedule);
6448 24138 : HPInletRelHum = ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientRHSchedule);
6449 24138 : HPInletHumRat = Psychrometrics::PsyWFnTdbRhPb(state, HPInletDryBulbTemp, HPInletRelHum, state.dataEnvrn->OutBaroPress, RoutineName);
6450 24138 : state.dataLoopNodes->Node(HPAirInletNode).Temp = HPInletDryBulbTemp;
6451 24138 : state.dataLoopNodes->Node(HPAirInletNode).HumRat = HPInletHumRat;
6452 24138 : state.dataLoopNodes->Node(HPAirInletNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6453 24138 : state.dataLoopNodes->Node(HPAirInletNode).Press = state.dataEnvrn->OutBaroPress;
6454 :
6455 24138 : break;
6456 : }
6457 0 : default:
6458 0 : assert(false);
6459 : break;
6460 : }
6461 :
6462 644656 : state.dataWaterThermalTanks->mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
6463 :
6464 : // set up initial conditions on nodes
6465 644656 : if (InletAirMixerNode > 0) {
6466 25611 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate = 0.0;
6467 25611 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6468 25611 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6469 25611 : state.dataLoopNodes->Node(InletAirMixerNode).Temp = HPInletDryBulbTemp;
6470 25611 : state.dataLoopNodes->Node(InletAirMixerNode).HumRat = HPInletHumRat;
6471 25611 : state.dataLoopNodes->Node(InletAirMixerNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6472 25611 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6473 25611 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6474 25611 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6475 25611 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6476 : } else {
6477 619045 : if (OutdoorAirNode == 0) {
6478 336017 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6479 336017 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6480 336017 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6481 336017 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6482 : } else {
6483 283028 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6484 283028 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6485 283028 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6486 283028 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6487 : }
6488 : }
6489 :
6490 644656 : if (OutletAirSplitterNode > 0) state.dataLoopNodes->Node(OutletAirSplitterNode).MassFlowRate = 0.0;
6491 : // these are water nodes are not managed by plant. the HP connects
6492 : // directly to the WH without using plant.
6493 644656 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
6494 465245 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
6495 465245 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
6496 : }
6497 :
6498 : // set the max mass flow rate for outdoor fans
6499 644656 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6500 644656 : state.dataWaterThermalTanks->mdotAir;
6501 :
6502 : // Curve objects in DXCoils::CalcHPWHDXCoil will use inlet conditions to HPWH not inlet air conditions to DX Coil
6503 : // HPWHInletDBTemp and HPWHInletWBTemp are DataHVACGlobals to pass info to HPWHDXCoil
6504 644656 : state.dataHVACGlobal->HPWHInletDBTemp = HPInletDryBulbTemp;
6505 644656 : state.dataHVACGlobal->HPWHInletWBTemp =
6506 1289312 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, HPInletHumRat, state.dataEnvrn->OutBaroPress);
6507 :
6508 : // initialize flow rates at speed levels for variable-speed HPWH
6509 654754 : if ((state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) &&
6510 10098 : (0 == state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) // use SCWH coil represents
6511 : {
6512 1 : IntegratedHeatPump::SizeIHP(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum); //
6513 : // IntegratedHeatPump::SimIHP(modBlankString, HPWaterHeater(HPNum).DXCoilNum,
6514 : // 0, EMP1, EMP2, EMP3, 0, 0.0, 1, 0.0, 0.0, 0.0, false, 0.0); //conduct the sizing operation in the IHP
6515 1 : int VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6516 1 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6517 :
6518 1289310 : } else if (UtilityRoutines::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
6519 1354356 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") &&
6520 65046 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed == 0)) {
6521 6 : Real64 EMP1 = 4.0;
6522 6 : Real64 EMP2 = 0.0;
6523 6 : Real64 EMP3 = 0.0;
6524 24 : VariableSpeedCoils::SimVariableSpeedCoils(state,
6525 12 : std::string(),
6526 6 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
6527 : 0,
6528 : EMP1,
6529 : EMP2,
6530 : EMP3,
6531 : DataHVACGlobals::CompressorOperation::Off,
6532 : 0.0,
6533 : 1,
6534 : 0.0,
6535 : 0.0,
6536 : 0.0,
6537 : 0.0); // conduct the sizing operation in the VS WSHP
6538 6 : int VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6539 6 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6540 : // below pass the flow rates from the VS coil to the water heater object
6541 : }
6542 :
6543 644656 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed > 0) {
6544 : int VSCoilID;
6545 75144 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP)
6546 10098 : VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6547 : else
6548 65046 : VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6549 :
6550 : // scale air flow rates
6551 75144 : Real64 MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedAirVolFlowRate /
6552 150288 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(
6553 150288 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6554 826584 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6555 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6556 751440 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(Iter) * MulSpeedFlowScale;
6557 : }
6558 :
6559 : // check fan flow rate, should be larger than the max flow rate of the VS coil
6560 75144 : Real64 FanVolFlow = 0.0;
6561 75144 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
6562 60343 : FanVolFlow = state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->designAirVolFlowRate;
6563 14801 : } else if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SimpleOnOff) {
6564 14801 : Fans::GetFanVolFlow(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum, FanVolFlow);
6565 : }
6566 :
6567 150288 : if (FanVolFlow < state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6568 75144 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) { // but this is the not the scaled mas flow
6569 : // if ( FanVolFlow < HPWaterHeater( HPNum ).HPWHAirVolFlowRate( HPWaterHeater( HPNum ).NumofSpeed ) ) {
6570 :
6571 0 : ShowWarningError(state,
6572 0 : format("InitWaterThermalTank: -air flow rate = {:.7T} in fan object is less than the MSHP system air flow rate "
6573 : "when waterheating is required({:.7T}).",
6574 : FanVolFlow,
6575 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6576 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)));
6577 0 : ShowContinueError(state,
6578 : " The MSHP system flow rate when waterheating is required is reset to the"
6579 : " fan flow rate and the simulation continues.");
6580 0 : ShowContinueError(state, " Occurs in " + state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name);
6581 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed) =
6582 : FanVolFlow;
6583 : // Check flow rates in other speeds and ensure flow rates are not above the max flow rate
6584 0 : for (int Iter = state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed - 1; Iter >= 1; --Iter) {
6585 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) >
6586 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1)) {
6587 0 : ShowContinueError(state,
6588 0 : format(" The MSHP system flow rate when waterheating is required is reset to the flow rate at higher "
6589 : "speed and the simulation continues at Speed{}.",
6590 0 : Iter));
6591 0 : ShowContinueError(state, " Occurs in " + state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name);
6592 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6593 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1);
6594 : }
6595 : }
6596 : }
6597 :
6598 826584 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6599 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSAirSpeedRatio(Iter) =
6600 1502880 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) /
6601 1502880 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6602 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6603 : }
6604 :
6605 : // scale water flow rates
6606 150288 : MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedWaterVolFlowRate /
6607 150288 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6608 75144 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6609 826584 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6610 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterVolFlowRate(Iter) =
6611 751440 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) * MulSpeedFlowScale;
6612 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterMassFlowRate(Iter) =
6613 751440 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterMassFlowRate(Iter) * MulSpeedFlowScale;
6614 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSWaterSpeedRatio(Iter) =
6615 1502880 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) /
6616 1502880 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6617 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6618 : }
6619 :
6620 75144 : Real64 rhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, HPInletDryBulbTemp, HPInletHumRat);
6621 :
6622 826584 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6623 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(Iter) =
6624 751440 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) * rhoAir;
6625 : }
6626 :
6627 : // set the max mass flow rate for outdoor fans
6628 75144 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6629 75144 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6630 : }
6631 :
6632 : } // IF(WaterThermalTank(WaterThermalTankNum)%HeatPumpNum .GT. 0)THEN
6633 :
6634 : // calling CalcStandardRatings early bypasses fan sizing since DataSizing::DataNonZoneNonAirloopValue has not been set yet
6635 6113834 : if (!this->AlreadyRated) {
6636 278 : if (this->IsChilledWaterTank) {
6637 5 : this->AlreadyRated = true;
6638 : } else {
6639 367 : if (!state.dataGlobal->AnyPlantInModel || state.dataPlnt->PlantFirstSizesOkayToReport || this->MaxCapacity > 0.0 ||
6640 94 : this->HeatPumpNum > 0) {
6641 181 : this->CalcStandardRatings(state);
6642 : }
6643 : }
6644 : }
6645 6113834 : }
6646 :
6647 6395474 : void WaterThermalTankData::CalcWaterThermalTankMixed(EnergyPlusData &state) // Water Heater being simulated
6648 : {
6649 :
6650 : // SUBROUTINE INFORMATION:
6651 : // AUTHOR Peter Graham Ellis
6652 : // DATE WRITTEN January 2005
6653 : // MODIFIED na
6654 : // RE-ENGINEERED na
6655 :
6656 : // PURPOSE OF THIS SUBROUTINE:
6657 : // Simulates a well-mixed, single node water heater tank.
6658 :
6659 : // METHODOLOGY EMPLOYED:
6660 : // This model uses analytical calculations based on the differential equation describing the tank energy
6661 : // balance. The model operates in three different modes: heating, floating, and venting. Temperatures and
6662 : // energies change dynamically over the timestep. The final reported tank temperature is the average over
6663 : // the timestep. The final reported heat rates are averages based on the total energy transfer over the
6664 : // timestep.
6665 :
6666 : static constexpr std::string_view RoutineName("CalcWaterThermalTankMixed");
6667 :
6668 : Real64 TimeElapsed_loc =
6669 6395474 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
6670 :
6671 6395474 : if (this->TimeElapsed != TimeElapsed_loc) {
6672 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
6673 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
6674 586910 : this->SavedTankTemp = this->TankTemp;
6675 586910 : this->SavedMode = this->Mode;
6676 :
6677 : // Save outlet temperatures for demand-side flow control
6678 586910 : this->SavedUseOutletTemp = this->UseOutletTemp;
6679 586910 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
6680 :
6681 586910 : this->TimeElapsed = TimeElapsed_loc;
6682 : }
6683 :
6684 6395474 : Real64 TankTemp_loc = this->SavedTankTemp;
6685 6395474 : TankOperatingMode Mode_loc = this->SavedMode;
6686 :
6687 6395474 : Real64 Qmaxcap = this->MaxCapacity;
6688 6395474 : Real64 Qmincap = this->MinCapacity;
6689 6395474 : Real64 Qoffcycfuel = this->OffCycParaLoad;
6690 6395474 : Real64 Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
6691 6395474 : Real64 Qoncycfuel = this->OnCycParaLoad;
6692 6395474 : Real64 Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
6693 :
6694 6395474 : Real64 SetPointTemp_loc = this->SetPointTemp;
6695 6395474 : Real64 DeadBandTemp = this->getDeadBandTemp();
6696 6395474 : Real64 MaxTemp = this->TankTempLimit;
6697 6395474 : Real64 AmbientTemp_loc = this->AmbientTemp;
6698 :
6699 6395474 : Real64 UseInletTemp_loc = this->UseInletTemp;
6700 6395474 : Real64 UseMassFlowRate_loc = this->UseMassFlowRate * this->UseEffectiveness;
6701 6395474 : Real64 SourceInletTemp_loc = this->SourceInletTemp;
6702 6395474 : Real64 SourceMassFlowRate_loc = this->SourceMassFlowRate * this->SourceEffectiveness;
6703 :
6704 : Real64 rho;
6705 6395474 : if (this->UseSidePlantLoc.loopNum > 0) {
6706 11578888 : rho = FluidProperties::GetDensityGlycol(state,
6707 5789444 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6708 : TankTemp_loc,
6709 5789444 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6710 : RoutineName);
6711 : } else {
6712 606030 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, TankTemp_loc, this->waterIndex, RoutineName);
6713 : }
6714 :
6715 6395474 : Real64 TankMass = rho * this->Volume;
6716 :
6717 : Real64 Cp;
6718 6395474 : if (this->UseSidePlantLoc.loopNum > 0) {
6719 11578888 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
6720 5789444 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6721 : TankTemp_loc,
6722 5789444 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6723 : RoutineName);
6724 : } else {
6725 606030 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, TankTemp_loc, this->waterIndex, RoutineName);
6726 : }
6727 :
6728 6395474 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
6729 6395474 : Real64 TimeRemaining = SecInTimeStep;
6730 6395474 : int CycleOnCount_loc = 0;
6731 6395474 : int MaxCycles = SecInTimeStep;
6732 6395474 : Real64 Runtime = 0.0;
6733 6395474 : bool SetPointRecovered = false;
6734 :
6735 6395474 : Real64 Tsum = 0.0;
6736 6395474 : Real64 Eloss = 0.0;
6737 6395474 : Real64 Elosszone = 0.0;
6738 6395474 : Real64 Euse = 0.0;
6739 6395474 : Real64 Esource = 0.0;
6740 6395474 : Real64 Eheater = 0.0;
6741 6395474 : Real64 Event = 0.0;
6742 6395474 : Real64 Eneeded = 0.0;
6743 6395474 : Real64 Eunmet = 0.0;
6744 6395474 : Real64 Efuel = 0.0;
6745 6395474 : Real64 Eoncycfuel = 0.0;
6746 6395474 : Real64 Eoffcycfuel = 0.0;
6747 6395474 : Real64 PLR = 0.0;
6748 6395474 : Real64 PLRsum = 0.0;
6749 :
6750 6395474 : Real64 Qheat = 0.0;
6751 6395474 : Real64 Qheater = 0.0;
6752 6395474 : Real64 Qvent = 0.0;
6753 6395474 : Real64 Qneeded = 0.0;
6754 6395474 : Real64 Qunmet = 0.0;
6755 6395474 : Real64 Qfuel = 0.0;
6756 :
6757 : // Calculate the heating rate from the heat pump.
6758 6395474 : Real64 HPWHCondenserDeltaT = 0.0;
6759 :
6760 6395474 : if (this->HeatPumpNum > 0) {
6761 1122477 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
6762 1122477 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
6763 1122477 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
6764 1122477 : HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
6765 : }
6766 6395474 : assert(HPWHCondenserDeltaT >= 0);
6767 :
6768 : Real64 Qheatpump;
6769 : Real64 Qsource;
6770 6395474 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
6771 : HPWHCondenserDeltaT, SourceInletTemp_loc, Cp, SetPointTemp_loc, SourceMassFlowRate_loc, Qheatpump, Qsource);
6772 :
6773 : // Calculate steady-state use heat rate.
6774 6395474 : Real64 Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - SetPointTemp_loc);
6775 6395474 : Real64 Qloss = 0.0, PLF = 0.0;
6776 :
6777 40633914 : while (TimeRemaining > 0.0) {
6778 :
6779 17119220 : Real64 TimeNeeded = 0.0;
6780 :
6781 17119220 : Real64 NewTankTemp = TankTemp_loc;
6782 17119220 : Real64 LossCoeff_loc = 0.0;
6783 17119220 : Real64 LossFracToZone = 0.0;
6784 :
6785 17119220 : switch (Mode_loc) {
6786 :
6787 7562716 : case TankOperatingMode::Heating: // Heater is on
6788 :
6789 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
6790 7562716 : LossCoeff_loc = this->OnCycLossCoeff;
6791 7562716 : LossFracToZone = this->OnCycLossFracToZone;
6792 7562716 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
6793 7562716 : Qneeded = -Quse - Qsource - Qloss - Qoncycheat;
6794 :
6795 7562724 : if (TankTemp_loc > SetPointTemp_loc) {
6796 : // Heater is not needed after all, possibly due to step change in scheduled SetPointTemp
6797 :
6798 8 : Qheater = 0.0;
6799 8 : Qunmet = 0.0;
6800 8 : Mode_loc = TankOperatingMode::Floating;
6801 8 : continue;
6802 :
6803 7562708 : } else if (TankTemp_loc < SetPointTemp_loc) {
6804 : // Attempt to recover to the setpoint as quickly as possible by using maximum heater capacity
6805 :
6806 : // Qneeded is calculated above
6807 : // Qneeded does not account for the extra energy needed to recover to the setpoint
6808 4033165 : Qheater = Qmaxcap;
6809 4033165 : Qunmet = max(Qneeded - Qheater, 0.0);
6810 4033165 : Qheat = Qoncycheat + Qheater + Qheatpump;
6811 :
6812 : // Calculate time needed to recover to the setpoint at maximum heater capacity
6813 4033165 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
6814 : SetPointTemp_loc,
6815 : AmbientTemp_loc,
6816 : UseInletTemp_loc,
6817 : SourceInletTemp_loc,
6818 : TankMass,
6819 : Cp,
6820 : UseMassFlowRate_loc,
6821 : SourceMassFlowRate_loc,
6822 : LossCoeff_loc,
6823 : Qheat);
6824 :
6825 4033165 : if (TimeNeeded > TimeRemaining) {
6826 : // Heater is at maximum capacity and heats for all of the remaining time
6827 : // Setpoint temperature WILL NOT be recovered
6828 :
6829 556173 : TimeNeeded = TimeRemaining;
6830 :
6831 556173 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6832 : AmbientTemp_loc,
6833 : UseInletTemp_loc,
6834 : SourceInletTemp_loc,
6835 : TankMass,
6836 : Cp,
6837 : UseMassFlowRate_loc,
6838 : SourceMassFlowRate_loc,
6839 : LossCoeff_loc,
6840 : Qheat,
6841 : TimeNeeded);
6842 :
6843 : } else { // TimeNeeded <= TimeRemaining
6844 : // Heater is at maximum capacity but will not heat for all of the remaining time (at maximum anyway)
6845 : // Setpoint temperature WILL be recovered
6846 :
6847 3476992 : NewTankTemp = SetPointTemp_loc;
6848 :
6849 3476992 : SetPointRecovered = true;
6850 :
6851 : } // TimeNeeded > TimeRemaining
6852 :
6853 : } else { // TankTemp == SetPointTemp
6854 : // Attempt to maintain the setpoint by using the needed heater capacity (modulating, if allowed)
6855 :
6856 3538695 : if (Qneeded <= 0.0) {
6857 : // Heater is not needed
6858 :
6859 9152 : Qneeded = 0.0;
6860 9152 : Qheater = 0.0;
6861 9152 : Qunmet = 0.0;
6862 9152 : Mode_loc = TankOperatingMode::Floating;
6863 9152 : continue;
6864 :
6865 3520391 : } else if (Qneeded < Qmincap) {
6866 : // Heater is required at less than the minimum capacity
6867 : // If cycling, Qmincap = Qmaxcap. Once the setpoint is reached, heater will almost always be shut off here
6868 :
6869 6934366 : if (this->ControlType == HeaterControlMode::Cycle) {
6870 : // Control will cycle on and off based on DeadBandTemp
6871 3467183 : Qheater = 0.0;
6872 3467183 : Qunmet = 0.0;
6873 3467183 : Mode_loc = TankOperatingMode::Floating;
6874 3467183 : continue;
6875 :
6876 0 : } else if (this->ControlType == HeaterControlMode::Modulate) {
6877 : // Control will cycle on and off based on DeadBandTemp until Qneeded > Qmincap again
6878 0 : Qheater = 0.0;
6879 0 : Qunmet = Qneeded;
6880 0 : Mode_loc = TankOperatingMode::Floating;
6881 0 : continue;
6882 :
6883 : // CASE (ControlTypeModulateWithOverheat) ! Not yet implemented
6884 : // Calculate time to reach steady-state temp; check for venting at MaxTemp limit
6885 : // Qheater = Qmincap
6886 :
6887 : // CASE (ControlTypeModulateWithUnderheat) ! Not yet implemented
6888 : // Heater must not come back on until Qneeded >= Qmincap
6889 : // Mode = modfloatMode
6890 : }
6891 :
6892 53208 : } else if (Qneeded <= Qmaxcap) {
6893 : // Heater can exactly meet the needed heat rate (usually by modulating) and heats for all of the remaining time
6894 : // Setpoint temperature WILL be maintained
6895 :
6896 53171 : TimeNeeded = TimeRemaining;
6897 :
6898 53171 : Qheater = Qneeded;
6899 53171 : Qunmet = 0.0;
6900 :
6901 53171 : NewTankTemp = SetPointTemp_loc;
6902 :
6903 : } else { // Qneeded > Qmaxcap
6904 : // Heater is at maximum capacity and heats for all of the remaining time
6905 : // Setpoint temperature WILL NOT be maintained
6906 :
6907 37 : TimeNeeded = TimeRemaining;
6908 :
6909 37 : Qheater = Qmaxcap;
6910 37 : Qunmet = Qneeded - Qheater;
6911 37 : Qheat = Qoncycheat + Qheater + Qheatpump;
6912 :
6913 37 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6914 : AmbientTemp_loc,
6915 : UseInletTemp_loc,
6916 : SourceInletTemp_loc,
6917 : TankMass,
6918 : Cp,
6919 : UseMassFlowRate_loc,
6920 : SourceMassFlowRate_loc,
6921 : LossCoeff_loc,
6922 : Qheat,
6923 : TimeNeeded);
6924 :
6925 : } // Qneeded > Qmaxcap
6926 :
6927 : } // TankTemp > SetPointTemp
6928 :
6929 : // Update summed values
6930 4086373 : Eneeded += Qneeded * TimeNeeded;
6931 4086373 : Eheater += Qheater * TimeNeeded;
6932 4086373 : Eunmet += Qunmet * TimeNeeded;
6933 4086373 : Eoncycfuel += Qoncycfuel * TimeNeeded;
6934 :
6935 4086373 : if (Qmaxcap > 0.0) PLR = Qheater / Qmaxcap;
6936 4086373 : PLF = this->PartLoadFactor(state, PLR);
6937 4086373 : Efuel += Qheater * TimeNeeded / (PLF * this->Efficiency);
6938 :
6939 4086373 : Runtime += TimeNeeded;
6940 4086373 : PLRsum += PLR * TimeNeeded;
6941 :
6942 4086373 : if (!this->FirstRecoveryDone) {
6943 128258 : this->FirstRecoveryFuel += Efuel + Eoffcycfuel + Eoncycfuel;
6944 128258 : if (SetPointRecovered) this->FirstRecoveryDone = true;
6945 : }
6946 4086373 : break;
6947 :
6948 9427850 : case TankOperatingMode::Floating:
6949 : case TankOperatingMode::Cooling: // Heater is off
6950 :
6951 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
6952 9427850 : LossCoeff_loc = this->OffCycLossCoeff;
6953 9427850 : LossFracToZone = this->OffCycLossFracToZone;
6954 9427850 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
6955 9427850 : Qneeded = -Quse - Qsource - Qloss - Qoffcycheat;
6956 :
6957 : // This section really needs to work differently depending on ControlType
6958 : // CYCLE will look at TankTemp, MODULATE will look at Qneeded
6959 :
6960 9428275 : if ((TankTemp_loc < DeadBandTemp) && (!this->IsChilledWaterTank)) {
6961 : // Tank temperature is already below the minimum, possibly due to step change in scheduled SetPointTemp
6962 :
6963 425 : Mode_loc = TankOperatingMode::Heating;
6964 425 : ++CycleOnCount_loc;
6965 425 : continue;
6966 :
6967 9427425 : } else if ((TankTemp_loc >= DeadBandTemp) && (!this->IsChilledWaterTank)) {
6968 :
6969 9112392 : Qheat = Qoffcycheat + Qheatpump;
6970 :
6971 : // Calculate time needed for tank temperature to fall to minimum (setpoint - deadband)
6972 9112392 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
6973 : DeadBandTemp,
6974 : AmbientTemp_loc,
6975 : UseInletTemp_loc,
6976 : SourceInletTemp_loc,
6977 : TankMass,
6978 : Cp,
6979 : UseMassFlowRate_loc,
6980 : SourceMassFlowRate_loc,
6981 : LossCoeff_loc,
6982 : Qheat);
6983 :
6984 18224784 : if (TimeNeeded <= TimeRemaining) {
6985 : // Heating will be needed in this DataGlobals::TimeStep
6986 :
6987 3708620 : NewTankTemp = DeadBandTemp;
6988 3708620 : Mode_loc = TankOperatingMode::Heating;
6989 3708620 : ++CycleOnCount_loc;
6990 :
6991 : } else { // TimeNeeded > TimeRemaining
6992 : // Heating will not be needed for all of the remaining time
6993 :
6994 5403772 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6995 : AmbientTemp_loc,
6996 : UseInletTemp_loc,
6997 : SourceInletTemp_loc,
6998 : TankMass,
6999 : Cp,
7000 : UseMassFlowRate_loc,
7001 : SourceMassFlowRate_loc,
7002 : LossCoeff_loc,
7003 : Qheat,
7004 : TimeRemaining);
7005 :
7006 5403772 : if ((NewTankTemp < MaxTemp) || (this->IsChilledWaterTank)) {
7007 : // Neither heating nor venting is needed for all of the remaining time
7008 :
7009 5384743 : TimeNeeded = TimeRemaining;
7010 :
7011 : } else { // NewTankTemp >= MaxTemp
7012 : // Venting will be needed in this DataGlobals::TimeStep
7013 :
7014 : // Calculate time needed for tank temperature to rise to the maximum
7015 19029 : TimeNeeded = CalcTimeNeeded(TankTemp_loc,
7016 : MaxTemp,
7017 : AmbientTemp_loc,
7018 : UseInletTemp_loc,
7019 : SourceInletTemp_loc,
7020 : TankMass,
7021 : Cp,
7022 : UseMassFlowRate_loc,
7023 : SourceMassFlowRate_loc,
7024 : LossCoeff_loc,
7025 : Qheat);
7026 :
7027 : // if limit NewTankTemp >= MaxTemp
7028 19029 : if (TankTemp_loc >= MaxTemp) {
7029 0 : TimeNeeded = TimeRemaining;
7030 : }
7031 19029 : NewTankTemp = MaxTemp;
7032 19029 : Mode_loc = TankOperatingMode::Venting;
7033 :
7034 : } // NewTankTemp >= MaxTemp
7035 :
7036 : } // TimeNeeded <= TimeRemaining
7037 :
7038 315033 : } else if ((TankTemp_loc > DeadBandTemp) && (this->IsChilledWaterTank)) {
7039 2480 : Mode_loc = TankOperatingMode::Cooling;
7040 2480 : Qheat = Qheatpump;
7041 :
7042 2480 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7043 : AmbientTemp_loc,
7044 : UseInletTemp_loc,
7045 : SourceInletTemp_loc,
7046 : TankMass,
7047 : Cp,
7048 : UseMassFlowRate_loc,
7049 : SourceMassFlowRate_loc,
7050 : LossCoeff_loc,
7051 : Qheat,
7052 : TimeRemaining);
7053 2480 : TimeNeeded = TimeRemaining;
7054 312553 : } else if ((TankTemp_loc <= DeadBandTemp) && (this->IsChilledWaterTank)) {
7055 :
7056 312553 : if (TankTemp_loc < SetPointTemp_loc) Mode_loc = TankOperatingMode::Floating;
7057 :
7058 312553 : Qheat = Qheatpump;
7059 :
7060 312553 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7061 : AmbientTemp_loc,
7062 : UseInletTemp_loc,
7063 : SourceInletTemp_loc,
7064 : TankMass,
7065 : Cp,
7066 : UseMassFlowRate_loc,
7067 : SourceMassFlowRate_loc,
7068 : LossCoeff_loc,
7069 : Qheat,
7070 : TimeRemaining);
7071 312553 : TimeNeeded = TimeRemaining;
7072 : } // TankTemp vs DeadBandTemp for heaters and chilled water tanks
7073 :
7074 : // Update summed values
7075 9427425 : Eneeded += Qneeded * TimeNeeded;
7076 9427425 : Eunmet += Qunmet * TimeNeeded; // Qunmet may be propagated thru from the previous iteration
7077 9427425 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7078 9427425 : break;
7079 :
7080 128654 : case TankOperatingMode::Venting: // Excess heat is vented
7081 :
7082 128654 : LossCoeff_loc = this->OffCycLossCoeff;
7083 128654 : LossFracToZone = this->OffCycLossFracToZone;
7084 128654 : Qheat = Qoffcycheat + Qheatpump;
7085 :
7086 128654 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7087 : AmbientTemp_loc,
7088 : UseInletTemp_loc,
7089 : SourceInletTemp_loc,
7090 : TankMass,
7091 : Cp,
7092 : UseMassFlowRate_loc,
7093 : SourceMassFlowRate_loc,
7094 : LossCoeff_loc,
7095 : Qheat,
7096 : TimeRemaining);
7097 :
7098 170991 : if (NewTankTemp < MaxTemp) {
7099 : // Venting is no longer needed because conditions have changed
7100 :
7101 42337 : Mode_loc = TankOperatingMode::Floating;
7102 42337 : continue;
7103 :
7104 : } else { // NewTankTemp >= MaxTemp
7105 :
7106 86317 : TimeNeeded = TimeRemaining;
7107 :
7108 : // Calculate the steady-state venting rate needed to maintain the tank at maximum temperature
7109 86317 : Real64 Qloss = LossCoeff_loc * (AmbientTemp_loc - MaxTemp);
7110 86317 : Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - MaxTemp);
7111 86317 : Qsource = SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc - MaxTemp);
7112 86317 : Qvent = -Quse - Qsource - Qloss - Qoffcycheat;
7113 :
7114 86317 : NewTankTemp = MaxTemp;
7115 :
7116 : } // NewTankTemp < MaxTemp
7117 :
7118 : // Update summed values
7119 86317 : Event += Qvent * TimeNeeded;
7120 86317 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7121 86317 : break;
7122 0 : default:
7123 0 : assert(false); // should never get here
7124 : }
7125 :
7126 13600115 : Real64 deltaTsum = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTempIntegral(TankTemp_loc,
7127 : NewTankTemp,
7128 : AmbientTemp_loc,
7129 : UseInletTemp_loc,
7130 : SourceInletTemp_loc,
7131 : TankMass,
7132 : Cp,
7133 : UseMassFlowRate_loc,
7134 : SourceMassFlowRate_loc,
7135 : LossCoeff_loc,
7136 : Qheat,
7137 13600115 : TimeNeeded);
7138 :
7139 : // Update summed values
7140 13600115 : Tsum += deltaTsum;
7141 13600115 : Eloss += LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7142 13600115 : Elosszone += LossFracToZone * LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7143 13600115 : Euse += UseMassFlowRate_loc * Cp * (UseInletTemp_loc * TimeNeeded - deltaTsum);
7144 13600115 : if (this->HeatPumpNum > 0) {
7145 1293386 : Esource += Qheatpump * TimeNeeded;
7146 : } else {
7147 12306729 : Esource += SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc * TimeNeeded - deltaTsum);
7148 : }
7149 :
7150 13600115 : TankTemp_loc = NewTankTemp; // Update tank temperature
7151 :
7152 13600115 : TimeRemaining -= TimeNeeded;
7153 :
7154 13600115 : if (CycleOnCount_loc > MaxCycles) {
7155 :
7156 0 : if (!state.dataGlobal->WarmupFlag) {
7157 0 : if (this->MaxCycleErrorIndex == 0) {
7158 0 : ShowWarningError(state, "WaterHeater:Mixed = " + this->Name + ": Heater is cycling on and off more than once per second.");
7159 0 : ShowContinueError(state, "Try increasing Deadband Temperature Difference or Tank Volume");
7160 0 : ShowContinueErrorTimeStamp(state, "");
7161 : }
7162 0 : ShowRecurringWarningErrorAtEnd(state,
7163 0 : "WaterHeater:Mixed = " + this->Name + " Heater is cycling on and off more than once per second:",
7164 : this->MaxCycleErrorIndex);
7165 : }
7166 :
7167 0 : break;
7168 :
7169 : } // CycleOnCount > MaxCycles
7170 :
7171 : } // TimeRemaining > 0.0
7172 :
7173 : // Calculate average values over the DataGlobals::TimeStep based on summed values, Q > 0 is a gain to the tank, Q < 0 is a loss to the tank
7174 6395474 : Real64 TankTempAvg_loc = Tsum / SecInTimeStep;
7175 6395474 : Qloss = Eloss / SecInTimeStep;
7176 6395474 : Real64 Qlosszone = Elosszone / SecInTimeStep;
7177 6395474 : Quse = Euse / SecInTimeStep;
7178 6395474 : Qsource = Esource / SecInTimeStep;
7179 6395474 : Qheater = Eheater / SecInTimeStep;
7180 6395474 : Qoffcycfuel = Eoffcycfuel / SecInTimeStep;
7181 6395474 : Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
7182 6395474 : Qoncycfuel = Eoncycfuel / SecInTimeStep;
7183 6395474 : Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
7184 6395474 : Qvent = Event / SecInTimeStep;
7185 6395474 : Qneeded = Eneeded / SecInTimeStep;
7186 6395474 : Qunmet = Eunmet / SecInTimeStep;
7187 6395474 : Real64 RTF = Runtime / SecInTimeStep;
7188 6395474 : PLR = PLRsum / SecInTimeStep;
7189 :
7190 6395474 : if (this->ControlType == HeaterControlMode::Cycle) {
7191 : // Recalculate Part Load Factor and fuel energy based on Runtime Fraction, instead of Part Load Ratio
7192 6340886 : PLF = this->PartLoadFactor(state, RTF);
7193 6340886 : Efuel = Eheater / (PLF * this->Efficiency);
7194 : }
7195 :
7196 6395474 : Qfuel = Efuel / SecInTimeStep;
7197 :
7198 6395474 : this->Mode = Mode_loc; // Operating mode for carry-over to next DataGlobals::TimeStep
7199 6395474 : this->TankTemp = TankTemp_loc; // Final tank temperature for carry-over to next DataGlobals::TimeStep
7200 6395474 : this->TankTempAvg = TankTempAvg_loc; // Average tank temperature over the DataGlobals::TimeStep for reporting
7201 :
7202 6395474 : if (!state.dataGlobal->WarmupFlag) {
7203 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
7204 825294 : if (this->TankTemp < 2) {
7205 0 : if (this->FreezingErrorIndex == 0) {
7206 0 : ShowWarningError(state,
7207 0 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
7208 : RoutineName,
7209 : this->Type,
7210 : this->Name,
7211 0 : this->TankTemp));
7212 0 : ShowContinueErrorTimeStamp(state, "");
7213 : }
7214 0 : ShowRecurringWarningErrorAtEnd(state,
7215 0 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
7216 : this->FreezingErrorIndex,
7217 : this->TankTemp, // Report Max
7218 : this->TankTemp, // Report Min
7219 : _, // Don't report Sum
7220 : "{C}", // Max Unit
7221 : "{C}"); // Min Unit
7222 : }
7223 : }
7224 6395474 : this->UseOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7225 6395474 : this->SourceOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7226 6395474 : if (this->HeatPumpNum > 0) {
7227 1122477 : this->SourceInletTemp =
7228 1122477 : TankTempAvg_loc + HPWHCondenserDeltaT; // Update the source inlet temperature to the average over the DataGlobals::TimeStep
7229 : }
7230 :
7231 6395474 : this->LossRate = Qloss;
7232 6395474 : this->UseRate = Quse;
7233 6395474 : this->SourceRate = Qsource;
7234 6395474 : this->OffCycParaRateToTank = Qoffcycheat;
7235 6395474 : this->OnCycParaRateToTank = Qoncycheat;
7236 6395474 : this->TotalDemandRate = -Quse - Qsource - Qloss - Qoffcycheat - Qoncycheat;
7237 6395474 : this->HeaterRate = Qheater;
7238 6395474 : this->UnmetRate = Qunmet;
7239 6395474 : this->VentRate = Qvent;
7240 6395474 : this->NetHeatTransferRate = Quse + Qsource + Qloss + Qoffcycheat + Qoncycheat + Qheater + Qvent;
7241 :
7242 6395474 : this->CycleOnCount = CycleOnCount_loc;
7243 6395474 : this->RuntimeFraction = RTF;
7244 6395474 : this->PartLoadRatio = PLR;
7245 :
7246 6395474 : this->FuelRate = Qfuel;
7247 6395474 : this->OffCycParaFuelRate = Qoffcycfuel;
7248 6395474 : this->OnCycParaFuelRate = Qoncycfuel;
7249 :
7250 : // Add water heater skin losses and venting losses to ambient zone, if specified
7251 6395474 : if (this->AmbientTempZone > 0) this->AmbientZoneGain = -Qlosszone - Qvent;
7252 6395474 : }
7253 :
7254 6395474 : void WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
7255 : Real64 HPWHCondenserDeltaT, // input, The temperature difference (C) across the heat pump, zero if
7256 : // there is no heat pump or if the heat pump is off
7257 : Real64 SourceInletTemp, // input, Source inlet temperature (C)
7258 : Real64 Cp, // Specific heat of fluid (J/kg deltaC)
7259 : Real64 SetPointTemp, // input, Mixed tank set point temperature
7260 : Real64 &SourceMassFlowRate, // source mass flow rate (kg/s)
7261 : Real64 &Qheatpump, // heat transfer rate from heat pump
7262 : Real64 &Qsource // steady state heat transfer rate from a constant temperature source side flow
7263 : )
7264 : {
7265 : // Function Information:
7266 : // Author: Noel Merket
7267 : // Date Written: January 2015
7268 : // Modified: na
7269 : // Re-engineered: na
7270 :
7271 : // Purpose of this function:
7272 : // Determines if the source side heat transfer is coming from a heat pump.
7273 : // If so it treats the source side heat transfer as a constant heat source
7274 : // If it is not coming from a heat pump it treats the source side heat transfer
7275 : // as a constant temperature.
7276 :
7277 : // Determine if the source side heating is coming from a heat pump.
7278 6395474 : Qheatpump = SourceMassFlowRate * Cp * HPWHCondenserDeltaT;
7279 6395474 : if (Qheatpump > 0.0) {
7280 630608 : SourceMassFlowRate = 0.0; // Handle this heating as a constant heat source
7281 630608 : Qsource = Qheatpump;
7282 : } else {
7283 5764866 : Qsource = SourceMassFlowRate * Cp * (SourceInletTemp - SetPointTemp);
7284 : }
7285 6395474 : }
7286 :
7287 13164586 : Real64 WaterThermalTankData::CalcTimeNeeded(Real64 const Ti, // Initial tank temperature (C)
7288 : Real64 const Tf, // Final tank temperature (C)
7289 : Real64 const Ta, // Ambient environment temperature (C)
7290 : Real64 const T1, // Temperature of flow 1 (C)
7291 : Real64 const T2, // Temperature of flow 2 (C)
7292 : Real64 const m, // Mass of tank fluid (kg)
7293 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7294 : Real64 const m1, // Mass flow rate 1 (kg/s)
7295 : Real64 const m2, // Mass flow rate 2 (kg/s)
7296 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7297 : Real64 const Q // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7298 : )
7299 : {
7300 :
7301 : // SUBROUTINE INFORMATION:
7302 : // AUTHOR Peter Graham Ellis
7303 : // DATE WRITTEN February 2005
7304 : // MODIFIED na
7305 : // RE-ENGINEERED na
7306 :
7307 : // PURPOSE OF THIS SUBROUTINE:
7308 : // Calculates the time needed for the tank temperature to change from Ti to Tf given heat loss,
7309 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7310 :
7311 : // METHODOLOGY EMPLOYED:
7312 : // Equations are derived by solving the differential equation governing the tank energy balance.
7313 : // Special cases which cause the natural logarithm to blow up are trapped and interpreted as
7314 : // requiring an infinite amount of time because Tf can never be reached under the given conditions.
7315 :
7316 : // Return value
7317 : Real64 CalcTimeNeeded;
7318 :
7319 : // SUBROUTINE PARAMETER DEFINITIONS:
7320 13164586 : Real64 constexpr Infinity(99999999.9); // A time interval much larger than any single DataGlobals::TimeStep (s)
7321 :
7322 : Real64 t; // Time elapsed from Ti to Tf (s)
7323 :
7324 13164586 : if (Tf == Ti) {
7325 : // Already at Tf; no time is needed
7326 0 : t = 0.0;
7327 :
7328 : } else {
7329 : Real64 a; // Intermediate variable
7330 : Real64 b; // Intermediate variable
7331 : Real64 Tm; // Mixed temperature after an infinite amount of time has passed (C)
7332 : Real64 quotient; // Intermediate variable
7333 :
7334 13164586 : if (UA / Cp + m1 + m2 == 0.0) {
7335 :
7336 23543 : if (Q == 0.0) {
7337 : // With no mass flow and no heat flow and Tf<>Ti, then Tf can never be reached
7338 22578 : t = Infinity;
7339 :
7340 : } else {
7341 965 : a = Q / (m * Cp);
7342 :
7343 965 : t = (Tf - Ti) / a;
7344 : }
7345 :
7346 : } else {
7347 13141043 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7348 13141043 : b = -(UA / Cp + m1 + m2) / m;
7349 :
7350 : // Calculate the mixed temperature Tm of the tank after an infinite amount of time has passed
7351 13141043 : Tm = -a / b;
7352 :
7353 13141043 : if (Tm == Ti) {
7354 : // Mixed temperature is the same as Ti; if Tf<>Ti, then Tf can never be reached
7355 1971 : t = Infinity;
7356 :
7357 13139072 : } else if (Tm == Tf) {
7358 : // Tf only approaches Tm; it can never actually get there in finite time (also avoids divide by zero error)
7359 0 : t = Infinity;
7360 :
7361 : } else {
7362 13139072 : quotient = (Tf - Tm) / (Ti - Tm);
7363 :
7364 13139072 : if (quotient <= 0.0) { // Autodesk:Num Changed < to <= to elim poss floating point error in LOG call
7365 : // Tm is in between Ti and Tf; Tf can never be reached
7366 554895 : t = Infinity;
7367 :
7368 : } else {
7369 12584177 : t = std::log(quotient) / b;
7370 : }
7371 : }
7372 : }
7373 :
7374 13164586 : if (t < 0.0) t = Infinity; // If negative time, Tf can never be reached in the future
7375 : }
7376 :
7377 13164586 : CalcTimeNeeded = t;
7378 :
7379 13164586 : return CalcTimeNeeded;
7380 : }
7381 :
7382 6420614 : Real64 WaterThermalTankData::CalcTankTemp(Real64 const Ti, // Initial tank temperature (C)
7383 : Real64 const Ta, // Ambient environment temperature (C)
7384 : Real64 const T1, // Temperature of flow 1 (C)
7385 : Real64 const T2, // Temperature of flow 2 (C)
7386 : Real64 const m, // Mass of tank fluid (kg)
7387 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7388 : Real64 const m1, // Mass flow rate 1 (kg/s)
7389 : Real64 const m2, // Mass flow rate 2 (kg/s)
7390 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7391 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7392 : Real64 const t // Time elapsed from Ti to Tf (s)
7393 : )
7394 : {
7395 :
7396 : // SUBROUTINE INFORMATION:
7397 : // AUTHOR Peter Graham Ellis
7398 : // DATE WRITTEN February 2005
7399 : // MODIFIED na
7400 : // RE-ENGINEERED na
7401 :
7402 : // PURPOSE OF THIS SUBROUTINE:
7403 : // Calculates the final tank temperature Tf after time t has elapsed given heat loss,
7404 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7405 :
7406 : // METHODOLOGY EMPLOYED:
7407 : // Equations are derived by solving the differential equation governing the tank energy balance.
7408 :
7409 : // Return value
7410 : Real64 CalcTankTemp;
7411 :
7412 : // Locals
7413 : // SUBROUTINE ARGUMENT DEFINITIONS:
7414 :
7415 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7416 : Real64 a; // Intermediate variable
7417 : Real64 b; // Intermediate variable
7418 : Real64 Tf; // Final tank temperature (C)
7419 :
7420 6420614 : if (UA / Cp + m1 + m2 == 0.0) {
7421 23471 : a = Q / (m * Cp);
7422 :
7423 23471 : Tf = a * t + Ti;
7424 :
7425 : } else {
7426 6397143 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7427 6397143 : b = -(UA / Cp + m1 + m2) / m;
7428 :
7429 6397143 : Tf = (a / b + Ti) * std::exp(b * t) - a / b;
7430 : }
7431 :
7432 6420614 : CalcTankTemp = Tf;
7433 :
7434 6420614 : return CalcTankTemp;
7435 : }
7436 :
7437 13617060 : Real64 WaterThermalTankData::CalcTempIntegral(Real64 const Ti, // Initial tank temperature (C)
7438 : Real64 const Tf, // Final tank temperature (C)
7439 : Real64 const Ta, // Ambient environment temperature (C)
7440 : Real64 const T1, // Temperature of flow 1 (C)
7441 : Real64 const T2, // Temperature of flow 2 (C)
7442 : Real64 const m, // Mass of tank fluid (kg)
7443 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7444 : Real64 const m1, // Mass flow rate 1 (kg/s)
7445 : Real64 const m2, // Mass flow rate 2 (kg/s)
7446 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7447 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7448 : Real64 const t // Time elapsed from Ti to Tf (s)
7449 : )
7450 : {
7451 :
7452 : // SUBROUTINE INFORMATION:
7453 : // AUTHOR Peter Graham Ellis
7454 : // DATE WRITTEN February 2005
7455 : // MODIFIED na
7456 : // RE-ENGINEERED na
7457 :
7458 : // PURPOSE OF THIS SUBROUTINE:
7459 : // Calculates the integral of the tank temperature from Ti to Tf. The integral is added to a sum which is
7460 : // later divided by the elapsed time to yield the average tank temperature over the DataGlobals::TimeStep.
7461 :
7462 : // METHODOLOGY EMPLOYED:
7463 : // Equations are the mathematical integrals of the governing differential equations.
7464 :
7465 : // Return value
7466 : Real64 CalcTempIntegral;
7467 :
7468 : // Locals
7469 : // SUBROUTINE ARGUMENT DEFINITIONS:
7470 :
7471 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7472 : Real64 a; // Intermediate variable
7473 : Real64 b; // Intermediate variable
7474 : Real64 dTsum; // Integral of tank temperature (C s)
7475 :
7476 13617060 : if (t == 0.0) {
7477 0 : dTsum = 0.0;
7478 :
7479 13617060 : } else if (Tf == Ti) { // Steady-state conditions
7480 179295 : dTsum = Tf * t;
7481 :
7482 13437765 : } else if (UA / Cp + m1 + m2 == 0.0) {
7483 965 : a = Q / (m * Cp);
7484 :
7485 : // Integral of T(t) = a * t + Ti, evaluated from 0 to t
7486 965 : dTsum = 0.5 * a * t * t + Ti * t;
7487 :
7488 : } else {
7489 13436800 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7490 13436800 : b = -(UA / Cp + m1 + m2) / m;
7491 :
7492 : // Integral of T(t) = (a / b + Ti) * EXP(b * t) - a / b, evaluated from 0 to t
7493 13436800 : dTsum = (a / b + Ti) * (std::exp(b * t) - 1.0) / b - a * t / b;
7494 : }
7495 :
7496 13617060 : CalcTempIntegral = dTsum;
7497 :
7498 13617060 : return CalcTempIntegral;
7499 : }
7500 :
7501 10427259 : Real64 WaterThermalTankData::PartLoadFactor(EnergyPlusData &state, Real64 const PartLoadRatio_loc)
7502 : {
7503 :
7504 : // SUBROUTINE INFORMATION:
7505 : // AUTHOR Peter Graham Ellis
7506 : // DATE WRITTEN January 2005
7507 : // MODIFIED na
7508 : // RE-ENGINEERED na
7509 :
7510 : // PURPOSE OF THIS SUBROUTINE:
7511 : // Calculates the Part Load Factor (PLF) based on a curve correlated to Part Load Ratio, if Heater Control Type
7512 : // is MODULATE, or correlated to Runtime Fraction, if Heater Control Type is CYCLE.
7513 :
7514 10427259 : if (this->PLFCurve > 0) {
7515 1260 : return max(Curve::CurveValue(state, this->PLFCurve, PartLoadRatio_loc), 0.1);
7516 : } else {
7517 10425999 : return 1.0;
7518 : }
7519 : }
7520 :
7521 1542319 : void WaterThermalTankData::CalcWaterThermalTankStratified(EnergyPlusData &state)
7522 : {
7523 : // SUBROUTINE INFORMATION:
7524 : // AUTHOR Noel Merket, originally by Peter Graham Ellis
7525 : // DATE WRITTEN January 2007
7526 : // MODIFIED Nov 2011, BAN; modified the use and source outlet temperature calculation
7527 : // RE-ENGINEERED Noel Merket, November 2018
7528 :
7529 : // PURPOSE OF THIS SUBROUTINE:
7530 : // Simulates a stratified, multi-node water heater tank with up to two heating elements.
7531 :
7532 : // METHODOLOGY EMPLOYED:
7533 : // This model uses a numerical calculation based on an analytical solution of the ODE dT/dt = a*T + b.
7534 : // A heat balance is calculated for each node.
7535 : // Temperatures and energies change dynamically over the system time step.
7536 : // Final node temperatures are reported as final instantaneous values as well as averages over the
7537 : // time step. Heat transfer rates are averages over the time step.
7538 :
7539 : static constexpr std::string_view RoutineName("CalcWaterThermalTankStratified");
7540 1542319 : constexpr Real64 TemperatureConvergenceCriteria = 0.0001;
7541 1542319 : const Real64 SubTimestepMax = 60.0 * 10.0; // seconds
7542 1542319 : constexpr Real64 SubTimestepMin = 10.0; // seconds
7543 : Real64 dt;
7544 :
7545 : // Tank object reference
7546 1542319 : const Real64 &nTankNodes = this->Nodes;
7547 :
7548 : // Fraction of the current hour that has elapsed (h)
7549 : const Real64 TimeElapsed_loc =
7550 1542319 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
7551 :
7552 : // Seconds in one DataGlobals::TimeStep (s)
7553 1542319 : const Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
7554 :
7555 : // Advance tank simulation to the next system DataGlobals::TimeStep, if applicable
7556 1542319 : if (this->TimeElapsed != TimeElapsed_loc) {
7557 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
7558 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
7559 424997 : for (auto &e : this->Node)
7560 371968 : e.SavedTemp = e.Temp;
7561 :
7562 53029 : this->SavedHeaterOn1 = this->HeaterOn1;
7563 53029 : this->SavedHeaterOn2 = this->HeaterOn2;
7564 :
7565 : // Save outlet temperatures for demand-side flow control
7566 53029 : this->SavedUseOutletTemp = this->UseOutletTemp;
7567 53029 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
7568 :
7569 53029 : this->TimeElapsed = TimeElapsed_loc;
7570 : }
7571 :
7572 : // Reset node temperatures to what they were at the beginning of the system DataGlobals::TimeStep.
7573 10107085 : for (auto &e : this->Node)
7574 8564766 : e.Temp = e.SavedTemp;
7575 :
7576 1542319 : this->HeaterOn1 = this->SavedHeaterOn1;
7577 1542319 : this->HeaterOn2 = this->SavedHeaterOn2;
7578 :
7579 : // Condenser configuration of heat pump water heater
7580 : const DataPlant::PlantEquipmentType HPWHCondenserConfig =
7581 1542319 : this->HeatPumpNum > 0 ? state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType : DataPlant::PlantEquipmentType::Invalid;
7582 :
7583 : // Heat rate from the heat pump (W)
7584 4123851 : const Real64 Qheatpump = [&, this] { // BLB
7585 1542319 : if (this->HeatPumpNum == 0) return 0.0;
7586 3872298 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
7587 : Real64 CoilTotalHeatingEnergyRate;
7588 1290766 : if (HPWH.NumofSpeed > 0) {
7589 : // VSHPWH
7590 70618 : VariableSpeedCoils::VariableSpeedCoilData const &Coil = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum);
7591 70618 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7592 : } else {
7593 : // Single speed HPWH
7594 1220148 : DXCoils::DXCoilData const &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
7595 1220148 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7596 : }
7597 1290766 : return CoilTotalHeatingEnergyRate * this->SourceEffectiveness;
7598 1542319 : }();
7599 :
7600 : // Minimum tank temperatures
7601 1542319 : const Real64 MinTemp1 = this->SetPointTemp - this->DeadBandDeltaTemp;
7602 1542319 : const Real64 MinTemp2 = this->SetPointTemp2 - this->DeadBandDeltaTemp2;
7603 :
7604 : // Specific Heat of water (J/kg K)
7605 1542319 : const Real64 Cp = [&] {
7606 6096703 : if (this->UseSidePlantLoc.loopNum > 0) {
7607 5951557 : return FluidProperties::GetSpecificHeatGlycol(state,
7608 2939492 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
7609 : this->TankTemp,
7610 2939492 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
7611 1469746 : RoutineName);
7612 : } else {
7613 217719 : return FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, this->TankTemp, this->waterIndex, RoutineName);
7614 : }
7615 1542319 : }();
7616 :
7617 1542319 : Real64 Eloss = 0.0; // Energy change due to ambient losses over the DataGlobals::TimeStep (J)
7618 1542319 : Real64 Euse = 0.0; // Energy change due to use side mass flow over the DataGlobals::TimeStep (J)
7619 1542319 : Real64 Esource = 0.0; // Energy change due to source side mass flow over the DataGlobals::TimeStep (J)
7620 1542319 : Real64 Eheater1 = 0.0; // Energy change due to heater 1 over the DataGlobals::TimeStep (J)
7621 1542319 : Real64 Eheater2 = 0.0; // Energy change due to heater 2 over the DataGlobals::TimeStep (J)
7622 1542319 : Real64 Eunmet = 0.0; // Energy change unmet over the DataGlobals::TimeStep (J)
7623 1542319 : Real64 Event = 0.0; // Energy change due to venting over the DataGlobals::TimeStep (J)
7624 1542319 : int CycleOnCount1_loc = 0; // Number of times heater 1 cycles on in the current time step
7625 1542319 : int CycleOnCount2_loc = 0; // Number of times heater 2 cycles on in the current time step
7626 1542319 : Real64 Runtime = 0.0; // Time that either heater is running (s)
7627 1542319 : Real64 Runtime1 = 0.0; // Time that heater 1 is running (s)
7628 1542319 : Real64 Runtime2 = 0.0; // Time that heater 2 is running (s)
7629 1542319 : bool SetPointRecovered = false; // Flag to indicate when set point is recovered for the first time
7630 : // Added three variables for desuperheater sourceinlet temperature update
7631 : Real64 MdotDesuperheaterWater; // mass flow rate of desuperheater source side water, kg/s
7632 1542319 : Real64 DesuperheaterPLR = 0.0; // Desuperheater part load ratio
7633 1542319 : Real64 DesuperheaterHeaterRate = 0.0; // Desuperheater heater rate (W)
7634 1542319 : Real64 SourceInletTempSum = 0.0; // Sum the source inlet temperature in sub time step to calculate average tempearature
7635 : Real64 Qheater1; // Heating rate of burner or electric heating element 1 (W)
7636 : Real64 Qheater2; // Heating rate of burner or electric heating element 2 (W)
7637 :
7638 1542319 : if (this->InletMode == InletPositionMode::Fixed) CalcNodeMassFlows(InletPositionMode::Fixed);
7639 :
7640 : // Time remaining in the current DataGlobals::TimeStep (s)
7641 1542319 : Real64 TimeRemaining = SecInTimeStep;
7642 :
7643 : // Diff Eq. Coefficients for each node
7644 3084638 : std::vector<Real64> A;
7645 1542319 : A.resize(nTankNodes);
7646 3084638 : std::vector<Real64> B;
7647 1542319 : B.resize(nTankNodes);
7648 :
7649 : // Temperature at the end of the internal DataGlobals::TimeStep
7650 3084638 : std::vector<Real64> Tfinal;
7651 1542319 : Tfinal.resize(nTankNodes);
7652 :
7653 : // Average temperature of each node over the internal DataGlobals::TimeStep
7654 3084638 : std::vector<Real64> Tavg;
7655 1542319 : Tavg.resize(nTankNodes);
7656 :
7657 1542319 : int SubTimestepCount = 0;
7658 :
7659 15771569 : while (TimeRemaining > 0.0) {
7660 :
7661 7114625 : ++SubTimestepCount;
7662 :
7663 7114625 : bool PrevHeaterOn1 = this->HeaterOn1;
7664 7114625 : bool PrevHeaterOn2 = this->HeaterOn2;
7665 :
7666 7114625 : if (this->InletMode == InletPositionMode::Seeking) CalcNodeMassFlows(InletPositionMode::Seeking);
7667 :
7668 : // Heater control logic
7669 7114625 : if (this->IsChilledWaterTank) {
7670 : // Chilled Water Tank, no heating
7671 346050 : Qheater1 = 0.0;
7672 346050 : Qheater2 = 0.0;
7673 : } else {
7674 : // Control the first heater element (master)
7675 6768575 : if (this->MaxCapacity > 0.0) {
7676 2845198 : const Real64 &NodeTemp = this->Node(this->HeaterNode1).Temp;
7677 :
7678 2845198 : if (this->HeaterOn1) {
7679 284631 : if (NodeTemp >= this->SetPointTemp) {
7680 43767 : this->HeaterOn1 = false;
7681 43767 : SetPointRecovered = true;
7682 : }
7683 : } else { // Heater is off
7684 2560567 : if (NodeTemp < MinTemp1) {
7685 85038 : this->HeaterOn1 = true;
7686 85038 : ++CycleOnCount1_loc;
7687 : }
7688 : }
7689 : }
7690 :
7691 6768575 : if (this->HeaterOn1) {
7692 325939 : Qheater1 = this->MaxCapacity;
7693 : } else {
7694 6442636 : Qheater1 = 0.0;
7695 : }
7696 :
7697 : // Control the second heater element (slave)
7698 6768575 : if (this->MaxCapacity2 > 0.0) {
7699 756839 : if ((this->StratifiedControlMode == PriorityControlMode::MasterSlave) && this->HeaterOn1) {
7700 30941 : this->HeaterOn2 = false;
7701 :
7702 : } else {
7703 725898 : const Real64 &NodeTemp = this->Node(this->HeaterNode2).Temp;
7704 :
7705 725898 : if (this->HeaterOn2) {
7706 422923 : if (NodeTemp >= this->SetPointTemp2) {
7707 6982 : this->HeaterOn2 = false;
7708 6982 : SetPointRecovered = true;
7709 : }
7710 : } else { // Heater is off
7711 302975 : if (NodeTemp < MinTemp2) {
7712 58350 : this->HeaterOn2 = true;
7713 58350 : ++CycleOnCount2_loc;
7714 : }
7715 : }
7716 : }
7717 : }
7718 :
7719 6768575 : if (this->HeaterOn2) {
7720 474291 : Qheater2 = this->MaxCapacity2;
7721 : } else {
7722 6294284 : Qheater2 = 0.0;
7723 : }
7724 : }
7725 :
7726 7114625 : if (SubTimestepCount == 1) {
7727 1542319 : dt = SubTimestepMin;
7728 : } else {
7729 :
7730 : // Set the maximum tank temperature change allowed
7731 5572306 : Real64 dT_max = std::numeric_limits<Real64>::max();
7732 5572306 : if (this->HeaterOn1) {
7733 316073 : if (this->Node(this->HeaterNode1).Temp < this->SetPointTemp) {
7734 : // Node temperature is less than setpoint and heater is on
7735 316057 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7736 : } else {
7737 : // Node temperature is greater than or equal to setpoint and heater is on
7738 : // Heater will turn off next time around, calculate assuming that
7739 16 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7740 : }
7741 : } else { // Heater off
7742 5256233 : if (this->Node(this->HeaterNode1).Temp >= MinTemp1) {
7743 : // Node temperature is greater than or equal to cut in temperature and heater is off
7744 3481805 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7745 : } else {
7746 : // Heater will turn on next time around, calculate to setpoint
7747 1774428 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7748 : }
7749 : }
7750 5572306 : if (this->HeaterOn2) {
7751 454788 : if (this->Node(this->HeaterNode2).Temp < this->SetPointTemp2) {
7752 : // Node temperature is less than setpoint and heater is on
7753 454788 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7754 : } else {
7755 : // Node temperature is greater than or equal to setpoint and heater is on
7756 : // Heater will turn off next time around, calculate assuming that
7757 0 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7758 : }
7759 : } else { // Heater off
7760 5117518 : if (this->Node(this->HeaterNode2).Temp >= MinTemp2) {
7761 : // Node temperature is greater than or equal to cut in temperature and heater is off
7762 2514282 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7763 : } else {
7764 : // Heater will turn on next time around, calculate to setpoint
7765 2603236 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7766 : }
7767 : }
7768 :
7769 : // Make adjustments to A and B to account for heaters being on or off now
7770 5572306 : if (this->HeaterOn1 && !PrevHeaterOn1) {
7771 : // If heater 1 is on now and wasn't before add the heat rate to the B term
7772 83716 : B[this->HeaterNode1 - 1] += Qheater1 / (this->Node(this->HeaterNode1).Mass * Cp);
7773 5488590 : } else if (!this->HeaterOn1 && PrevHeaterOn1) {
7774 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7775 42010 : B[this->HeaterNode1 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode1).Mass * Cp);
7776 : }
7777 5572306 : if (this->HeaterOn2 && !PrevHeaterOn2) {
7778 : // If heater 2 is on now and wasn't before add the heat rate to the B term
7779 57698 : B[this->HeaterNode2 - 1] += Qheater2 / (this->Node(this->HeaterNode2).Mass * Cp);
7780 5514608 : } else if (!this->HeaterOn2 && PrevHeaterOn2) {
7781 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7782 12221 : B[this->HeaterNode2 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode2).Mass * Cp);
7783 : }
7784 :
7785 5572306 : if ((this->HeaterOn1 || this->HeaterOn2) && !(PrevHeaterOn1 || PrevHeaterOn2)) {
7786 : // Remove off cycle loads
7787 : // Apply on cycle loads
7788 1267079 : for (int i = 0; i < nTankNodes; i++) {
7789 1131604 : auto &node(this->Node[i]);
7790 1131604 : Real64 NodeCapacitance = node.Mass * Cp;
7791 1131604 : A[i] += (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7792 1131604 : B[i] += (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7793 : NodeCapacitance;
7794 135475 : }
7795 5436831 : } else if (!(this->HeaterOn1 || this->HeaterOn2) && (PrevHeaterOn1 || PrevHeaterOn2)) {
7796 : // Remove on cycle loads
7797 : // Apply off cycle loads
7798 505302 : for (int i = 0; i < nTankNodes; i++) {
7799 457010 : auto &node(this->Node[i]);
7800 457010 : Real64 NodeCapacitance = node.Mass * Cp;
7801 457010 : A[i] -= (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7802 457010 : B[i] -= (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7803 : NodeCapacitance;
7804 : }
7805 : }
7806 :
7807 : // Set the sub DataGlobals::TimeStep (dt)
7808 5572306 : dt = TimeRemaining;
7809 33065752 : for (int i = 0; i < nTankNodes; ++i) {
7810 27493446 : const Real64 Denominator = fabs(A[i] * Tavg[i] + B[i]);
7811 27493446 : if (Denominator != 0.0) dt = min(dt, dT_max / Denominator);
7812 : }
7813 5572306 : dt = max(min(SubTimestepMin, TimeRemaining), dt);
7814 5572306 : dt = min(SubTimestepMax, dt);
7815 : }
7816 :
7817 : // Make initial guess that average and final temperatures over the DataGlobals::TimeStep are equal to the starting temperatures
7818 43172837 : for (int i = 0; i < nTankNodes; i++) {
7819 36058212 : const auto &NodeTemp = this->Node[i].Temp;
7820 36058212 : Tfinal[i] = NodeTemp;
7821 36058212 : Tavg[i] = NodeTemp;
7822 : }
7823 :
7824 26552541 : for (int ConvergenceCounter = 1; ConvergenceCounter <= 10; ConvergenceCounter++) {
7825 :
7826 26208318 : std::fill(A.begin(), A.end(), 0.0);
7827 26208318 : std::fill(B.begin(), B.end(), 0.0);
7828 :
7829 : // Heater Coefficients
7830 26208318 : B[this->HeaterNode1 - 1] += Qheater1;
7831 26208318 : B[this->HeaterNode2 - 1] += Qheater2;
7832 :
7833 212378278 : for (int i = 0; i < nTankNodes; i++) {
7834 186169960 : const int NodeNum = i + 1;
7835 186169960 : const auto &tank_node(this->Node(NodeNum));
7836 :
7837 : // Parasitic Loads and Losses to Ambient
7838 186169960 : if (this->HeaterOn1 || this->HeaterOn2) {
7839 : // Parasitic Loads
7840 31722920 : B[i] += tank_node.OnCycParaLoad;
7841 : // Losses to Ambient
7842 31722920 : A[i] += -tank_node.OnCycLossCoeff;
7843 31722920 : B[i] += tank_node.OnCycLossCoeff * this->AmbientTemp;
7844 : } else {
7845 : // Parasitic Loads
7846 154447040 : B[i] += tank_node.OffCycParaLoad;
7847 : // Losses to Ambient
7848 154447040 : A[i] += -tank_node.OffCycLossCoeff;
7849 154447040 : B[i] += tank_node.OffCycLossCoeff * this->AmbientTemp;
7850 : }
7851 :
7852 : // Conduction to adjacent nodes
7853 186169960 : A[i] += -(tank_node.CondCoeffDn + tank_node.CondCoeffUp);
7854 186169960 : if (NodeNum > 1) B[i] += tank_node.CondCoeffUp * Tavg[i - 1];
7855 186169960 : if (NodeNum < nTankNodes) B[i] += tank_node.CondCoeffDn * Tavg[i + 1];
7856 :
7857 : // Use side plant connection
7858 186169960 : const Real64 use_e_mdot_cp = tank_node.UseMassFlowRate * Cp;
7859 186169960 : A[i] += -use_e_mdot_cp;
7860 186169960 : B[i] += use_e_mdot_cp * this->UseInletTemp;
7861 :
7862 : // Source side heat transfer rate
7863 186169960 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
7864 : // Pumped Condenser Heat Pump Water Heater
7865 125704068 : if (tank_node.SourceMassFlowRate > 0.0) B[i] += Qheatpump;
7866 : } else {
7867 : // Source side plant connection (constant temperature)
7868 60465892 : const Real64 src_e_mdot_cp = tank_node.SourceMassFlowRate * Cp;
7869 60465892 : A[i] += -src_e_mdot_cp;
7870 60465892 : B[i] += src_e_mdot_cp * this->SourceInletTemp;
7871 : }
7872 :
7873 : // Wrapped condenser heat pump water heater
7874 186169960 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped)) {
7875 31555538 : B[i] += Qheatpump * tank_node.HPWHWrappedCondenserHeatingFrac;
7876 : }
7877 :
7878 : // Internodal flow
7879 186169960 : A[i] += -(tank_node.MassFlowFromUpper + tank_node.MassFlowFromLower) * Cp;
7880 186169960 : if (NodeNum > 1) B[i] += tank_node.MassFlowFromUpper * Cp * Tavg[i - 1];
7881 186169960 : if (NodeNum < nTankNodes) B[i] += tank_node.MassFlowFromLower * Cp * Tavg[i + 1];
7882 :
7883 : // Divide by mass and specific heat
7884 : // m * cp * dT/dt = q_net => dT/dt = a * T + b
7885 186169960 : A[i] /= tank_node.Mass * Cp;
7886 186169960 : B[i] /= tank_node.Mass * Cp;
7887 :
7888 : } // end for each node
7889 :
7890 : // Calculate the average and final temperatures over the interval
7891 26208318 : Real64 TfinalDiff = 0.0;
7892 212378278 : for (int i = 0; i < nTankNodes; ++i) {
7893 186169960 : const Real64 Tstart = this->Node[i].Temp;
7894 186169960 : const Real64 b_a = B[i] / A[i];
7895 186169960 : const Real64 e_a_dt = exp(A[i] * dt);
7896 186169960 : Tavg[i] = (Tstart + b_a) * (e_a_dt - 1.0) / (A[i] * dt) - b_a;
7897 186169960 : const Real64 Tfinal_old = Tfinal[i];
7898 186169960 : Tfinal[i] = (Tstart + b_a) * e_a_dt - b_a;
7899 186169960 : TfinalDiff = max(fabs(Tfinal[i] - Tfinal_old), TfinalDiff);
7900 : }
7901 :
7902 26208318 : if (TfinalDiff < TemperatureConvergenceCriteria) break;
7903 :
7904 19437916 : if (this->DesuperheaterNum > 0) {
7905 47662 : DesuperheaterPLR = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR;
7906 47662 : DesuperheaterHeaterRate = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate;
7907 95324 : MdotDesuperheaterWater = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OperatingWaterFlowRate *
7908 47662 : Psychrometrics::RhoH2O(Tavg[this->SourceOutletStratNode - 1]);
7909 47662 : if (DesuperheaterPLR > 0.0 && MdotDesuperheaterWater > 0.0) {
7910 34056 : this->SourceInletTemp =
7911 34056 : Tavg[this->SourceOutletStratNode - 1] + (DesuperheaterHeaterRate / DesuperheaterPLR) / (MdotDesuperheaterWater * Cp);
7912 : } else {
7913 13606 : this->SourceInletTemp = Tavg[this->SourceOutletStratNode - 1];
7914 : }
7915 : }
7916 : } // end temperature convergence loop
7917 :
7918 : // Inversion mixing
7919 : bool HasInversion;
7920 10123019 : do {
7921 10123019 : HasInversion = false;
7922 : // Starting from the top of the tank check if the node below has a temperature inversion.
7923 48993567 : for (int j = 0; j < nTankNodes - 1; ++j) {
7924 41878942 : if (Tfinal[j] < Tfinal[j + 1]) {
7925 :
7926 : // Temperature inversion!
7927 3008394 : HasInversion = true;
7928 :
7929 : // From the node above the inversion, move down calculating a weighted average
7930 : // of node temperatures until the node below the group of mixed nodes isn't hotter
7931 : // or we hit the bottom of the tank.
7932 3008394 : Real64 Tmixed = 0.0;
7933 3008394 : Real64 MassMixed = 0.0;
7934 : int m;
7935 14936272 : for (m = j; m < nTankNodes; ++m) {
7936 14936272 : Tmixed += Tfinal[m] * this->Node[m].Mass;
7937 14936272 : MassMixed += this->Node[m].Mass;
7938 14936272 : if ((m == nTankNodes - 1) || (Tmixed / MassMixed > Tfinal[m + 1])) break;
7939 : }
7940 3008394 : Tmixed /= MassMixed;
7941 :
7942 : // Now we have a range of nodes (j = top, m = bottom) that are mixed
7943 : // and the mixed temperature (Tmixed).
7944 : // Move through the mixed nodes and set the final temperature to the mixed temperature.
7945 : // Also calculate a corrected average temperature for each node.
7946 17944666 : for (int k = j; k <= m; ++k) {
7947 : Real64 FinalFactorMixing;
7948 : Real64 AvgFactorMixing;
7949 14936272 : const Real64 NodeCapacitance = this->Node[k].Mass * Cp;
7950 14936272 : if (A[k] == 0.0) {
7951 0 : FinalFactorMixing = dt / NodeCapacitance;
7952 0 : AvgFactorMixing = FinalFactorMixing / 2.0;
7953 : } else {
7954 14936272 : FinalFactorMixing = (exp(A[k] * dt) - 1.0) / A[k] / NodeCapacitance;
7955 14936272 : AvgFactorMixing = ((exp(A[k] * dt) - 1.0) / A[k] / dt - 1.0) / A[k] / NodeCapacitance;
7956 : }
7957 14936272 : const Real64 Q_AdiabaticMixing = (Tmixed - Tfinal[k]) / FinalFactorMixing;
7958 14936272 : Tfinal[k] = Tmixed;
7959 14936272 : Tavg[k] += Q_AdiabaticMixing * AvgFactorMixing;
7960 : }
7961 :
7962 : // Since we mixed, get out of here and start from the top to check again for mixing.
7963 3008394 : break;
7964 : }
7965 : }
7966 : } while (HasInversion);
7967 :
7968 : // Venting
7969 7114625 : if (!this->IsChilledWaterTank) {
7970 6768575 : if (Tfinal[0] > this->TankTempLimit) {
7971 1061418 : for (int i = 0; i < nTankNodes; ++i) {
7972 917526 : if (Tfinal[i] > this->TankTempLimit) {
7973 369579 : Event += this->Node[i].Mass * Cp * (this->TankTempLimit - Tfinal[i]);
7974 369579 : Tfinal[i] = this->TankTempLimit;
7975 : }
7976 : }
7977 : }
7978 : }
7979 :
7980 : // Increment to next internal time step
7981 7114625 : TimeRemaining -= dt;
7982 7114625 : Real64 Qloss = 0.0;
7983 43172837 : for (int i = 0; i < nTankNodes; ++i) {
7984 36058212 : auto &node = this->Node[i];
7985 36058212 : node.Temp = Tfinal[i];
7986 36058212 : node.TempSum += Tavg[i] * dt;
7987 :
7988 : // Bookkeeping for reporting variables, mostly for Qunmet.
7989 36058212 : Real64 Qloss_node = (this->AmbientTemp - Tavg[i]);
7990 : Real64 Qheat_node;
7991 36058212 : const Real64 Quse_node = node.UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[i]);
7992 36058212 : const Real64 Qsource_node = [&] {
7993 42017608 : if (this->HeatPumpNum > 0) {
7994 38611886 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
7995 36058212 : if (node.SourceMassFlowRate > 0.0) {
7996 10103581 : return Qheatpump;
7997 : } else {
7998 19995235 : return 0.0;
7999 : }
8000 : } else {
8001 8513070 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
8002 17026140 : return Qheatpump * node.HPWHWrappedCondenserHeatingFrac;
8003 : }
8004 : } else {
8005 11918792 : return node.SourceMassFlowRate * Cp * (this->SourceInletTemp - Tavg[i]);
8006 : }
8007 36058212 : }();
8008 :
8009 36058212 : if (this->HeaterOn1 || this->HeaterOn2) {
8010 6043470 : Qloss_node *= node.OnCycLossCoeff;
8011 6043470 : Qheat_node = node.OnCycParaLoad * this->OnCycParaFracToTank;
8012 : } else {
8013 30014742 : Qloss_node *= node.OffCycLossCoeff;
8014 30014742 : Qheat_node = node.OffCycParaLoad * this->OffCycParaFracToTank;
8015 : }
8016 36058212 : Qloss += Qloss_node;
8017 36058212 : const Real64 Qneeded_node = max(-Quse_node - Qsource_node - Qloss_node - Qheat_node, 0.0);
8018 36058212 : const Real64 Qunmet_node = max(Qneeded_node - Qheater1 - Qheater2, 0.0);
8019 36058212 : Eunmet += Qunmet_node * dt;
8020 : }
8021 7114625 : SourceInletTempSum += this->SourceInletTemp * dt;
8022 : // More bookkeeping for reporting variables
8023 7114625 : Eloss += Qloss * dt;
8024 7114625 : const Real64 Quse = (this->UseOutletStratNode > 0)
8025 7114625 : ? this->UseEffectiveness * this->UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[this->UseOutletStratNode - 1])
8026 7114625 : : 0.0;
8027 7114625 : Euse += Quse * dt;
8028 7114625 : const Real64 Qsource = [&] {
8029 9997725 : if (this->HeatPumpNum > 0) {
8030 10262433 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8031 2426441 : return Qheatpump;
8032 : } else {
8033 3917996 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
8034 3917996 : return 0.0;
8035 : }
8036 : } else {
8037 770188 : if (this->SourceOutletStratNode > 0) {
8038 1056456 : return this->SourceEffectiveness * this->SourceMassFlowRate * Cp *
8039 1056456 : (this->SourceInletTemp - Tavg[this->SourceOutletStratNode - 1]);
8040 : } else {
8041 241960 : return 0.0;
8042 : }
8043 : }
8044 7114625 : }();
8045 7114625 : Esource += Qsource * dt;
8046 7114625 : if (this->HeaterOn1) Runtime1 += dt;
8047 7114625 : if (this->HeaterOn2) Runtime2 += dt;
8048 7114625 : if (this->HeaterOn1 || this->HeaterOn2) Runtime += dt;
8049 7114625 : Eheater1 += Qheater1 * dt;
8050 7114625 : Eheater2 += Qheater2 * dt;
8051 :
8052 : // Calculation for standard ratings
8053 7114625 : if (!this->FirstRecoveryDone) {
8054 : Real64 Qrecovery;
8055 5853557 : if (this->HeaterOn1 || this->HeaterOn2) {
8056 108820 : Qrecovery = (Qheater1 + Qheater1) / this->Efficiency + this->OnCycParaLoad;
8057 : } else {
8058 5744737 : Qrecovery = this->OffCycParaLoad;
8059 : }
8060 5853557 : this->FirstRecoveryFuel += Qrecovery * dt;
8061 5853557 : if (SetPointRecovered) this->FirstRecoveryDone = true;
8062 : }
8063 : } // end while TimeRemaining > 0.0
8064 :
8065 10107085 : for (auto &e : this->Node) {
8066 8564766 : e.TempAvg = e.TempSum / SecInTimeStep;
8067 8564766 : e.TempSum = 0.0;
8068 : }
8069 :
8070 1542319 : this->TankTemp = sum(this->Node, &StratifiedNodeData::Temp) / this->Nodes;
8071 1542319 : this->TankTempAvg = sum(this->Node, &StratifiedNodeData::TempAvg) / this->Nodes;
8072 :
8073 1542319 : if (!state.dataGlobal->WarmupFlag) {
8074 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
8075 252605 : if (this->TankTemp < 2) {
8076 0 : if (this->FreezingErrorIndex == 0) {
8077 0 : ShowWarningError(state,
8078 0 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
8079 : RoutineName,
8080 : this->Type,
8081 : this->Name,
8082 0 : this->TankTemp));
8083 0 : ShowContinueErrorTimeStamp(state, "");
8084 : }
8085 0 : ShowRecurringWarningErrorAtEnd(state,
8086 0 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
8087 : this->FreezingErrorIndex,
8088 : this->TankTemp, // Report Max
8089 : this->TankTemp, // Report Min
8090 : _, // Don't report Sum
8091 : "{C}", // Max Unit
8092 : "{C}"); // Min Unit
8093 : }
8094 : }
8095 :
8096 1542319 : if (this->UseOutletStratNode > 0) {
8097 1542319 : this->UseOutletTemp = this->Node(this->UseOutletStratNode).TempAvg;
8098 : // Revised use outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8099 1542319 : if (this->UseMassFlowRate > 0.0) {
8100 1271610 : this->UseOutletTemp = this->UseInletTemp * (1.0 - this->UseEffectiveness) + this->UseOutletTemp * this->UseEffectiveness;
8101 : }
8102 : }
8103 :
8104 1542319 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
8105 : // If we have a wrapped condenser HPWH, set the source outlet to the weighted average of the node
8106 : // temperatures the condenser sees
8107 859481 : Real64 WeightedAverageSourceOutletTemp(0.0);
8108 3679063 : for (int i = 1; i <= this->Nodes; ++i) {
8109 2819582 : WeightedAverageSourceOutletTemp += this->Node(i).TempAvg * this->Node(i).HPWHWrappedCondenserHeatingFrac;
8110 : }
8111 859481 : this->SourceOutletTemp = WeightedAverageSourceOutletTemp;
8112 682838 : } else if (this->SourceOutletStratNode > 0) {
8113 : // otherwise set it to the temperature of the source outlet node
8114 601934 : this->SourceOutletTemp = this->Node(this->SourceOutletStratNode).TempAvg;
8115 : // Output the average inlet temperature for the DataGlobals::TimeStep
8116 601934 : this->SourceInletTemp = SourceInletTempSum / SecInTimeStep;
8117 : }
8118 1542319 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8119 : // For pumped condensers, set the source inlet and outlets to match the delta T
8120 : // across the water side of the DX coil.
8121 431285 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8122 431285 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
8123 431285 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
8124 431285 : Real64 const HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
8125 431285 : this->SourceInletTemp = this->SourceOutletTemp + HPWHCondenserDeltaT;
8126 : }
8127 :
8128 : // Revised source outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8129 1542319 : if (this->SourceOutletStratNode > 0) {
8130 601934 : if (this->SourceMassFlowRate > 0.0) {
8131 276915 : this->SourceOutletTemp = this->SourceInletTemp * (1.0 - this->SourceEffectiveness) + this->SourceOutletTemp * this->SourceEffectiveness;
8132 : }
8133 : }
8134 :
8135 1542319 : this->LossRate = Eloss / SecInTimeStep;
8136 1542319 : this->UseRate = Euse / SecInTimeStep;
8137 1542319 : Real64 WrappedCondenserHeatPumpRate = 0.0;
8138 1542319 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
8139 431285 : this->SourceRate = Qheatpump;
8140 : } else {
8141 1111034 : this->SourceRate = Esource / SecInTimeStep;
8142 1111034 : WrappedCondenserHeatPumpRate = Qheatpump;
8143 : }
8144 :
8145 1542319 : this->OffCycParaFuelRate = this->OffCycParaLoad * (SecInTimeStep - Runtime) / SecInTimeStep;
8146 1542319 : this->OnCycParaFuelRate = this->OnCycParaLoad * Runtime / SecInTimeStep;
8147 1542319 : this->OffCycParaRateToTank = this->OffCycParaFuelRate * this->OffCycParaFracToTank;
8148 1542319 : this->OnCycParaRateToTank = this->OnCycParaFuelRate * this->OnCycParaFracToTank;
8149 1542319 : this->TotalDemandRate =
8150 1542319 : -this->UseRate - this->SourceRate - this->LossRate - this->OffCycParaRateToTank - this->OnCycParaRateToTank - WrappedCondenserHeatPumpRate;
8151 1542319 : this->HeaterRate1 = Eheater1 / SecInTimeStep;
8152 1542319 : this->HeaterRate2 = Eheater2 / SecInTimeStep;
8153 1542319 : this->HeaterRate = this->HeaterRate1 + this->HeaterRate2;
8154 :
8155 1542319 : this->UnmetRate = Eunmet / SecInTimeStep;
8156 1542319 : this->VentRate = Event / SecInTimeStep;
8157 4626957 : this->NetHeatTransferRate = this->UseRate + this->SourceRate + this->LossRate + this->OffCycParaRateToTank + this->OnCycParaRateToTank +
8158 3084638 : this->HeaterRate + this->VentRate + WrappedCondenserHeatPumpRate;
8159 :
8160 1542319 : this->CycleOnCount = CycleOnCount1_loc + CycleOnCount2_loc;
8161 1542319 : this->CycleOnCount1 = CycleOnCount1_loc;
8162 1542319 : this->CycleOnCount2 = CycleOnCount2_loc;
8163 :
8164 1542319 : this->RuntimeFraction = Runtime / SecInTimeStep;
8165 1542319 : this->RuntimeFraction1 = Runtime1 / SecInTimeStep;
8166 1542319 : this->RuntimeFraction2 = Runtime2 / SecInTimeStep;
8167 :
8168 1542319 : this->FuelRate = (Eheater1 + Eheater2) / this->Efficiency / SecInTimeStep;
8169 :
8170 : // Add water heater skin losses and venting losses to ambient zone, if specified
8171 1542319 : if (this->AmbientTempZone > 0) this->AmbientZoneGain = -this->LossRate * this->SkinLossFracToZone - this->VentRate;
8172 1542319 : }
8173 :
8174 1715396 : void WaterThermalTankData::CalcNodeMassFlows(InletPositionMode inletMode)
8175 : {
8176 :
8177 : // SUBROUTINE INFORMATION:
8178 : // AUTHOR Peter Graham Ellis
8179 : // DATE WRITTEN January 2007
8180 : // MODIFIED na
8181 : // RE-ENGINEERED na
8182 :
8183 : // PURPOSE OF THIS SUBROUTINE:
8184 : // Determines mass flow rates between nodes according to the locations of the use- and source-side inlet and outlet
8185 : // nodes.
8186 :
8187 : // METHODOLOGY EMPLOYED:
8188 : // In 'Seeking' mode, nodes are searched between the user-specified inlet and outlet nodes to find the node closest
8189 : // in temperature to the inlet fluid temperature. In 'Fixed' mode, the user-specified nodes are always used.
8190 : // Upward and downward flows are added to each node between an inlet and outlet. Flows in both directions cancel out
8191 : // to leave only the net flow in one direction.
8192 :
8193 1715396 : int useInletStratNod = this->UseInletStratNode;
8194 1715396 : int useOutletStratNode = this->UseOutletStratNode;
8195 1715396 : int sourceInletStratNode = this->SourceInletStratNode;
8196 1715396 : int sourceOutletStratNode = this->SourceOutletStratNode;
8197 :
8198 1715396 : Real64 useMassFlowRate = this->UseMassFlowRate * this->UseEffectiveness;
8199 1715396 : Real64 sourceMassFlowRate = this->SourceMassFlowRate * this->SourceEffectiveness;
8200 :
8201 11318624 : for (auto &e : this->Node) {
8202 9603228 : e.UseMassFlowRate = 0.0;
8203 9603228 : e.SourceMassFlowRate = 0.0;
8204 9603228 : e.MassFlowFromUpper = 0.0;
8205 9603228 : e.MassFlowFromLower = 0.0;
8206 9603228 : e.MassFlowToUpper = 0.0;
8207 9603228 : e.MassFlowToLower = 0.0;
8208 : }
8209 :
8210 1715396 : if (inletMode == InletPositionMode::Seeking) {
8211 : // 'Seek' the node with the temperature closest to the inlet temperature
8212 : // Start at the user-specified inlet node and search to the user-specified outlet node
8213 : int Step;
8214 346050 : if (useMassFlowRate > 0.0) {
8215 160952 : if (useInletStratNod > useOutletStratNode) {
8216 0 : Step = -1;
8217 : } else {
8218 160952 : Step = 1;
8219 : }
8220 160952 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8221 160952 : int const NodeNum_stop(floop_end(useInletStratNod, useOutletStratNode, Step));
8222 552164 : for (int NodeNum = useInletStratNod; NodeNum != NodeNum_stop; NodeNum += Step) {
8223 508345 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->UseInletTemp);
8224 508345 : if (DeltaTemp < MinDeltaTemp) {
8225 330644 : MinDeltaTemp = DeltaTemp;
8226 330644 : useInletStratNod = NodeNum;
8227 177701 : } else if (DeltaTemp > MinDeltaTemp) {
8228 117133 : break;
8229 : }
8230 : }
8231 : }
8232 :
8233 346050 : if (sourceMassFlowRate > 0.0) {
8234 43024 : if (sourceInletStratNode > sourceOutletStratNode) {
8235 43024 : Step = -1;
8236 : } else {
8237 0 : Step = 1;
8238 : }
8239 43024 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8240 43024 : int const NodeNum_stop(floop_end(sourceInletStratNode, sourceOutletStratNode, Step));
8241 86796 : for (int NodeNum = sourceInletStratNode; NodeNum != NodeNum_stop; NodeNum += Step) {
8242 86796 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->SourceInletTemp);
8243 86796 : if (DeltaTemp < MinDeltaTemp) {
8244 43024 : MinDeltaTemp = DeltaTemp;
8245 43024 : sourceInletStratNode = NodeNum;
8246 43772 : } else if (DeltaTemp > MinDeltaTemp) {
8247 43024 : break;
8248 : }
8249 : }
8250 : }
8251 : }
8252 :
8253 1715396 : if (useInletStratNod > 0) this->Node(useInletStratNod).UseMassFlowRate = useMassFlowRate;
8254 1715396 : if (sourceInletStratNode > 0) this->Node(sourceInletStratNode).SourceMassFlowRate = sourceMassFlowRate;
8255 :
8256 1715396 : if (useMassFlowRate > 0.0) {
8257 1352089 : if (useOutletStratNode > useInletStratNod) {
8258 : // Use-side flow is down
8259 768272 : for (int NodeNum = useInletStratNod; NodeNum <= useOutletStratNode - 1; ++NodeNum) {
8260 635068 : this->Node(NodeNum).MassFlowToLower += useMassFlowRate;
8261 : }
8262 768272 : for (int NodeNum = useInletStratNod + 1; NodeNum <= useOutletStratNode; ++NodeNum) {
8263 635068 : this->Node(NodeNum).MassFlowFromUpper += useMassFlowRate;
8264 : }
8265 :
8266 1218885 : } else if (useOutletStratNode < useInletStratNod) {
8267 : // Use-side flow is up
8268 5088968 : for (int NodeNum = useOutletStratNode; NodeNum <= useInletStratNod - 1; ++NodeNum) {
8269 4578137 : this->Node(NodeNum).MassFlowFromLower += useMassFlowRate;
8270 : }
8271 5088968 : for (int NodeNum = useOutletStratNode + 1; NodeNum <= useInletStratNod; ++NodeNum) {
8272 4578137 : this->Node(NodeNum).MassFlowToUpper += useMassFlowRate;
8273 : }
8274 :
8275 : } else {
8276 : // Use-side flow is across the node; no flow to other nodes
8277 : }
8278 : }
8279 :
8280 1715396 : if (sourceMassFlowRate > 0.0) {
8281 298427 : if (sourceOutletStratNode > sourceInletStratNode) {
8282 : // Source-side flow is down
8283 637438 : for (int NodeNum = sourceInletStratNode; NodeNum <= sourceOutletStratNode - 1; ++NodeNum) {
8284 542589 : this->Node(NodeNum).MassFlowToLower += sourceMassFlowRate;
8285 : }
8286 637438 : for (int NodeNum = sourceInletStratNode + 1; NodeNum <= sourceOutletStratNode; ++NodeNum) {
8287 542589 : this->Node(NodeNum).MassFlowFromUpper += sourceMassFlowRate;
8288 : }
8289 :
8290 203578 : } else if (sourceOutletStratNode < sourceInletStratNode) {
8291 : // Source-side flow is up
8292 1139320 : for (int NodeNum = sourceOutletStratNode; NodeNum <= sourceInletStratNode - 1; ++NodeNum) {
8293 935742 : this->Node(NodeNum).MassFlowFromLower += sourceMassFlowRate;
8294 : }
8295 1139320 : for (int NodeNum = sourceOutletStratNode + 1; NodeNum <= sourceInletStratNode; ++NodeNum) {
8296 935742 : this->Node(NodeNum).MassFlowToUpper += sourceMassFlowRate;
8297 : }
8298 :
8299 : } else {
8300 : // Source-side flow is across the node; no flow to other nodes
8301 : }
8302 : }
8303 :
8304 : // Cancel out any up and down flows
8305 11318624 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
8306 9603228 : this->Node(NodeNum).MassFlowFromUpper = max((this->Node(NodeNum).MassFlowFromUpper - this->Node(NodeNum).MassFlowToUpper), 0.0);
8307 9603228 : this->Node(NodeNum).MassFlowFromLower = max((this->Node(NodeNum).MassFlowFromLower - this->Node(NodeNum).MassFlowToLower), 0.0);
8308 : }
8309 1715396 : }
8310 :
8311 30050 : void WaterThermalTankData::CalcDesuperheaterWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8312 : {
8313 :
8314 : // SUBROUTINE INFORMATION:
8315 : // AUTHOR Richard Raustad
8316 : // DATE WRITTEN July 2005
8317 : // MODIFIED na
8318 : // RE-ENGINEERED na
8319 :
8320 : // PURPOSE OF THIS SUBROUTINE:
8321 : // Simulates a refrigerant desuperheater to heat water
8322 :
8323 : // METHODOLOGY EMPLOYED:
8324 : // This model uses the rated heat reclaim recovery efficiency, recovery efficiency modifier curve,
8325 : // set point temperature, and dead band temperature difference to simulate the desuperheater coil
8326 : // and sets up inputs to the tank model associated with the desuperheater coil
8327 :
8328 30050 : int constexpr MaxIte(500); // Maximum number of iterations for RegulaFalsi
8329 :
8330 30050 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum);
8331 :
8332 30050 : int WaterInletNode = DesupHtr.WaterInletNode;
8333 30050 : int WaterOutletNode = DesupHtr.WaterOutletNode;
8334 :
8335 : // store first iteration tank temperature and desuperheater mode of operation
8336 30050 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && DesupHtr.FirstTimeThroughFlag) {
8337 : // Save conditions from end of previous system timestep
8338 : // Every iteration that does not advance time should reset to these values
8339 13954 : this->SavedTankTemp = this->TankTemp;
8340 13954 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
8341 13954 : DesupHtr.SaveMode = DesupHtr.Mode;
8342 13954 : DesupHtr.FirstTimeThroughFlag = false;
8343 : }
8344 :
8345 16096 : else if (!FirstHVACIteration) {
8346 15025 : DesupHtr.FirstTimeThroughFlag = true;
8347 : }
8348 :
8349 : // initialize variables before invoking any RETURN statement
8350 30050 : this->SourceMassFlowRate = 0.0;
8351 : // reset tank inlet temp from previous time step
8352 30050 : this->SourceInletTemp = this->SavedSourceOutletTemp;
8353 30050 : DesupHtr.DesuperheaterPLR = 0.0;
8354 :
8355 30050 : state.dataLoopNodes->Node(WaterInletNode).MassFlowRate = 0.0;
8356 30050 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = 0.0;
8357 30050 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SavedSourceOutletTemp;
8358 :
8359 30050 : DesupHtr.DesuperheaterPLR = 0.0;
8360 30050 : DesupHtr.OnCycParaFuelRate = 0.0;
8361 30050 : DesupHtr.OnCycParaFuelEnergy = 0.0;
8362 30050 : DesupHtr.OffCycParaFuelRate = 0.0;
8363 30050 : DesupHtr.OffCycParaFuelEnergy = 0.0;
8364 30050 : DesupHtr.HEffFTempOutput = 0.0;
8365 30050 : DesupHtr.HeaterRate = 0.0;
8366 30050 : DesupHtr.HeaterEnergy = 0.0;
8367 30050 : DesupHtr.PumpPower = 0.0;
8368 30050 : DesupHtr.PumpEnergy = 0.0;
8369 :
8370 : // simulate only the water heater tank if the desuperheater coil is scheduled off
8371 30050 : Real64 AvailSchedule = ScheduleManager::GetCurrentScheduleValue(state, DesupHtr.AvailSchedPtr);
8372 30050 : if (AvailSchedule == 0.0) {
8373 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8374 0 : this->CalcWaterThermalTank(state);
8375 18637 : return;
8376 : }
8377 :
8378 : // simulate only the water heater tank if the lowest temperature available from the desuperheater coil
8379 : // is less than water inlet temperature if the reclaim source is a refrigeration condenser
8380 30050 : if (DesupHtr.ValidSourceType) {
8381 30050 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8382 30050 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8383 4564 : if (state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailTemperature <= this->SourceInletTemp) {
8384 24 : DesupHtr.Mode = TankOperatingMode::Floating;
8385 24 : this->CalcWaterThermalTank(state);
8386 72 : ShowRecurringWarningErrorAtEnd(state,
8387 48 : "WaterHeating:Desuperheater " + DesupHtr.Name +
8388 : " - Waste heat source temperature was too low to be useful.",
8389 : DesupHtr.InsuffTemperatureWarn);
8390 24 : return;
8391 : } // Temp too low
8392 : } // desuperheater source is condenser_refrigeration
8393 : } // validsourcetype
8394 :
8395 30026 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad;
8396 30026 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
8397 :
8398 : // check that water heater tank cut-in temp is greater than desuperheater cut-in temp
8399 30026 : Real64 desupHtrSetPointTemp = DesupHtr.SetPointTemp;
8400 30026 : Real64 DeadBandTempDiff = DesupHtr.DeadBandTempDiff;
8401 30026 : if ((desupHtrSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
8402 4 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
8403 0 : Real64 MinTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8404 0 : ++DesupHtr.SetPointError;
8405 0 : if (DesupHtr.SetPointError < 5) {
8406 0 : ShowWarningError(state,
8407 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8408 : "\": Water heater tank set point temperature is greater than or equal to the cut-in temperature of the "
8409 : "desuperheater. Desuperheater will be disabled.");
8410 0 : ShowContinueErrorTimeStamp(state, format(" ...Desuperheater cut-in temperature = {:.2R}", MinTemp));
8411 : } else {
8412 0 : ShowRecurringWarningErrorAtEnd(state,
8413 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8414 : "\": Water heater tank set point temperature is greater than or equal to the cut-in "
8415 : "temperature of the desuperheater. Desuperheater will be disabled warning continues...",
8416 : DesupHtr.SetPointErrIndex1,
8417 : MinTemp,
8418 : MinTemp);
8419 : }
8420 : }
8421 :
8422 : // Simulate tank if desuperheater unavailable for water heating
8423 4 : this->CalcWaterThermalTank(state);
8424 4 : return;
8425 : }
8426 :
8427 30022 : Real64 Effic = DesupHtr.HeatReclaimRecoveryEff;
8428 :
8429 30022 : state.dataLoopNodes->Node(WaterInletNode).Temp = this->SavedSourceOutletTemp;
8430 30022 : DesupHtr.Mode = DesupHtr.SaveMode;
8431 :
8432 : Real64 HEffFTemp;
8433 30022 : if (DesupHtr.HEffFTemp > 0) {
8434 30022 : HEffFTemp = max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, this->SavedTankTemp, state.dataEnvrn->OutDryBulbTemp));
8435 : } else {
8436 0 : HEffFTemp = 1.0;
8437 : }
8438 :
8439 : // set limits on heat recovery efficiency
8440 30022 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8441 4540 : if ((HEffFTemp * Effic) > 0.9) HEffFTemp = 0.9 / Effic;
8442 : } else { // max is 0.3 for all other sources
8443 25482 : if ((HEffFTemp * Effic) > 0.3) HEffFTemp = 0.3 / Effic;
8444 : } // setting limits on heat recovery efficiency
8445 :
8446 : // Access the appropriate structure to find the average heating capacity of the desuperheater heating coil
8447 30022 : Real64 AverageWasteHeat = 0.0;
8448 30022 : if (DesupHtr.ValidSourceType) {
8449 30022 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8450 30022 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8451 : // Refrigeration systems are solved outside the time step iteration, so the
8452 : // appropriate decrement for other waste heat applications is handled differently
8453 0 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).AvailCapacity -
8454 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8455 0 : DesupHtr.DXSysPLR = 1.0;
8456 30022 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8457 9080 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailCapacity -
8458 4540 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8459 4540 : DesupHtr.DXSysPLR = 1.0;
8460 38796 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8461 26628 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8462 13314 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8463 24336 : AverageWasteHeat = state.dataHeatBal->HeatReclaimDXCoil(SourceID).AvailCapacity -
8464 12168 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8465 12168 : DesupHtr.DXSysPLR = state.dataDXCoils->DXCoil(SourceID).PartLoadRatio;
8466 13314 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling) {
8467 7800 : AverageWasteHeat = state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).AvailCapacity -
8468 3900 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8469 3900 : DesupHtr.DXSysPLR = state.dataVariableSpeedCoils->VarSpeedCoil(SourceID).PartLoadRatio;
8470 9414 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8471 6660 : AverageWasteHeat = state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).AvailCapacity -
8472 3330 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8473 3330 : DesupHtr.DXSysPLR = state.dataWaterToAirHeatPumpSimple->SimpleWatertoAirHP(SourceID).PartLoadRatio;
8474 6084 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8475 6084 : AverageWasteHeat =
8476 6084 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.AvailCapacity -
8477 6084 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.HVACDesuperheaterReclaimedHeatTotal;
8478 6084 : DesupHtr.DXSysPLR = state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].partLoadRatioReport;
8479 : }
8480 : } else {
8481 0 : AverageWasteHeat = 0.0;
8482 : }
8483 :
8484 : // simulate only water heater tank if reclaim heating source is off
8485 30022 : if (DesupHtr.DXSysPLR == 0.0) {
8486 18609 : this->CalcWaterThermalTank(state);
8487 18609 : return;
8488 : }
8489 :
8490 : // If the set point is higher than the maximum water temp, reset both the set point and the dead band temperature difference
8491 11413 : if (desupHtrSetPointTemp > DesupHtr.MaxInletWaterTemp) {
8492 11413 : Real64 CutInTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8493 11413 : desupHtrSetPointTemp = DesupHtr.MaxInletWaterTemp;
8494 11413 : DeadBandTempDiff = max(0.0, (desupHtrSetPointTemp - CutInTemp));
8495 : }
8496 :
8497 : Real64 Acc; // Accuracy of result from RegulaFalsi
8498 11413 : if (DesupHtr.TankTypeNum == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8499 951 : Acc = 0.001;
8500 : } else {
8501 10462 : Acc = 0.00001;
8502 : }
8503 :
8504 : // set the water-side mass flow rate
8505 11413 : Real64 CpWater = Psychrometrics::CPHW(state.dataLoopNodes->Node(WaterInletNode).Temp);
8506 11413 : Real64 MdotWater = DesupHtr.OperatingWaterFlowRate * Psychrometrics::RhoH2O(state.dataLoopNodes->Node(WaterInletNode).Temp);
8507 11413 : Real64 QHeatRate = 0.0;
8508 11413 : if (state.dataLoopNodes->Node(WaterInletNode).Temp <= DesupHtr.MaxInletWaterTemp + Acc) {
8509 10217 : QHeatRate = ((AverageWasteHeat * Effic * HEffFTemp) / DesupHtr.DXSysPLR) + (DesupHtr.PumpElecPower * DesupHtr.PumpFracToWater);
8510 : }
8511 :
8512 : // change to tanktypenum using parameters?
8513 11413 : Real64 partLoadRatio = 0.0;
8514 : Real64 NewTankTemp;
8515 : {
8516 11413 : auto const TankType(DesupHtr.TankTypeNum);
8517 :
8518 11413 : if (TankType == DataPlant::PlantEquipmentType::WtrHeaterMixed || TankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8519 :
8520 11413 : DesupHtr.SaveWHMode = this->Mode;
8521 11413 : Real64 PreTankAvgTemp = this->TankTempAvg;
8522 11413 : Real64 NewTankAvgTemp = 0.0; // Initialization
8523 11413 : int max_count = 200;
8524 11413 : int count = 0;
8525 11413 : bool firstThrough = true;
8526 11413 : switch (DesupHtr.Mode) {
8527 30917 : case TankOperatingMode::Heating:
8528 : // Calculate until consistency of desuperheater and tank source side energy transfer achieved
8529 51325 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > DataHVACGlobals::SmallTempDiff || firstThrough) && count < max_count) {
8530 20408 : count++;
8531 20408 : firstThrough = false;
8532 20408 : PreTankAvgTemp = this->TankTempAvg;
8533 20408 : partLoadRatio = DesupHtr.DXSysPLR;
8534 20408 : if (MdotWater > 0.0) {
8535 20408 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8536 : } else {
8537 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8538 : }
8539 :
8540 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8541 20408 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8542 :
8543 : // set the source mass flow rate for the tank
8544 20408 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8545 :
8546 20408 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8547 20408 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8548 20408 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8549 20408 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8550 20408 : this->CalcWaterThermalTank(state);
8551 20408 : Real64 NewTankTemp = this->TankTemp;
8552 :
8553 20408 : if (NewTankTemp > desupHtrSetPointTemp) {
8554 : // Only revert to floating mode if the tank temperature is higher than the cut out temperature
8555 6856 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8556 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8557 : }
8558 : int SolFla;
8559 13712 : std::string IterNum;
8560 137130 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8561 27426 : this->Mode = DesupHtr.SaveWHMode;
8562 27426 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8563 27426 : this->CalcWaterThermalTank(state);
8564 27426 : Real64 NewTankTemp = this->TankTemp;
8565 27426 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8566 27426 : return PLRResidualWaterThermalTank;
8567 6856 : };
8568 6856 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8569 6856 : if (SolFla == -1) {
8570 0 : IterNum = fmt::to_string(MaxIte);
8571 0 : if (!state.dataGlobal->WarmupFlag) {
8572 0 : ++DesupHtr.IterLimitExceededNum1;
8573 0 : if (DesupHtr.IterLimitExceededNum1 == 1) {
8574 0 : ShowWarningError(state, DesupHtr.Type + " \"" + DesupHtr.Name + "\"");
8575 0 : ShowContinueError(state,
8576 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8577 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8578 : IterNum,
8579 0 : partLoadRatio));
8580 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8581 : } else {
8582 0 : ShowRecurringWarningErrorAtEnd(state,
8583 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8584 : "\": Iteration limit exceeded in heating mode warning continues. "
8585 : "Part-load ratio statistics follow.",
8586 : DesupHtr.IterLimitErrIndex1,
8587 : partLoadRatio,
8588 : partLoadRatio);
8589 : }
8590 : }
8591 6856 : } else if (SolFla == -2) {
8592 0 : partLoadRatio =
8593 0 : max(0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8594 0 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8595 0 : this->CalcWaterThermalTank(state);
8596 0 : if (!state.dataGlobal->WarmupFlag) {
8597 0 : ++DesupHtr.RegulaFalsiFailedNum1;
8598 0 : if (DesupHtr.RegulaFalsiFailedNum1 == 1) {
8599 0 : ShowWarningError(state, DesupHtr.Type + " \"" + DesupHtr.Name + "\"");
8600 0 : ShowContinueError(state,
8601 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to 1 "
8602 : "exceeded. Part-load ratio used = {:.3R}",
8603 0 : partLoadRatio));
8604 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8605 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8606 : } else {
8607 0 : ShowRecurringWarningErrorAtEnd(state,
8608 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8609 : "\": Part-load ratio calculation failed in heating mode warning "
8610 : "continues. Part-load ratio statistics follow.",
8611 : DesupHtr.RegulaFalsiFailedIndex1,
8612 : partLoadRatio,
8613 : partLoadRatio);
8614 : }
8615 : }
8616 : }
8617 : } else {
8618 13552 : partLoadRatio = DesupHtr.DXSysPLR;
8619 : }
8620 20408 : NewTankAvgTemp = this->TankTempAvg;
8621 : }
8622 10509 : break;
8623 904 : case TankOperatingMode::Floating:
8624 904 : if (MdotWater > 0.0) {
8625 904 : state.dataLoopNodes->Node(WaterOutletNode).Temp =
8626 904 : state.dataLoopNodes->Node(WaterInletNode).Temp + QHeatRate / (MdotWater * CpWater);
8627 : } else {
8628 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = state.dataLoopNodes->Node(WaterInletNode).Temp;
8629 : }
8630 : // check tank temperature by setting source inlet mass flow rate to zero
8631 904 : partLoadRatio = 0.0;
8632 :
8633 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8634 904 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8635 :
8636 : // check tank temperature by setting source inlet mass flow rate to zero
8637 904 : this->SourceMassFlowRate = 0.0;
8638 :
8639 : // disable the tank heater to find PLR of the HPWH
8640 904 : this->MaxCapacity = 0.0;
8641 904 : this->MinCapacity = 0.0;
8642 904 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8643 904 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8644 904 : this->CalcWaterThermalTank(state);
8645 904 : NewTankTemp = this->TankTemp;
8646 :
8647 904 : if (NewTankTemp <= (desupHtrSetPointTemp - DeadBandTempDiff)) {
8648 44 : this->Mode = DesupHtr.SaveWHMode;
8649 44 : if ((this->SavedTankTemp - NewTankTemp) != 0.0) {
8650 44 : partLoadRatio =
8651 44 : min(DesupHtr.DXSysPLR,
8652 44 : max(0.0, ((desupHtrSetPointTemp - DeadBandTempDiff) - NewTankTemp) / (this->SavedTankTemp - NewTankTemp)));
8653 : } else {
8654 0 : partLoadRatio = DesupHtr.DXSysPLR;
8655 : }
8656 358 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > DataHVACGlobals::SmallTempDiff || firstThrough) && count < max_count) {
8657 157 : count++;
8658 157 : firstThrough = false;
8659 157 : PreTankAvgTemp = this->TankTempAvg;
8660 157 : DesupHtr.Mode = TankOperatingMode::Heating;
8661 157 : if (MdotWater > 0.0) {
8662 157 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8663 : } else {
8664 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8665 : }
8666 :
8667 : // set the full load outlet temperature on the water heater source inlet node
8668 157 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8669 :
8670 : // set the source mass flow rate for the tank and enable backup heating element
8671 157 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8672 157 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8673 157 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8674 157 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8675 157 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8676 157 : this->CalcWaterThermalTank(state);
8677 157 : NewTankTemp = this->TankTemp;
8678 :
8679 157 : if (NewTankTemp > desupHtrSetPointTemp) {
8680 : // Only revert to floating mode if the tank temperature is higher than the cut-out temperature
8681 0 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8682 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8683 : }
8684 : int SolFla;
8685 0 : std::string IterNum;
8686 0 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8687 0 : this->Mode = DesupHtr.SaveWHMode;
8688 0 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8689 0 : this->CalcWaterThermalTank(state);
8690 0 : Real64 NewTankTemp = this->TankTemp;
8691 0 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8692 0 : return PLRResidualWaterThermalTank;
8693 0 : };
8694 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8695 0 : if (SolFla == -1) {
8696 0 : IterNum = fmt::to_string(MaxIte);
8697 0 : if (!state.dataGlobal->WarmupFlag) {
8698 0 : ++DesupHtr.IterLimitExceededNum2;
8699 0 : if (DesupHtr.IterLimitExceededNum2 == 1) {
8700 0 : ShowWarningError(state, DesupHtr.Type + " \"" + DesupHtr.Name + "\"");
8701 0 : ShowContinueError(state,
8702 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8703 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8704 : IterNum,
8705 0 : partLoadRatio));
8706 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8707 : } else {
8708 0 : ShowRecurringWarningErrorAtEnd(state,
8709 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8710 : "\": Iteration limit exceeded in float mode warning continues. "
8711 : "Part-load ratio statistics follow.",
8712 : DesupHtr.IterLimitErrIndex2,
8713 : partLoadRatio,
8714 : partLoadRatio);
8715 : }
8716 : }
8717 0 : } else if (SolFla == -2) {
8718 0 : partLoadRatio = max(
8719 0 : 0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8720 0 : if (!state.dataGlobal->WarmupFlag) {
8721 0 : ++DesupHtr.RegulaFalsiFailedNum2;
8722 0 : if (DesupHtr.RegulaFalsiFailedNum2 == 1) {
8723 0 : ShowWarningError(state, DesupHtr.Type + " \"" + DesupHtr.Name + "\"");
8724 0 : ShowContinueError(state,
8725 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to "
8726 : "1 exceeded. Part-load ratio used = {:.3R}",
8727 0 : partLoadRatio));
8728 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8729 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8730 : } else {
8731 0 : ShowRecurringWarningErrorAtEnd(
8732 : state,
8733 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8734 : "\": Part-load ratio calculation failed in float mode warning "
8735 : "continues. Part-load ratio statistics follow.",
8736 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).RegulaFalsiFailedIndex2,
8737 : partLoadRatio,
8738 : partLoadRatio);
8739 : }
8740 : }
8741 : }
8742 : }
8743 157 : NewTankAvgTemp = this->TankTempAvg;
8744 : }
8745 : } else {
8746 860 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8747 860 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8748 : }
8749 904 : break;
8750 0 : default:
8751 0 : break;
8752 11413 : }
8753 :
8754 : // should never get here, case is checked in GetWaterThermalTankInput
8755 : } else {
8756 0 : ShowFatalError(state,
8757 0 : "Coil:WaterHeating:Desuperheater = " + state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).Name +
8758 0 : ": invalid water heater tank type and name entered = " +
8759 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankType + ", " +
8760 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankName);
8761 : }
8762 : }
8763 :
8764 11413 : if (QHeatRate == 0) partLoadRatio = 0.0;
8765 :
8766 11413 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = MdotWater * partLoadRatio;
8767 11413 : DesupHtr.HEffFTempOutput = HEffFTemp;
8768 11413 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8769 11413 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8770 :
8771 11413 : if (partLoadRatio == 0) {
8772 1196 : this->SourceInletTemp = this->SourceOutletTemp;
8773 1196 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8774 1196 : DesupHtr.HEffFTempOutput = 0.0;
8775 1196 : DesupHtr.HeaterRate = 0.0;
8776 : }
8777 :
8778 11413 : DesupHtr.HeaterEnergy = DesupHtr.HeaterRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
8779 11413 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8780 11413 : DesupHtr.OnCycParaFuelRate = DesupHtr.OnCycParaLoad * partLoadRatio;
8781 11413 : DesupHtr.OnCycParaFuelEnergy = DesupHtr.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
8782 11413 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad * (1 - partLoadRatio);
8783 11413 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
8784 11413 : DesupHtr.PumpPower = DesupHtr.PumpElecPower * (partLoadRatio);
8785 11413 : DesupHtr.PumpEnergy = DesupHtr.PumpPower * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
8786 :
8787 : // Update used waste heat (just in case multiple users of waste heat use same source)
8788 11413 : if (DesupHtr.ValidSourceType) {
8789 11413 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8790 11413 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8791 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8792 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8793 0 : for (auto &num : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8794 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8795 11413 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8796 4540 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8797 4540 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8798 9080 : for (auto &num : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8799 4540 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8800 11474 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8801 9202 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8802 4601 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8803 2272 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8804 2272 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8805 4544 : for (auto &num : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8806 4544 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8807 4601 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling) {
8808 2514 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8809 2514 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8810 5028 : for (auto &num : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8811 2514 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8812 2087 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8813 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8814 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8815 1902 : for (auto &num : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8816 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8817 1136 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8818 1136 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat(
8819 2272 : DesuperheaterNum) = DesupHtr.HeaterRate;
8820 1136 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal =
8821 : 0.0;
8822 2272 : for (auto &num :
8823 1136 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat)
8824 1136 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum]
8825 1136 : .reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8826 : }
8827 : }
8828 : }
8829 :
8830 644606 : void WaterThermalTankData::CalcHeatPumpWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8831 : {
8832 :
8833 : // SUBROUTINE INFORMATION:
8834 : // AUTHOR Richard Raustad
8835 : // DATE WRITTEN March 2005
8836 : // MODIFIED B. Griffith, Jan 2012 for stratified tank
8837 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
8838 : // RE-ENGINEERED na
8839 :
8840 : // PURPOSE OF THIS SUBROUTINE:
8841 : // Simulates a heat pump water heater
8842 :
8843 : // METHODOLOGY EMPLOYED:
8844 : // Simulate the water heater tank, DX coil, and fan to meet the water heating requirements.
8845 :
8846 644606 : int constexpr MaxIte(500); // maximum number of iterations
8847 644606 : Real64 constexpr Acc(0.001); // Accuracy of result from RegulaFalsi
8848 :
8849 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8850 : Real64 MdotWater; // mass flow rate of condenser water, kg/s
8851 644606 : IntegratedHeatPump::IHPOperationMode IHPMode(IntegratedHeatPump::IHPOperationMode::Idle); // IHP working mode
8852 644606 : Real64 EMP1(0.0), EMP2(0.0), EMP3(0.0);
8853 :
8854 : // References to objects used in this function
8855 644606 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8856 :
8857 : // initialize local variables
8858 644606 : int AvailSchedule = ScheduleManager::GetCurrentScheduleValue(state, HeatPump.AvailSchedPtr);
8859 644606 : int HPAirInletNode = HeatPump.HeatPumpAirInletNode;
8860 644606 : int HPAirOutletNode = HeatPump.HeatPumpAirOutletNode;
8861 644606 : int OutdoorAirNode = HeatPump.OutsideAirNode;
8862 644606 : int ExhaustAirNode = HeatPump.ExhaustAirNode;
8863 644606 : int HPWaterInletNode = HeatPump.CondWaterInletNode;
8864 644606 : int HPWaterOutletNode = HeatPump.CondWaterOutletNode;
8865 644606 : int InletAirMixerNode = HeatPump.InletAirMixerNode;
8866 644606 : int OutletAirSplitterNode = HeatPump.OutletAirSplitterNode;
8867 644606 : int DXCoilAirInletNode = HeatPump.DXCoilAirInletNode;
8868 644606 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
8869 644606 : DataHVACGlobals::CompressorOperation CompressorOp = DataHVACGlobals::CompressorOperation::Off; // DX compressor operation; 1=on, 0=off
8870 644606 : HeatPump.OnCycParaFuelRate = 0.0;
8871 644606 : HeatPump.OnCycParaFuelEnergy = 0.0;
8872 644606 : HeatPump.OffCycParaFuelRate = 0.0;
8873 644606 : HeatPump.OffCycParaFuelEnergy = 0.0;
8874 644606 : state.dataLoopNodes->Node(HPWaterOutletNode) = state.dataLoopNodes->Node(HPWaterInletNode);
8875 644606 : int MaxSpeedNum = HeatPump.NumofSpeed; // speed number of variable speed HPWH coil
8876 :
8877 : // assign set point temperature (cut-out) and dead band temp diff (cut-in = cut-out minus dead band temp diff)
8878 644606 : Real64 HPSetPointTemp = HeatPump.SetPointTemp;
8879 644606 : Real64 DeadBandTempDiff = HeatPump.DeadBandTempDiff;
8880 644606 : Real64 RhoWater = Psychrometrics::RhoH2O(HPSetPointTemp); // initialize
8881 :
8882 : // store first iteration tank temperature and HP mode of operation
8883 : // this code can be called more than once with FirstHVACIteration = .TRUE., use FirstTimeThroughFlag to control save
8884 644606 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && HeatPump.FirstTimeThroughFlag) {
8885 84350 : this->SavedTankTemp = this->TankTemp;
8886 84350 : HeatPump.SaveMode = HeatPump.Mode;
8887 84350 : HeatPump.SaveWHMode = this->Mode;
8888 84350 : HeatPump.FirstTimeThroughFlag = false;
8889 : }
8890 :
8891 644606 : if (!FirstHVACIteration) HeatPump.FirstTimeThroughFlag = true;
8892 :
8893 : // check if HPWH is off for some reason and simulate HPWH air- and water-side mass flow rates of 0
8894 : // simulate only water heater tank if HP compressor is scheduled off
8895 : // simulate only water heater tank if HP compressor cut-out temperature is lower than the tank's cut-in temperature
8896 : // simulate only water heater tank if HP inlet air temperature is below minimum temperature for HP compressor operation
8897 : // if the tank maximum temperature limit is less than the HPWH set point temp, disable HPWH
8898 2578424 : if (AvailSchedule == 0.0 || (HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp ||
8899 1136722 : state.dataHVACGlobal->HPWHInletDBTemp < HeatPump.MinAirTempForHPOperation ||
8900 1476348 : state.dataHVACGlobal->HPWHInletDBTemp > HeatPump.MaxAirTempForHPOperation || HPSetPointTemp >= this->TankTempLimit ||
8901 671512 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime && this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed &&
8902 1291324 : this->SavedMode == TankOperatingMode::Heating) ||
8903 671512 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime &&
8904 358792 : this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified && (this->SavedHeaterOn1 || this->SavedHeaterOn2))) {
8905 : // revert to float mode any time HPWH compressor is OFF
8906 154602 : HeatPump.Mode = TankOperatingMode::Floating;
8907 154602 : if (InletAirMixerNode > 0) {
8908 0 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
8909 : }
8910 : // pass node info and simulate crankcase heater
8911 154602 : if (MaxSpeedNum > 0) {
8912 32774 : int VSCoilNum = HeatPump.DXCoilNum;
8913 :
8914 32774 : if (HeatPump.bIsIHP) {
8915 5028 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(VSCoilNum).SCWHCoilIndex;
8916 : }
8917 : // set the SCWH mode
8918 32774 : Real64 SpeedRatio = 1.0; // speed ratio for interpolating between two speed levels
8919 32774 : int SpeedNum = 1;
8920 32774 : if (HeatPump.FanPlacement == DataHVACGlobals::BlowThru) {
8921 32774 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
8922 25398 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
8923 : } else {
8924 7376 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
8925 : }
8926 32774 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
8927 32774 : if (HeatPump.bIsIHP)
8928 15084 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8929 : "",
8930 : VSCoilNum,
8931 : DataHVACGlobals::CycFanCycCoil,
8932 : EMP1,
8933 : EMP2,
8934 : EMP3,
8935 : DataHVACGlobals::CompressorOperation::On,
8936 5028 : state.dataWaterThermalTanks->hpPartLoadRatio,
8937 : SpeedNum,
8938 : SpeedRatio,
8939 : 0.0,
8940 : 0.0,
8941 5028 : 1.0);
8942 : else
8943 55492 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8944 : HeatPump.DXCoilName,
8945 : VSCoilNum,
8946 : DataHVACGlobals::CycFanCycCoil,
8947 : EMP1,
8948 : EMP2,
8949 : EMP3,
8950 : DataHVACGlobals::CompressorOperation::On,
8951 27746 : state.dataWaterThermalTanks->hpPartLoadRatio,
8952 : SpeedNum,
8953 : SpeedRatio,
8954 : 0.0,
8955 : 0.0,
8956 : 1.0);
8957 : } else {
8958 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
8959 0 : if (HeatPump.bIsIHP)
8960 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8961 : "",
8962 : VSCoilNum,
8963 : DataHVACGlobals::CycFanCycCoil,
8964 : EMP1,
8965 : EMP2,
8966 : EMP3,
8967 : DataHVACGlobals::CompressorOperation::On,
8968 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8969 : SpeedNum,
8970 : SpeedRatio,
8971 : 0.0,
8972 : 0.0,
8973 0 : 1.0);
8974 : else
8975 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8976 : HeatPump.DXCoilName,
8977 : VSCoilNum,
8978 : DataHVACGlobals::CycFanCycCoil,
8979 : EMP1,
8980 : EMP2,
8981 : EMP3,
8982 : DataHVACGlobals::CompressorOperation::On,
8983 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8984 : SpeedNum,
8985 : SpeedRatio,
8986 : 0.0,
8987 : 0.0,
8988 : 1.0);
8989 0 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
8990 0 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
8991 : } else {
8992 0 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
8993 : }
8994 : }
8995 :
8996 : // set the DWH mode
8997 32774 : if (HeatPump.bIsIHP) {
8998 5028 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
8999 :
9000 5028 : if (VSCoilNum > 0) // if DWH coil exists
9001 : {
9002 5028 : if (HeatPump.FanPlacement == DataHVACGlobals::BlowThru) {
9003 5028 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9004 0 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9005 : } else {
9006 5028 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9007 : }
9008 5028 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9009 15084 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9010 : "",
9011 : VSCoilNum,
9012 : DataHVACGlobals::CycFanCycCoil,
9013 : EMP1,
9014 : EMP2,
9015 : EMP3,
9016 : DataHVACGlobals::CompressorOperation::On,
9017 5028 : state.dataWaterThermalTanks->hpPartLoadRatio,
9018 : SpeedNum,
9019 : SpeedRatio,
9020 : 0.0,
9021 : 0.0,
9022 5028 : 1.0);
9023 : } else {
9024 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9025 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9026 : "",
9027 : VSCoilNum,
9028 : DataHVACGlobals::CycFanCycCoil,
9029 : EMP1,
9030 : EMP2,
9031 : EMP3,
9032 : DataHVACGlobals::CompressorOperation::On,
9033 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9034 : SpeedNum,
9035 : SpeedRatio,
9036 : 0.0,
9037 : 0.0,
9038 0 : 1.0);
9039 0 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9040 0 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9041 : } else {
9042 0 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9043 : }
9044 : }
9045 : }
9046 : }
9047 :
9048 : } else {
9049 121828 : if (HeatPump.FanPlacement == DataHVACGlobals::BlowThru) {
9050 119716 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9051 93922 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9052 : } else {
9053 25794 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9054 : }
9055 239432 : DXCoils::SimDXCoil(state,
9056 : HeatPump.DXCoilName,
9057 : CompressorOp,
9058 : FirstHVACIteration,
9059 : HeatPump.DXCoilNum,
9060 : DataHVACGlobals::CycFanCycCoil,
9061 119716 : state.dataWaterThermalTanks->hpPartLoadRatio);
9062 : } else {
9063 4224 : DXCoils::SimDXCoil(state,
9064 : HeatPump.DXCoilName,
9065 : CompressorOp,
9066 : FirstHVACIteration,
9067 : HeatPump.DXCoilNum,
9068 : DataHVACGlobals::CycFanCycCoil,
9069 2112 : state.dataWaterThermalTanks->hpPartLoadRatio);
9070 2112 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9071 2112 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9072 : } else {
9073 0 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9074 : }
9075 : }
9076 : }
9077 :
9078 154602 : if (OutletAirSplitterNode > 0) {
9079 0 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
9080 : }
9081 :
9082 : // Simulate tank if HP compressor unavailable for water heating
9083 154602 : this->CalcWaterThermalTank(state);
9084 :
9085 : // If HPWH compressor is available and unit is off for another reason, off-cycle parasitics are calculated
9086 154602 : if (AvailSchedule != 0) {
9087 154602 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
9088 154602 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
9089 : }
9090 :
9091 : // Warn if HPWH compressor cut-in temperature is less than the water heater tank's set point temp
9092 154602 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
9093 13746 : if ((HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
9094 0 : Real64 HPMinTemp = HPSetPointTemp - DeadBandTempDiff;
9095 0 : const auto HPMinTempChar = fmt::to_string(HPMinTemp);
9096 0 : ++HeatPump.HPSetPointError;
9097 : // add logic for warmup, DataGlobals::KickOffSimulation and doing sizing here
9098 0 : if (HeatPump.HPSetPointError == 1) {
9099 0 : ShowWarningError(state,
9100 0 : HeatPump.Type + " \"" + HeatPump.Name +
9101 : ": Water heater tank set point temperature is greater than or equal to the cut-in temperature of the heat "
9102 : "pump water heater. Heat Pump will be disabled and simulation continues.");
9103 0 : ShowContinueErrorTimeStamp(state, " ...Heat Pump cut-in temperature=" + HPMinTempChar);
9104 : } else {
9105 0 : ShowRecurringWarningErrorAtEnd(state,
9106 0 : HeatPump.Type + " \"" + HeatPump.Name +
9107 : ": Water heater tank set point temperature is greater than or equal to the cut-in "
9108 : "temperature of the heat pump water heater. Heat Pump will be disabled error continues...",
9109 : HeatPump.HPSetPointErrIndex1,
9110 : HPMinTemp,
9111 : HPMinTemp);
9112 : }
9113 : }
9114 : }
9115 309204 : return;
9116 : }
9117 490004 : Real64 savedTankTemp = this->SavedTankTemp;
9118 490004 : HeatPump.Mode = HeatPump.SaveMode;
9119 :
9120 490004 : RhoWater = Psychrometrics::RhoH2O(savedTankTemp); // update water density using tank temp
9121 :
9122 : // set the heat pump air- and water-side mass flow rate
9123 490004 : MdotWater = HeatPump.OperatingWaterFlowRate * Psychrometrics::RhoH2O(savedTankTemp);
9124 :
9125 : // Select mode of operation (float mode or heat mode) from last iteration.
9126 : // Determine if heating will occur this iteration and get an estimate of the PLR
9127 490004 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9128 : // HPWH was heating last iteration and will continue to heat until the set point is reached
9129 161071 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9130 161071 : if (savedTankTemp > HPSetPointTemp) { // tank set point temp may have been reduced since last iteration and float mode may be needed
9131 56 : HeatPump.Mode = TankOperatingMode::Floating;
9132 56 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9133 : // check to see if HP needs to operate
9134 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9135 56 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9136 56 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9137 56 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9138 0 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9139 0 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9140 0 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9141 : }
9142 56 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9143 56 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9144 :
9145 : // Check tank temperature by setting source inlet mass flow rate to zero.
9146 56 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9147 :
9148 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9149 56 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9150 :
9151 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9152 56 : this->MaxCapacity = 0.0;
9153 56 : this->MinCapacity = 0.0;
9154 56 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9155 56 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9156 56 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9157 56 : this->CalcWaterThermalTank(state);
9158 56 : this->SourceEffectiveness = SourceEffectivenessBackup;
9159 56 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9160 :
9161 : // Reset the tank's internal heating element capacity.
9162 56 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9163 56 : this->MinCapacity = HeatPump.BackupElementCapacity;
9164 :
9165 : // Check to see if the tank drifts below set point if no heating happens.
9166 56 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9167 :
9168 : // HPWH is now in heating mode
9169 36 : HeatPump.Mode = TankOperatingMode::Heating;
9170 :
9171 : // Reset the water heater's mode (call above may have changed modes)
9172 36 : this->Mode = HeatPump.SaveWHMode;
9173 :
9174 36 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9175 : }
9176 : } else { // or use side nodes may meet set point without need for heat pump compressor operation
9177 : // check to see if HP needs to operate
9178 161015 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9179 47670 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9180 47670 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9181 113345 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9182 113345 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9183 113345 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9184 : }
9185 : // Check tank temperature by setting source inlet mass flow rate to zero.
9186 161015 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9187 161015 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9188 :
9189 161015 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9190 :
9191 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9192 161015 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9193 :
9194 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9195 161015 : this->MaxCapacity = 0.0;
9196 161015 : this->MinCapacity = 0.0;
9197 161015 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9198 161015 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9199 161015 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9200 161015 : this->CalcWaterThermalTank(state);
9201 161015 : this->SourceEffectiveness = SourceEffectivenessBackup;
9202 161015 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9203 :
9204 : // Reset the tank's internal heating element capacity.
9205 161015 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9206 161015 : this->MinCapacity = HeatPump.BackupElementCapacity;
9207 :
9208 : // Check to see if the tank meets set point if no heating happens.
9209 161015 : if (NewTankTemp > HPSetPointTemp) {
9210 :
9211 : // HPWH is now in floating mode
9212 0 : HeatPump.Mode = TankOperatingMode::Floating;
9213 :
9214 : } else {
9215 :
9216 : // HPWH remains in heating mode
9217 161015 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9218 : }
9219 :
9220 : // Reset the water heater's mode (call above may have changed modes)
9221 161015 : this->Mode = HeatPump.SaveWHMode;
9222 : }
9223 : } else {
9224 328933 : assert(HeatPump.Mode == TankOperatingMode::Floating);
9225 : // HPWH was floating last iteration and will continue to float until the cut-in temperature is reached
9226 :
9227 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9228 328933 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9229 165712 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9230 165712 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9231 163221 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9232 163221 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9233 163221 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9234 : }
9235 328933 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9236 328933 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9237 :
9238 : // Check tank temperature by setting source inlet mass flow rate to zero.
9239 328933 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9240 :
9241 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9242 328933 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9243 :
9244 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9245 328933 : this->MaxCapacity = 0.0;
9246 328933 : this->MinCapacity = 0.0;
9247 328933 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9248 328933 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9249 328933 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9250 328933 : this->CalcWaterThermalTank(state);
9251 328933 : this->SourceEffectiveness = SourceEffectivenessBackup;
9252 328933 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9253 :
9254 : // Reset the tank's internal heating element capacity.
9255 328933 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9256 328933 : this->MinCapacity = HeatPump.BackupElementCapacity;
9257 :
9258 : // Check to see if the tank drifts below set point if no heating happens.
9259 328933 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9260 :
9261 : // HPWH is now in heating mode
9262 72148 : HeatPump.Mode = TankOperatingMode::Heating;
9263 :
9264 : // Reset the water heater's mode (call above may have changed modes)
9265 72148 : this->Mode = HeatPump.SaveWHMode;
9266 :
9267 72148 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9268 : }
9269 : }
9270 :
9271 490004 : if (HeatPump.bIsIHP) // mark the water heating call, if existing
9272 : {
9273 5070 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9274 2534 : if (HeatPump.Mode == TankOperatingMode::Heating)
9275 1258 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = true;
9276 : else
9277 1276 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = false;
9278 : }
9279 : }
9280 :
9281 : // If the HPWH was in heating mode during the last DataGlobals::TimeStep or if it was determined that
9282 : // heating would be needed during this DataGlobals::TimeStep to maintain setpoint, do the heating calculation.
9283 490004 : int SpeedNum = 0;
9284 490004 : Real64 SpeedRatio = 0.0;
9285 490004 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9286 :
9287 : // set up air flow on DX coil inlet node
9288 233199 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate =
9289 233199 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9290 :
9291 : // set the condenser inlet node mass flow rate prior to calling the DXCoils::CalcHPWHDXCoil
9292 233199 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9293 233199 : this->SourceMassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9294 :
9295 : // Do the coil and tank calculations at full PLR to see if it overshoots setpoint.
9296 233199 : bool bIterSpeed = false;
9297 233199 : if (MaxSpeedNum > 0) { // lowest speed of VS HPWH coil
9298 20583 : SpeedRatio = 1.0;
9299 20583 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9300 20583 : bIterSpeed = true; // prepare for iterating between speed levels
9301 20583 : SpeedNum = 1;
9302 20583 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9303 :
9304 20583 : if (HeatPump.bIsIHP) {
9305 2517 : bIterSpeed = false; // don't iterate speed unless match conditions below
9306 2517 : IHPMode = IntegratedHeatPump::GetCurWorkMode(state, HeatPump.DXCoilNum);
9307 :
9308 2517 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9309 : int VSCoilNum;
9310 1258 : if (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) {
9311 48 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
9312 48 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode =
9313 : IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg;
9314 : } else {
9315 1210 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).SCWHCoilIndex;
9316 1210 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IntegratedHeatPump::IHPOperationMode::SCWHMatchWH;
9317 : }
9318 :
9319 1258 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9320 :
9321 3774 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9322 : "",
9323 : VSCoilNum,
9324 : DataHVACGlobals::CycFanCycCoil,
9325 : EMP1,
9326 : EMP2,
9327 : EMP3,
9328 : DataHVACGlobals::CompressorOperation::On,
9329 1258 : state.dataWaterThermalTanks->hpPartLoadRatio,
9330 : SpeedNum,
9331 : SpeedRatio,
9332 : 0.0,
9333 : 0.0,
9334 1258 : 1.0);
9335 :
9336 1258 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IHPMode;
9337 1258 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9338 : } else {
9339 1259 : SpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9340 :
9341 1259 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9342 2518 : IntegratedHeatPump::SimIHP(state,
9343 : HeatPump.DXCoilName,
9344 : HeatPump.DXCoilNum,
9345 : DataHVACGlobals::CycFanCycCoil,
9346 : EMP1,
9347 : EMP2,
9348 : EMP3,
9349 : DataHVACGlobals::CompressorOperation::On,
9350 1259 : state.dataWaterThermalTanks->hpPartLoadRatio,
9351 : SpeedNum,
9352 : SpeedRatio,
9353 : 0.0,
9354 : 0.0,
9355 : true,
9356 : false,
9357 : 1.0);
9358 :
9359 1259 : if ((IntegratedHeatPump::IHPOperationMode::SCWHMatchWH == IHPMode) ||
9360 : (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode)) {
9361 112 : bIterSpeed = true;
9362 : } else {
9363 1147 : this->SourceMassFlowRate = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TankSourceWaterMassFlowRate;
9364 1147 : MdotWater = this->SourceMassFlowRate;
9365 : }
9366 :
9367 1259 : if (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) // turn off heater element
9368 : {
9369 0 : this->MaxCapacity = 0.0;
9370 0 : this->MinCapacity = 0.0;
9371 : }
9372 : }
9373 : } else {
9374 36132 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9375 : HeatPump.DXCoilName,
9376 : HeatPump.DXCoilNum,
9377 : DataHVACGlobals::CycFanCycCoil,
9378 : EMP1,
9379 : EMP2,
9380 : EMP3,
9381 : DataHVACGlobals::CompressorOperation::On,
9382 18066 : state.dataWaterThermalTanks->hpPartLoadRatio,
9383 : SpeedNum,
9384 : SpeedRatio,
9385 : 0.0,
9386 : 0.0,
9387 : 1.0);
9388 : }
9389 :
9390 20583 : this->CalcWaterThermalTank(state);
9391 : } else {
9392 212616 : this->ConvergeSingleSpeedHPWHCoilAndTank(state, state.dataWaterThermalTanks->hpPartLoadRatio);
9393 : }
9394 :
9395 233199 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9396 233199 : Real64 LowSpeedTankTemp = NewTankTemp;
9397 233199 : Real64 HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9398 :
9399 233199 : if (NewTankTemp > HPSetPointTemp) {
9400 14811 : HeatPump.Mode = TankOperatingMode::Floating;
9401 14811 : TankOperatingMode tmpMode = HeatPump.SaveWHMode;
9402 66889 : auto f = [&state, this, HPSetPointTemp, tmpMode, MdotWater](Real64 const HPPartLoadRatio) {
9403 : return this->PLRResidualHPWH(state, HPPartLoadRatio, HPSetPointTemp, tmpMode, MdotWater);
9404 81700 : };
9405 14811 : Real64 zeroResidual = 1.0;
9406 14811 : if (MaxSpeedNum > 0) {
9407 : // square the solving, and avoid warning
9408 : // due to very small capacity at lowest speed of VSHPWH coil
9409 1039 : if (bIterSpeed)
9410 1023 : zeroResidual = this->PLRResidualHPWH(state, 0.0, HPSetPointTemp, tmpMode, MdotWater);
9411 : else
9412 16 : zeroResidual = -1.0;
9413 : }
9414 :
9415 14811 : if (zeroResidual > 0.0) { // then iteration
9416 : int SolFla;
9417 14412 : General::SolveRoot(state, Acc, MaxIte, SolFla, state.dataWaterThermalTanks->hpPartLoadRatio, f, 0.0, 1.0);
9418 14412 : if (SolFla == -1) {
9419 0 : std::string IterNum;
9420 0 : IterNum = fmt::to_string(MaxIte);
9421 0 : if (!state.dataGlobal->WarmupFlag) {
9422 0 : ++HeatPump.IterLimitExceededNum2;
9423 0 : if (HeatPump.IterLimitExceededNum2 == 1) {
9424 0 : ShowWarningError(state, HeatPump.Type + " \"" + HeatPump.Name + "\"");
9425 0 : ShowContinueError(state,
9426 0 : format("Iteration limit exceeded calculating heat pump water heater compressor part-load ratio, "
9427 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
9428 : IterNum,
9429 0 : state.dataWaterThermalTanks->hpPartLoadRatio));
9430 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9431 : } else {
9432 0 : ShowRecurringWarningErrorAtEnd(
9433 : state,
9434 0 : HeatPump.Type + " \"" + HeatPump.Name +
9435 : "\": Iteration limit exceeded in float mode warning continues. Part-load ratio statistics follow.",
9436 : HeatPump.IterLimitErrIndex2,
9437 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9438 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9439 : }
9440 : }
9441 14412 : } else if (SolFla == -2) {
9442 304 : state.dataWaterThermalTanks->hpPartLoadRatio =
9443 304 : max(0.0, min(1.0, (HPSetPointTemp - savedTankTemp) / (NewTankTemp - savedTankTemp)));
9444 304 : if (!state.dataGlobal->WarmupFlag) {
9445 152 : ++HeatPump.RegulaFalsiFailedNum2;
9446 152 : if (HeatPump.RegulaFalsiFailedNum2 == 1) {
9447 1 : ShowWarningError(state, HeatPump.Type + " \"" + HeatPump.Name + "\"");
9448 3 : ShowContinueError(state,
9449 3 : format("Heat pump water heater compressor part-load ratio calculation failed: PLR limits of 0 to 1 "
9450 : "exceeded. Part-load ratio used = {:.3R}",
9451 2 : state.dataWaterThermalTanks->hpPartLoadRatio));
9452 1 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9453 1 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9454 : } else {
9455 755 : ShowRecurringWarningErrorAtEnd(
9456 : state,
9457 302 : HeatPump.Type + " \"" + HeatPump.Name +
9458 : "\": Part-load ratio calculation failed in float mode warning continues. Part-load ratio statistics follow.",
9459 : HeatPump.RegulaFalsiFailedIndex2,
9460 151 : state.dataWaterThermalTanks->hpPartLoadRatio,
9461 151 : state.dataWaterThermalTanks->hpPartLoadRatio);
9462 : }
9463 : }
9464 : }
9465 : } else {
9466 399 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9467 : }
9468 :
9469 : // Re-calculate the HPWH Coil to get the correct heat transfer rate.
9470 14811 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9471 14811 : if (MaxSpeedNum > 0) {
9472 1039 : SpeedRatio = 1.0;
9473 1039 : SpeedNum = 1;
9474 :
9475 1039 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9476 :
9477 1039 : if (HeatPump.bIsIHP) {
9478 32 : if (bIterSpeed) {
9479 32 : IntegratedHeatPump::SimIHP(state,
9480 : HeatPump.DXCoilName,
9481 : HeatPump.DXCoilNum,
9482 : DataHVACGlobals::CycFanCycCoil,
9483 : EMP1,
9484 : EMP2,
9485 : EMP3,
9486 : DataHVACGlobals::CompressorOperation::On,
9487 16 : state.dataWaterThermalTanks->hpPartLoadRatio,
9488 : SpeedNum,
9489 : SpeedRatio,
9490 : 0.0,
9491 : 0.0,
9492 : true,
9493 : false,
9494 : 1.0);
9495 : }
9496 : } else {
9497 2014 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9498 : HeatPump.DXCoilName,
9499 : HeatPump.DXCoilNum,
9500 : DataHVACGlobals::CycFanCycCoil,
9501 : EMP1,
9502 : EMP2,
9503 : EMP3,
9504 : DataHVACGlobals::CompressorOperation::On,
9505 1007 : state.dataWaterThermalTanks->hpPartLoadRatio,
9506 : SpeedNum,
9507 : SpeedRatio,
9508 : 0.0,
9509 : 0.0,
9510 : 1.0);
9511 : }
9512 :
9513 : } else {
9514 13772 : DXCoils::CalcHPWHDXCoil(state, HeatPump.DXCoilNum, state.dataWaterThermalTanks->hpPartLoadRatio);
9515 : }
9516 218388 : } else if (bIterSpeed) {
9517 43889 : for (int loopIter = 1; loopIter <= 4; ++loopIter) {
9518 43889 : HeatPump.Mode = TankOperatingMode::Heating; // modHeatMode is important for system convergence
9519 43889 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9520 43889 : SpeedRatio = 1.0;
9521 43889 : int LowSpeedNum = 2;
9522 43889 : if (HeatPump.bIsIHP) {
9523 272 : LowSpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9524 272 : MaxSpeedNum = IntegratedHeatPump::GetMaxSpeedNumIHP(state, HeatPump.DXCoilNum);
9525 : }
9526 :
9527 356538 : for (int i = LowSpeedNum; i <= MaxSpeedNum; ++i) {
9528 324425 : SpeedNum = i;
9529 324425 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9530 324425 : if (HeatPump.bIsIHP) {
9531 5248 : IntegratedHeatPump::SimIHP(state,
9532 : HeatPump.DXCoilName,
9533 : HeatPump.DXCoilNum,
9534 : DataHVACGlobals::CycFanCycCoil,
9535 : EMP1,
9536 : EMP2,
9537 : EMP3,
9538 : DataHVACGlobals::CompressorOperation::On,
9539 2624 : state.dataWaterThermalTanks->hpPartLoadRatio,
9540 : SpeedNum,
9541 : SpeedRatio,
9542 : 0.0,
9543 : 0.0,
9544 : true,
9545 : false,
9546 : 1.0);
9547 : } else {
9548 643602 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9549 : HeatPump.DXCoilName,
9550 : HeatPump.DXCoilNum,
9551 : DataHVACGlobals::CycFanCycCoil,
9552 : EMP1,
9553 : EMP2,
9554 : EMP3,
9555 : DataHVACGlobals::CompressorOperation::On,
9556 321801 : state.dataWaterThermalTanks->hpPartLoadRatio,
9557 : SpeedNum,
9558 : SpeedRatio,
9559 : 0.0,
9560 : 0.0,
9561 : 1.0);
9562 : }
9563 :
9564 : // HPWH condenser water temperature difference
9565 324425 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9566 :
9567 : // move the full load outlet temperature rate to the water heater structure variables
9568 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has
9569 : // been called)
9570 324425 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9571 : // this CALL does not update node temps, must use WaterThermalTank variables
9572 : // select tank type
9573 324425 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9574 317463 : this->CalcWaterThermalTankMixed(state);
9575 317463 : NewTankTemp = this->TankTemp;
9576 6962 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9577 6962 : this->CalcWaterThermalTankStratified(state);
9578 6962 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9579 : }
9580 :
9581 324425 : if (NewTankTemp > HPSetPointTemp) {
9582 11776 : SpeedNum = i;
9583 11776 : break;
9584 : } else {
9585 312649 : LowSpeedTankTemp = NewTankTemp;
9586 : }
9587 : }
9588 :
9589 43889 : if (NewTankTemp > HPSetPointTemp) {
9590 : int SolFla;
9591 23552 : std::string IterNum;
9592 : auto f = [&state, this, SpeedNum, HPWaterInletNode, HPWaterOutletNode, RhoWater, HPSetPointTemp, &HeatPump, FirstHVACIteration](
9593 304120 : Real64 const SpeedRatio) {
9594 228090 : return this->PLRResidualIterSpeed(state,
9595 : SpeedRatio,
9596 : this->HeatPumpNum,
9597 : SpeedNum,
9598 : HPWaterInletNode,
9599 : HPWaterOutletNode,
9600 : RhoWater,
9601 : HPSetPointTemp,
9602 : HeatPump.SaveWHMode,
9603 : FirstHVACIteration);
9604 87806 : };
9605 11776 : General::SolveRoot(state, Acc, MaxIte, SolFla, SpeedRatio, f, 1.0e-10, 1.0);
9606 :
9607 11776 : if (SolFla == -1) {
9608 0 : IterNum = fmt::to_string(MaxIte);
9609 0 : if (!state.dataGlobal->WarmupFlag) {
9610 0 : ++HeatPump.IterLimitExceededNum1;
9611 0 : if (HeatPump.IterLimitExceededNum1 == 1) {
9612 0 : ShowWarningError(state, HeatPump.Type + " \"" + HeatPump.Name + "\"");
9613 0 : ShowContinueError(state,
9614 0 : format("Iteration limit exceeded calculating heat pump water heater speed speed ratio ratio, "
9615 : "maximum iterations = {}. speed ratio returned = {:.3R}",
9616 : IterNum,
9617 0 : SpeedRatio));
9618 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9619 : } else {
9620 0 : ShowRecurringWarningErrorAtEnd(
9621 : state,
9622 0 : HeatPump.Type + " \"" + HeatPump.Name +
9623 : "\": Iteration limit exceeded in heating mode warning continues. speed ratio statistics follow.",
9624 : HeatPump.IterLimitErrIndex1,
9625 : SpeedRatio,
9626 : SpeedRatio);
9627 : }
9628 : }
9629 11776 : } else if (SolFla == -2) {
9630 0 : SpeedRatio = max(0.0, min(1.0, (HPSetPointTemp - LowSpeedTankTemp) / (NewTankTemp - LowSpeedTankTemp)));
9631 0 : if (!state.dataGlobal->WarmupFlag) {
9632 0 : ++HeatPump.RegulaFalsiFailedNum1;
9633 0 : if (HeatPump.RegulaFalsiFailedNum1 == 1) {
9634 0 : ShowWarningError(state, HeatPump.Type + " \"" + HeatPump.Name + "\"");
9635 0 : ShowContinueError(state,
9636 0 : format("Heat pump water heater speed ratio calculation failed: speed ratio limits of 0 to 1 "
9637 : "exceeded. speed ratio used = {:.3R}",
9638 0 : SpeedRatio));
9639 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9640 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9641 : } else {
9642 0 : ShowRecurringWarningErrorAtEnd(
9643 : state,
9644 0 : HeatPump.Type + " \"" + HeatPump.Name +
9645 : "\": Speed ratio calculation failed in heating mode warning continues. Speed ratio statistics follow.",
9646 : HeatPump.RegulaFalsiFailedIndex1,
9647 : SpeedRatio,
9648 : SpeedRatio);
9649 : }
9650 : }
9651 : }
9652 : } else {
9653 32113 : SpeedNum = MaxSpeedNum;
9654 32113 : SpeedRatio = 1.0;
9655 : }
9656 :
9657 43889 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9658 43889 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9659 :
9660 43889 : if (HeatPump.bIsIHP) {
9661 544 : IntegratedHeatPump::SimIHP(state,
9662 : HeatPump.DXCoilName,
9663 : HeatPump.DXCoilNum,
9664 : DataHVACGlobals::CycFanCycCoil,
9665 : EMP1,
9666 : EMP2,
9667 : EMP3,
9668 : DataHVACGlobals::CompressorOperation::On,
9669 272 : state.dataWaterThermalTanks->hpPartLoadRatio,
9670 : SpeedNum,
9671 : SpeedRatio,
9672 : 0.0,
9673 : 0.0,
9674 : true,
9675 : false,
9676 : 1.0);
9677 : } else {
9678 87234 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9679 : HeatPump.DXCoilName,
9680 : HeatPump.DXCoilNum,
9681 : DataHVACGlobals::CycFanCycCoil,
9682 : EMP1,
9683 : EMP2,
9684 : EMP3,
9685 : DataHVACGlobals::CompressorOperation::On,
9686 43617 : state.dataWaterThermalTanks->hpPartLoadRatio,
9687 : SpeedNum,
9688 : SpeedRatio,
9689 : 0.0,
9690 : 0.0,
9691 : 1.0);
9692 : }
9693 :
9694 : // HPWH condenser water temperature difference
9695 43889 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9696 :
9697 : // move the full load outlet temperature rate to the water heater structure variables
9698 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been
9699 : // called)
9700 43889 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9701 : // this CALL does not update node temps, must use WaterThermalTank variables
9702 : // select tank type
9703 43889 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9704 40408 : this->CalcWaterThermalTankMixed(state);
9705 40408 : NewTankTemp = this->TankTemp;
9706 3481 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9707 3481 : this->CalcWaterThermalTankStratified(state);
9708 3481 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9709 : }
9710 : // update inlet temp
9711 43889 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9712 43889 : if (std::abs(state.dataLoopNodes->Node(HPWaterInletNode).Temp - HPWHCondInletNodeLast) < DataHVACGlobals::SmallTempDiff) break;
9713 26734 : HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9714 : }
9715 :
9716 : } else {
9717 : // Set the PLR to 1 if we're not going to reach setpoint during this DataGlobals::TimeStep.
9718 201233 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9719 : }
9720 : }
9721 :
9722 490004 : if (HeatPump.bIsIHP) {
9723 5070 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9724 2534 : IntegratedHeatPump::ClearCoils(state, HeatPump.DXCoilNum); // clear node info when checking the heating load
9725 : }
9726 : }
9727 :
9728 : // set air-side mass flow rate for final calculation
9729 490004 : if (InletAirMixerNode > 0) {
9730 25611 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate =
9731 25611 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9732 51222 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio *
9733 25611 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule);
9734 25611 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9735 25611 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * state.dataWaterThermalTanks->mixerInletAirSchedule;
9736 : // IF HPWH is off, pass zone node conditions through HPWH air-side
9737 25611 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0)
9738 18404 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
9739 : } else {
9740 464393 : if (OutdoorAirNode == 0) {
9741 335997 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate =
9742 335997 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9743 : } else {
9744 128396 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9745 128396 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9746 : }
9747 : }
9748 490004 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0) this->SourceInletTemp = this->SourceOutletTemp;
9749 :
9750 : // set water-side mass flow rate for final calculation
9751 490004 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9752 :
9753 490004 : if (MaxSpeedNum > 0) {
9754 :
9755 : // it is important to use mdotAir to reset the notes, otherwise, could fail to converge
9756 42360 : if (InletAirMixerNode > 0) {
9757 5064 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9758 5064 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9759 : } else {
9760 37296 : if (OutdoorAirNode == 0) {
9761 10082 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9762 10082 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9763 : } else {
9764 27214 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9765 27214 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9766 : }
9767 : }
9768 :
9769 : // set the max mass flow rate for outdoor fans
9770 42360 : state.dataLoopNodes->Node(HeatPump.FanOutletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9771 :
9772 42360 : if (HeatPump.bIsIHP) {
9773 : // pass node information using resulting PLR
9774 5070 : if (HeatPump.FanPlacement == DataHVACGlobals::BlowThru) {
9775 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9776 5070 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9777 0 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9778 : } else {
9779 5070 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9780 : }
9781 10140 : IntegratedHeatPump::SimIHP(state,
9782 : HeatPump.DXCoilName,
9783 : HeatPump.DXCoilNum,
9784 : DataHVACGlobals::CycFanCycCoil,
9785 : EMP1,
9786 : EMP2,
9787 : EMP3,
9788 : CompressorOp,
9789 5070 : state.dataWaterThermalTanks->hpPartLoadRatio,
9790 : SpeedNum,
9791 : SpeedRatio,
9792 : 0.0,
9793 : 0.0,
9794 : true,
9795 : false,
9796 : 1.0);
9797 5070 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9798 0 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9799 : } else {
9800 5070 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9801 : }
9802 10140 : IntegratedHeatPump::SimIHP(state,
9803 : HeatPump.DXCoilName,
9804 : HeatPump.DXCoilNum,
9805 : DataHVACGlobals::CycFanCycCoil,
9806 : EMP1,
9807 : EMP2,
9808 : EMP3,
9809 : CompressorOp,
9810 5070 : state.dataWaterThermalTanks->hpPartLoadRatio,
9811 : SpeedNum,
9812 : SpeedRatio,
9813 : 0.0,
9814 : 0.0,
9815 : true,
9816 : false,
9817 : 1.0);
9818 : } else {
9819 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9820 0 : IntegratedHeatPump::SimIHP(state,
9821 : HeatPump.DXCoilName,
9822 : HeatPump.DXCoilNum,
9823 : DataHVACGlobals::CycFanCycCoil,
9824 : EMP1,
9825 : EMP2,
9826 : EMP3,
9827 : CompressorOp,
9828 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9829 : SpeedNum,
9830 : SpeedRatio,
9831 : 0.0,
9832 : 0.0,
9833 : true,
9834 : false,
9835 : 1.0);
9836 0 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9837 0 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9838 : } else {
9839 0 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9840 : }
9841 0 : IntegratedHeatPump::SimIHP(state,
9842 : HeatPump.DXCoilName,
9843 : HeatPump.DXCoilNum,
9844 : DataHVACGlobals::CycFanCycCoil,
9845 : EMP1,
9846 : EMP2,
9847 : EMP3,
9848 : CompressorOp,
9849 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9850 : SpeedNum,
9851 : SpeedRatio,
9852 : 0.0,
9853 : 0.0,
9854 : true,
9855 : false,
9856 : 1.0);
9857 0 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9858 0 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9859 : } else {
9860 0 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9861 : }
9862 : }
9863 : } else {
9864 : // pass node information using resulting PLR
9865 37290 : if (HeatPump.FanPlacement == DataHVACGlobals::BlowThru) {
9866 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9867 27162 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9868 24812 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9869 : } else {
9870 2350 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9871 : }
9872 54324 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9873 : HeatPump.DXCoilName,
9874 : HeatPump.DXCoilNum,
9875 : DataHVACGlobals::CycFanCycCoil,
9876 : EMP1,
9877 : EMP2,
9878 : EMP3,
9879 : CompressorOp,
9880 27162 : state.dataWaterThermalTanks->hpPartLoadRatio,
9881 : SpeedNum,
9882 : SpeedRatio,
9883 : 0.0,
9884 : 0.0,
9885 : 1.0);
9886 27162 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9887 24812 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9888 : } else {
9889 2350 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9890 : }
9891 54324 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9892 : HeatPump.DXCoilName,
9893 : HeatPump.DXCoilNum,
9894 : DataHVACGlobals::CycFanCycCoil,
9895 : EMP1,
9896 : EMP2,
9897 : EMP3,
9898 : CompressorOp,
9899 27162 : state.dataWaterThermalTanks->hpPartLoadRatio,
9900 : SpeedNum,
9901 : SpeedRatio,
9902 : 0.0,
9903 : 0.0,
9904 : 1.0);
9905 : } else {
9906 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9907 20256 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9908 : HeatPump.DXCoilName,
9909 : HeatPump.DXCoilNum,
9910 : DataHVACGlobals::CycFanCycCoil,
9911 : EMP1,
9912 : EMP2,
9913 : EMP3,
9914 : CompressorOp,
9915 10128 : state.dataWaterThermalTanks->hpPartLoadRatio,
9916 : SpeedNum,
9917 : SpeedRatio,
9918 : 0.0,
9919 : 0.0,
9920 : 1.0);
9921 10128 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9922 10128 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9923 : } else {
9924 0 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9925 : }
9926 20256 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9927 : HeatPump.DXCoilName,
9928 : HeatPump.DXCoilNum,
9929 : DataHVACGlobals::CycFanCycCoil,
9930 : EMP1,
9931 : EMP2,
9932 : EMP3,
9933 : CompressorOp,
9934 10128 : state.dataWaterThermalTanks->hpPartLoadRatio,
9935 : SpeedNum,
9936 : SpeedRatio,
9937 : 0.0,
9938 : 0.0,
9939 : 1.0);
9940 10128 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9941 10128 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9942 : } else {
9943 0 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9944 : }
9945 : }
9946 : }
9947 : } else { // single speed
9948 :
9949 : // pass node information using resulting PLR
9950 447644 : if (HeatPump.FanPlacement == DataHVACGlobals::BlowThru) {
9951 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9952 76246 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9953 42190 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9954 : } else {
9955 34056 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9956 : }
9957 152492 : DXCoils::SimDXCoil(state,
9958 : HeatPump.DXCoilName,
9959 : CompressorOp,
9960 : FirstHVACIteration,
9961 : HeatPump.DXCoilNum,
9962 : DataHVACGlobals::CycFanCycCoil,
9963 76246 : state.dataWaterThermalTanks->hpPartLoadRatio);
9964 76246 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9965 42190 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9966 : } else {
9967 34056 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9968 : }
9969 152492 : DXCoils::SimDXCoil(state,
9970 : HeatPump.DXCoilName,
9971 : CompressorOp,
9972 : FirstHVACIteration,
9973 : HeatPump.DXCoilNum,
9974 : DataHVACGlobals::CycFanCycCoil,
9975 76246 : state.dataWaterThermalTanks->hpPartLoadRatio);
9976 : } else {
9977 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9978 742796 : DXCoils::SimDXCoil(state,
9979 : HeatPump.DXCoilName,
9980 : CompressorOp,
9981 : FirstHVACIteration,
9982 : HeatPump.DXCoilNum,
9983 : DataHVACGlobals::CycFanCycCoil,
9984 371398 : state.dataWaterThermalTanks->hpPartLoadRatio);
9985 371398 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9986 177640 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9987 : } else {
9988 193758 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
9989 : }
9990 742796 : DXCoils::SimDXCoil(state,
9991 : HeatPump.DXCoilName,
9992 : CompressorOp,
9993 : FirstHVACIteration,
9994 : HeatPump.DXCoilNum,
9995 : DataHVACGlobals::CycFanCycCoil,
9996 371398 : state.dataWaterThermalTanks->hpPartLoadRatio);
9997 371398 : if (HeatPump.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
9998 177640 : state.dataHVACFan->fanObjs[HeatPump.FanNum]->simulate(state, _, _, _, _);
9999 : } else {
10000 193758 : Fans::SimulateFanComponents(state, HeatPump.FanName, FirstHVACIteration, HeatPump.FanNum);
10001 : }
10002 : }
10003 : }
10004 :
10005 : // Call the tank one more time with the final PLR
10006 490004 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10007 213438 : this->CalcWaterThermalTankMixed(state);
10008 276566 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10009 276566 : this->CalcWaterThermalTankStratified(state);
10010 : } else {
10011 0 : assert(0);
10012 : }
10013 :
10014 : // set HPWH outlet node equal to the outlet air splitter node conditions if outlet air splitter node exists
10015 490004 : if (OutletAirSplitterNode > 0) {
10016 25611 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
10017 25611 : state.dataLoopNodes->Node(ExhaustAirNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
10018 : }
10019 :
10020 : // Check schedule to divert air-side cooling to outdoors.
10021 490004 : if (HeatPump.OutletAirSplitterSchPtr > 0) {
10022 25611 : Real64 OutletAirSplitterSch = ScheduleManager::GetCurrentScheduleValue(state, HeatPump.OutletAirSplitterSchPtr);
10023 25611 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate =
10024 25611 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * (1.0 - OutletAirSplitterSch);
10025 25611 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate =
10026 25611 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * OutletAirSplitterSch;
10027 : }
10028 :
10029 490004 : HeatPump.HeatingPLR = state.dataWaterThermalTanks->hpPartLoadRatio;
10030 490004 : HeatPump.OnCycParaFuelRate = HeatPump.OnCycParaLoad * state.dataWaterThermalTanks->hpPartLoadRatio;
10031 490004 : HeatPump.OnCycParaFuelEnergy = HeatPump.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
10032 490004 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
10033 490004 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
10034 490004 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10035 213438 : HeatPump.ControlTempAvg = this->TankTempAvg;
10036 213438 : HeatPump.ControlTempFinal = this->TankTemp;
10037 276566 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10038 276566 : HeatPump.ControlTempAvg = this->FindStratifiedTankSensedTemp(state, true);
10039 276566 : HeatPump.ControlTempFinal = this->FindStratifiedTankSensedTemp(state);
10040 : } else {
10041 0 : assert(0);
10042 : }
10043 :
10044 490004 : switch (HeatPump.InletAirConfiguration) {
10045 :
10046 : // no sensible capacity to zone for outdoor and scheduled HPWH
10047 152534 : case WTTAmbientTemp::OutsideAir:
10048 : case WTTAmbientTemp::Schedule: {
10049 152534 : HeatPump.HPWaterHeaterSensibleCapacity = 0.0;
10050 152534 : HeatPump.HPWaterHeaterLatentCapacity = 0.0;
10051 :
10052 : // calculate sensible capacity to zone for inlet air configuration equals Zone Only or Zone And Outdoor Air configurations
10053 152534 : break;
10054 : }
10055 337470 : default:
10056 :
10057 337470 : Real64 CpAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(HPAirInletNode).HumRat);
10058 :
10059 : // add parasitics to zone heat balance if parasitic heat load is to zone otherwise neglect parasitics
10060 337470 : if (HeatPump.ParasiticTempIndicator == WTTAmbientTemp::TempZone) {
10061 204242 : HeatPump.HPWaterHeaterSensibleCapacity =
10062 408484 : (state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
10063 408484 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp)) +
10064 408484 : HeatPump.OnCycParaFuelRate + HeatPump.OffCycParaFuelRate;
10065 : } else {
10066 133228 : HeatPump.HPWaterHeaterSensibleCapacity =
10067 266456 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
10068 133228 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp);
10069 : }
10070 :
10071 674940 : HeatPump.HPWaterHeaterLatentCapacity = state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate *
10072 337470 : (state.dataLoopNodes->Node(HPAirOutletNode).HumRat - state.dataLoopNodes->Node(HPAirInletNode).HumRat);
10073 337470 : break;
10074 : }
10075 : }
10076 :
10077 1474867 : void WaterThermalTankData::CalcWaterThermalTank(EnergyPlusData &state)
10078 : {
10079 1474867 : switch (this->WaterThermalTankType) {
10080 560734 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
10081 560734 : this->CalcWaterThermalTankMixed(state);
10082 560734 : break;
10083 914133 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
10084 914133 : this->CalcWaterThermalTankStratified(state);
10085 914133 : break;
10086 0 : default:
10087 0 : assert(false);
10088 : }
10089 1474867 : }
10090 :
10091 791115 : Real64 WaterThermalTankData::GetHPWHSensedTankTemp(EnergyPlusData &state)
10092 : {
10093 791115 : switch (this->WaterThermalTankType) {
10094 310157 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
10095 310157 : return this->TankTemp;
10096 : break;
10097 480958 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
10098 480958 : return this->FindStratifiedTankSensedTemp(state);
10099 : break;
10100 0 : default:
10101 0 : assert(false);
10102 : return 0.0; // silence compiler
10103 : }
10104 : }
10105 :
10106 212616 : void WaterThermalTankData::ConvergeSingleSpeedHPWHCoilAndTank(EnergyPlusData &state, Real64 const partLoadRatio)
10107 : {
10108 212616 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
10109 212616 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
10110 :
10111 212616 : Real64 PrevTankTemp = this->SourceOutletTemp;
10112 742149 : for (int i = 1; i <= 10; ++i) {
10113 :
10114 742146 : DXCoils::CalcHPWHDXCoil(state, HPWH.DXCoilNum, partLoadRatio);
10115 742146 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWH.CondWaterOutletNode).Temp;
10116 :
10117 742146 : this->CalcWaterThermalTank(state);
10118 742146 : state.dataLoopNodes->Node(Coil.WaterInNode).Temp = this->SourceOutletTemp;
10119 :
10120 742146 : if (std::abs(this->SourceOutletTemp - PrevTankTemp) < DataHVACGlobals::SmallTempDiff) {
10121 212613 : break;
10122 : }
10123 :
10124 529533 : PrevTankTemp = this->SourceOutletTemp;
10125 : }
10126 212616 : }
10127 :
10128 508311 : void WaterThermalTankData::SetVSHPWHFlowRates(EnergyPlusData &state,
10129 : HeatPumpWaterHeaterData &HPWH, // heat pump coil
10130 : int const SpeedNum, // upper speed number
10131 : Real64 const SpeedRatio, // interpolation ration between upper and lower speed
10132 : Real64 const WaterDens, // tank water density
10133 : Real64 &MdotWater, // water flow rate
10134 : bool const FirstHVACIteration)
10135 : {
10136 : // FUNCTION INFORMATION:
10137 : // AUTHOR B.Shen, ORNL, 12/2014
10138 : // DATE WRITTEN May 2005
10139 : // MODIFIED
10140 : // RE-ENGINEERED
10141 :
10142 : // PURPOSE OF THIS FUNCTION:
10143 : // set water and air flow rates driven by the variable-speed HPWH coil
10144 : // calculate resultant HPWH coil output
10145 :
10146 508311 : int SpeedLow = SpeedNum - 1;
10147 508311 : if (SpeedLow < 1) SpeedLow = 1;
10148 :
10149 508311 : int HPWaterInletNode = HPWH.CondWaterInletNode;
10150 508311 : int DXCoilAirInletNode = HPWH.DXCoilAirInletNode;
10151 508311 : if (HPWH.bIsIHP) {
10152 19516 : HPWH.OperatingWaterFlowRate = IntegratedHeatPump::GetWaterVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio);
10153 19516 : state.dataWaterThermalTanks->mdotAir = IntegratedHeatPump::GetAirMassFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10154 19516 : HPWH.OperatingAirFlowRate = IntegratedHeatPump::GetAirVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10155 19516 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10156 19516 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10157 19516 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10158 : } else {
10159 488795 : HPWH.OperatingWaterFlowRate = HPWH.HPWHWaterVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHWaterVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10160 488795 : HPWH.OperatingAirFlowRate = HPWH.HPWHAirVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10161 488795 : state.dataWaterThermalTanks->mdotAir =
10162 488795 : HPWH.HPWHAirMassFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirMassFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10163 : }
10164 :
10165 508311 : MdotWater = HPWH.OperatingWaterFlowRate * WaterDens;
10166 508311 : this->SourceMassFlowRate = MdotWater;
10167 :
10168 508311 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10169 508311 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater;
10170 508311 : this->SourceMassFlowRate = MdotWater;
10171 :
10172 508311 : if (HPWH.InletAirMixerNode > 0) {
10173 21698 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10174 21698 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10175 : } else {
10176 486613 : if (HPWH.OutsideAirNode == 0) {
10177 31241 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10178 31241 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10179 : } else {
10180 455372 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10181 455372 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10182 : }
10183 : }
10184 :
10185 : // put fan component first, regardless placement, to calculate fan power
10186 : int FanInNode;
10187 508311 : if (HPWH.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
10188 423368 : FanInNode = state.dataHVACFan->fanObjs[HPWH.FanNum]->inletNodeNum;
10189 : } else {
10190 84943 : FanInNode = state.dataFans->Fan(HPWH.FanNum).InletNodeNum;
10191 : }
10192 :
10193 508311 : state.dataLoopNodes->Node(FanInNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10194 508311 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10195 508311 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10196 508311 : if (HPWH.FanType_Num != DataHVACGlobals::FanType_SystemModelObject) {
10197 84943 : state.dataFans->Fan(HPWH.FanNum).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10198 : } // system fan will use the inlet node max avail.
10199 :
10200 508311 : if (HPWH.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
10201 423368 : state.dataHVACFan->fanObjs[HPWH.FanNum]->simulate(state, _, _, _, _);
10202 : } else {
10203 84943 : Fans::SimulateFanComponents(state, HPWH.FanName, FirstHVACIteration, HPWH.FanNum);
10204 : }
10205 508311 : }
10206 :
10207 76030 : Real64 WaterThermalTankData::PLRResidualIterSpeed(EnergyPlusData &state,
10208 : Real64 const SpeedRatio, // speed ratio between two speed levels
10209 : int const HPNum,
10210 : int const SpeedNum,
10211 : int const HPWaterInletNode,
10212 : int const HPWaterOutletNode,
10213 : Real64 const RhoWater,
10214 : Real64 const desTankTemp,
10215 : TankOperatingMode const mode,
10216 : bool const FirstHVACIteration)
10217 : {
10218 : // FUNCTION INFORMATION:
10219 : // AUTHOR B.Shen, ORNL, 12/2014
10220 : // MODIFIED
10221 : // RE-ENGINEERED
10222 :
10223 : // PURPOSE OF THIS FUNCTION:
10224 : // Calculates residual function (desired tank temp - actual tank temp), when iterating speed ration between two speed levels
10225 : // HP water heater output depends on the speed ratio which is being varied to zero the residual.
10226 :
10227 : // METHODOLOGY EMPLOYED:
10228 : // Calls residuals to get tank temperature at the given speed ratio between a lower and an upper speed levels
10229 : // and calculates the residual as defined respectively for DataPlant::PlantEquipmentType::WtrHeaterMixed or
10230 : // DataPlant::PlantEquipmentType::WtrHeaterStratified
10231 :
10232 76030 : Real64 EMP1(0.0), EMP2(0.0), EMP3(0.0); // place holder to calling variable-speed coil function
10233 76030 : this->Mode = mode;
10234 76030 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
10235 76030 : Real64 MdotWater = 0.0;
10236 :
10237 76030 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
10238 :
10239 76030 : this->SetVSHPWHFlowRates(state, HPWH, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
10240 :
10241 76030 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
10242 384 : IntegratedHeatPump::SimIHP(state,
10243 96 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10244 96 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10245 : DataHVACGlobals::CycFanCycCoil,
10246 : EMP1,
10247 : EMP2,
10248 : EMP3,
10249 : DataHVACGlobals::CompressorOperation::On,
10250 96 : state.dataWaterThermalTanks->hpPartLoadRatio,
10251 : SpeedNum,
10252 : SpeedRatio,
10253 : 0.0,
10254 : 0.0,
10255 : true,
10256 : false,
10257 : 1.0);
10258 : } else {
10259 303736 : VariableSpeedCoils::SimVariableSpeedCoils(state,
10260 75934 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10261 75934 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10262 : DataHVACGlobals::CycFanCycCoil,
10263 : EMP1,
10264 : EMP2,
10265 : EMP3,
10266 : DataHVACGlobals::CompressorOperation::On,
10267 75934 : state.dataWaterThermalTanks->hpPartLoadRatio,
10268 : SpeedNum,
10269 : SpeedRatio,
10270 : 0.0,
10271 : 0.0,
10272 : 1.0);
10273 : }
10274 :
10275 : Real64 CondenserDeltaT;
10276 76030 : CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
10277 :
10278 : // move the full load outlet temperature rate to the water heater structure variables
10279 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been called)
10280 76030 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
10281 :
10282 : // this CALL does not update node temps, must use WaterThermalTank variables
10283 : // select tank type
10284 76030 : Real64 NewTankTemp = 0.0;
10285 76030 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10286 24885 : this->CalcWaterThermalTankMixed(state);
10287 24885 : NewTankTemp = this->TankTemp;
10288 51145 : } else if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10289 51145 : this->CalcWaterThermalTankStratified(state);
10290 51145 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
10291 : }
10292 :
10293 76030 : return desTankTemp - NewTankTemp;
10294 : }
10295 :
10296 67912 : Real64 WaterThermalTankData::PLRResidualHPWH(
10297 : EnergyPlusData &state, Real64 const HPPartLoadRatio, Real64 const desTankTemp, TankOperatingMode const mode, Real64 const mDotWater)
10298 : {
10299 : // FUNCTION INFORMATION:
10300 : // AUTHOR B.Griffith, Richard Raustad
10301 : // DATE WRITTEN Jan 2012
10302 : // MODIFIED
10303 : // RE-ENGINEERED Noel Merket, Oct 2015
10304 :
10305 : // PURPOSE OF THIS FUNCTION:
10306 : // Calculates residual function (desired tank temp - actual tank temp)
10307 : // HP water heater output depends on the part load ratio which is being varied to zero the residual.
10308 :
10309 : // METHODOLOGY EMPLOYED:
10310 : // Calls with CalcWaterThermalTankMixed or CalcWaterThermalTankStratified to get tank temperature at the given part load ratio (source water
10311 : // mass flow rate) and calculates the residual as defined above
10312 :
10313 67912 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
10314 67912 : bool const isVariableSpeed = (HeatPump.NumofSpeed > 0);
10315 67912 : this->Mode = mode;
10316 : // Apply the PLR
10317 67912 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10318 : // For a mixed tank, the PLR is applied to the source mass flow rate.
10319 26534 : this->SourceMassFlowRate = mDotWater * HPPartLoadRatio;
10320 26534 : this->CalcWaterThermalTankMixed(state);
10321 : } else {
10322 41378 : assert(this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified);
10323 : // For a stratified tank, the PLR is applied to the Coil.TotalHeatingEnergyRate
10324 : // whether that's a VarSpeedCoil or DXCoils::DXCoil.
10325 : // Here we create a pointer to the TotalHeatingEnergyRate for the appropriate coil type.
10326 : Real64 *CoilTotalHeatingEnergyRatePtr;
10327 41378 : if (isVariableSpeed) {
10328 621 : if (HeatPump.bIsIHP)
10329 0 : CoilTotalHeatingEnergyRatePtr = &state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TotalWaterHeatingRate;
10330 : else
10331 621 : CoilTotalHeatingEnergyRatePtr = &state.dataVariableSpeedCoils->VarSpeedCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10332 : } else {
10333 40757 : CoilTotalHeatingEnergyRatePtr = &state.dataDXCoils->DXCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10334 : }
10335 : // Copy the value of the total heating energy rate
10336 41378 : Real64 const CoilTotalHeatingEnergyRateBackup = *CoilTotalHeatingEnergyRatePtr;
10337 : // Apply the PLR
10338 41378 : *CoilTotalHeatingEnergyRatePtr *= HPPartLoadRatio;
10339 : // Tank Calculation
10340 41378 : this->CalcWaterThermalTankStratified(state);
10341 : // Restore the original value
10342 41378 : *CoilTotalHeatingEnergyRatePtr = CoilTotalHeatingEnergyRateBackup;
10343 : }
10344 67912 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
10345 67912 : return desTankTemp - NewTankTemp;
10346 : }
10347 :
10348 1316276 : bool WaterThermalTankData::SourceHeatNeed(EnergyPlusData &state, Real64 const OutletTemp, Real64 const DeadBandTemp, Real64 const SetPointTemp_loc)
10349 : {
10350 : // FUNCTION INFORMATION:
10351 : // AUTHOR Yueyue Zhou
10352 : // DATE WRITTEN May 2019
10353 : // MODIFIED na
10354 : // RE-ENGINEERED na
10355 :
10356 : // PURPOSE OF THIS FUNCTION:
10357 : // Determine by tank type, tank temperature and control mode if source side flow is needed
10358 :
10359 : // return value initialization
10360 1316276 : bool NeedsHeatOrCool = false;
10361 :
10362 1316276 : if (!this->IsChilledWaterTank) {
10363 896980 : if (this->SourceSideControlMode == SourceSideControl::IndirectHeatPrimarySetpoint) {
10364 896980 : if (OutletTemp < DeadBandTemp) {
10365 125406 : NeedsHeatOrCool = true;
10366 771574 : } else if ((OutletTemp >= DeadBandTemp) && (OutletTemp < SetPointTemp_loc)) {
10367 : // inside the deadband, use saved mode from water heater calcs
10368 702830 : if (this->SavedMode == TankOperatingMode::Heating) {
10369 87716 : NeedsHeatOrCool = true;
10370 263699 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10371 263699 : NeedsHeatOrCool = false;
10372 : }
10373 :
10374 420159 : } else if (OutletTemp >= SetPointTemp_loc) {
10375 420159 : NeedsHeatOrCool = false;
10376 : }
10377 0 : } else if (this->SourceSideControlMode == SourceSideControl::IndirectHeatAltSetpoint) {
10378 : // get alternate setpoint
10379 0 : Real64 const AltSetpointTemp = ScheduleManager::GetCurrentScheduleValue(state, this->SourceSideAltSetpointSchedNum);
10380 0 : Real64 const AltDeadBandTemp = AltSetpointTemp - this->DeadBandDeltaTemp;
10381 0 : if (OutletTemp < AltDeadBandTemp) {
10382 0 : NeedsHeatOrCool = true;
10383 0 : } else if ((OutletTemp >= AltDeadBandTemp) && (OutletTemp < AltSetpointTemp)) {
10384 : // inside the deadband, use saved mode from water heater calcs
10385 0 : if (this->SavedMode == TankOperatingMode::Heating) {
10386 0 : NeedsHeatOrCool = true;
10387 0 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10388 0 : NeedsHeatOrCool = false;
10389 : }
10390 :
10391 0 : } else if (OutletTemp >= AltSetpointTemp) {
10392 0 : NeedsHeatOrCool = false;
10393 : }
10394 0 : } else if (this->SourceSideControlMode == SourceSideControl::StorageTank) {
10395 0 : if (OutletTemp < this->TankTempLimit) {
10396 0 : NeedsHeatOrCool = true;
10397 : } else {
10398 0 : NeedsHeatOrCool = false;
10399 : }
10400 : }
10401 : } else { // is a chilled water tank so flip logic
10402 419296 : if (OutletTemp > DeadBandTemp) {
10403 10744 : NeedsHeatOrCool = true;
10404 408552 : } else if ((OutletTemp <= DeadBandTemp) && (OutletTemp > SetPointTemp_loc)) {
10405 : // inside the deadband, use saved mode from water thermal tank calcs (modes only for mixed)
10406 603716 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) {
10407 276181 : if (this->SavedMode == TankOperatingMode::Cooling) {
10408 36240 : NeedsHeatOrCool = true;
10409 239941 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10410 239941 : NeedsHeatOrCool = false;
10411 : }
10412 25677 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
10413 25677 : NeedsHeatOrCool = true;
10414 : }
10415 :
10416 106694 : } else if (OutletTemp <= SetPointTemp_loc) {
10417 106694 : NeedsHeatOrCool = false;
10418 : }
10419 : }
10420 1316276 : return NeedsHeatOrCool;
10421 : }
10422 :
10423 7656984 : Real64 WaterThermalTankData::PlantMassFlowRatesFunc(EnergyPlusData &state,
10424 : int const InNodeNum,
10425 : bool const FirstHVACIteration,
10426 : WaterHeaterSide const WaterThermalTankSide,
10427 : const DataPlant::LoopSideLocation PlantLoopSide,
10428 : [[maybe_unused]] bool const PlumbedInSeries,
10429 : DataBranchAirLoopPlant::ControlType const BranchControlType,
10430 : Real64 const OutletTemp,
10431 : Real64 const DeadBandTemp,
10432 : Real64 const SetPointTemp_loc)
10433 : {
10434 :
10435 : // FUNCTION INFORMATION:
10436 : // AUTHOR Brent Griffith
10437 : // DATE WRITTEN October 2007
10438 : // MODIFIED na
10439 : // RE-ENGINEERED na
10440 :
10441 : // PURPOSE OF THIS FUNCTION:
10442 : // collect routines for setting flow rates for Water heaters
10443 : // with plant connections.
10444 :
10445 : // determine current mode. there are three possible
10446 : // 1. passing thru what was given to inlet node
10447 : // 2. potentially making a flow request
10448 : // 3. throttling flow in response to Plant's restrictions (MassFlowRateMaxAvail)
10449 : // init default mode changed to Unassigned
10450 7656984 : FlowMode CurrentMode = FlowMode::Invalid; // default
10451 :
10452 7656984 : if (PlantLoopSide == DataPlant::LoopSideLocation::Invalid) {
10453 674706 : CurrentMode = FlowMode::PassingFlowThru;
10454 6982278 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Supply) {
10455 : // If FlowLock is False (0), the tank sets the plant loop mdot
10456 : // If FlowLock is True (1), the new resolved plant loop mdot is used
10457 6431458 : if (this->UseCurrentFlowLock == DataPlant::FlowLock::Unlocked) {
10458 2685528 : CurrentMode = FlowMode::PassingFlowThru;
10459 2685528 : if ((this->UseSideLoadRequested > 0.0) && (WaterThermalTankSide == WaterHeaterSide::Use)) {
10460 2083360 : CurrentMode = FlowMode::MaybeRequestingFlow;
10461 : }
10462 : } else {
10463 3745930 : CurrentMode = FlowMode::PassingFlowThru;
10464 : }
10465 6431458 : if (WaterThermalTankSide == WaterHeaterSide::Source) {
10466 765456 : CurrentMode = FlowMode::MaybeRequestingFlow;
10467 : }
10468 550820 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Demand) {
10469 :
10470 : // 2. Might be Requesting Flow.
10471 550820 : if (FirstHVACIteration) {
10472 275168 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10473 0 : CurrentMode = FlowMode::PassingFlowThru;
10474 : } else {
10475 275168 : CurrentMode = FlowMode::MaybeRequestingFlow;
10476 : }
10477 : } else {
10478 275652 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10479 0 : CurrentMode = FlowMode::PassingFlowThru;
10480 : } else {
10481 275652 : CurrentMode = FlowMode::ThrottlingFlow;
10482 : }
10483 : }
10484 : }
10485 :
10486 : // evaluate Availability schedule,
10487 7656984 : bool ScheduledAvail = true;
10488 7656984 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10489 5666002 : if (ScheduleManager::GetCurrentScheduleValue(state, this->UseSideAvailSchedNum) == 0.0) {
10490 0 : ScheduledAvail = false;
10491 : }
10492 1990982 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10493 1990982 : if (ScheduleManager::GetCurrentScheduleValue(state, this->SourceSideAvailSchedNum) == 0.0) {
10494 58864 : ScheduledAvail = false;
10495 : }
10496 : }
10497 :
10498 : // now act based on current mode
10499 7656984 : Real64 FlowResult = 0.0;
10500 7656984 : switch (CurrentMode) {
10501 :
10502 4257348 : case FlowMode::PassingFlowThru: {
10503 4257348 : if (!ScheduledAvail) {
10504 0 : FlowResult = 0.0;
10505 : } else {
10506 4257348 : FlowResult = state.dataLoopNodes->Node(InNodeNum).MassFlowRate;
10507 : }
10508 :
10509 4257348 : break;
10510 : }
10511 275652 : case FlowMode::ThrottlingFlow: {
10512 : // first determine what mass flow would be if it is to requested
10513 275652 : Real64 MassFlowRequest = 0.0;
10514 275652 : if (!ScheduledAvail) {
10515 29432 : MassFlowRequest = 0.0;
10516 : } else {
10517 246220 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10518 0 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10519 246220 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10520 246220 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10521 : } else {
10522 0 : assert(false);
10523 : }
10524 : }
10525 :
10526 : // next determine if tank temperature is such that source side flow might be requested
10527 275652 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10528 :
10529 275652 : if (MassFlowRequest > 0.0) {
10530 246220 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10531 0 : FlowResult = MassFlowRequest;
10532 246220 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10533 246220 : if (NeedsHeatOrCool) {
10534 52600 : FlowResult = MassFlowRequest;
10535 : } else {
10536 193620 : FlowResult = 0.0;
10537 : }
10538 : } else {
10539 0 : assert(false);
10540 : }
10541 : } else {
10542 29432 : FlowResult = 0.0;
10543 : }
10544 :
10545 : // now throttle against MassFlowRateMaxAvail, MassFlowRateMinAvail, MassFlowRateMax, and MassFlowRateMin
10546 : // see notes about reverse dd compliance (specifically 5ZoneWaterSystems file)
10547 275652 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMinAvail, FlowResult); // okay for compliance (reverse dd)
10548 275652 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMin, FlowResult); // okay for compliance (reverse dd)
10549 275652 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMaxAvail, FlowResult);
10550 : //=> following might take out of reverse dd compliance
10551 275652 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMax, FlowResult);
10552 :
10553 275652 : break;
10554 : }
10555 3123984 : case FlowMode::MaybeRequestingFlow: {
10556 :
10557 : // first determine what mass flow would be if it is to requested
10558 3123984 : Real64 MassFlowRequest = 0.0;
10559 3123984 : if (!ScheduledAvail) {
10560 29432 : MassFlowRequest = 0.0;
10561 : } else {
10562 3094552 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10563 2083360 : if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested > 0.0)) {
10564 26764 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10565 2056596 : } else if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested == 0.0)) {
10566 0 : MassFlowRequest = 0.0;
10567 : } else {
10568 2056596 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10569 : }
10570 :
10571 1011192 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10572 1011192 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10573 : }
10574 : }
10575 :
10576 3123984 : if (WaterThermalTankSide == WaterHeaterSide::Source) { // temperature dependent controls for indirect heating/cooling
10577 1040624 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10578 1040624 : if (MassFlowRequest > 0.0) {
10579 1011174 : if (NeedsHeatOrCool) {
10580 212816 : FlowResult = MassFlowRequest;
10581 : } else {
10582 798358 : FlowResult = 0.0;
10583 : }
10584 : } else {
10585 29450 : FlowResult = 0.0;
10586 : }
10587 : } else { // end source side, begin use side
10588 2083360 : if (MassFlowRequest > 0.0) {
10589 2083360 : FlowResult = MassFlowRequest;
10590 : } else {
10591 0 : FlowResult = 0.0;
10592 : }
10593 : }
10594 3123984 : break;
10595 : }
10596 0 : default:
10597 0 : break;
10598 : }
10599 :
10600 7656984 : if (FlowResult < DataHVACGlobals::VerySmallMassFlow) FlowResult = 0.0; // Catch underflow problems
10601 :
10602 7656984 : return FlowResult;
10603 : }
10604 :
10605 927 : void WaterThermalTankData::MinePlantStructForInfo(EnergyPlusData &state)
10606 : {
10607 :
10608 : // SUBROUTINE INFORMATION:
10609 : // AUTHOR Brent Griffith
10610 : // DATE WRITTEN October 2007
10611 : // MODIFIED na
10612 : // RE-ENGINEERED na
10613 :
10614 : // PURPOSE OF THIS SUBROUTINE:
10615 : // get information from plant loop data structure
10616 : // check what we can learn from plant structure against user inputs
10617 :
10618 927 : bool ErrorsFound = false;
10619 :
10620 927 : if (allocated(state.dataPlnt->PlantLoop) && this->UseSidePlantLoc.loopNum > 0) {
10621 :
10622 : // check plant structure for useful data.
10623 :
10624 854 : int PlantLoopNum = this->UseSidePlantLoc.loopNum;
10625 854 : DataPlant::LoopSideLocation LoopSideNum = this->UseSidePlantLoc.loopSideNum;
10626 :
10627 854 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
10628 0 : ShowSevereError(state,
10629 0 : "Water heater = " + this->Name + " for autosizing Use side flow rate, did not find Sizing:Plant object " +
10630 0 : state.dataPlnt->PlantLoop(PlantLoopNum).Name);
10631 0 : ErrorsFound = true;
10632 : }
10633 : // Is this wh Use side plumbed in series (default) or are there other branches in parallel?
10634 854 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.Exists) {
10635 854 : if (any_eq(state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.NodeNumOut,
10636 : this->UseInletNode)) { // this wh is on the splitter
10637 825 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.TotalOutletNodes > 1) {
10638 755 : this->UseSideSeries = false;
10639 : }
10640 : }
10641 : }
10642 : }
10643 :
10644 927 : if (allocated(state.dataPlnt->PlantLoop) && this->SrcSidePlantLoc.loopNum > 0) {
10645 : // was user's input correct for plant loop name?
10646 220 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0) && (this->DesuperheaterNum == 0) &&
10647 0 : (this->HeatPumpNum == 0)) {
10648 0 : ShowSevereError(state,
10649 0 : "Water heater = " + this->Name + "for autosizing Source side flow rate, did not find Sizing:Plant object " +
10650 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).Name);
10651 0 : ErrorsFound = true;
10652 : }
10653 : // Is this wh Source side plumbed in series (default) or are there other branches in parallel?
10654 220 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.Exists) {
10655 220 : if (any_eq(state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.NodeNumOut,
10656 : this->SourceInletNode)) { // this wh is on the splitter
10657 220 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.TotalOutletNodes >
10658 : 1) {
10659 77 : this->SourceSideSeries = false;
10660 : }
10661 : }
10662 : }
10663 : }
10664 :
10665 927 : if (ErrorsFound) {
10666 0 : ShowFatalError(state, "Preceding water heater input errors cause program termination");
10667 : }
10668 927 : }
10669 :
10670 731 : void WaterThermalTankData::SizeSupplySidePlantConnections(EnergyPlusData &state, Optional_int_const LoopNum)
10671 : {
10672 :
10673 : // SUBROUTINE INFORMATION:
10674 : // AUTHOR Brent Griffith
10675 : // DATE WRITTEN October 2007
10676 : // MODIFIED na
10677 : // RE-ENGINEERED na
10678 :
10679 : // PURPOSE OF THIS SUBROUTINE:
10680 : // This subroutine is for sizing water heater plant connection flow rates
10681 : // on the supply that have not been specified in the input.
10682 :
10683 : // METHODOLOGY EMPLOYED:
10684 : // This routine is called later in the simulation than the sizing routine for the demand side
10685 : // because the simulation needs to be further along before the needed data are available.
10686 : // For water heaters sides on Supply LoopSide, obtains hot water flow rate from the plant sizing array
10687 : // (adapted approach from boiler sizing routines)
10688 :
10689 : static constexpr std::string_view RoutineName("SizeSupplySidePlantConnections");
10690 :
10691 731 : auto &PlantSizData(state.dataSize->PlantSizData);
10692 :
10693 731 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
10694 731 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
10695 :
10696 : int tmpLoopNum;
10697 731 : if (!present(LoopNum)) {
10698 0 : tmpLoopNum = this->SrcSidePlantLoc.loopNum;
10699 : } else {
10700 731 : tmpLoopNum = LoopNum;
10701 : }
10702 :
10703 731 : if ((this->UseInletNode > 0) && (tmpLoopNum == this->UseSidePlantLoc.loopNum)) {
10704 631 : if (this->UseDesignVolFlowRateWasAutoSized) {
10705 601 : int PltSizNum = this->UseSidePlantSizNum;
10706 601 : if (PltSizNum > 0) { // we have a Plant Sizing Object
10707 601 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10708 601 : if (PlantSizData(PltSizNum).DesVolFlowRate >= DataHVACGlobals::SmallWaterVolFlow) {
10709 484 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10710 133 : this->UseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10711 : } else {
10712 351 : tmpUseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10713 : }
10714 : } else {
10715 117 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10716 0 : this->UseDesignVolFlowRate = 0.0;
10717 : } else {
10718 117 : tmpUseDesignVolFlowRate = 0.0;
10719 : }
10720 : }
10721 601 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10722 117 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10723 : }
10724 601 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10725 16 : BaseSizer::reportSizerOutput(
10726 8 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10727 : }
10728 601 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10729 133 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10730 : } else {
10731 468 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
10732 : }
10733 :
10734 1202 : Real64 rho = FluidProperties::GetDensityGlycol(state,
10735 601 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
10736 : DataGlobalConstants::InitConvTemp,
10737 601 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
10738 601 : RoutineName);
10739 601 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10740 133 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10741 : } else {
10742 468 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
10743 : }
10744 : }
10745 : } else {
10746 : // do nothing
10747 : } // plant sizing object
10748 : } else {
10749 30 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10750 : Real64 rho;
10751 30 : if (this->UseSidePlantLoc.loopNum > 0) {
10752 60 : rho = FluidProperties::GetDensityGlycol(state,
10753 30 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
10754 : DataGlobalConstants::InitConvTemp,
10755 30 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
10756 : RoutineName);
10757 : } else {
10758 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, this->waterIndex, RoutineName);
10759 : }
10760 :
10761 30 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10762 :
10763 : } // autosizing needed.
10764 : } // connected to plant
10765 :
10766 731 : if ((this->SourceInletNode > 0) && (tmpLoopNum == this->SrcSidePlantLoc.loopNum)) {
10767 100 : if (this->SourceDesignVolFlowRateWasAutoSized) {
10768 50 : int PltSizNum = this->SourceSidePlantSizNum;
10769 50 : if (PltSizNum > 0) {
10770 50 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10771 20 : if (PlantSizData(PltSizNum).DesVolFlowRate >= DataHVACGlobals::SmallWaterVolFlow) {
10772 14 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10773 4 : this->SourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10774 : } else {
10775 10 : tmpSourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10776 : }
10777 : } else {
10778 6 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10779 0 : this->SourceDesignVolFlowRate = 0.0;
10780 : } else {
10781 6 : tmpSourceDesignVolFlowRate = 0.0;
10782 : }
10783 : }
10784 20 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10785 8 : BaseSizer::reportSizerOutput(
10786 4 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10787 : }
10788 20 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10789 0 : BaseSizer::reportSizerOutput(
10790 0 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10791 : }
10792 20 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10793 4 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10794 : } else {
10795 16 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
10796 : }
10797 40 : Real64 rho = FluidProperties::GetDensityGlycol(state,
10798 20 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
10799 : DataGlobalConstants::InitConvTemp,
10800 20 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
10801 20 : RoutineName);
10802 20 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10803 4 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10804 : } else {
10805 16 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
10806 : }
10807 : } // plant loop allocation
10808 : } else {
10809 : // do nothing
10810 : } // plant sizing object
10811 : } else {
10812 50 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10813 45 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10814 : Real64 rho;
10815 45 : if (this->SrcSidePlantLoc.loopNum > 0) {
10816 90 : rho = FluidProperties::GetDensityGlycol(state,
10817 45 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
10818 : DataGlobalConstants::InitConvTemp,
10819 45 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
10820 : RoutineName);
10821 : } else {
10822 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, this->waterIndex, RoutineName);
10823 : }
10824 45 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10825 : }
10826 : } // autosizing needed.
10827 : } // connected to plant
10828 731 : }
10829 :
10830 731 : void WaterThermalTankData::SizeTankForDemandSide(EnergyPlusData &state)
10831 : {
10832 :
10833 : // SUBROUTINE INFORMATION:
10834 : // AUTHOR Brent Griffith
10835 : // DATE WRITTEN February 2008
10836 : // MODIFIED na
10837 : // RE-ENGINEERED na
10838 :
10839 : // PURPOSE OF THIS SUBROUTINE:
10840 : // This subroutine is for sizing water heater tank volume and heater
10841 : // as best we can at this point in simulation. (prior to demand side
10842 : // sizing that needs volume).
10843 :
10844 : // METHODOLOGY EMPLOYED:
10845 : // depending on the sizing design mode...
10846 :
10847 : // REFERENCES:
10848 : // BA benchmark report for residential design mode
10849 :
10850 : // SUBROUTINE PARAMETER DEFINITIONS:
10851 : static constexpr std::string_view RoutineName("SizeTankForDemandSide");
10852 731 : Real64 constexpr GalTocubicMeters(0.0037854);
10853 731 : Real64 constexpr kBtuPerHrToWatts(293.1);
10854 :
10855 731 : Real64 Tstart = 14.44;
10856 731 : Real64 Tfinish = 57.22;
10857 :
10858 731 : Real64 tmpTankVolume = this->Volume;
10859 731 : Real64 tmpMaxCapacity = this->MaxCapacity;
10860 :
10861 731 : switch (this->Sizing.DesignMode) {
10862 :
10863 721 : case SizingMode::Invalid:
10864 : case SizingMode::PeakDraw: {
10865 :
10866 721 : break;
10867 : }
10868 10 : case SizingMode::ResidentialMin: {
10869 :
10870 : // assume can propagate rules for gas to other fuels.
10871 10 : bool FuelTypeIsLikeGas = false;
10872 10 : switch (this->FuelType) {
10873 0 : case Fuel::NaturalGas:
10874 : case Fuel::Diesel:
10875 : case Fuel::Gasoline:
10876 : case Fuel::Coal:
10877 : case Fuel::FuelOilNo1:
10878 : case Fuel::FuelOilNo2:
10879 : case Fuel::Propane:
10880 : case Fuel::Steam:
10881 : case Fuel::OtherFuel1:
10882 : case Fuel::OtherFuel2:
10883 : case Fuel::DistrictHeating:
10884 0 : FuelTypeIsLikeGas = true;
10885 0 : break;
10886 10 : default: // FuelTypeIsLikeGas stays false
10887 10 : break;
10888 : }
10889 :
10890 10 : if (this->Sizing.NumberOfBedrooms == 1) {
10891 0 : if (this->FuelType == Fuel::Electricity) {
10892 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
10893 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
10894 0 : } else if (FuelTypeIsLikeGas) {
10895 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
10896 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
10897 : }
10898 :
10899 10 : } else if (this->Sizing.NumberOfBedrooms == 2) {
10900 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10901 0 : if (this->FuelType == Fuel::Electricity) {
10902 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10903 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
10904 0 : } else if (FuelTypeIsLikeGas) {
10905 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10906 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10907 : }
10908 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10909 0 : if (this->FuelType == Fuel::Electricity) {
10910 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10911 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10912 0 : } else if (FuelTypeIsLikeGas) {
10913 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10914 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10915 : }
10916 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10917 0 : if (this->FuelType == Fuel::Electricity) {
10918 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10919 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10920 0 : } else if (FuelTypeIsLikeGas) {
10921 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10922 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10923 : }
10924 : }
10925 10 : } else if (this->Sizing.NumberOfBedrooms == 3) {
10926 10 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10927 0 : if (this->FuelType == Fuel::Electricity) {
10928 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10929 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10930 0 : } else if (FuelTypeIsLikeGas) {
10931 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10932 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10933 : }
10934 10 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10935 0 : if (this->FuelType == Fuel::Electricity) {
10936 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10937 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10938 0 : } else if (FuelTypeIsLikeGas) {
10939 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10940 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10941 : }
10942 10 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10943 10 : if (this->FuelType == Fuel::Electricity) {
10944 10 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10945 10 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10946 0 : } else if (FuelTypeIsLikeGas) {
10947 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10948 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10949 : }
10950 : }
10951 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
10952 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10953 0 : if (this->FuelType == Fuel::Electricity) {
10954 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10955 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10956 0 : } else if (FuelTypeIsLikeGas) {
10957 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10958 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10959 : }
10960 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10961 0 : if (this->FuelType == Fuel::Electricity) {
10962 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10963 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10964 0 : } else if (FuelTypeIsLikeGas) {
10965 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10966 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10967 : }
10968 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10969 0 : if (this->FuelType == Fuel::Electricity) {
10970 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10971 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10972 0 : } else if (FuelTypeIsLikeGas) {
10973 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10974 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10975 : }
10976 : }
10977 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
10978 0 : if (this->FuelType == Fuel::Electricity) {
10979 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10980 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10981 0 : } else if (FuelTypeIsLikeGas) {
10982 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10983 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
10984 : }
10985 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
10986 0 : if (this->FuelType == Fuel::Electricity) {
10987 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10988 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10989 0 : } else if (FuelTypeIsLikeGas) {
10990 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10991 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
10992 : }
10993 : }
10994 :
10995 10 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10996 0 : this->Volume = tmpTankVolume;
10997 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10998 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
10999 : }
11000 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11001 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11002 : }
11003 : }
11004 10 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11005 2 : this->MaxCapacity = tmpMaxCapacity;
11006 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11007 2 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11008 : }
11009 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11010 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11011 : }
11012 : }
11013 10 : break;
11014 : }
11015 0 : case SizingMode::PerPerson: {
11016 : // how to get number of people?
11017 :
11018 0 : Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
11019 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
11020 :
11021 0 : if (this->MaxCapacityWasAutoSized) {
11022 : Real64 rho;
11023 : Real64 Cp;
11024 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11025 0 : rho = FluidProperties::GetDensityGlycol(state,
11026 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11027 0 : ((Tfinish + Tstart) / 2.0),
11028 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11029 : RoutineName);
11030 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11031 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11032 0 : ((Tfinish + Tstart) / 2.0),
11033 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11034 : RoutineName);
11035 : } else {
11036 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11037 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11038 : }
11039 :
11040 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) *
11041 0 : (1.0 / DataGlobalConstants::SecInHour) * rho * Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11042 : }
11043 :
11044 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11045 0 : this->Volume = tmpTankVolume;
11046 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11047 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11048 : }
11049 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11050 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11051 : }
11052 : }
11053 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11054 0 : this->MaxCapacity = tmpMaxCapacity;
11055 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11056 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11057 : }
11058 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11059 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11060 : }
11061 : }
11062 0 : break;
11063 : }
11064 0 : case SizingMode::PerFloorArea: {
11065 :
11066 0 : Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
11067 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
11068 0 : if (this->MaxCapacityWasAutoSized) {
11069 : Real64 rho;
11070 : Real64 Cp;
11071 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11072 0 : rho = FluidProperties::GetDensityGlycol(state,
11073 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11074 0 : ((Tfinish + Tstart) / 2.0),
11075 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11076 : RoutineName);
11077 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11078 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11079 0 : ((Tfinish + Tstart) / 2.0),
11080 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11081 : RoutineName);
11082 : } else {
11083 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11084 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11085 : }
11086 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) *
11087 0 : (1.0 / DataGlobalConstants::SecInHour) * rho * Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11088 : }
11089 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11090 0 : this->Volume = tmpTankVolume;
11091 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11092 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11093 : }
11094 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11095 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11096 : }
11097 : }
11098 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11099 0 : this->MaxCapacity = tmpMaxCapacity;
11100 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11101 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11102 : }
11103 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11104 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11105 : }
11106 : }
11107 0 : break;
11108 : }
11109 0 : case SizingMode::PerUnit: {
11110 :
11111 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
11112 :
11113 0 : if (this->MaxCapacityWasAutoSized) {
11114 : Real64 rho;
11115 : Real64 Cp;
11116 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11117 0 : rho = FluidProperties::GetDensityGlycol(state,
11118 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11119 0 : ((Tfinish + Tstart) / 2.0),
11120 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11121 : RoutineName);
11122 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11123 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11124 0 : ((Tfinish + Tstart) / 2.0),
11125 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11126 : RoutineName);
11127 : } else {
11128 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11129 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11130 : }
11131 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) *
11132 0 : (1.0 / DataGlobalConstants::SecInHour) * rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11133 : }
11134 :
11135 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11136 0 : this->Volume = tmpTankVolume;
11137 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11138 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11139 : }
11140 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11141 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11142 : }
11143 : }
11144 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11145 0 : this->MaxCapacity = tmpMaxCapacity;
11146 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11147 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11148 : }
11149 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11150 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11151 : }
11152 : }
11153 0 : break;
11154 : }
11155 0 : case SizingMode::PerSolarColArea: {
11156 0 : break;
11157 : }
11158 0 : default:
11159 0 : break;
11160 : }
11161 :
11162 731 : if (this->MaxCapacityWasAutoSized) this->setBackupElementCapacity(state);
11163 :
11164 : // if stratified, might set height.
11165 731 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
11166 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
11167 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
11168 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / DataGlobalConstants::Pi, 0.3333333333333333);
11169 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11170 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
11171 : }
11172 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11173 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
11174 : }
11175 : // check if DataGlobalConstants::AutoCalculate() Use outlet and source inlet are still set to autosize by earlier
11176 0 : if (this->UseOutletHeightWasAutoSized) {
11177 0 : this->UseOutletHeight = this->Height;
11178 : }
11179 0 : if (this->SourceInletHeightWasAutoSized) {
11180 0 : this->SourceInletHeight = this->Height;
11181 : }
11182 : }
11183 : }
11184 731 : }
11185 :
11186 731 : void WaterThermalTankData::SizeTankForSupplySide(EnergyPlusData &state)
11187 : {
11188 :
11189 : // SUBROUTINE INFORMATION:
11190 : // AUTHOR Brent Griffith
11191 : // DATE WRITTEN February 2008
11192 : // MODIFIED na
11193 : // RE-ENGINEERED na
11194 :
11195 : // PURPOSE OF THIS SUBROUTINE:
11196 : // This subroutine is for sizing water heater tank volume and heater
11197 : // at a later point in the simulation when more of the plant is ready.
11198 :
11199 : // METHODOLOGY EMPLOYED:
11200 : // depending on the sizing design mode...
11201 :
11202 : // REFERENCES:
11203 : // BA benchmark report for residential design mode
11204 :
11205 : static constexpr std::string_view RoutineName("SizeTankForSupplySide");
11206 :
11207 731 : Real64 Tstart = 14.44;
11208 731 : Real64 Tfinish = 57.22;
11209 :
11210 731 : Real64 tmpTankVolume = this->Volume;
11211 731 : Real64 tmpMaxCapacity = this->MaxCapacity;
11212 :
11213 731 : if (this->Sizing.DesignMode == SizingMode::PeakDraw) {
11214 20 : if (this->VolumeWasAutoSized)
11215 20 : tmpTankVolume = this->Sizing.TankDrawTime * this->UseDesignVolFlowRate * DataGlobalConstants::SecInHour; // hours | m3/s | (3600 s/1 hour)
11216 20 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11217 4 : this->Volume = tmpTankVolume;
11218 4 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11219 4 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11220 : }
11221 4 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11222 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11223 : }
11224 : }
11225 20 : if (this->MaxCapacityWasAutoSized) {
11226 0 : if (this->Sizing.RecoveryTime > 0.0) {
11227 : Real64 rho;
11228 : Real64 Cp;
11229 0 : if (this->SrcSidePlantLoc.loopNum > 0) {
11230 0 : rho = FluidProperties::GetDensityGlycol(state,
11231 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11232 0 : ((Tfinish + Tstart) / 2.0),
11233 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11234 : RoutineName);
11235 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11236 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11237 0 : ((Tfinish + Tstart) / 2.0),
11238 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11239 : RoutineName);
11240 : } else {
11241 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11242 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11243 : }
11244 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11245 0 : (this->Sizing.RecoveryTime * DataGlobalConstants::SecInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11246 : } else {
11247 0 : ShowFatalError(state,
11248 0 : "SizeTankForSupplySide: Tank=\"" + this->Name +
11249 : "\", requested sizing for max capacity but entered Recovery Time is zero.");
11250 : }
11251 : }
11252 :
11253 20 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11254 0 : this->MaxCapacity = tmpMaxCapacity;
11255 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11256 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11257 : }
11258 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11259 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11260 : }
11261 : }
11262 711 : } else if (this->Sizing.DesignMode == SizingMode::PerSolarColArea) {
11263 :
11264 0 : this->Sizing.TotalSolarCollectorArea = 0.0;
11265 0 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11266 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(state.dataSolarCollectors->Collector(CollectorNum).Surface).Area;
11267 : }
11268 :
11269 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11270 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 0.0;
11271 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11272 0 : this->Volume = tmpTankVolume;
11273 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11274 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11275 : }
11276 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11277 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11278 : }
11279 : }
11280 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11281 0 : this->MaxCapacity = tmpMaxCapacity;
11282 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11283 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11284 : }
11285 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11286 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11287 : }
11288 : }
11289 : }
11290 :
11291 731 : if (this->MaxCapacityWasAutoSized) this->setBackupElementCapacity(state);
11292 :
11293 731 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
11294 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
11295 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
11296 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / DataGlobalConstants::Pi, 0.3333333333333333);
11297 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11298 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
11299 : }
11300 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11301 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
11302 : }
11303 : }
11304 : }
11305 731 : }
11306 :
11307 731 : void WaterThermalTankData::SizeDemandSidePlantConnections(EnergyPlusData &state)
11308 : {
11309 :
11310 : // SUBROUTINE INFORMATION:
11311 : // AUTHOR Brent Griffith
11312 : // DATE WRITTEN October 2007
11313 : // MODIFIED na
11314 : // RE-ENGINEERED na
11315 :
11316 : // PURPOSE OF THIS SUBROUTINE:
11317 : // This subroutine is for sizing water heater plant connection flow rates
11318 : // on the demand side that have not been specified in the input.
11319 :
11320 : // METHODOLOGY EMPLOYED:
11321 : // For water heater sides on the Demand side, hot water flow rates are modeled entirely from user input data
11322 : // because the plant loop is not yet set up nor is plant sizing info populated.
11323 : // sizing is done by calculating an initial
11324 : // recovery rate that if continued would reheat tank in user specified amount of time.
11325 : // initial and final tank temperatures are 14.44 and reheat to 57.22 (values from CalcStandardRatings routine)
11326 :
11327 : static constexpr std::string_view RoutineName("SizeDemandSidePlantConnections");
11328 :
11329 731 : auto &PlantSizData(state.dataSize->PlantSizData);
11330 :
11331 731 : Real64 tankRecoverhours = this->SizingRecoveryTime;
11332 731 : bool ErrorsFound = false;
11333 731 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
11334 731 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
11335 :
11336 : Real64 Tstart;
11337 : Real64 Tfinish;
11338 731 : if (!this->IsChilledWaterTank) {
11339 686 : Tstart = 14.44;
11340 686 : Tfinish = 57.22;
11341 : } else {
11342 45 : Tstart = 14.44;
11343 45 : Tfinish = 9.0;
11344 : }
11345 :
11346 : // determine tank volume to use for sizing.
11347 731 : Real64 TankVolume = this->Volume;
11348 731 : if (this->VolumeWasAutoSized) {
11349 20 : TankVolume = this->Sizing.NominalVolForSizingDemandSideFlow;
11350 : }
11351 :
11352 731 : if (this->UseInletNode > 0) {
11353 731 : if (this->UseDesignVolFlowRateWasAutoSized) {
11354 671 : int PltSizNum = this->UseSidePlantSizNum;
11355 671 : if (PltSizNum > 0) { // we have a Plant Sizing Object
11356 671 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11357 : // probably shouldn't come here as Use side is unlikley to be on demand side (?)
11358 : // but going to treat component with symetry so if connections are reversed it'll still work
11359 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11360 : // in user specified hours.
11361 : // using the plant inlet design temp for sizing.
11362 0 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11363 0 : Real64 eff = this->UseEffectiveness;
11364 0 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11365 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11366 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11367 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11368 : } else {
11369 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11370 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11371 : }
11372 0 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11373 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11374 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11375 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11376 : } else {
11377 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11378 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11379 : }
11380 : } else {
11381 0 : if (!this->IsChilledWaterTank) {
11382 : // plant sizing object design temperature is set too low throw warning.
11383 0 : ShowSevereError(state,
11384 : "Autosizing of Use side water heater design flow rate requires Sizing:Plant object to have an exit "
11385 : "temperature >= 58C");
11386 0 : ShowContinueError(state, "Occurs for water heater object=" + this->Name);
11387 : } else {
11388 : // plant sizing object design temperature is set too hi throw warning.
11389 0 : ShowSevereError(state,
11390 : "Autosizing of Use side chilled water tank design flow rate requires Sizing:Plant object to have an "
11391 : "exit temperature <= 8C");
11392 0 : ShowContinueError(state, "Occurs for chilled water storage tank object=" + this->Name);
11393 : }
11394 0 : ErrorsFound = true;
11395 : }
11396 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11397 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11398 : }
11399 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11400 0 : BaseSizer::reportSizerOutput(
11401 0 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11402 : }
11403 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11404 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11405 : } else {
11406 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
11407 : }
11408 0 : Real64 rho = FluidProperties::GetDensityGlycol(state,
11409 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11410 : DataGlobalConstants::InitConvTemp,
11411 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11412 0 : RoutineName);
11413 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11414 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11415 : } else {
11416 0 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
11417 : }
11418 : } // Demand side
11419 : } else {
11420 : // do nothing
11421 : } // plant sizing object
11422 :
11423 : } else {
11424 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11425 60 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11426 : Real64 rho;
11427 60 : if (this->UseSidePlantLoc.loopNum > 0) {
11428 120 : rho = FluidProperties::GetDensityGlycol(state,
11429 60 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11430 : DataGlobalConstants::InitConvTemp,
11431 60 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11432 : RoutineName);
11433 : } else {
11434 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, this->waterIndex, RoutineName);
11435 : }
11436 60 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11437 : } // autosizing needed.
11438 : } // connected to plant
11439 :
11440 731 : if (this->SourceInletNode > 0) {
11441 250 : if (this->SourceDesignVolFlowRateWasAutoSized) {
11442 115 : int PltSizNum = this->SourceSidePlantSizNum;
11443 115 : if (PltSizNum > 0) {
11444 100 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11445 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11446 : // in user specified hours.
11447 : // using the plant inlet design temp for sizing.
11448 60 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11449 60 : Real64 eff = this->SourceEffectiveness;
11450 60 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11451 :
11452 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11453 12 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11454 6 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11455 : } else {
11456 48 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11457 24 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11458 : }
11459 30 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11460 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11461 12 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11462 6 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11463 : } else {
11464 48 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * DataGlobalConstants::SecInHour * eff)) *
11465 24 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11466 : }
11467 : } else {
11468 0 : if (!this->IsChilledWaterTank) {
11469 : // plant sizing object design temperature is set too low throw warning.
11470 0 : ShowSevereError(state,
11471 : "Autosizing of Source side water heater design flow rate requires Sizing:Plant object to have an "
11472 : "exit temperature >= 58C");
11473 0 : ShowContinueError(state, "Occurs for WaterHeater:Mixed object=" + this->Name);
11474 : } else {
11475 : // plant sizing object design temperature is set too hi throw warning.
11476 0 : ShowSevereError(state,
11477 : "Autosizing of Source side chilled water tank design flow rate requires Sizing:Plant object to have "
11478 : "an exit temperature <= 8C");
11479 0 : ShowContinueError(state, "Occurs for chilled water storage tank object=" + this->Name);
11480 : }
11481 0 : ErrorsFound = true;
11482 : }
11483 60 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11484 24 : BaseSizer::reportSizerOutput(
11485 12 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11486 : }
11487 60 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11488 0 : BaseSizer::reportSizerOutput(
11489 0 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11490 : }
11491 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11492 12 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11493 : } else {
11494 48 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
11495 : }
11496 120 : Real64 rho = FluidProperties::GetDensityGlycol(state,
11497 60 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11498 : DataGlobalConstants::InitConvTemp,
11499 60 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11500 60 : RoutineName);
11501 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11502 12 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11503 : } else {
11504 48 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
11505 : }
11506 : } // demand side
11507 : } else {
11508 : // do nothing
11509 : } // plant sizing object
11510 :
11511 : } else {
11512 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11513 135 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11514 : Real64 rho;
11515 135 : if (this->SrcSidePlantLoc.loopNum > 0) {
11516 200 : rho = FluidProperties::GetDensityGlycol(state,
11517 100 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11518 : DataGlobalConstants::InitConvTemp,
11519 100 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11520 : RoutineName);
11521 : } else {
11522 35 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, this->waterIndex, RoutineName);
11523 : }
11524 135 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11525 : } // autosizing needed.
11526 : } // connected to plant
11527 :
11528 731 : if (ErrorsFound) {
11529 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
11530 : }
11531 731 : }
11532 :
11533 49 : void WaterThermalTankData::SizeStandAloneWaterHeater(EnergyPlusData &state)
11534 : {
11535 :
11536 : // SUBROUTINE INFORMATION:
11537 : // AUTHOR B. Griffith
11538 : // DATE WRITTEN October 2013
11539 : // MODIFIED na
11540 : // RE-ENGINEERED na
11541 :
11542 : // PURPOSE OF THIS SUBROUTINE:
11543 : // allow autosizing of tank volume and heat capacity for stand alone tanks
11544 :
11545 : // METHODOLOGY EMPLOYED:
11546 : // same as for plant connected water heaters, only draws are scheduled.
11547 :
11548 : // SUBROUTINE PARAMETER DEFINITIONS:
11549 49 : Real64 constexpr GalTocubicMeters(0.0037854);
11550 49 : Real64 constexpr kBtuPerHrToWatts(293.1);
11551 : static constexpr std::string_view RoutineName("SizeStandAloneWaterHeater");
11552 :
11553 49 : Real64 Tstart = 14.44;
11554 49 : Real64 Tfinish = 57.22;
11555 49 : Real64 tmpTankVolume = this->Volume;
11556 49 : Real64 tmpMaxCapacity = this->MaxCapacity;
11557 :
11558 49 : if (this->VolumeWasAutoSized || this->MaxCapacityWasAutoSized) {
11559 :
11560 0 : switch (this->Sizing.DesignMode) {
11561 :
11562 0 : case SizingMode::PeakDraw: {
11563 : // get draw rate from maximum in schedule
11564 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, DataGlobalConstants::InitConvTemp, this->waterIndex, RoutineName);
11565 0 : Real64 DrawDesignVolFlowRate = ScheduleManager::GetScheduleMaxValue(state, this->FlowRateSchedule) * this->MassFlowRateMax / rho;
11566 :
11567 0 : if (this->VolumeWasAutoSized) {
11568 0 : tmpTankVolume = this->Sizing.TankDrawTime * DrawDesignVolFlowRate * DataGlobalConstants::SecInHour; // hours | m3/s | (3600 s/1 hour)
11569 0 : this->Volume = tmpTankVolume;
11570 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11571 : }
11572 0 : if (this->MaxCapacityWasAutoSized) {
11573 0 : if (this->Sizing.RecoveryTime > 0.0) {
11574 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11575 : Real64 Cp =
11576 0 : FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11577 :
11578 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11579 0 : (this->Sizing.RecoveryTime * DataGlobalConstants::SecInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11580 : } else {
11581 0 : ShowFatalError(state,
11582 0 : "SizeStandAloneWaterHeater: Tank=\"" + this->Name +
11583 : "\", requested sizing for max capacity but entered Recovery Time is zero.");
11584 : }
11585 0 : this->MaxCapacity = tmpMaxCapacity;
11586 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11587 : }
11588 :
11589 0 : break;
11590 : }
11591 0 : case SizingMode::ResidentialMin: {
11592 : // assume can propagate rules for gas to other fuels.
11593 0 : bool FuelTypeIsLikeGas = false;
11594 0 : switch (this->FuelType) {
11595 0 : case Fuel::NaturalGas:
11596 : case Fuel::Diesel:
11597 : case Fuel::Gasoline:
11598 : case Fuel::Coal:
11599 : case Fuel::FuelOilNo1:
11600 : case Fuel::FuelOilNo2:
11601 : case Fuel::Propane:
11602 : case Fuel::Steam:
11603 : case Fuel::OtherFuel1:
11604 : case Fuel::OtherFuel2:
11605 : case Fuel::DistrictHeating:
11606 0 : FuelTypeIsLikeGas = true;
11607 0 : break;
11608 0 : default: // FuelTypeIsLikeGas stays false
11609 0 : break;
11610 : }
11611 :
11612 0 : if (this->Sizing.NumberOfBedrooms == 1) {
11613 0 : if (this->FuelType == Fuel::Electricity) {
11614 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
11615 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
11616 0 : } else if (FuelTypeIsLikeGas) {
11617 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
11618 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
11619 : }
11620 :
11621 0 : } else if (this->Sizing.NumberOfBedrooms == 2) {
11622 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11623 0 : if (this->FuelType == Fuel::Electricity) {
11624 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11625 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
11626 0 : } else if (FuelTypeIsLikeGas) {
11627 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11628 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11629 : }
11630 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11631 0 : if (this->FuelType == Fuel::Electricity) {
11632 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11633 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11634 0 : } else if (FuelTypeIsLikeGas) {
11635 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11636 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11637 : }
11638 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11639 0 : if (this->FuelType == Fuel::Electricity) {
11640 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11641 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11642 0 : } else if (FuelTypeIsLikeGas) {
11643 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11644 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11645 : }
11646 : }
11647 0 : } else if (this->Sizing.NumberOfBedrooms == 3) {
11648 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11649 0 : if (this->FuelType == Fuel::Electricity) {
11650 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11651 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11652 0 : } else if (FuelTypeIsLikeGas) {
11653 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11654 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11655 : }
11656 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11657 0 : if (this->FuelType == Fuel::Electricity) {
11658 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11659 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11660 0 : } else if (FuelTypeIsLikeGas) {
11661 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11662 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11663 : }
11664 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11665 0 : if (this->FuelType == Fuel::Electricity) {
11666 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11667 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11668 0 : } else if (FuelTypeIsLikeGas) {
11669 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11670 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11671 : }
11672 : }
11673 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
11674 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11675 0 : if (this->FuelType == Fuel::Electricity) {
11676 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11677 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11678 0 : } else if (FuelTypeIsLikeGas) {
11679 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11680 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11681 : }
11682 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11683 0 : if (this->FuelType == Fuel::Electricity) {
11684 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11685 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11686 0 : } else if (FuelTypeIsLikeGas) {
11687 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11688 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11689 : }
11690 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11691 0 : if (this->FuelType == Fuel::Electricity) {
11692 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11693 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11694 0 : } else if (FuelTypeIsLikeGas) {
11695 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11696 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11697 : }
11698 : }
11699 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
11700 0 : if (this->FuelType == Fuel::Electricity) {
11701 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11702 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11703 0 : } else if (FuelTypeIsLikeGas) {
11704 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11705 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
11706 : }
11707 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
11708 0 : if (this->FuelType == Fuel::Electricity) {
11709 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11710 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11711 0 : } else if (FuelTypeIsLikeGas) {
11712 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11713 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
11714 : }
11715 : }
11716 0 : if (this->VolumeWasAutoSized) {
11717 0 : this->Volume = tmpTankVolume;
11718 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11719 : }
11720 0 : if (this->MaxCapacityWasAutoSized) {
11721 0 : this->MaxCapacity = tmpMaxCapacity;
11722 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11723 : }
11724 :
11725 0 : break;
11726 : }
11727 0 : case SizingMode::PerPerson: {
11728 : // how to get number of people?
11729 :
11730 0 : Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
11731 0 : if (this->VolumeWasAutoSized) {
11732 0 : tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
11733 : }
11734 0 : if (this->MaxCapacityWasAutoSized) {
11735 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11736 0 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11737 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) *
11738 0 : (1.0 / DataGlobalConstants::SecInHour) * rho * Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11739 : }
11740 :
11741 0 : if (this->VolumeWasAutoSized) {
11742 0 : this->Volume = tmpTankVolume;
11743 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11744 : }
11745 0 : if (this->MaxCapacityWasAutoSized) {
11746 0 : this->MaxCapacity = tmpMaxCapacity;
11747 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11748 : }
11749 :
11750 0 : break;
11751 : }
11752 0 : case SizingMode::PerFloorArea: {
11753 :
11754 0 : Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
11755 0 : if (this->VolumeWasAutoSized) {
11756 0 : tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
11757 : }
11758 :
11759 0 : if (this->MaxCapacityWasAutoSized) {
11760 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11761 0 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11762 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) *
11763 0 : (1.0 / DataGlobalConstants::SecInHour) * rho * Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11764 : }
11765 0 : if (this->VolumeWasAutoSized) {
11766 0 : this->Volume = tmpTankVolume;
11767 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11768 : }
11769 0 : if (this->MaxCapacityWasAutoSized) {
11770 0 : this->MaxCapacity = tmpMaxCapacity;
11771 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11772 : }
11773 0 : break;
11774 : }
11775 0 : case SizingMode::PerUnit: {
11776 :
11777 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
11778 :
11779 0 : if (this->MaxCapacityWasAutoSized) {
11780 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11781 0 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11782 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) *
11783 0 : (1.0 / DataGlobalConstants::SecInHour) * rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11784 : }
11785 :
11786 0 : if (this->VolumeWasAutoSized) {
11787 0 : this->Volume = tmpTankVolume;
11788 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11789 : }
11790 0 : if (this->MaxCapacityWasAutoSized) {
11791 0 : this->MaxCapacity = tmpMaxCapacity;
11792 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11793 : }
11794 0 : break;
11795 : }
11796 0 : case SizingMode::PerSolarColArea: {
11797 0 : this->Sizing.TotalSolarCollectorArea = 0.0;
11798 0 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11799 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(state.dataSolarCollectors->Collector(CollectorNum).Surface).Area;
11800 : }
11801 :
11802 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11803 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 0.0;
11804 0 : if (this->VolumeWasAutoSized) {
11805 0 : this->Volume = tmpTankVolume;
11806 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11807 : }
11808 0 : if (this->MaxCapacityWasAutoSized) {
11809 0 : this->MaxCapacity = tmpMaxCapacity;
11810 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11811 : }
11812 0 : break;
11813 : }
11814 0 : default:
11815 0 : if (this->MaxCapacityWasAutoSized) this->setBackupElementCapacity(state);
11816 0 : break;
11817 : }
11818 : }
11819 49 : }
11820 :
11821 6113098 : void WaterThermalTankData::UpdateWaterThermalTank(EnergyPlusData &state)
11822 : {
11823 :
11824 : // SUBROUTINE INFORMATION:
11825 : // AUTHOR Brandon Anderson
11826 : // DATE WRITTEN May 2000
11827 : // MODIFIED na
11828 : // Nov 2011, BAN; removed the use and source heat rate re-calculation for stratified tank
11829 : // for energy conservation verification.
11830 : // RE-ENGINEERED Feb 2004, PGE
11831 :
11832 : // PURPOSE OF THIS SUBROUTINE:
11833 : // Updates the node variables with local variables.
11834 :
11835 6113098 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
11836 5666446 : state.dataLoopNodes->Node(UseOutletNode) = state.dataLoopNodes->Node(this->UseInletNode); // this could wipe out setpoints on outlet node
11837 :
11838 5666446 : state.dataLoopNodes->Node(this->UseOutletNode).Temp = this->UseOutletTemp;
11839 : }
11840 :
11841 6113098 : if (this->SourceInletNode > 0 && this->SourceOutletNode > 0) {
11842 1991072 : state.dataLoopNodes->Node(this->SourceOutletNode) = state.dataLoopNodes->Node(this->SourceInletNode);
11843 :
11844 1991072 : state.dataLoopNodes->Node(this->SourceOutletNode).Temp = this->SourceOutletTemp;
11845 : }
11846 6113098 : }
11847 :
11848 6113098 : void WaterThermalTankData::ReportWaterThermalTank(EnergyPlusData &state)
11849 : {
11850 :
11851 : // SUBROUTINE INFORMATION:
11852 : // AUTHOR Brandon Anderson
11853 : // DATE WRITTEN May 2000
11854 : // MODIFIED na
11855 : // RE-ENGINEERED Feb 2004, PGE
11856 :
11857 6113098 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
11858 :
11859 6113098 : this->UnmetEnergy = this->UnmetRate * SecInTimeStep;
11860 6113098 : this->LossEnergy = this->LossRate * SecInTimeStep;
11861 6113098 : this->FlueLossEnergy = this->FlueLossRate * SecInTimeStep;
11862 6113098 : this->UseEnergy = this->UseRate * SecInTimeStep;
11863 6113098 : this->TotalDemandEnergy = this->TotalDemandRate * SecInTimeStep;
11864 6113098 : this->SourceEnergy = this->SourceRate * SecInTimeStep;
11865 6113098 : this->HeaterEnergy = this->HeaterRate * SecInTimeStep;
11866 6113098 : this->HeaterEnergy1 = this->HeaterRate1 * SecInTimeStep;
11867 6113098 : this->HeaterEnergy2 = this->HeaterRate2 * SecInTimeStep;
11868 6113098 : this->FuelEnergy = this->FuelRate * SecInTimeStep;
11869 6113098 : this->VentEnergy = this->VentRate * SecInTimeStep;
11870 6113098 : this->OffCycParaFuelEnergy = this->OffCycParaFuelRate * SecInTimeStep;
11871 6113098 : this->OffCycParaEnergyToTank = this->OffCycParaRateToTank * SecInTimeStep;
11872 6113098 : this->OnCycParaFuelEnergy = this->OnCycParaFuelRate * SecInTimeStep;
11873 6113098 : this->OnCycParaEnergyToTank = this->OnCycParaRateToTank * SecInTimeStep;
11874 6113098 : this->NetHeatTransferEnergy = this->NetHeatTransferRate * SecInTimeStep;
11875 6113098 : this->VolumeConsumed = this->VolFlowRate * SecInTimeStep;
11876 6113098 : }
11877 :
11878 331 : void WaterThermalTankData::CalcStandardRatings(EnergyPlusData &state)
11879 : {
11880 :
11881 : // SUBROUTINE INFORMATION:
11882 : // AUTHOR Peter Graham Ellis
11883 : // DATE WRITTEN January 2005
11884 : // MODIFIED R. Raustad, July 2005 - added HPWH to ratings procedure
11885 : // RE-ENGINEERED na
11886 :
11887 : // PURPOSE OF THIS SUBROUTINE:
11888 : // Calculates the water heater standard ratings, such as Energy Factor and Recovery Efficiency. Results are written
11889 : // to the EIO file. Standard ratings are not calculated for storage-only tanks, i.e., MaxCapacity = 0, nor for Integrated Heat Pumps
11890 :
11891 : // METHODOLOGY EMPLOYED:
11892 : // Water heater inputs are set to the specified test conditions. For HPWHs, the heating capacity and COP are assumed
11893 : // to be the primary element in the water heater and are used during the rating procedure. CalcWaterThermalTankMixed
11894 : // is iteratively called in a self-contained, 24 hour simulation of the standard test procedure.
11895 :
11896 : // REFERENCES:
11897 : // Title 10, Code of Federal Regulations, Part 430- Energy Conservation Program for Consumer Products, Appendix E to
11898 : // Subpart B- Uniform Test Procedure for Measuring the Energy Consumption of Water Heaters, January 1, 2004.
11899 :
11900 331 : if (this->AlreadyRated) { // bail we already did this one
11901 145 : return;
11902 : }
11903 :
11904 : bool FirstTimeFlag; // used during HPWH rating procedure
11905 186 : bool bIsVSCoil = false;
11906 : Real64 RecoveryEfficiency;
11907 : Real64 EnergyFactor;
11908 186 : Real64 RatedDXCoilTotalCapacity = 0.0;
11909 186 : if (this->MaxCapacity > 0.0 || this->HeatPumpNum > 0) {
11910 : // Set test conditions
11911 181 : this->AmbientTemp = 19.7222; // 67.5 F
11912 181 : this->UseInletTemp = 14.4444; // 58 F
11913 181 : this->SetPointTemp = 57.2222; // 135 F
11914 181 : this->SetPointTemp2 = 57.2222; // 135 F
11915 181 : this->TankTemp = 57.2222; // Initialize tank temperature
11916 181 : if (this->Nodes > 0)
11917 144 : for (auto &e : this->Node)
11918 128 : e.Temp = 57.2222;
11919 :
11920 181 : Real64 TotalDrawMass = 0.243402 * Psychrometrics::RhoH2O(DataGlobalConstants::InitConvTemp); // 64.3 gal * rho
11921 181 : Real64 DrawMass = TotalDrawMass / 6.0; // 6 equal draws
11922 181 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
11923 181 : Real64 DrawMassFlowRate = DrawMass / SecInTimeStep;
11924 181 : Real64 FuelEnergy_loc = 0.0;
11925 181 : FirstTimeFlag = true;
11926 :
11927 181 : int TimeStepPerHour = int(1.0 / state.dataHVACGlobal->TimeStepSys);
11928 : // Simulate 24 hour test
11929 22549 : for (int Step = 1; Step <= TimeStepPerHour * 24; ++Step) {
11930 :
11931 44012 : if (Step == 1 || Step == (1 + TimeStepPerHour) || Step == (1 + TimeStepPerHour * 2) || Step == (1 + TimeStepPerHour * 3) ||
11932 43107 : Step == (1 + TimeStepPerHour * 4) || Step == (1 + TimeStepPerHour * 5)) { // Hour 1 | Hour 2 | Hour 3 | Hour 4 | Hour 5 | Hour 6
11933 :
11934 1086 : this->UseMassFlowRate = DrawMassFlowRate;
11935 : } else {
11936 21282 : this->UseMassFlowRate = 0.0;
11937 : }
11938 :
11939 22368 : this->SavedTankTemp = this->TankTemp;
11940 22368 : this->SavedMode = this->Mode;
11941 22368 : if (this->Nodes > 0) {
11942 33552 : for (auto &e : this->Node)
11943 30336 : e.SavedTemp = e.Temp;
11944 3216 : this->SavedHeaterOn1 = this->HeaterOn1;
11945 3216 : this->SavedHeaterOn2 = this->HeaterOn2;
11946 : }
11947 :
11948 22368 : if (this->HeatPumpNum == 0) {
11949 :
11950 18576 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
11951 17952 : this->CalcWaterThermalTankMixed(state);
11952 :
11953 624 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
11954 624 : this->CalcWaterThermalTankStratified(state);
11955 : }
11956 :
11957 : } else {
11958 :
11959 3792 : int HPNum = this->HeatPumpNum; // Convenience variable
11960 3792 : Real64 AmbientHumRat = 0.00717; // Humidity ratio at 67.5 F / 50% RH
11961 :
11962 : // set the heat pump air- and water-side mass flow rate
11963 3792 : Real64 MdotWater = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate * Psychrometrics::RhoH2O(this->TankTemp);
11964 3792 : Real64 mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
11965 :
11966 : // ?? why is HPWH condenser inlet node temp reset inside the for loop? shouldn't it chnage with the tank temp throughout these
11967 : // iterations?
11968 3792 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
11969 : // set the condenser inlet node mass flow rate and temperature
11970 2064 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).MassFlowRate = MdotWater;
11971 2064 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
11972 : }
11973 :
11974 : // initialize temperatures for HPWH DX Coil heating capacity and COP curves
11975 3792 : state.dataHVACGlobal->HPWHInletDBTemp = this->AmbientTemp;
11976 3792 : state.dataHVACGlobal->HPWHInletWBTemp =
11977 7584 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, AmbientHumRat, state.dataEnvrn->OutBaroPress);
11978 :
11979 : // set up full air flow on DX coil inlet node
11980 3792 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode > 0) {
11981 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRate = mdotAir;
11982 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRateMaxAvail = mdotAir;
11983 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Temp = this->AmbientTemp;
11984 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).HumRat = AmbientHumRat;
11985 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Enthalpy =
11986 288 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
11987 : } else {
11988 3504 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode == 0) {
11989 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRate = mdotAir;
11990 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRateMaxAvail =
11991 : mdotAir;
11992 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Temp = this->AmbientTemp;
11993 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).HumRat = AmbientHumRat;
11994 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Enthalpy =
11995 1056 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
11996 : } else {
11997 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRate = mdotAir;
11998 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRateMaxAvail = mdotAir;
11999 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Temp = this->AmbientTemp;
12000 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).HumRat = AmbientHumRat;
12001 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Enthalpy =
12002 2448 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
12003 : }
12004 : }
12005 :
12006 3792 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = this->AmbientTemp;
12007 :
12008 7584 : if (UtilityRoutines::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
12009 10752 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") ||
12010 3168 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP)) {
12011 768 : bIsVSCoil = true;
12012 1536 : std::string VSCoilName = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName;
12013 768 : int VSCoilNum = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
12014 768 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
12015 144 : VSCoilNum =
12016 144 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
12017 144 : VSCoilName =
12018 144 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilName;
12019 : }
12020 :
12021 768 : Real64 RhoWater = Psychrometrics::RhoH2O(this->TankTemp);
12022 768 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
12023 768 : this->SetVSHPWHFlowRates(
12024 : state,
12025 : HPWH,
12026 768 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12027 : 1.0,
12028 : RhoWater,
12029 : MdotWater,
12030 : true);
12031 : // simulate the HPWH coil/fan to find heating capacity
12032 768 : Real64 EMP1 = 0.0;
12033 768 : Real64 EMP2 = 0.0;
12034 768 : Real64 EMP3 = 0.0;
12035 768 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanPlacement == DataHVACGlobals::BlowThru) {
12036 : // simulate fan and DX coil twice
12037 576 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12038 288 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12039 : } else {
12040 864 : Fans::SimulateFanComponents(state,
12041 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12042 : true,
12043 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12044 : }
12045 1152 : VariableSpeedCoils::SimVariableSpeedCoils(
12046 : state,
12047 : VSCoilName,
12048 : VSCoilNum,
12049 : DataHVACGlobals::CycFanCycCoil,
12050 : EMP1,
12051 : EMP2,
12052 : EMP3,
12053 : DataHVACGlobals::CompressorOperation::On,
12054 : 1.0,
12055 576 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12056 : 1.0,
12057 : 0.0,
12058 : 0.0,
12059 : 1.0);
12060 576 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12061 288 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12062 : } else {
12063 864 : Fans::SimulateFanComponents(state,
12064 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12065 : true,
12066 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12067 : }
12068 1152 : VariableSpeedCoils::SimVariableSpeedCoils(
12069 : state,
12070 : VSCoilName,
12071 : VSCoilNum,
12072 : DataHVACGlobals::CycFanCycCoil,
12073 : EMP1,
12074 : EMP2,
12075 : EMP3,
12076 : DataHVACGlobals::CompressorOperation::On,
12077 : 1.0,
12078 576 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12079 : 1.0,
12080 : 0.0,
12081 : 0.0,
12082 : 1.0);
12083 : } else {
12084 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
12085 384 : VariableSpeedCoils::SimVariableSpeedCoils(
12086 : state,
12087 : VSCoilName,
12088 : VSCoilNum,
12089 : DataHVACGlobals::CycFanCycCoil,
12090 : EMP1,
12091 : EMP2,
12092 : EMP3,
12093 : DataHVACGlobals::CompressorOperation::On,
12094 : 1.0,
12095 192 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12096 : 1.0,
12097 : 0.0,
12098 : 0.0,
12099 : 1.0);
12100 192 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12101 192 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12102 : } else {
12103 0 : Fans::SimulateFanComponents(state,
12104 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12105 : true,
12106 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12107 : }
12108 384 : VariableSpeedCoils::SimVariableSpeedCoils(
12109 : state,
12110 : VSCoilName,
12111 : VSCoilNum,
12112 : DataHVACGlobals::CycFanCycCoil,
12113 : EMP1,
12114 : EMP2,
12115 : EMP3,
12116 : DataHVACGlobals::CompressorOperation::On,
12117 : 1.0,
12118 192 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12119 : 1.0,
12120 : 0.0,
12121 : 0.0,
12122 : 1.0);
12123 192 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12124 192 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12125 : } else {
12126 0 : Fans::SimulateFanComponents(state,
12127 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12128 : true,
12129 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12130 : }
12131 : }
12132 :
12133 768 : this->MaxCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
12134 768 : this->MinCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
12135 768 : this->Efficiency = state.dataVariableSpeedCoils->VSHPWHHeatingCOP;
12136 : } else {
12137 3024 : bIsVSCoil = false;
12138 : // simulate the HPWH coil/fan to find heating capacity
12139 3024 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanPlacement == DataHVACGlobals::BlowThru) {
12140 720 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
12141 : // reset water inlet temp. If already sized, no harm.
12142 7 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12143 4 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12144 : } else {
12145 9 : Fans::SimulateFanComponents(state,
12146 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12147 : true,
12148 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12149 : }
12150 21 : DXCoils::SimDXCoil(state,
12151 7 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12152 : DataHVACGlobals::CompressorOperation::On,
12153 : true,
12154 7 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12155 : DataHVACGlobals::CycFanCycCoil,
12156 : 1.0);
12157 7 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12158 : }
12159 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
12160 : // PLR=1 here.
12161 720 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12162 432 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12163 : } else {
12164 864 : Fans::SimulateFanComponents(state,
12165 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12166 : true,
12167 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12168 : }
12169 2160 : DXCoils::SimDXCoil(state,
12170 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12171 : DataHVACGlobals::CompressorOperation::On,
12172 : true,
12173 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12174 : DataHVACGlobals::CycFanCycCoil,
12175 : 1.0);
12176 720 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12177 432 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12178 : } else {
12179 864 : Fans::SimulateFanComponents(state,
12180 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12181 : true,
12182 288 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12183 : }
12184 2160 : DXCoils::SimDXCoil(state,
12185 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12186 : DataHVACGlobals::CompressorOperation::On,
12187 : true,
12188 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12189 : DataHVACGlobals::CycFanCycCoil,
12190 : 1.0);
12191 : } else {
12192 2304 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
12193 : // reset water inlet temp. If already sized, no harm.
12194 27 : DXCoils::SimDXCoil(state,
12195 9 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12196 : DataHVACGlobals::CompressorOperation::On,
12197 : true,
12198 9 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12199 : DataHVACGlobals::CycFanCycCoil,
12200 : 1.0);
12201 9 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12202 : }
12203 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
12204 : // PLR=1 here.
12205 6912 : DXCoils::SimDXCoil(state,
12206 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12207 : DataHVACGlobals::CompressorOperation::On,
12208 : true,
12209 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12210 : DataHVACGlobals::CycFanCycCoil,
12211 : 1.0);
12212 2304 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12213 1728 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12214 : } else {
12215 1728 : Fans::SimulateFanComponents(state,
12216 576 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12217 : true,
12218 576 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12219 : }
12220 6912 : DXCoils::SimDXCoil(state,
12221 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12222 : DataHVACGlobals::CompressorOperation::On,
12223 : true,
12224 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12225 : DataHVACGlobals::CycFanCycCoil,
12226 : 1.0);
12227 2304 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12228 1728 : state.dataHVACFan->fanObjs[state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum]->simulate(state, _, _, _, _);
12229 : } else {
12230 1728 : Fans::SimulateFanComponents(state,
12231 576 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanName,
12232 : true,
12233 576 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum);
12234 : }
12235 : }
12236 :
12237 3024 : this->MaxCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12238 3024 : this->MinCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12239 3024 : this->Efficiency = state.dataDXCoils->HPWHHeatingCOP;
12240 : }
12241 :
12242 3792 : if (FirstTimeFlag) {
12243 23 : RatedDXCoilTotalCapacity = state.dataHVACGlobal->DXCoilTotalCapacity;
12244 23 : FirstTimeFlag = false;
12245 : }
12246 :
12247 : // Switch the HPWH info with the tank info and call CalcWaterThermalTankMixed to get Standard Rating
12248 : // (backup element is assumed to be disabled during the rating procedure)
12249 3792 : this->SourceMassFlowRate = 0.0;
12250 3792 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OnCycParaLoad;
12251 3792 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OffCycParaLoad;
12252 3792 : this->OffCycParaFracToTank = 0.0;
12253 3792 : this->OnCycParaFracToTank = 0.0;
12254 3792 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilPLFFPLR;
12255 :
12256 3792 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
12257 1200 : if (this->Efficiency > 0.0) this->CalcWaterThermalTankMixed(state);
12258 :
12259 2592 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12260 2592 : if (this->Efficiency > 0.0) this->CalcWaterThermalTankStratified(state);
12261 : }
12262 :
12263 : // reset the water heater data to original values
12264 3792 : this->MaxCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12265 3792 : this->MinCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12266 3792 : this->Efficiency = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementEfficiency;
12267 3792 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaLoad;
12268 3792 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaLoad;
12269 3792 : this->OnCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaFracToTank;
12270 3792 : this->OffCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaFracToTank;
12271 3792 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHPLFCurve;
12272 : }
12273 :
12274 22368 : FuelEnergy_loc += (this->FuelRate + this->OffCycParaFuelRate + this->OnCycParaFuelRate) * SecInTimeStep;
12275 :
12276 : } // Step
12277 :
12278 181 : if (this->FirstRecoveryDone && this->FirstRecoveryFuel > 0.0) {
12279 : // Calculate Recovery Efficiency based on energy used to recover from the first draw
12280 : // FirstRecoveryFuel is recorded inside the CalcWaterThermalTank subroutine
12281 164 : RecoveryEfficiency = DrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / this->FirstRecoveryFuel;
12282 :
12283 : // Calculate Energy Factor based on total energy (including parasitics) used over entire test
12284 164 : EnergyFactor = TotalDrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / FuelEnergy_loc;
12285 :
12286 : } else {
12287 17 : RecoveryEfficiency = 0.0;
12288 17 : EnergyFactor = 0.0;
12289 : // If this a regular tank, or an HPWH that's not an Integrated one
12290 17 : if ((this->HeatPumpNum == 0) || !state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).bIsIHP) {
12291 48 : ShowWarningError(state,
12292 32 : "Water heater = " + this->Name +
12293 : ": Recovery Efficiency and Energy Factor could not be calculated during the test for standard ratings");
12294 16 : ShowContinueError(state, "Setpoint was never recovered and/or heater never turned on");
12295 : }
12296 181 : }
12297 :
12298 : } else {
12299 :
12300 : // Storage-only tank
12301 5 : RecoveryEfficiency = 0.0;
12302 5 : EnergyFactor = 0.0;
12303 :
12304 : } // WaterThermalTank(WaterThermalTankNum)%MaxCapacity > 0.0
12305 :
12306 : // create predefined report
12307 : // Store values for the input verification and summary report
12308 372 : std::string equipName;
12309 186 : if (this->HeatPumpNum == 0) {
12310 163 : equipName = this->Name;
12311 163 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHType, equipName, this->Type);
12312 163 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12313 163 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, this->MaxCapacity);
12314 163 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12315 163 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12316 163 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12317 : } else {
12318 23 : equipName = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name;
12319 46 : OutputReportPredefined::PreDefTableEntry(
12320 46 : state, state.dataOutRptPredefined->pdchSWHType, equipName, state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type);
12321 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12322 23 : if (bIsVSCoil) {
12323 21 : OutputReportPredefined::PreDefTableEntry(
12324 14 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataVariableSpeedCoils->VSHPWHHeatingCapacity);
12325 : } else {
12326 48 : OutputReportPredefined::PreDefTableEntry(
12327 32 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataDXCoils->HPWHHeatingCapacity);
12328 : }
12329 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12330 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12331 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12332 : }
12333 :
12334 : // Write test results
12335 186 : if (this->HeatPumpNum == 0) {
12336 : Real64 MaxCapacity_loc;
12337 163 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12338 5 : if (this->StratifiedControlMode == PriorityControlMode::MasterSlave) {
12339 5 : MaxCapacity_loc = max(this->MaxCapacity, this->MaxCapacity2);
12340 : } else { // PrioritySimultaneous
12341 0 : MaxCapacity_loc = this->MaxCapacity + this->MaxCapacity2;
12342 : }
12343 : } else { // WaterHeaterMixed
12344 158 : MaxCapacity_loc = this->MaxCapacity;
12345 : }
12346 :
12347 : static constexpr std::string_view Format_720("Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T}\n");
12348 163 : print(state.files.eio, Format_720, this->Type, this->Name, this->Volume, MaxCapacity_loc, RecoveryEfficiency, EnergyFactor);
12349 : } else {
12350 : static constexpr std::string_view Format_721("Heat Pump Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T},{:.0T}\n");
12351 69 : print(state.files.eio,
12352 : Format_721,
12353 23 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type,
12354 23 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
12355 : this->Volume,
12356 23 : state.dataDXCoils->HPWHHeatingCapacity,
12357 : RecoveryEfficiency,
12358 : EnergyFactor,
12359 : RatedDXCoilTotalCapacity);
12360 : }
12361 :
12362 186 : this->AlreadyRated = true;
12363 : }
12364 :
12365 9 : void WaterThermalTankData::ReportCWTankInits(EnergyPlusData &state)
12366 : {
12367 :
12368 : // SUBROUTINE INFORMATION:
12369 : // AUTHOR B. Griffith
12370 : // DATE WRITTEN March 2009
12371 : // MODIFIED na
12372 : // RE-ENGINEERED na
12373 :
12374 : // PURPOSE OF THIS SUBROUTINE:
12375 : // send chilled water tank info to EIO
12376 :
12377 9 : if (this->myOneTimeInitFlag) {
12378 0 : this->setupOutputVars(state);
12379 0 : this->myOneTimeInitFlag = false;
12380 : }
12381 :
12382 9 : if (this->AlreadyReported) { // bail we already did this one
12383 4 : return;
12384 : }
12385 :
12386 : static constexpr std::string_view Format_728("Chilled Water Tank Information,{},{},{:.4T},{:.4T},{:.4T}\n");
12387 5 : print(state.files.eio, Format_728, this->Type, this->Name, this->Volume, this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
12388 :
12389 5 : this->AlreadyReported = true;
12390 : }
12391 :
12392 1095678 : Real64 WaterThermalTankData::FindStratifiedTankSensedTemp(EnergyPlusData &state, bool UseAverage)
12393 : {
12394 :
12395 : // FUNCTION INFORMATION:
12396 : // AUTHOR B. Griffith
12397 : // DATE WRITTEN March 2012
12398 : // MODIFIED na
12399 : // RE-ENGINEERED Noel Merket, April 2015
12400 :
12401 : // PURPOSE OF THIS FUNCTION:
12402 : // find tank temperature depending on how sensed
12403 :
12404 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
12405 1095678 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
12406 : Real64 ControlSensor1Temp;
12407 : Real64 ControlSensor2Temp;
12408 :
12409 1095678 : if (UseAverage) {
12410 276566 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).TempAvg;
12411 276566 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).TempAvg;
12412 : } else {
12413 819112 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).Temp;
12414 819112 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).Temp;
12415 : }
12416 :
12417 1095678 : Real64 SensedTemp = ControlSensor1Temp * HPWH.ControlSensor1Weight + ControlSensor2Temp * HPWH.ControlSensor2Weight;
12418 :
12419 1095678 : return SensedTemp;
12420 : }
12421 :
12422 6395474 : Real64 WaterThermalTankData::getDeadBandTemp()
12423 : {
12424 6395474 : if (this->IsChilledWaterTank) {
12425 315033 : return (this->SetPointTemp + this->DeadBandDeltaTemp);
12426 : } else {
12427 6080441 : return (this->SetPointTemp - this->DeadBandDeltaTemp);
12428 : }
12429 : }
12430 5468492 : void WaterThermalTankData::oneTimeInit(EnergyPlusData &state)
12431 : {
12432 5468492 : if (this->myOneTimeInitFlag) {
12433 168 : this->setupOutputVars(state);
12434 168 : this->myOneTimeInitFlag = false;
12435 : }
12436 5468492 : }
12437 :
12438 20 : void WaterThermalTankData::setBackupElementCapacity(EnergyPlusData &state)
12439 : {
12440 : // Fix for #9001: The BackupElementCapacity was not being reset from the autosize value (-99999) which resulted in
12441 : // negative electric consumption. Using a test for any negative numbers here instead of just -99999 for safety.
12442 : // Only reset the backup element capacity if a problem has been occured.
12443 20 : if (this->HeatPumpNum > 0) {
12444 20 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) return;
12445 0 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity < 0.0) {
12446 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity = this->MaxCapacity;
12447 : }
12448 0 : } else if (this->DesuperheaterNum > 0) {
12449 0 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity < 0.0) {
12450 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity = this->MaxCapacity;
12451 : }
12452 : }
12453 : }
12454 :
12455 16 : bool GetHeatPumpWaterHeaterNodeNumber(EnergyPlusData &state, int const NodeNumber)
12456 : {
12457 : // PURPOSE OF THIS FUNCTION:
12458 : // Check if a node is used by a heat pump water heater
12459 : // and can be excluded from an airflow network.
12460 :
12461 : // Return value
12462 : bool HeatPumpWaterHeaterNodeException;
12463 :
12464 : int HeatPumpWaterHeaterIndex;
12465 :
12466 16 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
12467 0 : GetWaterThermalTankInput(state);
12468 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
12469 : }
12470 :
12471 16 : HeatPumpWaterHeaterNodeException = false;
12472 :
12473 26 : for (HeatPumpWaterHeaterIndex = 1; HeatPumpWaterHeaterIndex <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HeatPumpWaterHeaterIndex) {
12474 :
12475 : // Get heat pump water heater data
12476 16 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpWaterHeaterIndex);
12477 :
12478 : // "Zone and outdoor air" configuration is expected break the conservation of mass
12479 16 : if (HPWH.InletAirConfiguration != WTTAmbientTemp::ZoneAndOA) {
12480 :
12481 : // Air outlet node
12482 16 : if (NodeNumber == HPWH.HeatPumpAirOutletNode) {
12483 2 : HeatPumpWaterHeaterNodeException = true;
12484 8 : break;
12485 : }
12486 :
12487 : // Air inlet node
12488 14 : if (NodeNumber == HPWH.HeatPumpAirInletNode) {
12489 2 : HeatPumpWaterHeaterNodeException = true;
12490 2 : break;
12491 : }
12492 :
12493 : // Get fan inlet node index
12494 12 : bool ErrorsFound{false};
12495 12 : int FanInletNodeIndex(0);
12496 12 : if (HPWH.FanType_Num == DataHVACGlobals::FanType_SystemModelObject) {
12497 0 : FanInletNodeIndex = state.dataHVACFan->fanObjs[HPWH.FanNum]->inletNodeNum;
12498 : } else {
12499 12 : FanInletNodeIndex = Fans::GetFanInletNode(state, HPWH.FanType, HPWH.FanName, ErrorsFound);
12500 12 : if (ErrorsFound) {
12501 0 : ShowWarningError(state, "Could not retrieve fan outlet node for this unit=\"" + HPWH.Name + "\".");
12502 0 : ErrorsFound = true;
12503 : }
12504 : }
12505 :
12506 : // Fan inlet node
12507 12 : if (NodeNumber == FanInletNodeIndex) {
12508 2 : HeatPumpWaterHeaterNodeException = true;
12509 2 : break;
12510 : }
12511 :
12512 : // Fan outlet node
12513 10 : if (NodeNumber == HPWH.FanOutletNode) {
12514 0 : HeatPumpWaterHeaterNodeException = true;
12515 0 : break;
12516 : }
12517 :
12518 : // Outside air node
12519 10 : if (NodeNumber == HPWH.OutsideAirNode) {
12520 0 : HeatPumpWaterHeaterNodeException = true;
12521 0 : break;
12522 : }
12523 :
12524 : // Exhaust air node
12525 10 : if (NodeNumber == HPWH.ExhaustAirNode) {
12526 0 : HeatPumpWaterHeaterNodeException = true;
12527 0 : break;
12528 : }
12529 : }
12530 : }
12531 :
12532 16 : return HeatPumpWaterHeaterNodeException;
12533 : }
12534 :
12535 2313 : } // namespace EnergyPlus::WaterThermalTanks
|