Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // ObjexxFCL Headers
49 : #include <ObjexxFCL/Array.functions.hh>
50 : #include <ObjexxFCL/floops.hh>
51 : #include <ObjexxFCL/member.functions.hh>
52 :
53 : // EnergyPlus Headers
54 : #include <EnergyPlus/Autosizing/Base.hh>
55 : #include <EnergyPlus/BranchNodeConnections.hh>
56 : #include <EnergyPlus/Coils/CoilCoolingDX.hh>
57 : #include <EnergyPlus/CurveManager.hh>
58 : #include <EnergyPlus/DXCoils.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
61 : #include <EnergyPlus/DataGlobalConstants.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/HeatBalanceInternalHeatGains.hh>
74 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
75 : #include <EnergyPlus/IntegratedHeatPump.hh>
76 : #include <EnergyPlus/NodeInputManager.hh>
77 : #include <EnergyPlus/OutAirNodeManager.hh>
78 : #include <EnergyPlus/OutputProcessor.hh>
79 : #include <EnergyPlus/OutputReportPredefined.hh>
80 : #include <EnergyPlus/PhotovoltaicThermalCollectors.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 applications.
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 : std::string const cMixedWHModuleObj = "WaterHeater:Mixed";
128 : std::string const cStratifiedWHModuleObj = "WaterHeater:Stratified";
129 : std::string const cMixedCWTankModuleObj = "ThermalStorage:ChilledWater:Mixed";
130 : std::string const cStratifiedCWTankModuleObj = "ThermalStorage:ChilledWater:Stratified";
131 : std::string const cHPWHPumpedCondenser = "WaterHeater:HeatPump:PumpedCondenser";
132 : std::string const cHPWHWrappedCondenser = "WaterHeater:HeatPump:WrappedCondenser";
133 : std::string const cCoilDesuperheater = "Coil:WaterHeating:Desuperheater";
134 :
135 146 : PlantComponent *WaterThermalTankData::factory(EnergyPlusData &state, std::string const &objectName)
136 : {
137 : // Process the input data
138 146 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
139 12 : GetWaterThermalTankInput(state);
140 12 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
141 : }
142 :
143 : // Now look for this object in the list
144 167 : for (auto &tank : state.dataWaterThermalTanks->WaterThermalTank) {
145 167 : if (tank.Name == objectName) {
146 146 : return &tank;
147 : }
148 : }
149 : // If we didn't find it, fatal
150 : ShowFatalError(state, format("LocalWaterTankFactory: Error getting inputs for tank named: {}", objectName)); // LCOV_EXCL_LINE
151 : // Shut up the compiler
152 : return nullptr; // LCOV_EXCL_LINE
153 : }
154 :
155 791 : void WaterThermalTankData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
156 : {
157 791 : this->initialize(state, true);
158 791 : this->MinePlantStructForInfo(state);
159 791 : if (calledFromLocation.loopNum > 0) {
160 791 : if ((this->SrcSidePlantLoc.loopNum == calledFromLocation.loopNum) || (this->UseSidePlantLoc.loopNum == calledFromLocation.loopNum)) {
161 786 : this->SizeTankForDemandSide(state);
162 786 : this->SizeDemandSidePlantConnections(state);
163 786 : this->SizeSupplySidePlantConnections(state, calledFromLocation.loopNum);
164 786 : this->SizeTankForSupplySide(state);
165 : } else {
166 5 : return;
167 : }
168 : } else {
169 0 : this->SizeTankForDemandSide(state);
170 0 : this->SizeDemandSidePlantConnections(state);
171 0 : this->SizeSupplySidePlantConnections(state, this->SrcSidePlantLoc.loopNum);
172 0 : this->SizeTankForSupplySide(state);
173 : }
174 :
175 786 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
176 170 : if (!this->IsChilledWaterTank) {
177 160 : this->CalcStandardRatings(state);
178 : } else {
179 10 : this->ReportCWTankInits(state);
180 : }
181 : }
182 : }
183 :
184 741 : void WaterThermalTankData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
185 : [[maybe_unused]] const PlantLocation &calledFromLocation,
186 : Real64 &MaxLoad,
187 : Real64 &MinLoad,
188 : Real64 &OptLoad)
189 : {
190 741 : MinLoad = 0.0;
191 741 : MaxLoad = this->MaxCapacity;
192 741 : OptLoad = this->MaxCapacity;
193 741 : }
194 :
195 0 : int getTankIDX(EnergyPlusData &state, std::string_view CompName, int &CompIndex)
196 : {
197 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
198 0 : GetWaterThermalTankInput(state);
199 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
200 : }
201 :
202 : int CompNum;
203 :
204 0 : if (CompIndex == 0) {
205 0 : CompNum = Util::FindItem(CompName, state.dataWaterThermalTanks->WaterThermalTank);
206 0 : if (CompNum == 0) {
207 0 : ShowFatalError(state, format("SimWaterThermalTank_WaterTank: Unit not found={}", CompName));
208 : }
209 0 : CompIndex = CompNum;
210 : } else {
211 0 : CompNum = CompIndex;
212 0 : if (CompNum > state.dataWaterThermalTanks->numWaterThermalTank || CompNum < 1) {
213 0 : ShowFatalError(state,
214 0 : format("SimWaterThermalTank_WaterTank: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
215 : CompNum,
216 0 : state.dataWaterThermalTanks->numWaterThermalTank,
217 : CompName));
218 : }
219 0 : if (state.dataWaterThermalTanks->WaterThermalTank(CompNum).CheckWTTEquipName) {
220 0 : if (CompName != state.dataWaterThermalTanks->WaterThermalTank(CompNum).Name) {
221 0 : ShowFatalError(state,
222 0 : format("SimWaterThermalTank_WaterTank: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
223 : CompNum,
224 : CompName,
225 0 : state.dataWaterThermalTanks->WaterThermalTank(CompNum).Name));
226 : }
227 0 : state.dataWaterThermalTanks->WaterThermalTank(CompNum).CheckWTTEquipName = false;
228 : }
229 : }
230 :
231 0 : return CompNum;
232 : }
233 :
234 5022 : int getHPTankIDX(EnergyPlusData &state, std::string_view CompName, int &CompIndex)
235 : {
236 5022 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
237 0 : GetWaterThermalTankInput(state);
238 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
239 : }
240 :
241 : int CompNum;
242 :
243 5022 : if (CompIndex == 0) {
244 0 : CompNum = Util::FindItem(CompName, state.dataWaterThermalTanks->HPWaterHeater);
245 0 : if (CompNum == 0) {
246 0 : ShowFatalError(state, format("SimWaterThermalTank_HeatPump: Unit not found={}", CompName));
247 : }
248 0 : CompIndex = CompNum;
249 : } else {
250 5022 : CompNum = CompIndex;
251 5022 : if (CompNum > state.dataWaterThermalTanks->numWaterThermalTank || CompNum < 1) {
252 0 : ShowFatalError(state,
253 0 : format("SimWaterThermalTank_HeatPump: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
254 : CompNum,
255 0 : state.dataWaterThermalTanks->numHeatPumpWaterHeater,
256 : CompName));
257 : }
258 5022 : if (state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName) {
259 1 : if (CompName != state.dataWaterThermalTanks->HPWaterHeater(CompNum).Name) {
260 0 : ShowFatalError(state,
261 0 : format("SimWaterThermalTank_HeatPump: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
262 : CompNum,
263 : CompName,
264 0 : state.dataWaterThermalTanks->HPWaterHeater(CompNum).Name));
265 : }
266 1 : state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName = false;
267 : }
268 : }
269 :
270 5022 : return CompNum;
271 : }
272 :
273 5928161 : void WaterThermalTankData::simulate(
274 : EnergyPlusData &state, const PlantLocation &calledFromLocation, bool FirstHVACIteration, Real64 &CurLoad, [[maybe_unused]] bool RunFlag)
275 : {
276 : // SUBROUTINE INFORMATION:
277 : // AUTHOR Brandon Anderson
278 : // DATE WRITTEN May 2000
279 : // MODIFIED FSEC, July 2005
280 : // RE-ENGINEERED na
281 :
282 : // set the caller loop num to mimic what was happening in plant loop equip
283 5928161 : this->callerLoopNum = calledFromLocation.loopNum;
284 :
285 5928161 : this->oneTimeInit(state);
286 :
287 5928161 : if (this->MyOneTimeFlagWH) {
288 178 : this->MyOneTimeFlagWH = false;
289 : } else {
290 5927983 : if (this->MyTwoTimeFlagWH) {
291 178 : this->MinePlantStructForInfo(state); // call it again to get control types filled out
292 178 : this->MyTwoTimeFlagWH = false;
293 : }
294 : }
295 5928161 : this->UseSideLoadRequested = std::abs(CurLoad);
296 11528958 : if (this->UseSidePlantLoc.loopNum > 0 && this->UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
297 5600797 : !state.dataGlobal->KickOffSimulation) {
298 5579848 : this->UseCurrentFlowLock = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).LoopSide(this->UseSidePlantLoc.loopSideNum).FlowLock;
299 : } else {
300 348313 : this->UseCurrentFlowLock = DataPlant::FlowLock::Locked;
301 : }
302 5928161 : this->initialize(state, FirstHVACIteration);
303 : // Plant connected water heaters may have a desuperheater heating coil attached
304 5928161 : if (this->DesuperheaterNum == 0) {
305 5898677 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) ||
306 628188 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed)) {
307 5660149 : this->CalcWaterThermalTankMixed(state);
308 238528 : } else if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
309 155633 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
310 238528 : this->CalcWaterThermalTankStratified(state);
311 : }
312 29484 : } else if (this->DesuperheaterNum > 0) {
313 29484 : this->CalcDesuperheaterWaterHeater(state, FirstHVACIteration);
314 : }
315 5928161 : this->UpdateWaterThermalTank(state);
316 5928161 : this->ReportWaterThermalTank(state);
317 : // reset the caller loop num to mimic what was happening in PlantLoopEquip
318 5928161 : this->callerLoopNum = 0;
319 5928161 : }
320 :
321 10 : PlantComponent *HeatPumpWaterHeaterData::factory(EnergyPlusData &state, std::string const &objectName)
322 : {
323 : // Process the input data
324 10 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
325 3 : GetWaterThermalTankInput(state);
326 3 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
327 : }
328 :
329 : // Now look for this object in the list
330 28 : for (auto &HPWH : state.dataWaterThermalTanks->HPWaterHeater) {
331 28 : if (HPWH.Name == objectName) {
332 10 : return &HPWH;
333 : }
334 : }
335 : // If we didn't find it, fatal
336 : ShowFatalError(state, format("LocalHeatPumpWaterHeaterFactory: Error getting inputs for object named: {}", objectName)); // LCOV_EXCL_LINE
337 : // Shut up the compiler
338 : return nullptr; // LCOV_EXCL_LINE
339 : }
340 :
341 50 : void HeatPumpWaterHeaterData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
342 : {
343 50 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
344 50 : Tank.onInitLoopEquip(state, calledFromLocation);
345 50 : }
346 :
347 50 : void HeatPumpWaterHeaterData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
348 : [[maybe_unused]] const PlantLocation &calledFromLocation,
349 : Real64 &MaxLoad,
350 : Real64 &MinLoad,
351 : Real64 &OptLoad)
352 : {
353 50 : MinLoad = 0.0;
354 50 : MaxLoad = this->Capacity;
355 50 : OptLoad = this->Capacity;
356 50 : }
357 :
358 408468 : void HeatPumpWaterHeaterData::simulate(
359 : EnergyPlusData &state, const PlantLocation &calledFromLocation, bool FirstHVACIteration, Real64 &CurLoad, [[maybe_unused]] bool RunFlag)
360 : {
361 : // SUBROUTINE INFORMATION:
362 : // AUTHOR Brandon Anderson
363 : // DATE WRITTEN May 2000
364 : // MODIFIED FSEC, July 2005
365 : // RE-ENGINEERED na
366 :
367 408468 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
368 :
369 : // set caller loop num to mimic what plantloopequip was doing
370 408468 : Tank.callerLoopNum = calledFromLocation.loopNum;
371 :
372 408468 : if (this->myOneTimeInitFlag) {
373 23 : if (Tank.myOneTimeInitFlag) {
374 23 : Tank.setupOutputVars(state);
375 23 : Tank.myOneTimeInitFlag = false;
376 : }
377 23 : this->myOneTimeInitFlag = false;
378 : }
379 :
380 408468 : if (this->MyOneTimeFlagHP) {
381 23 : this->MyOneTimeFlagHP = false;
382 : } else {
383 408445 : if (this->MyTwoTimeFlagHP) {
384 23 : Tank.MinePlantStructForInfo(state); // call it again to get control types filled out
385 23 : this->MyTwoTimeFlagHP = false;
386 : }
387 : }
388 408468 : Tank.UseSideLoadRequested = std::abs(CurLoad);
389 742982 : if (Tank.UseSidePlantLoc.loopNum > 0 && Tank.UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
390 334514 : !state.dataGlobal->KickOffSimulation) {
391 333134 : Tank.UseCurrentFlowLock = state.dataPlnt->PlantLoop(Tank.UseSidePlantLoc.loopNum).LoopSide(Tank.UseSidePlantLoc.loopSideNum).FlowLock;
392 : } else {
393 75334 : Tank.UseCurrentFlowLock = DataPlant::FlowLock::Locked;
394 : }
395 :
396 408468 : Tank.initialize(state, FirstHVACIteration);
397 :
398 408468 : int InletNodeSav = this->HeatPumpAirInletNode;
399 408468 : int OutletNodeSav = this->HeatPumpAirOutletNode;
400 408468 : int DXINletNodeSav = this->DXCoilAirInletNode;
401 408468 : int IHPFanIndexSav = this->FanNum;
402 408468 : std::string IHPFanNameSave = this->FanName;
403 408468 : HVAC::FanPlace IHPFanplaceSav = this->fanPlace;
404 :
405 408468 : if (this->bIsIHP) // pass the tank indexes to the IHP object
406 : {
407 10046 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankType = this->HPWHType;
408 10046 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankName = this->Name;
409 10046 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankID = this->WaterHeaterTankNum;
410 10046 : IntegratedHeatPump::IHPOperationMode IHPMode = IntegratedHeatPump::GetCurWorkMode(state, this->DXCoilNum);
411 :
412 10046 : if ((IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) ||
413 7643 : (IntegratedHeatPump::IHPOperationMode::SpaceClgDedicatedWaterHtg == IHPMode) ||
414 7643 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) ||
415 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOn == IHPMode)) { // default is to specify the air nodes for SCWH mode
416 2403 : bool bDWHCoilReading = false;
417 2403 : this->HeatPumpAirInletNode =
418 2403 : VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state,
419 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
420 2403 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
421 : bDWHCoilReading);
422 2403 : this->HeatPumpAirOutletNode =
423 2403 : VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state,
424 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
425 2403 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
426 : bDWHCoilReading);
427 2403 : this->DXCoilAirInletNode = this->HeatPumpAirInletNode;
428 2403 : } else // default is to input outdoor fan to the HPWH
429 : {
430 7643 : this->FanNum = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanID;
431 7643 : this->FanName = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanName;
432 7643 : this->fanPlace = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).fanPlace;
433 : }
434 : }
435 :
436 408468 : Tank.CalcHeatPumpWaterHeater(state, FirstHVACIteration);
437 408468 : Tank.UpdateWaterThermalTank(state);
438 408468 : Tank.ReportWaterThermalTank(state);
439 :
440 408468 : this->HeatPumpAirInletNode = InletNodeSav;
441 408468 : this->HeatPumpAirOutletNode = OutletNodeSav;
442 408468 : this->DXCoilAirInletNode = DXINletNodeSav;
443 408468 : this->FanNum = IHPFanIndexSav;
444 408468 : this->FanName = IHPFanNameSave;
445 408468 : this->fanPlace = IHPFanplaceSav;
446 : // reset caller loop num to 0 to mimic what plantloopequip was doing
447 408468 : Tank.callerLoopNum = 0;
448 408468 : }
449 0 : void HeatPumpWaterHeaterData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
450 : {
451 0 : }
452 :
453 1410236 : void SimulateWaterHeaterStandAlone(EnergyPlusData &state, int const WaterHeaterNum, bool const FirstHVACIteration)
454 : {
455 :
456 : // SUBROUTINE INFORMATION:
457 : // AUTHOR Peter Graham Ellis
458 : // DATE WRITTEN January 2004
459 : // MODIFIED July 2005, FSEC - added HPWHs and desuperheater water heating coils
460 : // RE-ENGINEERED na
461 :
462 : // PURPOSE OF THIS SUBROUTINE:
463 : // This subroutine acts an interface to SimWaterHeater for stand-alone water heaters with no plant connections,
464 : // HPWHs not defined as zone equipment with no plant connections, and stand-alone water heaters with
465 : // desuperheater heating coils with no plant connections.
466 :
467 : // METHODOLOGY EMPLOYED:
468 : // The necessary control flags and dummy variables are set and passed into SimWaterHeater. This subroutine is
469 : // called from NonZoneEquipmentManager.
470 :
471 : Real64 MyLoad;
472 :
473 1410236 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
474 7 : GetWaterThermalTankInput(state);
475 7 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
476 : }
477 :
478 1410236 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterHeaterNum);
479 :
480 : // Only simulate stand-alone water heaters here. Plant connected water heaters are called by the PlantLoopEquipments.
481 1410236 : if (Tank.StandAlone) {
482 284584 : bool localRunFlag = true;
483 284584 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
484 284584 : Tank.simulate(state, A, FirstHVACIteration, MyLoad, localRunFlag);
485 :
486 : // HPWHs with inlet air from a zone and not connected to a plant loop are simulated through a CALL from ZoneEquipmentManager.
487 : // HPWHs that are plant connected are always simulated through a CALL from PlantLoopEquipments directly to SimWaterThermalTank.
488 :
489 : // NOTE: HPWHs with inlet air from a zone AND plant connected are not stand alone and are simulated in PlantLoopEquipments
490 1125652 : } else if (Tank.HeatPumpNum > 0) {
491 : // Only HPWHs with inlet air from outdoors or scheduled HPWHs (not connected to a plant loop) are simulated here.
492 :
493 124634 : auto &HPWaterHtr = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum);
494 :
495 124634 : if (HPWaterHtr.StandAlone &&
496 68688 : (HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::Schedule)) {
497 36856 : bool LocalRunFlag = true;
498 36856 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
499 36856 : HPWaterHtr.simulate(state, A, FirstHVACIteration, MyLoad, LocalRunFlag);
500 : }
501 :
502 : // Only simulate stand-alone water heaters with desuperheater water heating coils here. Plant connected water heaters
503 : // with desuperheater water heating coils are called by PlantLoopEquipments.
504 1001018 : } else if (Tank.DesuperheaterNum > 0) {
505 29484 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).StandAlone) {
506 29484 : bool localRunFlag = true;
507 29484 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
508 29484 : Tank.simulate(state, A, FirstHVACIteration, MyLoad, localRunFlag);
509 : }
510 : }
511 1410236 : }
512 :
513 124230 : void SimHeatPumpWaterHeater(EnergyPlusData &state,
514 : std::string_view CompName,
515 : bool const FirstHVACIteration,
516 : Real64 &SensLoadMet, // sensible load met by this equipment and sent to zone, W
517 : Real64 &LatLoadMet, // net latent load met and sent to zone (kg/s), dehumid = negative
518 : int &CompIndex)
519 : {
520 : // SUBROUTINE INFORMATION:
521 : // AUTHOR Richard Raustad
522 : // DATE WRITTEN April 2005
523 : // MODIFIED Don Shirey, Aug 2009 (LatLoadMet)
524 : // RE-ENGINEERED na
525 :
526 : // PURPOSE OF THIS SUBROUTINE:
527 : // This subroutine acts as an interface to SimWaterHeater.
528 : // HPWHs defined as zone equipment and not connected to a plant loop are called here by ZoneEquipmentManager
529 :
530 : // METHODOLOGY EMPLOYED:
531 : // The necessary control flags and dummy variables are set and passed into SimWaterHeater.
532 :
533 124230 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
534 0 : GetWaterThermalTankInput(state);
535 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
536 : }
537 :
538 : // Find the correct Heat Pump Water Heater
539 : int HeatPumpNum;
540 124230 : if (CompIndex == 0) {
541 10 : HeatPumpNum = Util::FindItemInList(CompName, state.dataWaterThermalTanks->HPWaterHeater);
542 10 : if (HeatPumpNum == 0) {
543 0 : ShowFatalError(state, format("SimHeatPumpWaterHeater: Unit not found={}", CompName));
544 : }
545 10 : CompIndex = HeatPumpNum;
546 : } else {
547 124220 : HeatPumpNum = CompIndex;
548 124220 : if (HeatPumpNum > state.dataWaterThermalTanks->numHeatPumpWaterHeater || HeatPumpNum < 1) {
549 0 : ShowFatalError(state,
550 0 : format("SimHeatPumpWaterHeater: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
551 : HeatPumpNum,
552 0 : state.dataWaterThermalTanks->numHeatPumpWaterHeater,
553 : CompName));
554 : }
555 : }
556 :
557 : // Only simulate HPWHs specified as zone equipment and not connected to a plant loop.
558 : // HPWHs not defined as zone equipment with no plant connections are simulated in NonZoneEquipmentManager.
559 : // Plant connected HPWHs are called by PlantLoopEquipments (but only those on supply side ).
560 : // HPWH will not be included in sizing calculations, fan is initialized only during BeginEnvrnFlag (FALSE during sizing)
561 : // (fan will be turned off during Standard Ratings procedure yielding incorrect results)
562 124230 : if (state.dataGlobal->DoingSizing) {
563 10 : return;
564 : }
565 :
566 : // For HPWHs, StandAlone means not connected to a plant loop (use nodes are not used, source nodes are connected to a HPWH)
567 124220 : if (state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).StandAlone) {
568 32076 : bool LocalRunFlag = true;
569 : Real64 MyLoad;
570 :
571 32076 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
572 32076 : state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).simulate(state, A, FirstHVACIteration, MyLoad, LocalRunFlag);
573 :
574 32076 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
575 32076 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
576 : } else {
577 : // HPWH is plant connected and will get simulated when called from plant SimWaterThermalTank, but need to update loads met here
578 92144 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
579 92144 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
580 : }
581 : }
582 :
583 2828408 : void CalcWaterThermalTankZoneGains(EnergyPlusData &state)
584 : {
585 :
586 : // SUBROUTINE INFORMATION:
587 : // AUTHOR Peter Graham Ellis
588 : // DATE WRITTEN March 2005
589 : // MODIFIED B. Griffith November 2011, new internal gains structure
590 : // RE-ENGINEERED na
591 :
592 : // PURPOSE OF THIS SUBROUTINE:
593 : // Calculates the zone internal gains due to water heater skin losses during sizing.
594 : // initializes gains to zone at begin environment.
595 :
596 : // METHODOLOGY EMPLOYED:
597 : // Sums the tank losses from all of the water heaters in the zone to add as a gain to the zone.
598 : // Now used to determine tank losses during sizing. Internal gains are summed in a centralized way now
599 :
600 2828408 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) {
601 :
602 2294948 : if (!state.dataGlobal->DoingSizing) {
603 1690040 : return;
604 : } else {
605 604908 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
606 428 : GetWaterThermalTankInput(state);
607 428 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
608 : }
609 604908 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) {
610 604794 : return;
611 : }
612 : }
613 : }
614 :
615 533574 : if (state.dataGlobal->BeginEnvrnFlag && state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag) {
616 3375 : for (auto &e : state.dataWaterThermalTanks->WaterThermalTank) {
617 2018 : e.AmbientZoneGain = 0.0;
618 2018 : e.FuelEnergy = 0.0;
619 2018 : e.OffCycParaFuelEnergy = 0.0;
620 2018 : e.OnCycParaFuelEnergy = 0.0;
621 : }
622 1357 : state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = false;
623 : }
624 :
625 533574 : if (!state.dataGlobal->BeginEnvrnFlag) {
626 532217 : state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = true;
627 : }
628 :
629 1246939 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
630 713365 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
631 713365 : if (Tank.AmbientTempZone == 0) {
632 471594 : continue;
633 : }
634 241771 : auto const &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(Tank.AmbientTempZone);
635 241771 : if (state.dataGlobal->DoingSizing) {
636 : // Initialize tank temperature to setpoint
637 : // (use HPWH or Desuperheater heating coil set point if applicable)
638 107106 : Sched::Schedule *sched = nullptr;
639 107106 : if (Tank.HeatPumpNum > 0) {
640 14844 : sched = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum).setptTempSched;
641 92262 : } else if (Tank.DesuperheaterNum > 0) {
642 1350 : sched = state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).setptTempSched;
643 : } else {
644 90912 : sched = Tank.setptTempSched;
645 : }
646 :
647 107106 : Real64 TankTemp = (sched != nullptr) ? sched->getCurrentVal() : 20.0;
648 :
649 107106 : Real64 QLossToZone = 0.0;
650 107106 : switch (Tank.WaterThermalTankType) {
651 90918 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
652 90918 : QLossToZone = max(Tank.OnCycLossCoeff * Tank.OnCycLossFracToZone, Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone) *
653 90918 : (TankTemp - thisZoneHB.MAT);
654 90918 : break;
655 : }
656 8094 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
657 8094 : QLossToZone = max(Tank.Node(1).OnCycLossCoeff * Tank.SkinLossFracToZone, Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone) *
658 8094 : (TankTemp - thisZoneHB.MAT);
659 8094 : break;
660 : }
661 4050 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
662 4050 : QLossToZone = Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone * (TankTemp - thisZoneHB.MAT);
663 4050 : break;
664 : }
665 4044 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
666 4044 : QLossToZone = Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone * (TankTemp - thisZoneHB.MAT);
667 4044 : break;
668 : }
669 0 : default:
670 0 : break;
671 : }
672 107106 : Tank.AmbientZoneGain = QLossToZone;
673 : }
674 : }
675 : }
676 :
677 6 : bool getDesuperHtrInput(EnergyPlusData &state)
678 : {
679 6 : bool ErrorsFound = false;
680 : static constexpr std::string_view routineName = "getDesuperHtrInput";
681 : // Make local copies of IPShortCut because other getinputs might overwrite the ones in state <-- need to fix this idiom
682 6 : std::string cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
683 6 : Array1D<std::string> cAlphaArgs = state.dataIPShortCut->cAlphaArgs;
684 6 : Array1D<Real64> rNumericArgs = state.dataIPShortCut->rNumericArgs;
685 6 : Array1D<bool> lNumericFieldBlanks = state.dataIPShortCut->lNumericFieldBlanks;
686 6 : Array1D<bool> lAlphaFieldBlanks = state.dataIPShortCut->lAlphaFieldBlanks;
687 6 : Array1D<std::string> cAlphaFieldNames = state.dataIPShortCut->cAlphaFieldNames;
688 6 : Array1D<std::string> cNumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
689 :
690 6 : cCurrentModuleObject = cCoilDesuperheater;
691 12 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
692 : int NumAlphas;
693 : int NumNums;
694 : int IOStat;
695 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
696 : cCurrentModuleObject,
697 : DesuperheaterNum,
698 : cAlphaArgs,
699 : NumAlphas,
700 : rNumericArgs,
701 : NumNums,
702 : IOStat,
703 : lNumericFieldBlanks,
704 : lAlphaFieldBlanks,
705 : cAlphaFieldNames,
706 : cNumericFieldNames);
707 :
708 6 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
709 :
710 6 : Util::IsNameEmpty(state, cAlphaArgs(1), cCurrentModuleObject, ErrorsFound);
711 :
712 : // ErrorsFound will be set to True if problem was found, left untouched otherwise
713 6 : GlobalNames::VerifyUniqueCoilName(state, cCurrentModuleObject, cAlphaArgs(1), ErrorsFound, cCurrentModuleObject + " Name");
714 :
715 6 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
716 :
717 6 : DesupHtr.Name = cAlphaArgs(1);
718 6 : DesupHtr.Type = cCurrentModuleObject;
719 :
720 : // convert availability schedule name to pointer
721 6 : if (lAlphaFieldBlanks(2)) {
722 0 : DesupHtr.availSched = Sched::GetScheduleAlwaysOn(state);
723 6 : } else if ((DesupHtr.availSched = Sched::GetSchedule(state, cAlphaArgs(2))) == nullptr) {
724 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
725 0 : ErrorsFound = true;
726 : }
727 :
728 : // convert schedule name to pointer
729 6 : if (lAlphaFieldBlanks(3)) {
730 6 : } else if ((DesupHtr.setptTempSched = Sched::GetSchedule(state, cAlphaArgs(3))) == nullptr) {
731 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(3), cAlphaArgs(3));
732 0 : ErrorsFound = true;
733 : }
734 :
735 6 : DesupHtr.DeadBandTempDiff = rNumericArgs(1);
736 6 : if (DesupHtr.DeadBandTempDiff <= 0.0 || DesupHtr.DeadBandTempDiff > 20.0) {
737 0 : ShowSevereError(state,
738 0 : format("{} = {}: {} must be > 0 and <= 20. {} = {:.1T}",
739 : cCurrentModuleObject,
740 0 : DesupHtr.Name,
741 : cNumericFieldNames(1),
742 : cNumericFieldNames(1),
743 : rNumericArgs(1)));
744 0 : ErrorsFound = true;
745 : }
746 :
747 : // Error limits on heat reclaim efficiency applied after source type identified
748 :
749 6 : DesupHtr.RatedInletWaterTemp = rNumericArgs(3);
750 6 : DesupHtr.RatedOutdoorAirTemp = rNumericArgs(4);
751 6 : DesupHtr.MaxInletWaterTemp = rNumericArgs(5);
752 :
753 6 : if (!lAlphaFieldBlanks(4)) {
754 6 : DesupHtr.HEffFTemp = Curve::GetCurveIndex(state, cAlphaArgs(4));
755 6 : if (DesupHtr.HEffFTemp == 0) {
756 0 : ShowSevereError(state,
757 0 : format("{} = {}: {} not found = {}", cCurrentModuleObject, DesupHtr.Name, cAlphaFieldNames(4), cAlphaArgs(4)));
758 0 : ErrorsFound = true;
759 : } else {
760 12 : ErrorsFound |= Curve::CheckCurveDims(state,
761 : DesupHtr.HEffFTemp, // Curve index
762 : {2}, // Valid dimensions
763 : routineName, // Routine name
764 : cCurrentModuleObject, // Object Type
765 : DesupHtr.Name, // Object Name
766 6 : cAlphaFieldNames(4)); // Field Name
767 6 : if (!ErrorsFound) {
768 6 : if (DesupHtr.HEffFTemp > 0) {
769 6 : Real64 HEffFTemp = min(
770 6 : 1.0, max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, DesupHtr.RatedInletWaterTemp, DesupHtr.RatedOutdoorAirTemp)));
771 6 : if (std::abs(HEffFTemp - 1.0) > 0.05) {
772 0 : ShowWarningError(state, format("{}, \"{}\":", cCurrentModuleObject, DesupHtr.Name));
773 0 : ShowContinueError(state, format("The {} should be normalized ", cAlphaFieldNames(4)));
774 0 : ShowContinueError(state, format(" to 1.0 at the rating point. Curve output at the rating point = {:.3T}", HEffFTemp));
775 0 : ShowContinueError(state, " The simulation continues using the user-specified curve.");
776 : }
777 : }
778 : }
779 : }
780 : }
781 :
782 6 : DesupHtr.WaterInletNode = NodeInputManager::GetOnlySingleNode(state,
783 6 : cAlphaArgs(5),
784 : ErrorsFound,
785 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
786 6 : cAlphaArgs(1),
787 : DataLoopNode::NodeFluidType::Water,
788 : DataLoopNode::ConnectionType::Inlet,
789 : NodeInputManager::CompFluidStream::Primary,
790 : DataLoopNode::ObjectIsParent);
791 :
792 6 : DesupHtr.WaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
793 6 : cAlphaArgs(6),
794 : ErrorsFound,
795 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
796 6 : cAlphaArgs(1),
797 : DataLoopNode::NodeFluidType::Water,
798 : DataLoopNode::ConnectionType::Outlet,
799 : NodeInputManager::CompFluidStream::Primary,
800 : DataLoopNode::ObjectIsParent);
801 :
802 6 : DesupHtr.InletNodeName1 = cAlphaArgs(5);
803 6 : DesupHtr.OutletNodeName1 = cAlphaArgs(6);
804 :
805 6 : DesupHtr.TankType = cAlphaArgs(7);
806 :
807 6 : if (!Util::SameString(DesupHtr.TankType, cMixedWHModuleObj) && !Util::SameString(DesupHtr.TankType, cStratifiedWHModuleObj)) {
808 :
809 0 : ShowSevereError(state, format("{} = {}:", cCurrentModuleObject, state.dataWaterThermalTanks->HPWaterHeater(DesuperheaterNum).Name));
810 0 : ShowContinueError(state, format("Desuperheater can only be used with {} or {}.", cMixedWHModuleObj, cStratifiedWHModuleObj));
811 0 : ErrorsFound = true;
812 : }
813 :
814 6 : DesupHtr.TankName = cAlphaArgs(8);
815 :
816 : // Set up comp set for water side nodes (reverse inlet/outlet for water heater)
817 6 : BranchNodeConnections::SetUpCompSets(state, DesupHtr.Type, DesupHtr.Name, DesupHtr.TankType, DesupHtr.TankName, cAlphaArgs(6), cAlphaArgs(5));
818 :
819 6 : std::string const heatSourceObjType = cAlphaArgs(9);
820 :
821 11 : if ((Util::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
822 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
823 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
824 1 : if (lNumericFieldBlanks(2)) {
825 0 : DesupHtr.HeatReclaimRecoveryEff = 0.8;
826 : } else {
827 1 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
828 1 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.9) {
829 0 : ShowSevereError(state,
830 0 : format("{} = {}: {} must be > 0.0 and <= 0.9, Efficiency = {:.3T}",
831 : cCurrentModuleObject,
832 0 : DesupHtr.Name,
833 : cNumericFieldNames(2),
834 0 : DesupHtr.HeatReclaimRecoveryEff));
835 0 : ErrorsFound = true;
836 : }
837 : } // Blank Num(2)
838 : } else { // max is 0.3 for all other sources
839 5 : if (lNumericFieldBlanks(2)) {
840 0 : DesupHtr.HeatReclaimRecoveryEff = 0.25;
841 : } else {
842 5 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
843 5 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.3) {
844 0 : ShowSevereError(state,
845 0 : format("{} = {}: {} must be > 0.0 and <= 0.3, {} = {:.3T}",
846 : cCurrentModuleObject,
847 0 : DesupHtr.Name,
848 : cNumericFieldNames(2),
849 : cNumericFieldNames(2),
850 0 : DesupHtr.HeatReclaimRecoveryEff));
851 0 : ErrorsFound = true;
852 : }
853 : } // Blank Num(2)
854 : } // setting limits on heat recovery efficiency
855 :
856 : // Find the Refrigeration equipment index associated with the desuperheater heating coil.
857 6 : bool errFlag = false;
858 6 : DesupHtr.HeatingSourceType = heatSourceObjType;
859 6 : DesupHtr.HeatingSourceName = cAlphaArgs(10);
860 6 : if (Util::SameString(heatSourceObjType, "Refrigeration:CompressorRack")) {
861 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CompressorRackRefrigeratedCase;
862 0 : for (int RackNum = 1; RackNum <= state.dataRefrigCase->NumRefrigeratedRacks; ++RackNum) {
863 0 : if (!Util::SameString(state.dataHeatBal->HeatReclaimRefrigeratedRack(RackNum).Name, cAlphaArgs(10))) {
864 0 : continue;
865 : }
866 0 : DesupHtr.ReclaimHeatingSourceIndexNum = RackNum;
867 0 : if (allocated(state.dataHeatBal->HeatReclaimRefrigeratedRack)) {
868 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
869 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(DesupHtr.ReclaimHeatingSourceIndexNum);
870 0 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
871 0 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
872 0 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat) {
873 0 : num = 0.0;
874 : }
875 : }
876 0 : DesupHtr.ValidSourceType = true;
877 0 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
878 0 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
879 0 : ShowSevereError(
880 : state,
881 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
882 : cCurrentModuleObject,
883 0 : DesupHtr.Name,
884 0 : DesupHtr.HeatingSourceName));
885 0 : ErrorsFound = true;
886 : }
887 : }
888 0 : break;
889 : }
890 11 : } else if ((Util::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
891 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
892 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
893 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CondenserRefrigeration;
894 2 : for (int CondNum = 1; CondNum <= state.dataRefrigCase->NumRefrigCondensers; ++CondNum) {
895 2 : if (!Util::SameString(state.dataHeatBal->HeatReclaimRefrigCondenser(CondNum).Name, cAlphaArgs(10))) {
896 1 : continue;
897 : }
898 1 : DesupHtr.ReclaimHeatingSourceIndexNum = CondNum;
899 1 : if (allocated(state.dataHeatBal->HeatReclaimRefrigCondenser)) {
900 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
901 1 : state.dataHeatBal->HeatReclaimRefrigCondenser(DesupHtr.ReclaimHeatingSourceIndexNum);
902 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
903 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
904 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat) {
905 1 : num = 0.0;
906 : }
907 : }
908 1 : DesupHtr.ValidSourceType = true;
909 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
910 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.9) {
911 0 : ShowSevereError(
912 : state,
913 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.9",
914 : cCurrentModuleObject,
915 0 : DesupHtr.Name,
916 0 : DesupHtr.HeatingSourceName));
917 0 : ErrorsFound = true;
918 : }
919 : }
920 1 : break;
921 : }
922 8 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed") ||
923 6 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoSpeed") ||
924 11 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:MultiSpeed") ||
925 8 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
926 :
927 2 : if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed")) {
928 2 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXCooling;
929 0 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
930 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiMode;
931 : } else {
932 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiSpeed;
933 : }
934 2 : DXCoils::GetDXCoilIndex(state, DesupHtr.HeatingSourceName, DesupHtr.ReclaimHeatingSourceIndexNum, errFlag, cCurrentModuleObject);
935 2 : if (allocated(state.dataHeatBal->HeatReclaimDXCoil)) {
936 2 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimDXCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
937 2 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
938 2 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
939 4 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat) {
940 2 : num = 0.0;
941 : }
942 : }
943 2 : DesupHtr.ValidSourceType = true;
944 2 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
945 2 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
946 0 : ShowSevereError(state,
947 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
948 : cCurrentModuleObject,
949 0 : DesupHtr.Name,
950 0 : DesupHtr.HeatingSourceName));
951 0 : ErrorsFound = true;
952 : }
953 : }
954 5 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:VariableSpeed") ||
955 5 : Util::SameString(heatSourceObjType, "Coil:Cooling:WaterToAirHeatPump:VariableSpeedEquationFit")) {
956 :
957 1 : if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:VariableSpeed")) {
958 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXVariableCooling;
959 : } else {
960 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::AirWaterHeatPumpVSEQ;
961 : }
962 1 : DesupHtr.ReclaimHeatingSourceIndexNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(state, heatSourceObjType, cAlphaArgs(10), errFlag);
963 1 : if (allocated(state.dataHeatBal->HeatReclaimVS_Coil)) {
964 1 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimVS_Coil(DesupHtr.ReclaimHeatingSourceIndexNum);
965 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
966 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
967 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat) {
968 1 : num = 0.0;
969 : }
970 : }
971 1 : DesupHtr.ValidSourceType = true;
972 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
973 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
974 0 : ShowSevereError(state,
975 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
976 : cCurrentModuleObject,
977 0 : DesupHtr.Name,
978 0 : DesupHtr.HeatingSourceName));
979 0 : ErrorsFound = true;
980 : }
981 : }
982 2 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:WaterToAirHeatPump:EquationFit")) {
983 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::AirWaterHeatPumpEQ;
984 1 : DesupHtr.ReclaimHeatingSourceIndexNum = WaterToAirHeatPumpSimple::GetCoilIndex(state, heatSourceObjType, cAlphaArgs(10), errFlag);
985 1 : if (allocated(state.dataHeatBal->HeatReclaimSimple_WAHPCoil)) {
986 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
987 1 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
988 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
989 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
990 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat) {
991 1 : num = 0.0;
992 : }
993 : }
994 1 : DesupHtr.ValidSourceType = true;
995 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
996 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
997 0 : ShowSevereError(state,
998 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
999 : cCurrentModuleObject,
1000 0 : DesupHtr.Name,
1001 0 : DesupHtr.HeatingSourceName));
1002 0 : ErrorsFound = true;
1003 : }
1004 : }
1005 1 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX")) {
1006 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CoilCoolingDX;
1007 1 : DesupHtr.ReclaimHeatingSourceIndexNum = CoilCoolingDX::factory(state, cAlphaArgs(10));
1008 1 : if (DesupHtr.ReclaimHeatingSourceIndexNum < 0) {
1009 0 : ShowSevereError(
1010 : state,
1011 0 : format("{}={}, could not find desuperheater coil {}={}", cCurrentModuleObject, DesupHtr.Name, cAlphaArgs(9), cAlphaArgs(10)));
1012 0 : ErrorsFound = true;
1013 : } else {
1014 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
1015 1 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat;
1016 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
1017 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
1018 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat) {
1019 1 : num = 0.0;
1020 : }
1021 : }
1022 1 : DesupHtr.ValidSourceType = true;
1023 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
1024 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
1025 0 : ShowSevereError(
1026 : state,
1027 0 : format("{}, \"{}\" sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
1028 : cCurrentModuleObject,
1029 0 : DesupHtr.Name,
1030 0 : DesupHtr.HeatingSourceName));
1031 0 : ErrorsFound = true;
1032 : }
1033 : }
1034 : } else {
1035 0 : ShowSevereError(state, format("{} = {}:", cCurrentModuleObject, DesupHtr.Name));
1036 0 : ShowContinueError(state, " desuperheater can only be used with Coil:Cooling:DX:SingleSpeed, ");
1037 0 : ShowContinueError(state,
1038 : " Coil:Cooling:DX:TwoSpeed, Coil:Cooling:DX:MultiSpeed, Coil:Cooling:DX:TwoStageWithHumidityControlMode, "
1039 : "Coil:Cooling:DX:VariableSpeed, Coil:Cooling:WaterToAirHeatPump:VariableSpeedEquationFit, "
1040 : "Coil:Cooling:WaterToAirHeatPump:EquationFit, Refrigeration:CompressorRack,");
1041 0 : ShowContinueError(state, " Refrigeration:Condenser:AirCooled ,Refrigeration:Condenser:EvaporativeCooled, ");
1042 0 : ShowContinueError(state, " or Refrigeration:Condenser:WaterCooled.");
1043 0 : ShowContinueError(state, format(" Invalid desuperheater heat source object: {} \"{}\"", heatSourceObjType, cAlphaArgs(10)));
1044 0 : ErrorsFound = true;
1045 : }
1046 6 : if (errFlag) {
1047 0 : ShowContinueError(state, format("...occurs in {}={}", cCurrentModuleObject, DesupHtr.Name));
1048 0 : ErrorsFound = true;
1049 : }
1050 :
1051 6 : if (DesupHtr.ReclaimHeatingSourceIndexNum == 0 && DesupHtr.ReclaimHeatingSource != ReclaimHeatObjectType::CoilCoolingDX) {
1052 0 : ShowSevereError(state,
1053 0 : format("{}, \"{}\" desuperheater heat source object not found: {} \"{}\"",
1054 : cCurrentModuleObject,
1055 0 : DesupHtr.Name,
1056 : heatSourceObjType,
1057 : cAlphaArgs(10)));
1058 0 : ErrorsFound = true;
1059 : }
1060 :
1061 6 : DesupHtr.OperatingWaterFlowRate = rNumericArgs(6);
1062 6 : if (DesupHtr.OperatingWaterFlowRate <= 0.0) {
1063 0 : ShowSevereError(state,
1064 0 : format("{} = {}: {} must be greater than 0. {} = {:.6T}",
1065 : cCurrentModuleObject,
1066 0 : DesupHtr.Name,
1067 : cNumericFieldNames(6),
1068 : cNumericFieldNames(6),
1069 : rNumericArgs(6)));
1070 0 : ErrorsFound = true;
1071 : }
1072 :
1073 6 : DesupHtr.PumpElecPower = rNumericArgs(7);
1074 6 : if (DesupHtr.PumpElecPower < 0.0) {
1075 0 : ShowSevereError(state,
1076 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1077 : cCurrentModuleObject,
1078 0 : DesupHtr.Name,
1079 : cNumericFieldNames(7),
1080 : cNumericFieldNames(7),
1081 : rNumericArgs(7)));
1082 0 : ErrorsFound = true;
1083 : }
1084 :
1085 6 : if ((DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate) > 7.9264e6) {
1086 0 : ShowWarningError(state,
1087 0 : format("{} = {}: {} to {} ratio > 7.9264E6. {} to {} = {:.3T}",
1088 : cCurrentModuleObject,
1089 0 : DesupHtr.Name,
1090 : cNumericFieldNames(7),
1091 : cNumericFieldNames(6),
1092 : cNumericFieldNames(7),
1093 : cNumericFieldNames(6),
1094 0 : (DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate)));
1095 0 : ShowContinueError(state, format(" Suggest reducing {} or increasing {}.", cNumericFieldNames(7), cNumericFieldNames(6)));
1096 0 : ShowContinueError(state, " The simulation will continue using the user defined values.");
1097 : }
1098 :
1099 6 : DesupHtr.PumpFracToWater = rNumericArgs(8);
1100 6 : if (DesupHtr.PumpFracToWater < 0.0 || DesupHtr.PumpFracToWater > 1.0) {
1101 0 : ShowSevereError(state,
1102 0 : format("{} = {}: {} must be >= 0 or <= 1. {} = {:.3T}",
1103 : cCurrentModuleObject,
1104 0 : DesupHtr.Name,
1105 : cNumericFieldNames(8),
1106 : cNumericFieldNames(8),
1107 : rNumericArgs(8)));
1108 0 : ErrorsFound = true;
1109 : }
1110 :
1111 6 : DesupHtr.OnCycParaLoad = rNumericArgs(9);
1112 6 : if (DesupHtr.OnCycParaLoad < 0.0) {
1113 0 : ShowSevereError(state,
1114 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1115 : cCurrentModuleObject,
1116 0 : DesupHtr.Name,
1117 : cNumericFieldNames(9),
1118 : cNumericFieldNames(9),
1119 : rNumericArgs(9)));
1120 0 : ErrorsFound = true;
1121 : }
1122 :
1123 6 : DesupHtr.OffCycParaLoad = rNumericArgs(10);
1124 6 : if (DesupHtr.OffCycParaLoad < 0.0) {
1125 0 : ShowSevereError(state,
1126 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1127 : cCurrentModuleObject,
1128 0 : DesupHtr.Name,
1129 : cNumericFieldNames(10),
1130 : cNumericFieldNames(10),
1131 : rNumericArgs(10)));
1132 0 : ErrorsFound = true;
1133 : }
1134 6 : }
1135 :
1136 6 : if (ErrorsFound) {
1137 0 : ShowFatalError(state, format("Errors found in getting {} input. Preceding condition causes termination.", cCurrentModuleObject));
1138 : }
1139 :
1140 6 : return ErrorsFound;
1141 :
1142 6 : } // namespace WaterThermalTanks
1143 :
1144 9 : bool getHPWaterHeaterInput(EnergyPlusData &state)
1145 : {
1146 :
1147 : static constexpr std::string_view routineName = "getHPWaterHeaterInput";
1148 9 : bool ErrorsFound = false;
1149 :
1150 9 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
1151 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
1152 : int nAlphaOffset; // the difference of array location between alpha items between pumped and wrapped condensers
1153 : int nNumericOffset; // the difference of array location between numeric items between pumped and wrapped condensers
1154 : int nNumPossibleNumericArgs; // the number of possible numeric arguments in the idd
1155 : int nNumPossibleAlphaArgs; // the number of possible numeric arguments in the idd
1156 :
1157 : // For looking up in IDF/epJSON, you need the index that corresponds to the actual object type (Pumped or Wrapped)
1158 : int HPWaterHeaterNumOfSpecificType;
1159 :
1160 32 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
1161 :
1162 : // Create reference to current HPWH object in array.
1163 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
1164 :
1165 : // Initialize the offsets to zero
1166 23 : nAlphaOffset = 0;
1167 23 : nNumericOffset = 0;
1168 :
1169 : DataLoopNode::ConnectionObjectType objType;
1170 :
1171 23 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
1172 : // Pumped Condenser
1173 20 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
1174 20 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpPumpedCondenser;
1175 20 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped;
1176 20 : nNumPossibleAlphaArgs = 29;
1177 20 : nNumPossibleNumericArgs = 9;
1178 : // Actual index of Pumped type
1179 20 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum;
1180 : } else {
1181 : // Wrapped Condenser
1182 3 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
1183 3 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpWrappedCondenser;
1184 3 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped;
1185 3 : nNumPossibleAlphaArgs = 27;
1186 3 : nNumPossibleNumericArgs = 10;
1187 : // Actual index of Wrapped type
1188 3 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum - NumPumpedCondenser;
1189 : }
1190 :
1191 : int NumAlphas;
1192 : int NumNums;
1193 : int IOStat;
1194 46 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1195 23 : state.dataIPShortCut->cCurrentModuleObject,
1196 : HPWaterHeaterNumOfSpecificType,
1197 23 : state.dataIPShortCut->cAlphaArgs,
1198 : NumAlphas,
1199 23 : state.dataIPShortCut->rNumericArgs,
1200 : NumNums,
1201 : IOStat,
1202 23 : state.dataIPShortCut->lNumericFieldBlanks,
1203 23 : state.dataIPShortCut->lAlphaFieldBlanks,
1204 23 : state.dataIPShortCut->cAlphaFieldNames,
1205 23 : state.dataIPShortCut->cNumericFieldNames);
1206 :
1207 23 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
1208 :
1209 : // Copy those lists into C++ std::maps // Why, no really why? This is really dumb
1210 23 : std::map<int, std::string> hpwhAlpha;
1211 23 : std::map<int, Real64> hpwhNumeric;
1212 23 : std::map<int, bool> hpwhAlphaBlank;
1213 23 : std::map<int, bool> hpwhNumericBlank;
1214 23 : std::map<int, std::string> hpwhAlphaFieldNames;
1215 23 : std::map<int, std::string> hpwhNumericFieldNames;
1216 204 : for (int i = 1; i <= NumNums; ++i) {
1217 181 : hpwhNumeric[i] = state.dataIPShortCut->rNumericArgs(i);
1218 181 : hpwhNumericBlank[i] = state.dataIPShortCut->lNumericFieldBlanks(i);
1219 181 : hpwhNumericFieldNames[i] = state.dataIPShortCut->cNumericFieldNames(i);
1220 : }
1221 55 : for (int i = NumNums + 1; i <= nNumPossibleNumericArgs; ++i) {
1222 32 : hpwhNumericBlank[i] = true;
1223 : }
1224 642 : for (int i = 1; i <= NumAlphas; ++i) {
1225 619 : hpwhAlpha[i] = state.dataIPShortCut->cAlphaArgs(i);
1226 619 : hpwhAlphaBlank[i] = state.dataIPShortCut->lAlphaFieldBlanks(i);
1227 619 : hpwhAlphaFieldNames[i] = state.dataIPShortCut->cAlphaFieldNames(i);
1228 : }
1229 65 : for (int i = NumAlphas + 1; i <= nNumPossibleAlphaArgs; ++i) {
1230 42 : hpwhAlphaBlank[i] = true;
1231 : }
1232 23 : Util::IsNameEmpty(state, hpwhAlpha[1], state.dataIPShortCut->cCurrentModuleObject, ErrorsFound);
1233 :
1234 : // Name and type
1235 23 : HPWH.Name = hpwhAlpha[1];
1236 23 : HPWH.Type = state.dataIPShortCut->cCurrentModuleObject;
1237 :
1238 : // Availability Schedule
1239 : // convert schedule name to pointer
1240 23 : if (hpwhAlphaBlank[2]) {
1241 0 : HPWH.availSched = Sched::GetScheduleAlwaysOn(state);
1242 23 : } else if ((HPWH.availSched = Sched::GetSchedule(state, hpwhAlpha[2])) == nullptr) {
1243 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[2], hpwhAlpha[2]);
1244 0 : ErrorsFound = true;
1245 : }
1246 :
1247 : // Compressor Setpoint Temperature Schedule
1248 : // convert schedule name to pointer
1249 23 : if (hpwhAlphaBlank[3]) {
1250 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[3]);
1251 0 : ErrorsFound = true;
1252 23 : } else if ((HPWH.setptTempSched = Sched::GetSchedule(state, hpwhAlpha[3])) == nullptr) {
1253 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[3], hpwhAlpha[3]);
1254 0 : ErrorsFound = true;
1255 : }
1256 :
1257 : // Dead Band Temperature Difference
1258 23 : HPWH.DeadBandTempDiff = hpwhNumeric[1 + nNumericOffset];
1259 23 : if (HPWH.DeadBandTempDiff <= 0.0 || HPWH.DeadBandTempDiff > 20.0) {
1260 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1261 0 : ShowContinueError(state,
1262 0 : format("{}{}",
1263 0 : hpwhNumericFieldNames[1 + nNumericOffset],
1264 0 : format(" difference must be > 0 and <= 20. Dead band = {:.1T}", hpwhNumeric[1 + nNumericOffset])));
1265 0 : ErrorsFound = true;
1266 : }
1267 :
1268 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1269 :
1270 : // Condenser Inlet/Outlet Nodes
1271 20 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1272 20 : hpwhAlpha[4],
1273 : ErrorsFound,
1274 : objType,
1275 20 : HPWH.Name,
1276 : DataLoopNode::NodeFluidType::Water,
1277 : DataLoopNode::ConnectionType::Inlet,
1278 : NodeInputManager::CompFluidStream::Secondary,
1279 : DataLoopNode::ObjectIsParent);
1280 20 : HPWH.InletNodeName1 = hpwhAlpha[4];
1281 20 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1282 20 : hpwhAlpha[5],
1283 : ErrorsFound,
1284 : objType,
1285 20 : HPWH.Name,
1286 : DataLoopNode::NodeFluidType::Water,
1287 : DataLoopNode::ConnectionType::Outlet,
1288 : NodeInputManager::CompFluidStream::Secondary,
1289 : DataLoopNode::ObjectIsParent);
1290 20 : HPWH.OutletNodeName1 = hpwhAlpha[5];
1291 :
1292 : // Condenser Water Flow Rate
1293 20 : HPWH.OperatingWaterFlowRate = hpwhNumeric[2];
1294 20 : if (HPWH.OperatingWaterFlowRate <= 0.0 && hpwhNumeric[2] != Constant::AutoCalculate) {
1295 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1296 0 : ShowContinueError(state,
1297 0 : format("{} must be greater than 0. Condenser water flow rate = {:.6T}", hpwhNumericFieldNames[2], hpwhNumeric[2]));
1298 0 : ErrorsFound = true;
1299 : }
1300 :
1301 3 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1302 :
1303 : // Wrapped Condenser Location
1304 3 : HPWH.WrappedCondenserBottomLocation = hpwhNumeric[2 + nNumericOffset];
1305 3 : HPWH.WrappedCondenserTopLocation = hpwhNumeric[3 + nNumericOffset];
1306 :
1307 3 : if (HPWH.WrappedCondenserBottomLocation < 0.0) {
1308 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1309 0 : ShowContinueError(state,
1310 0 : format("{} must be greater than 0. Condenser bottom location = {:.6T}",
1311 0 : hpwhNumericFieldNames[2],
1312 0 : HPWH.WrappedCondenserBottomLocation));
1313 0 : ErrorsFound = true;
1314 : }
1315 :
1316 3 : if (HPWH.WrappedCondenserBottomLocation >= HPWH.WrappedCondenserTopLocation) {
1317 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1318 0 : ShowContinueError(state,
1319 0 : format("{} ({:.6T}) must be greater than {} ({:.6T}).",
1320 0 : HPWH.WrappedCondenserTopLocation,
1321 0 : hpwhNumericFieldNames[2],
1322 0 : hpwhNumericFieldNames[3],
1323 0 : HPWH.WrappedCondenserBottomLocation));
1324 0 : ErrorsFound = true;
1325 : }
1326 :
1327 : // Reset the offset
1328 3 : nAlphaOffset = -2;
1329 3 : nNumericOffset = 1;
1330 :
1331 : } else {
1332 0 : assert(0);
1333 : }
1334 :
1335 : // Evaporator Air Flow Rate
1336 23 : HPWH.OperatingAirFlowRate = hpwhNumeric[3 + nNumericOffset];
1337 23 : if (HPWH.OperatingAirFlowRate <= 0.0 && hpwhNumeric[3 + nNumericOffset] != Constant::AutoCalculate) {
1338 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1339 0 : ShowContinueError(state,
1340 0 : format("{}{}",
1341 0 : hpwhNumericFieldNames[3 + nNumericOffset],
1342 0 : format(" must be greater than 0. Evaporator air flow rate = {:.6T}", hpwhNumeric[3 + nNumericOffset])));
1343 0 : ErrorsFound = true;
1344 : }
1345 :
1346 : // Inlet Air Configuration
1347 23 : HPWH.InletAirConfiguration = static_cast<WTTAmbientTemp>(getEnumValue(HPWHAmbientTempNamesUC, Util::makeUPPER(hpwhAlpha[6 + nAlphaOffset])));
1348 23 : switch (HPWH.InletAirConfiguration) {
1349 3 : case WTTAmbientTemp::Schedule: {
1350 :
1351 : // Inlet Air Temperature Schedule
1352 3 : if (hpwhAlphaBlank[11 + nAlphaOffset]) {
1353 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[11 + nAlphaOffset]);
1354 0 : ErrorsFound = true;
1355 3 : } else if ((HPWH.ambientTempSched = Sched::GetSchedule(state, hpwhAlpha[11 + nAlphaOffset])) == nullptr) {
1356 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[11 + nAlphaOffset], hpwhAlpha[11 + nAlphaOffset]);
1357 0 : ErrorsFound = true;
1358 : }
1359 :
1360 : // Inlet Air Humidity Schedule
1361 3 : if (hpwhAlphaBlank[12 + nAlphaOffset]) {
1362 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[12 + nAlphaOffset]);
1363 0 : ErrorsFound = true;
1364 3 : } else if ((HPWH.ambientRHSched = Sched::GetSchedule(state, hpwhAlpha[12 + nAlphaOffset])) == nullptr) {
1365 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[12 + nAlphaOffset], hpwhAlpha[12 + nAlphaOffset]);
1366 0 : ErrorsFound = true;
1367 3 : } else if (!HPWH.ambientRHSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
1368 0 : Sched::ShowSevereBadMinMax(
1369 0 : state, eoh, hpwhAlphaFieldNames[12 + nAlphaOffset], hpwhAlpha[12 + nAlphaOffset], Clusive::In, 0.0, Clusive::In, 1.0);
1370 0 : ErrorsFound = true;
1371 : }
1372 3 : } break;
1373 :
1374 10 : case WTTAmbientTemp::ZoneAndOA:
1375 : case WTTAmbientTemp::TempZone: {
1376 :
1377 : // Inlet Air Zone
1378 10 : if (!hpwhAlphaBlank[13 + nAlphaOffset]) {
1379 10 : HPWH.AmbientTempZone = Util::FindItemInList(hpwhAlpha[13 + nAlphaOffset], state.dataHeatBal->Zone);
1380 10 : if (HPWH.AmbientTempZone == 0) {
1381 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1382 0 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[13 + nAlphaOffset], hpwhAlpha[13 + nAlphaOffset]));
1383 0 : ErrorsFound = true;
1384 : }
1385 : } else {
1386 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1387 0 : ShowContinueError(state, format("required {} is blank.", hpwhAlphaFieldNames[13 + nAlphaOffset]));
1388 0 : ErrorsFound = true;
1389 : }
1390 10 : break;
1391 : }
1392 10 : default:
1393 : case WTTAmbientTemp::OutsideAir:
1394 10 : break;
1395 : }
1396 :
1397 : // Read air inlet nodes after mixer/splitter nodes have been read in (state.dataIPShortCut->cAlphaArgs 7-10),
1398 : // Node_ConnectionType differs for inlet node if mixer/splitter node exists
1399 :
1400 : // Tank Name
1401 : // We will verify this exists and is the right kind of tank later when the tanks are all loaded.
1402 23 : HPWH.TankName = hpwhAlpha[15 + nAlphaOffset];
1403 23 : HPWH.TankType = hpwhAlpha[14 + nAlphaOffset];
1404 :
1405 : // Use Side Inlet/Outlet
1406 : // Get the water heater tank use side inlet node names for HPWHs connected to a plant loop
1407 : // Save the name of the node for use with set up comp sets
1408 23 : HPWH.InletNodeName2 = hpwhAlpha[16 + nAlphaOffset];
1409 23 : HPWH.OutletNodeName2 = hpwhAlpha[17 + nAlphaOffset];
1410 :
1411 23 : if (!hpwhAlphaBlank[16 + nAlphaOffset] && !hpwhAlphaBlank[17 + nAlphaOffset]) {
1412 10 : HPWH.WHUseInletNode = NodeInputManager::GetOnlySingleNode(state,
1413 10 : HPWH.InletNodeName2,
1414 : ErrorsFound,
1415 : objType,
1416 10 : HPWH.Name,
1417 : DataLoopNode::NodeFluidType::Water,
1418 : DataLoopNode::ConnectionType::Inlet,
1419 : NodeInputManager::CompFluidStream::Primary,
1420 : DataLoopNode::ObjectIsParent);
1421 20 : HPWH.WHUseOutletNode = NodeInputManager::GetOnlySingleNode(state,
1422 10 : HPWH.OutletNodeName2,
1423 : ErrorsFound,
1424 : objType,
1425 10 : HPWH.Name,
1426 : DataLoopNode::NodeFluidType::Water,
1427 : DataLoopNode::ConnectionType::Outlet,
1428 : NodeInputManager::CompFluidStream::Primary,
1429 : DataLoopNode::ObjectIsParent);
1430 : }
1431 :
1432 : // DX Coil
1433 : // get Coil:DX:HeatPumpWaterHeater object
1434 23 : HPWH.DXCoilName = hpwhAlpha[19 + nAlphaOffset];
1435 23 : HPWH.DXCoilType = hpwhAlpha[18 + nAlphaOffset];
1436 :
1437 : // check that the DX Coil exists
1438 23 : bool DXCoilErrFlag = false;
1439 23 : bool bIsVScoil = false;
1440 23 : DXCoils::GetDXCoilIndex(state, HPWH.DXCoilName, HPWH.DXCoilNum, DXCoilErrFlag, state.dataIPShortCut->cCurrentModuleObject, true);
1441 23 : if (DXCoilErrFlag) {
1442 : // This could be a variable speed heat pump water heater
1443 7 : bool bVSCoilErrFlag = false;
1444 :
1445 7 : bool checkIHPFirst = IntegratedHeatPump::IHPInModel(state);
1446 7 : if (checkIHPFirst) {
1447 1 : HPWH.DXCoilNum =
1448 2 : IntegratedHeatPump::GetCoilIndexIHP(state, "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE", HPWH.DXCoilName, bVSCoilErrFlag);
1449 :
1450 1 : if (!bVSCoilErrFlag) {
1451 1 : HPWH.bIsIHP = true;
1452 : }
1453 : }
1454 :
1455 7 : if (bVSCoilErrFlag || !checkIHPFirst) {
1456 6 : bVSCoilErrFlag = false;
1457 6 : HPWH.DXCoilNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(
1458 6 : state, "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed", HPWH.DXCoilName, bVSCoilErrFlag);
1459 :
1460 6 : if (bVSCoilErrFlag) {
1461 0 : ShowContinueError(state, format("...occurs in {} ={}", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1462 0 : ShowContinueError(state, format("...could not find either DXCoils::DXCoil or Variable Speed Coil {}", HPWH.DXCoilName));
1463 0 : ErrorsFound = true;
1464 : }
1465 : }
1466 :
1467 7 : bIsVScoil = true;
1468 7 : HPWH.DXCoilTypeNum = 0;
1469 7 : if (HPWH.bIsIHP) {
1470 1 : HPWH.DXCoilType = "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE";
1471 : } else {
1472 6 : HPWH.DXCoilType = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum).VarSpeedCoilType;
1473 : }
1474 : } else {
1475 : // this is a single speed coil
1476 16 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1477 16 : if (!Util::SameString(HPWH.DXCoilType, Coil.DXCoilType)) {
1478 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1479 0 : ShowContinueError(state, format("specifies the coil {}=\"{}\".", HPWH.DXCoilType, HPWH.DXCoilName));
1480 0 : ShowContinueError(state, format("However, {} is a coil of type {}.", HPWH.DXCoilName, Coil.DXCoilType));
1481 0 : ErrorsFound = true;
1482 : }
1483 16 : HPWH.DXCoilTypeNum = Coil.DXCoilType_Num;
1484 : }
1485 :
1486 : // Make sure that the coil and tank are compatible.
1487 23 : if (bIsVScoil) {
1488 7 : if (HPWH.HPWHType != DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1489 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1490 0 : ShowContinueError(state,
1491 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed can only be used with a pumped condenser heat pump "
1492 : "water heater.");
1493 0 : ErrorsFound = true;
1494 : }
1495 : } else {
1496 16 : if (!((HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterPumped &&
1497 13 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) ||
1498 3 : (HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterWrapped &&
1499 3 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped))) {
1500 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1501 0 : std::string ExpectedCoilType;
1502 0 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1503 0 : ExpectedCoilType = HVAC::cAllCoilTypes(HVAC::CoilDX_HeatPumpWaterHeaterPumped);
1504 0 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1505 0 : ExpectedCoilType = HVAC::cAllCoilTypes(HVAC::CoilDX_HeatPumpWaterHeaterWrapped);
1506 : } else {
1507 0 : assert(0);
1508 : }
1509 0 : ShowContinueError(state, format("can only be used with {}", ExpectedCoilType));
1510 0 : ErrorsFound = true;
1511 0 : }
1512 : }
1513 :
1514 : // Dummy condenser Inlet/Outlet Nodes for wrapped tanks
1515 23 : if (HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterWrapped) {
1516 3 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1517 :
1518 3 : HPWH.InletNodeName1 = "DUMMY CONDENSER INLET " + Coil.Name;
1519 3 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1520 3 : HPWH.InletNodeName1,
1521 : ErrorsFound,
1522 : objType,
1523 3 : HPWH.Name,
1524 : DataLoopNode::NodeFluidType::Water,
1525 : DataLoopNode::ConnectionType::Inlet,
1526 : NodeInputManager::CompFluidStream::Secondary,
1527 : DataLoopNode::ObjectIsParent);
1528 3 : HPWH.OutletNodeName1 = "DUMMY CONDENSER OUTLET " + Coil.Name;
1529 6 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1530 3 : HPWH.OutletNodeName1,
1531 : ErrorsFound,
1532 : objType,
1533 3 : HPWH.Name,
1534 : DataLoopNode::NodeFluidType::Water,
1535 : DataLoopNode::ConnectionType::Outlet,
1536 : NodeInputManager::CompFluidStream::Secondary,
1537 : DataLoopNode::ObjectIsParent);
1538 : }
1539 :
1540 : // Minimum Inlet Air Temperature for Compressor Operation
1541 23 : HPWH.MinAirTempForHPOperation = hpwhNumeric[4 + nNumericOffset];
1542 :
1543 : // Maximum Inlet Air Temperature for Compressor Operation
1544 23 : HPWH.MaxAirTempForHPOperation = hpwhNumeric[5 + nNumericOffset];
1545 23 : if (HPWH.MaxAirTempForHPOperation <= HPWH.MinAirTempForHPOperation) {
1546 0 : ShowWarningError(state,
1547 0 : format("{}=\"{}\": maximum inlet air temperature for heat pump compressor operation",
1548 0 : state.dataIPShortCut->cCurrentModuleObject,
1549 0 : HPWH.Name));
1550 0 : ShowContinueError(state, "must be greater than the minimum inlet air temperature for heat pump compressor operation.");
1551 0 : ShowContinueError(state, format("...Minimum inlet air temperature = {:.1T}", HPWH.MinAirTempForHPOperation));
1552 0 : ShowContinueError(state, format("...Maximum inlet air temperature = {:.1T}", HPWH.MaxAirTempForHPOperation));
1553 : }
1554 :
1555 : // Compressor Location
1556 23 : HPWH.CrankcaseTempIndicator =
1557 23 : static_cast<CrankcaseHeaterControlTemp>(getEnumValue(CrankcaseHeaterControlTempNamesUC, Util::makeUPPER(hpwhAlpha[20 + nAlphaOffset])));
1558 :
1559 23 : switch (HPWH.CrankcaseTempIndicator) {
1560 3 : case CrankcaseHeaterControlTemp::Schedule: {
1561 3 : if (hpwhAlphaBlank[21 + nAlphaOffset]) {
1562 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[21 + nAlphaOffset]);
1563 0 : ErrorsFound = true;
1564 3 : } else if ((HPWH.crankcaseTempSched = Sched::GetSchedule(state, hpwhAlpha[21 + nAlphaOffset])) == nullptr) {
1565 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[21 + nAlphaOffset], hpwhAlpha[21 + nAlphaOffset]);
1566 0 : ErrorsFound = true;
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 : format("{}=\"{}\": Inlet Air Configuration must be Zone Air Only or Zone And",
1574 0 : state.dataIPShortCut->cCurrentModuleObject,
1575 0 : HPWH.Name));
1576 0 : ShowContinueError(state, " Outdoor Air when compressor location equals ZONE.");
1577 0 : ErrorsFound = true;
1578 : }
1579 :
1580 10 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1581 0 : ShowWarningError(state,
1582 0 : format("{}=\"{}\" {} was provided but will not be used based on compressor location input=\"{}\".",
1583 0 : state.dataIPShortCut->cCurrentModuleObject,
1584 0 : HPWH.Name,
1585 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1586 0 : hpwhAlpha[20 + nAlphaOffset]));
1587 : }
1588 10 : break;
1589 : }
1590 10 : case CrankcaseHeaterControlTemp::Outdoors: {
1591 10 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1592 0 : ShowWarningError(state,
1593 0 : format("{}=\"{}\" {} was provided but will not be used based on {}=\"{}\".",
1594 0 : state.dataIPShortCut->cCurrentModuleObject,
1595 0 : HPWH.Name,
1596 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1597 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1598 0 : hpwhAlpha[20 + nAlphaOffset]));
1599 : }
1600 10 : break;
1601 : }
1602 0 : default:
1603 0 : break;
1604 : }
1605 :
1606 : // Fan Name
1607 23 : HPWH.FanName = hpwhAlpha[23 + nAlphaOffset];
1608 :
1609 23 : Real64 FanVolFlow = 0.0;
1610 23 : bool errFlag(false);
1611 :
1612 23 : HPWH.fanType = static_cast<HVAC::FanType>(getEnumValue(HVAC::fanTypeNamesUC, hpwhAlpha[22 + nAlphaOffset]));
1613 :
1614 23 : if ((HPWH.FanNum = Fans::GetFanIndex(state, HPWH.FanName)) == 0) {
1615 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[23 + nAlphaOffset], HPWH.FanName);
1616 0 : ErrorsFound = true;
1617 : } else {
1618 23 : assert(HPWH.fanType == state.dataFans->fans(HPWH.FanNum)->type);
1619 23 : FanVolFlow = state.dataFans->fans(HPWH.FanNum)->maxAirFlowRate;
1620 : }
1621 : // issue #5630, set fan info in coils.
1622 23 : if (bIsVScoil) {
1623 7 : VariableSpeedCoils::setVarSpeedHPWHFanType(state, HPWH.DXCoilNum, HPWH.fanType);
1624 7 : VariableSpeedCoils::setVarSpeedHPWHFanIndex(state, HPWH.DXCoilNum, HPWH.FanNum);
1625 : } else {
1626 : // LOL
1627 16 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanName);
1628 16 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanNum);
1629 16 : DXCoils::SetDXCoolingCoilData(
1630 16 : state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.fanType);
1631 : }
1632 :
1633 23 : if (errFlag) {
1634 0 : ErrorsFound = true;
1635 23 : } else if (HPWH.fanType != HVAC::FanType::OnOff && HPWH.fanType != HVAC::FanType::SystemModel) {
1636 0 : ShowSevereError(state, format("{}=\"{}\": illegal fan type specified.", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1637 0 : ShowContinueError(
1638 : state,
1639 0 : format(" The fan object ({}) type must be Fan:SystemModel or Fan:OnOff when used with a heat pump water heater.", HPWH.FanName));
1640 0 : ErrorsFound = true;
1641 23 : } else if (HPWH.fanType != HVAC::FanType::OnOff && HPWH.fanType != HVAC::FanType::SystemModel) {
1642 0 : ShowSevereError(state, format("{}=\"{}\": illegal fan type specified.", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1643 0 : ShowContinueError(state, format(" The {} must specify that the fan object", state.dataIPShortCut->cCurrentModuleObject));
1644 0 : ShowContinueError(state,
1645 : " is of type FanSystemModel or Fan:OnOff in addition to the fan actually being of that type and defined elsewhere.");
1646 : }
1647 :
1648 23 : if (FanVolFlow != DataSizing::AutoSize && !errFlag) {
1649 21 : if (FanVolFlow < HPWH.OperatingAirFlowRate) {
1650 0 : ShowSevereError(state,
1651 0 : format("{} - air flow rate = {:.7T} in fan object {} is less than the HPWHs evaporator air flow rate.",
1652 0 : state.dataIPShortCut->cCurrentModuleObject,
1653 : FanVolFlow,
1654 0 : HPWH.FanName));
1655 0 : ShowContinueError(state, " The fan flow rate must be >= to the HPWHs evaporator volumetric air flow rate.");
1656 0 : ShowContinueError(state, format(" Occurs in unit = {}", HPWH.Name));
1657 0 : ErrorsFound = true;
1658 : }
1659 : }
1660 :
1661 : // Fan Placement
1662 23 : HPWH.fanPlace = static_cast<HVAC::FanPlace>(getEnumValue(HVAC::fanPlaceNamesUC, hpwhAlpha[24 + nAlphaOffset]));
1663 23 : if (HPWH.fanPlace == HVAC::FanPlace::Invalid) {
1664 0 : ShowSevereInvalidKey(state, eoh, hpwhAlphaFieldNames[24 + nAlphaOffset], hpwhAlpha[24 + nAlphaOffset]);
1665 0 : ErrorsFound = true;
1666 : }
1667 :
1668 23 : if (HPWH.DXCoilNum > 0 && !bIsVScoil) {
1669 : // get HPWH capacity, air inlet node, and PLF curve info from DX coil object
1670 16 : HPWH.Capacity = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2;
1671 16 : HPWH.DXCoilAirInletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
1672 16 : HPWH.DXCoilPLFFPLR = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).PLFFPLR(1);
1673 : // check the range of condenser pump power to be <= 5 gpm/ton
1674 16 : if (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower / state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2 >
1675 : 0.1422) {
1676 0 : ShowWarningError(
1677 : state,
1678 0 : format("{}= {}{}",
1679 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).DXCoilType,
1680 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).Name,
1681 0 : format(": Rated condenser pump power per watt of rated heating capacity has exceeded the recommended maximum of 0.1422 "
1682 : "W/W (41.67 watt/MBH). Condenser pump power per watt = {:.4T}",
1683 0 : (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower /
1684 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2))));
1685 : }
1686 7 : } else if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
1687 :
1688 7 : if (HPWH.bIsIHP) {
1689 1 : HPWH.Capacity =
1690 1 : GetDWHCoilCapacityIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1691 1 : HPWH.DXCoilAirInletNode = IntegratedHeatPump::GetCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1692 1 : HPWH.DXCoilPLFFPLR =
1693 1 : GetIHPDWHCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1694 : } else {
1695 6 : HPWH.Capacity = VariableSpeedCoils::GetCoilCapacityVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1696 6 : HPWH.DXCoilAirInletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1697 6 : HPWH.DXCoilPLFFPLR = VariableSpeedCoils::GetVSCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1698 : }
1699 : // check the range of condenser pump power to be <= 5 gpm/ton, will be checked in the coil object
1700 : }
1701 :
1702 23 : if (HPWH.OperatingWaterFlowRate == Constant::AutoCalculate) {
1703 8 : HPWH.OperatingWaterFlowRate = 0.00000004487 * HPWH.Capacity;
1704 8 : HPWH.WaterFlowRateAutoSized = true;
1705 : }
1706 :
1707 23 : if (HPWH.OperatingAirFlowRate == Constant::AutoCalculate) {
1708 10 : HPWH.OperatingAirFlowRate = 0.00005035 * HPWH.Capacity;
1709 10 : HPWH.AirFlowRateAutoSized = true;
1710 : }
1711 :
1712 : // On Cycle Parasitic Electric Load
1713 23 : HPWH.OnCycParaLoad = hpwhNumeric[6 + nNumericOffset];
1714 23 : if (HPWH.OnCycParaLoad < 0.0) {
1715 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1716 0 : ShowContinueError(state,
1717 0 : format("{} must be >= 0. {}{}",
1718 0 : hpwhNumericFieldNames[6 + nNumericOffset],
1719 0 : hpwhNumericFieldNames[6 + nNumericOffset],
1720 0 : format(" = {:.2T}", hpwhNumeric[6 + nNumericOffset])));
1721 0 : ErrorsFound = true;
1722 : }
1723 :
1724 : // Off Cycle Parasitic Electric Load
1725 23 : HPWH.OffCycParaLoad = hpwhNumeric[7 + nNumericOffset];
1726 23 : if (HPWH.OffCycParaLoad < 0.0) {
1727 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1728 0 : ShowContinueError(state,
1729 0 : format("{} must be >= 0. {}{}",
1730 0 : hpwhNumericFieldNames[7 + nNumericOffset],
1731 0 : hpwhNumericFieldNames[2 + nNumericOffset],
1732 0 : format(" = {:.2T}", hpwhNumeric[7 + nNumericOffset])));
1733 0 : ErrorsFound = true;
1734 : }
1735 :
1736 : // Parasitic Heat Rejection Location
1737 23 : if (Util::SameString(hpwhAlpha[25 + nAlphaOffset], "Zone")) {
1738 8 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::TempZone;
1739 8 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1740 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1741 0 : ShowContinueError(state, format("{} must be ZoneAirOnly or ZoneAndOutdoorAir", hpwhAlphaFieldNames[25 + nAlphaOffset]));
1742 0 : ShowContinueError(state, " when parasitic heat rejection location equals Zone.");
1743 0 : ErrorsFound = true;
1744 : }
1745 15 : } else if (Util::SameString(hpwhAlpha[25 + nAlphaOffset], "Outdoors")) {
1746 15 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::OutsideAir;
1747 : } else {
1748 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1749 0 : ShowContinueError(state, " parasitic heat rejection location must be either Zone or Outdoors.");
1750 0 : ErrorsFound = true;
1751 : }
1752 :
1753 : // Inlet Air Mixer Node
1754 : // get mixer/splitter nodes only when Inlet Air Configuration is ZoneAndOutdoorAir
1755 23 : if (!hpwhAlphaBlank[26 + nAlphaOffset]) {
1756 : // For the inlet air mixer node, NodeConnectionType is outlet from the HPWH inlet air node
1757 3 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1758 3 : HPWH.InletAirMixerNode = NodeInputManager::GetOnlySingleNode(state,
1759 3 : hpwhAlpha[26 + nAlphaOffset],
1760 : ErrorsFound,
1761 : objType,
1762 6 : HPWH.Name + "-INLET AIR MIXER",
1763 : DataLoopNode::NodeFluidType::Air,
1764 : DataLoopNode::ConnectionType::Outlet,
1765 : NodeInputManager::CompFluidStream::Primary,
1766 : DataLoopNode::ObjectIsNotParent);
1767 : } else {
1768 0 : ShowWarningError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1769 0 : ShowContinueError(state,
1770 : "Inlet air mixer node name specified but only required when Inlet Air Configuration is selected as "
1771 : "Zone and OutdoorAir. Node name disregarded and simulation continues.");
1772 : }
1773 20 : } else if (hpwhAlphaBlank[26 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1774 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1775 0 : ShowContinueError(state, "Inlet air mixer node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1776 0 : ErrorsFound = true;
1777 : }
1778 :
1779 : // Outlet Air Splitter Node
1780 23 : if (!hpwhAlphaBlank[27 + nAlphaOffset]) {
1781 : // For the outlet air splitter node, NodeConnectionType is inlet to the HPWH outlet air node
1782 3 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1783 3 : HPWH.OutletAirSplitterNode = NodeInputManager::GetOnlySingleNode(state,
1784 3 : hpwhAlpha[27 + nAlphaOffset],
1785 : ErrorsFound,
1786 : objType,
1787 6 : HPWH.Name + "-OUTLET AIR SPLITTER",
1788 : DataLoopNode::NodeFluidType::Air,
1789 : DataLoopNode::ConnectionType::Inlet,
1790 : NodeInputManager::CompFluidStream::Primary,
1791 : DataLoopNode::ObjectIsNotParent);
1792 : } else {
1793 0 : ShowWarningError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1794 0 : ShowContinueError(state,
1795 : "Outlet air splitter node name specified but only required when Inlet Air Configuration is selected as "
1796 : "ZoneAndOutdoorAir. Node name disregarded and simulation continues.");
1797 : }
1798 20 : } else if (hpwhAlphaBlank[27 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1799 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1800 0 : ShowContinueError(state, "Outlet air splitter node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1801 0 : ErrorsFound = true;
1802 : }
1803 :
1804 : // get node data for HPWH
1805 23 : if (HPWH.InletAirMixerNode != 0) {
1806 : // when mixer/splitter nodes are used the HPWH's inlet/outlet node are set up as DataLoopNode::ObjectIsNotParent
1807 :
1808 3 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1809 3 : hpwhAlpha[7 + nAlphaOffset],
1810 : ErrorsFound,
1811 : objType,
1812 6 : HPWH.Name + "-INLET AIR MIXER",
1813 : DataLoopNode::NodeFluidType::Air,
1814 : DataLoopNode::ConnectionType::Inlet,
1815 : NodeInputManager::CompFluidStream::Primary,
1816 : DataLoopNode::ObjectIsNotParent);
1817 :
1818 3 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1819 3 : hpwhAlpha[8 + nAlphaOffset],
1820 : ErrorsFound,
1821 : objType,
1822 6 : HPWH.Name + "-OUTLET AIR SPLITTER",
1823 : DataLoopNode::NodeFluidType::Air,
1824 : DataLoopNode::ConnectionType::Outlet,
1825 : NodeInputManager::CompFluidStream::Primary,
1826 : DataLoopNode::ObjectIsNotParent);
1827 :
1828 3 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1829 3 : hpwhAlpha[9 + nAlphaOffset],
1830 : ErrorsFound,
1831 : objType,
1832 3 : HPWH.Name,
1833 : DataLoopNode::NodeFluidType::Air,
1834 : DataLoopNode::ConnectionType::OutsideAirReference,
1835 : NodeInputManager::CompFluidStream::Primary,
1836 : DataLoopNode::ObjectIsParent);
1837 3 : if (!hpwhAlpha[9 + nAlphaOffset].empty()) {
1838 : bool Okay;
1839 3 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, HPWH.OutsideAirNode, Okay);
1840 3 : if (!Okay) {
1841 0 : ShowWarningError(state,
1842 0 : format("{}=\"{}\": Adding outdoor air node={}",
1843 0 : state.dataIPShortCut->cCurrentModuleObject,
1844 0 : HPWH.Name,
1845 0 : hpwhAlpha[9 + nAlphaOffset]));
1846 : }
1847 : }
1848 :
1849 3 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1850 3 : hpwhAlpha[10 + nAlphaOffset],
1851 : ErrorsFound,
1852 : objType,
1853 3 : HPWH.Name,
1854 : DataLoopNode::NodeFluidType::Air,
1855 : DataLoopNode::ConnectionType::ReliefAir,
1856 : NodeInputManager::CompFluidStream::Primary,
1857 : 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 3 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1865 3 : hpwhAlpha[7 + nAlphaOffset],
1866 : ErrorsFound,
1867 : objType,
1868 3 : HPWH.Name,
1869 : DataLoopNode::NodeFluidType::Air,
1870 : DataLoopNode::ConnectionType::Outlet,
1871 : NodeInputManager::CompFluidStream::Primary,
1872 : DataLoopNode::ObjectIsParent);
1873 :
1874 3 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1875 3 : hpwhAlpha[8 + nAlphaOffset],
1876 : ErrorsFound,
1877 : objType,
1878 3 : HPWH.Name,
1879 : DataLoopNode::NodeFluidType::Air,
1880 : DataLoopNode::ConnectionType::Outlet,
1881 : NodeInputManager::CompFluidStream::Primary,
1882 : DataLoopNode::ObjectIsParent);
1883 :
1884 : } else { // HPWH is connected to a zone with no mixer/splitter nodes
1885 17 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1886 7 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1887 7 : hpwhAlpha[7 + nAlphaOffset],
1888 : ErrorsFound,
1889 : objType,
1890 7 : HPWH.Name,
1891 : DataLoopNode::NodeFluidType::Air,
1892 : DataLoopNode::ConnectionType::Inlet,
1893 : NodeInputManager::CompFluidStream::Primary,
1894 : DataLoopNode::ObjectIsParent);
1895 :
1896 7 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1897 7 : hpwhAlpha[8 + nAlphaOffset],
1898 : ErrorsFound,
1899 : objType,
1900 7 : HPWH.Name,
1901 : DataLoopNode::NodeFluidType::Air,
1902 : DataLoopNode::ConnectionType::Outlet,
1903 : NodeInputManager::CompFluidStream::Primary,
1904 : DataLoopNode::ObjectIsParent);
1905 : } else { // HPWH is located outdoors
1906 10 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1907 10 : hpwhAlpha[9 + nAlphaOffset],
1908 : ErrorsFound,
1909 : objType,
1910 10 : HPWH.Name,
1911 : DataLoopNode::NodeFluidType::Air,
1912 : DataLoopNode::ConnectionType::OutsideAirReference,
1913 : NodeInputManager::CompFluidStream::Primary,
1914 : 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 : format("{}=\"{}\": Adding outdoor air node ={}",
1921 0 : state.dataIPShortCut->cCurrentModuleObject,
1922 0 : HPWH.Name,
1923 0 : hpwhAlpha[9 + nAlphaOffset]));
1924 : }
1925 : }
1926 :
1927 10 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1928 10 : hpwhAlpha[10 + nAlphaOffset],
1929 : ErrorsFound,
1930 : objType,
1931 10 : HPWH.Name,
1932 : DataLoopNode::NodeFluidType::Air,
1933 : DataLoopNode::ConnectionType::ReliefAir,
1934 : NodeInputManager::CompFluidStream::Primary,
1935 : DataLoopNode::ObjectIsParent);
1936 : }
1937 : }
1938 : }
1939 : // check that required node names are present
1940 23 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule || HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1941 10 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1942 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1943 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1944 0 : ShowContinueError(
1945 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[7 + nAlphaOffset], hpwhAlphaFieldNames[8 + nAlphaOffset]));
1946 0 : ErrorsFound = true;
1947 : }
1948 13 : } else if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
1949 10 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1950 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1951 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1952 0 : ShowContinueError(
1953 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[9 + nAlphaOffset], hpwhAlphaFieldNames[10 + nAlphaOffset]));
1954 0 : ErrorsFound = true;
1955 : }
1956 3 : } else if (HPWH.InletAirMixerNode > 0 && HPWH.OutletAirSplitterNode > 0 && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1957 3 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0 || HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1958 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1959 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1960 0 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1961 0 : ShowContinueError(
1962 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[7 + nAlphaOffset], hpwhAlphaFieldNames[8 + nAlphaOffset]));
1963 : }
1964 0 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1965 0 : ShowContinueError(
1966 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[9 + nAlphaOffset], hpwhAlphaFieldNames[10 + nAlphaOffset]));
1967 : }
1968 0 : ErrorsFound = true;
1969 : }
1970 : }
1971 :
1972 : // check that the HPWH inlet and outlet nodes are in the same zone (ZoneHVAC:EquipmentConnections) when
1973 : // Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
1974 23 : if ((HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) &&
1975 10 : HPWH.AmbientTempZone > 0) {
1976 10 : if (allocated(state.dataZoneEquip->ZoneEquipConfig)) {
1977 10 : bool FoundInletNode = false;
1978 10 : bool FoundOutletNode = false;
1979 10 : int ZoneNum = HPWH.AmbientTempZone;
1980 10 : if (ZoneNum <= state.dataGlobal->NumOfZones) {
1981 37 : for (int SupAirIn = 1; SupAirIn <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumInletNodes; ++SupAirIn) {
1982 27 : if (HPWH.HeatPumpAirOutletNode != state.dataZoneEquip->ZoneEquipConfig(ZoneNum).InletNode(SupAirIn)) {
1983 17 : continue;
1984 : }
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)) {
1989 7 : continue;
1990 : }
1991 10 : FoundInletNode = true;
1992 : }
1993 10 : if (!FoundInletNode) {
1994 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1995 0 : ShowContinueError(state,
1996 0 : format("The HPWH's air inlet node name = {} was not properly specified ", hpwhAlpha[7 + nAlphaOffset]));
1997 0 : ShowContinueError(
1998 : state,
1999 0 : format("as an exhaust air node for zone = {} in a ZoneHVAC:EquipmentConnections object.", hpwhAlpha[13 + nAlphaOffset]));
2000 0 : ErrorsFound = true;
2001 : }
2002 10 : if (!FoundOutletNode) {
2003 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2004 0 : ShowContinueError(state,
2005 0 : format("The HPWH's air outlet node name = {} was not properly specified ", hpwhAlpha[8 + nAlphaOffset]));
2006 0 : ShowContinueError(
2007 : state,
2008 0 : format("as an inlet air node for zone = {} in a ZoneHVAC:EquipmentConnections object.", hpwhAlpha[13 + nAlphaOffset]));
2009 0 : ErrorsFound = true;
2010 : }
2011 : }
2012 : } else {
2013 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2014 0 : ShowContinueError(state,
2015 : "Heat pump water heater air inlet node name and air outlet node name must be listed in a "
2016 : "ZoneHVAC:EquipmentConnections object when Inlet Air Configuration is equal to ZoneAirOnly or "
2017 : "ZoneAndOutdoorAir.");
2018 0 : ErrorsFound = true;
2019 : }
2020 : }
2021 :
2022 : // only get the inlet air mixer schedule if the inlet air configuration is zone and outdoor air
2023 23 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
2024 3 : if (hpwhAlphaBlank[28 + nAlphaOffset]) {
2025 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[28 + nAlphaOffset]);
2026 0 : ErrorsFound = true;
2027 : // set outlet air splitter schedule index equal to inlet air mixer schedule index
2028 : // (place holder for when zone pressurization/depressurization is allowed and different schedules can be used)
2029 3 : } else if ((HPWH.inletAirMixerSched = HPWH.outletAirSplitterSched = Sched::GetSchedule(state, hpwhAlpha[28 + nAlphaOffset])) == nullptr) {
2030 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[28 + nAlphaOffset], hpwhAlpha[28 + nAlphaOffset]);
2031 0 : ErrorsFound = true;
2032 3 : } else if (!HPWH.inletAirMixerSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
2033 0 : Sched::ShowSevereBadMinMax(
2034 0 : state, eoh, hpwhAlphaFieldNames[28 + nAlphaOffset], hpwhAlpha[28 + nAlphaOffset], Clusive::In, 0.0, Clusive::In, 1.0);
2035 0 : ErrorsFound = true;
2036 : }
2037 : }
2038 :
2039 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2040 23 : if (HPWH.fanPlace == HVAC::FanPlace::DrawThru) {
2041 11 : if (HPWH.OutletAirSplitterNode != 0) {
2042 3 : HPWH.FanOutletNode = HPWH.OutletAirSplitterNode;
2043 8 : } else if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2044 1 : HPWH.FanOutletNode = HPWH.ExhaustAirNode;
2045 : } else {
2046 7 : HPWH.FanOutletNode = HPWH.HeatPumpAirOutletNode;
2047 : }
2048 12 : } else if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2049 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2050 12 : if (bIsVScoil) {
2051 5 : if (HPWH.bIsIHP) {
2052 1 : HPWH.FanOutletNode = IntegratedHeatPump::GetDWHCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2053 : } else {
2054 4 : HPWH.FanOutletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2055 : }
2056 : } else {
2057 7 : HPWH.FanOutletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
2058 : }
2059 : }
2060 :
2061 : // check that fan outlet node is indeed correct
2062 23 : int FanOutletNodeNum = state.dataFans->fans(HPWH.FanNum)->outletNodeNum;
2063 :
2064 23 : if (FanOutletNodeNum != HPWH.FanOutletNode) {
2065 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2066 0 : ShowContinueError(state, "Heat pump water heater fan outlet node name does not match next connected component.");
2067 0 : if (FanOutletNodeNum != 0) {
2068 0 : ShowContinueError(state, format("Fan outlet node name = {}", state.dataLoopNodes->NodeID(FanOutletNodeNum)));
2069 : }
2070 0 : if (HPWH.FanOutletNode != 0) {
2071 0 : ShowContinueError(state, format("Expected fan outlet node name = {}", state.dataLoopNodes->NodeID(HPWH.FanOutletNode)));
2072 : }
2073 0 : ErrorsFound = true;
2074 : }
2075 23 : int FanInletNodeNum = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
2076 :
2077 23 : int HPWHFanInletNodeNum(0);
2078 23 : if (HPWH.InletAirMixerNode != 0) {
2079 3 : HPWHFanInletNodeNum = HPWH.InletAirMixerNode;
2080 : } else {
2081 20 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2082 10 : HPWHFanInletNodeNum = HPWH.OutsideAirNode;
2083 : } else {
2084 10 : HPWHFanInletNodeNum = HPWH.HeatPumpAirInletNode;
2085 : }
2086 : }
2087 23 : if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2088 12 : if (FanInletNodeNum != HPWHFanInletNodeNum) {
2089 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2090 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2091 0 : if (FanOutletNodeNum != 0) {
2092 0 : ShowContinueError(state, format("Fan inlet node name = {}", state.dataLoopNodes->NodeID(FanInletNodeNum)));
2093 : }
2094 0 : if (HPWH.FanOutletNode != 0) {
2095 0 : ShowContinueError(state, format("Expected fan inlet node name = {}", state.dataLoopNodes->NodeID(HPWHFanInletNodeNum)));
2096 : }
2097 0 : ErrorsFound = true;
2098 : }
2099 : }
2100 :
2101 23 : int DXCoilAirOutletNodeNum(0);
2102 23 : if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
2103 7 : if (HPWH.bIsIHP) {
2104 1 : DXCoilAirOutletNodeNum = IntegratedHeatPump::GetDWHCoilOutletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2105 : } else {
2106 6 : DXCoilAirOutletNodeNum = VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2107 : }
2108 :
2109 16 : } else if (HPWH.DXCoilNum > 0) {
2110 16 : DXCoilAirOutletNodeNum = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirOutNode;
2111 : }
2112 23 : if (HPWH.fanPlace == HVAC::FanPlace::DrawThru) {
2113 11 : if (FanInletNodeNum != DXCoilAirOutletNodeNum) {
2114 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2115 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2116 0 : if (FanInletNodeNum != 0) {
2117 0 : ShowContinueError(state, format("Fan inlet node name = {}", state.dataLoopNodes->NodeID(FanInletNodeNum)));
2118 : }
2119 0 : if (DXCoilAirOutletNodeNum != 0) {
2120 0 : ShowContinueError(state, format("Expected fan inlet node name = {}", state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum)));
2121 : }
2122 0 : ErrorsFound = true;
2123 : }
2124 12 : } else if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2125 12 : int HPWHCoilOutletNodeNum(0);
2126 12 : if (HPWH.OutletAirSplitterNode != 0) {
2127 0 : HPWHCoilOutletNodeNum = HPWH.OutletAirSplitterNode;
2128 : } else {
2129 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2130 9 : HPWHCoilOutletNodeNum = HPWH.ExhaustAirNode;
2131 : } else {
2132 3 : HPWHCoilOutletNodeNum = HPWH.HeatPumpAirOutletNode;
2133 : }
2134 : }
2135 12 : if (DXCoilAirOutletNodeNum != HPWHCoilOutletNodeNum) {
2136 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2137 0 : ShowContinueError(state, "Heat pump water heater coil outlet node name does not match next connected component.");
2138 0 : if (DXCoilAirOutletNodeNum != 0) {
2139 0 : ShowContinueError(state, format("Coil outlet node name = {}", state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum)));
2140 : }
2141 0 : if (HPWHCoilOutletNodeNum != 0) {
2142 0 : ShowContinueError(state, format("Expected coil outlet node name = {}", state.dataLoopNodes->NodeID(HPWHCoilOutletNodeNum)));
2143 : }
2144 0 : ErrorsFound = true;
2145 : }
2146 : }
2147 :
2148 : // set the max mass flow rate for outdoor fans
2149 23 : if (HPWH.FanOutletNode > 0) {
2150 23 : state.dataLoopNodes->Node(HPWH.FanOutletNode).MassFlowRateMax =
2151 46 : HPWH.OperatingAirFlowRate * Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, 20.0, 0.0);
2152 : }
2153 :
2154 23 : if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2155 12 : if (HPWH.InletAirMixerNode > 0) {
2156 0 : HPWH.FanInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2157 0 : HPWH.FanOutletNode_str = "UNDEFINED";
2158 : } else {
2159 12 : if (HPWH.OutsideAirNode == 0) {
2160 3 : HPWH.FanInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2161 3 : HPWH.FanOutletNode_str = "UNDEFINED";
2162 : } else {
2163 9 : HPWH.FanInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2164 9 : HPWH.FanOutletNode_str = "UNDEFINED";
2165 : }
2166 : }
2167 12 : if (HPWH.OutletAirSplitterNode > 0) {
2168 0 : HPWH.CoilInletNode_str = "UNDEFINED";
2169 0 : HPWH.CoilOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2170 : } else {
2171 12 : if (HPWH.OutsideAirNode == 0) {
2172 3 : HPWH.CoilInletNode_str = "UNDEFINED";
2173 3 : HPWH.CoilOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2174 : } else {
2175 9 : HPWH.CoilInletNode_str = "UNDEFINED";
2176 9 : HPWH.CoilOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2177 : }
2178 : }
2179 : } else {
2180 11 : if (HPWH.InletAirMixerNode > 0) {
2181 3 : HPWH.CoilInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2182 3 : HPWH.CoilOutletNode_str = "UNDEFINED";
2183 : } else {
2184 8 : if (HPWH.OutsideAirNode == 0) {
2185 7 : HPWH.CoilInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2186 7 : HPWH.CoilOutletNode_str = "UNDEFINED";
2187 : } else {
2188 1 : HPWH.CoilInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2189 1 : HPWH.CoilOutletNode_str = "UNDEFINED";
2190 : }
2191 : }
2192 11 : if (HPWH.OutletAirSplitterNode > 0) {
2193 3 : HPWH.FanInletNode_str = "UNDEFINED";
2194 3 : HPWH.FanOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2195 : } else {
2196 8 : if (HPWH.OutsideAirNode == 0) {
2197 7 : HPWH.FanInletNode_str = "UNDEFINED";
2198 7 : HPWH.FanOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2199 : } else {
2200 1 : HPWH.FanInletNode_str = "UNDEFINED";
2201 1 : HPWH.FanOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2202 : }
2203 : }
2204 : }
2205 :
2206 : // set up comp set for air side nodes (can be blow thru or draw thru, may or may not have damper nodes)
2207 23 : if (HPWH.bIsIHP) {
2208 2 : BranchNodeConnections::SetUpCompSets(
2209 2 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName + " Outdoor Coil", HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2210 : } else {
2211 22 : BranchNodeConnections::SetUpCompSets(
2212 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2213 : }
2214 :
2215 46 : BranchNodeConnections::SetUpCompSets(
2216 23 : state, HPWH.Type, HPWH.Name, HVAC::fanTypeNames[(int)HPWH.fanType], HPWH.FanName, HPWH.FanInletNode_str, HPWH.FanOutletNode_str);
2217 :
2218 : // Control Logic Flag
2219 43 : std::string CtrlLogicFlag = hpwhAlphaBlank[29 + nAlphaOffset] ? "SIMULTANEOUS" : hpwhAlpha[29 + nAlphaOffset];
2220 23 : if (Util::SameString(CtrlLogicFlag, "SIMULTANEOUS")) {
2221 20 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = true;
2222 3 : } else if (Util::SameString(CtrlLogicFlag, "MUTUALLYEXCLUSIVE")) {
2223 3 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = false;
2224 : } else {
2225 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2226 0 : ShowContinueError(state, format("{} is not a valid value for field Tank Element Control Logic.", CtrlLogicFlag));
2227 0 : ErrorsFound = true;
2228 : }
2229 :
2230 : // Control Sensor 1 Location In Stratified Tank
2231 23 : if (!hpwhNumericBlank[8 + nNumericOffset]) {
2232 11 : HPWH.ControlSensor1Height = hpwhNumeric[8 + nNumericOffset];
2233 : } else {
2234 : // use heater1 location, which we don't know right now
2235 12 : HPWH.ControlSensor1Height = -1.0;
2236 : }
2237 :
2238 : // Control Sensor 1 Weight
2239 23 : HPWH.ControlSensor1Weight = hpwhNumericBlank[9 + nNumericOffset] ? 1.0 : hpwhNumeric[9 + nNumericOffset];
2240 :
2241 : // Control Sensor 2 Location In Stratified Tank
2242 23 : if (!hpwhNumericBlank[10 + nNumericOffset]) {
2243 23 : HPWH.ControlSensor2Height = hpwhNumeric[10 + nNumericOffset];
2244 : } else {
2245 0 : HPWH.ControlSensor2Height = -1.0;
2246 : }
2247 :
2248 : // Control Sensor 2 Weight
2249 23 : HPWH.ControlSensor2Weight = 1.0 - HPWH.ControlSensor1Weight;
2250 23 : }
2251 :
2252 9 : return ErrorsFound;
2253 : }
2254 :
2255 127 : bool getWaterHeaterMixedInputs(EnergyPlusData &state)
2256 : {
2257 127 : bool ErrorsFound = false;
2258 127 : state.dataIPShortCut->cCurrentModuleObject = cMixedWHModuleObj;
2259 : static constexpr std::string_view routineName = "getWaterHeaterMixedInputs";
2260 :
2261 307 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed; ++WaterThermalTankNum) {
2262 : int NumAlphas;
2263 : int NumNums;
2264 : int IOStat;
2265 360 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2266 180 : state.dataIPShortCut->cCurrentModuleObject,
2267 : WaterThermalTankNum,
2268 180 : state.dataIPShortCut->cAlphaArgs,
2269 : NumAlphas,
2270 180 : state.dataIPShortCut->rNumericArgs,
2271 : NumNums,
2272 : IOStat,
2273 180 : state.dataIPShortCut->lNumericFieldBlanks,
2274 180 : state.dataIPShortCut->lAlphaFieldBlanks,
2275 180 : state.dataIPShortCut->cAlphaFieldNames,
2276 180 : state.dataIPShortCut->cNumericFieldNames);
2277 :
2278 180 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
2279 180 : GlobalNames::VerifyUniqueInterObjectName(state,
2280 180 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2281 180 : state.dataIPShortCut->cAlphaArgs(1),
2282 180 : state.dataIPShortCut->cCurrentModuleObject,
2283 180 : state.dataIPShortCut->cAlphaFieldNames(1),
2284 : ErrorsFound);
2285 :
2286 180 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2287 :
2288 180 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2289 180 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2290 180 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterMixed;
2291 :
2292 180 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
2293 0 : ShowSevereError(state, "Fluid properties for WATER not found");
2294 0 : ErrorsFound = true;
2295 : }
2296 :
2297 : // default to always on
2298 180 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2299 180 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2300 :
2301 : // A user field will be added in a later release
2302 180 : Tank.EndUseSubcategoryName = "Water Heater";
2303 :
2304 180 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2305 180 : if (Tank.Volume == DataSizing::AutoSize) {
2306 2 : Tank.VolumeWasAutoSized = true;
2307 : }
2308 180 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
2309 : // Set volume to a really small number to simulate a tankless/instantaneous water heater
2310 0 : Tank.Volume = 0.000001; // = 1 cm3
2311 : }
2312 :
2313 180 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
2314 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2));
2315 0 : ErrorsFound = true;
2316 180 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
2317 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
2318 0 : ErrorsFound = true;
2319 : }
2320 :
2321 180 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
2322 174 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
2323 : } else {
2324 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2325 6 : Tank.DeadBandDeltaTemp = 0.5;
2326 : }
2327 :
2328 180 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2329 180 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
2330 : } else {
2331 : // Default to very large number
2332 : // BG comment why a large number here why not boilng point of water?
2333 0 : Tank.TankTempLimit = 100.0; // 1.0E9
2334 : }
2335 :
2336 180 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
2337 180 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2338 1 : Tank.MaxCapacityWasAutoSized = true;
2339 : }
2340 :
2341 180 : if ((state.dataIPShortCut->rNumericArgs(5) > Tank.MaxCapacity) && (!Tank.MaxCapacityWasAutoSized)) {
2342 0 : ShowSevereError(state,
2343 0 : format("{} = {}: Heater Minimum Capacity cannot be greater than Heater Maximum Capacity",
2344 0 : state.dataIPShortCut->cCurrentModuleObject,
2345 0 : state.dataIPShortCut->cAlphaArgs(1)));
2346 0 : ErrorsFound = true;
2347 : } else {
2348 180 : Tank.MinCapacity = state.dataIPShortCut->rNumericArgs(5);
2349 : }
2350 :
2351 : // Validate Heater Control Type
2352 180 : Tank.ControlType =
2353 180 : static_cast<HeaterControlMode>(getEnumValue(HeaterControlModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
2354 180 : switch (Tank.ControlType) {
2355 175 : case HeaterControlMode::Cycle: {
2356 175 : Tank.MinCapacity = Tank.MaxCapacity;
2357 175 : break;
2358 : }
2359 5 : case HeaterControlMode::Modulate: {
2360 :
2361 : // CASE ('MODULATE WITH OVERHEAT') ! Not yet implemented
2362 :
2363 : // CASE ('MODULATE WITH UNDERHEAT') ! Not yet implemented
2364 :
2365 5 : break;
2366 : }
2367 0 : default: {
2368 0 : ShowSevereError(state,
2369 0 : format("{} = {}: Invalid Control Type entered={}",
2370 0 : state.dataIPShortCut->cCurrentModuleObject,
2371 0 : state.dataIPShortCut->cAlphaArgs(1),
2372 0 : state.dataIPShortCut->cAlphaArgs(3)));
2373 0 : ErrorsFound = true;
2374 0 : break;
2375 : }
2376 : }
2377 :
2378 180 : Tank.VolFlowRateMin = state.dataIPShortCut->rNumericArgs(6);
2379 180 : Tank.VolFlowRateMin = max(0.0, Tank.VolFlowRateMin);
2380 180 : Tank.IgnitionDelay = state.dataIPShortCut->rNumericArgs(7); // Not yet implemented
2381 :
2382 : // Validate Heater Fuel Type
2383 180 : Tank.FuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(4)));
2384 180 : switch (Tank.FuelType) {
2385 0 : case Constant::eFuel::Invalid: {
2386 0 : ShowSevereError(state,
2387 0 : format("{} = {}: Invalid Heater Fuel Type entered={}",
2388 0 : state.dataIPShortCut->cCurrentModuleObject,
2389 0 : state.dataIPShortCut->cAlphaArgs(1),
2390 0 : state.dataIPShortCut->cAlphaArgs(4)));
2391 : // Set to Electric to avoid errors when setting up output variables
2392 0 : Tank.FuelType = Constant::eFuel::Electricity;
2393 0 : ErrorsFound = true;
2394 0 : break;
2395 : }
2396 180 : default:
2397 180 : break;
2398 : }
2399 :
2400 180 : if (state.dataIPShortCut->rNumericArgs(8) > 0.0) {
2401 180 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(8);
2402 180 : if (state.dataIPShortCut->rNumericArgs(8) > 1.0) {
2403 0 : ShowWarningError(state,
2404 0 : fmt::format("{} = {}: {}={} should not typically be greater than 1.",
2405 0 : state.dataIPShortCut->cCurrentModuleObject,
2406 0 : state.dataIPShortCut->cAlphaArgs(1),
2407 0 : state.dataIPShortCut->cNumericFieldNames(8),
2408 0 : state.dataIPShortCut->rNumericArgs(8)));
2409 : }
2410 : } else {
2411 0 : ShowSevereError(state,
2412 0 : format("{} = {}: Heater Thermal Efficiency must be greater than zero",
2413 0 : state.dataIPShortCut->cCurrentModuleObject,
2414 0 : state.dataIPShortCut->cAlphaArgs(1)));
2415 0 : ErrorsFound = true;
2416 : }
2417 :
2418 180 : if (!state.dataIPShortCut->cAlphaArgs(5).empty()) {
2419 0 : Tank.PLFCurve = Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(5));
2420 0 : if (Tank.PLFCurve == 0) {
2421 0 : ShowSevereError(state,
2422 0 : format("{} = {}: Part Load Factor curve not found = {}",
2423 0 : state.dataIPShortCut->cCurrentModuleObject,
2424 0 : state.dataIPShortCut->cAlphaArgs(1),
2425 0 : state.dataIPShortCut->cAlphaArgs(5)));
2426 0 : ErrorsFound = true;
2427 : } else {
2428 : bool IsValid;
2429 0 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::ValidatePLFCurve(state, Tank.PLFCurve, IsValid);
2430 :
2431 0 : if (!IsValid) {
2432 0 : ShowSevereError(
2433 : state,
2434 0 : format("{} = {}: Part Load Factor curve failed to evaluate to greater than zero for all numbers in the domain of 0 to 1",
2435 0 : state.dataIPShortCut->cCurrentModuleObject,
2436 0 : state.dataIPShortCut->cAlphaArgs(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 180 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(9);
2451 :
2452 : // Validate Off-Cycle Parasitic Fuel Type
2453 180 : Tank.OffCycParaFuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(6)));
2454 180 : switch (Tank.OffCycParaFuelType) {
2455 16 : case Constant::eFuel::Invalid:
2456 16 : if (state.dataIPShortCut->cAlphaArgs(6).empty()) { // If blank, default to Fuel Type for heater
2457 16 : Tank.OffCycParaFuelType = Tank.FuelType;
2458 : } else { // could have been an unsupported value
2459 0 : ShowSevereError(state,
2460 0 : format("{} = {}: Invalid Off-Cycle Parasitic Fuel Type entered={}",
2461 0 : state.dataIPShortCut->cCurrentModuleObject,
2462 0 : state.dataIPShortCut->cAlphaArgs(1),
2463 0 : state.dataIPShortCut->cAlphaArgs(6)));
2464 : // Set to Electric to avoid errors when setting up output variables
2465 0 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
2466 0 : ErrorsFound = true;
2467 : }
2468 16 : break;
2469 164 : default:
2470 164 : break;
2471 : }
2472 :
2473 180 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(10);
2474 :
2475 180 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(11);
2476 :
2477 : // Validate On-Cycle Parasitic Fuel Type
2478 180 : Tank.OnCycParaFuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(7)));
2479 180 : switch (Tank.OnCycParaFuelType) {
2480 56 : case Constant::eFuel::Invalid:
2481 56 : if (state.dataIPShortCut->cAlphaArgs(7).empty()) { // If blank, default to Fuel Type for heater
2482 56 : Tank.OnCycParaFuelType = Tank.FuelType;
2483 : } else { // could have been an unsupported value
2484 0 : ShowSevereError(state,
2485 0 : format("{} = {}: Invalid On-Cycle Parasitic Fuel Type entered={}",
2486 0 : state.dataIPShortCut->cCurrentModuleObject,
2487 0 : state.dataIPShortCut->cAlphaArgs(1),
2488 0 : state.dataIPShortCut->cAlphaArgs(7)));
2489 : // Set to Electric to avoid errors when setting up output variables
2490 0 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
2491 0 : ErrorsFound = true;
2492 : }
2493 56 : break;
2494 124 : default:
2495 124 : break;
2496 : }
2497 :
2498 180 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(12);
2499 :
2500 180 : Tank.AmbientTempIndicator =
2501 180 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(8))));
2502 180 : switch (Tank.AmbientTempIndicator) {
2503 :
2504 101 : case WTTAmbientTemp::Schedule: {
2505 101 : if (state.dataIPShortCut->lAlphaFieldBlanks(9)) {
2506 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(9));
2507 0 : ErrorsFound = true;
2508 101 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(9))) == nullptr) {
2509 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(9), state.dataIPShortCut->cAlphaArgs(9));
2510 0 : ErrorsFound = true;
2511 : }
2512 101 : } break;
2513 :
2514 72 : case WTTAmbientTemp::TempZone: {
2515 72 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(10), state.dataHeatBal->Zone);
2516 72 : if (Tank.AmbientTempZone == 0) {
2517 0 : ShowSevereError(state,
2518 0 : format("{} = {}: Ambient Temperature Zone not found = {}",
2519 0 : state.dataIPShortCut->cCurrentModuleObject,
2520 0 : state.dataIPShortCut->cAlphaArgs(1),
2521 0 : state.dataIPShortCut->cAlphaArgs(10)));
2522 0 : ErrorsFound = true;
2523 : }
2524 72 : } break;
2525 :
2526 7 : case WTTAmbientTemp::OutsideAir: {
2527 7 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
2528 7 : state.dataIPShortCut->cAlphaArgs(11),
2529 : ErrorsFound,
2530 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2531 7 : state.dataIPShortCut->cAlphaArgs(1),
2532 : DataLoopNode::NodeFluidType::Air,
2533 : DataLoopNode::ConnectionType::OutsideAirReference,
2534 : NodeInputManager::CompFluidStream::Primary,
2535 : DataLoopNode::ObjectIsNotParent);
2536 7 : if (!state.dataIPShortCut->cAlphaArgs(11).empty()) {
2537 7 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
2538 0 : ShowSevereError(state,
2539 0 : format("{} = {}: Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node",
2540 0 : state.dataIPShortCut->cCurrentModuleObject,
2541 0 : state.dataIPShortCut->cAlphaArgs(1)));
2542 0 : ShowContinueError(state, format("...Referenced Node Name={}", state.dataIPShortCut->cAlphaArgs(11)));
2543 0 : ErrorsFound = true;
2544 : }
2545 : } else {
2546 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
2547 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
2548 0 : ErrorsFound = true;
2549 : }
2550 :
2551 7 : break;
2552 : }
2553 0 : default: {
2554 0 : ShowSevereError(state,
2555 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
2556 0 : state.dataIPShortCut->cCurrentModuleObject,
2557 0 : state.dataIPShortCut->cAlphaArgs(1),
2558 0 : state.dataIPShortCut->cAlphaArgs(8)));
2559 0 : ShowContinueError(state, " Valid entries are SCHEDULE, ZONE, and OUTDOORS.");
2560 0 : ErrorsFound = true;
2561 0 : break;
2562 : }
2563 : }
2564 :
2565 180 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(13);
2566 180 : Tank.OffCycLossFracToZone = state.dataIPShortCut->rNumericArgs(14);
2567 :
2568 180 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(15);
2569 180 : Tank.OnCycLossFracToZone = state.dataIPShortCut->rNumericArgs(16);
2570 180 : Real64 rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
2571 :
2572 180 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(17) * rho;
2573 :
2574 180 : if ((state.dataIPShortCut->cAlphaArgs(14).empty()) && (state.dataIPShortCut->cAlphaArgs(15).empty())) {
2575 61 : if (state.dataIPShortCut->lAlphaFieldBlanks(12)) {
2576 61 : } else if ((Tank.flowRateSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(12))) == nullptr) {
2577 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(12), state.dataIPShortCut->cAlphaArgs(12));
2578 0 : ErrorsFound = true;
2579 : }
2580 : }
2581 :
2582 180 : if (state.dataIPShortCut->lAlphaFieldBlanks(13)) {
2583 9 : } else if ((Tank.useInletTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(13))) == nullptr) {
2584 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(13), state.dataIPShortCut->cAlphaArgs(13));
2585 0 : ErrorsFound = true;
2586 : }
2587 :
2588 180 : if (NumNums > 17) {
2589 135 : if ((state.dataIPShortCut->rNumericArgs(18) > 1) || (state.dataIPShortCut->rNumericArgs(18) < 0)) {
2590 0 : ShowSevereError(state,
2591 0 : format("{} = {}: Use Side Effectiveness is out of bounds (0 to 1)",
2592 0 : state.dataIPShortCut->cCurrentModuleObject,
2593 0 : state.dataIPShortCut->cAlphaArgs(1)));
2594 0 : ErrorsFound = true;
2595 : }
2596 135 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(18);
2597 : } else {
2598 45 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
2599 : }
2600 :
2601 180 : if (NumNums > 18) {
2602 135 : if ((state.dataIPShortCut->rNumericArgs(19) > 1) || (state.dataIPShortCut->rNumericArgs(19) <= 0)) {
2603 0 : ShowSevereError(state,
2604 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
2605 0 : state.dataIPShortCut->cCurrentModuleObject,
2606 0 : state.dataIPShortCut->cAlphaArgs(1)));
2607 0 : ErrorsFound = true;
2608 : }
2609 135 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(19);
2610 : } else {
2611 45 : Tank.SourceEffectiveness = 1.0;
2612 : }
2613 :
2614 : // If no plant nodes are connected, simulate in stand-alone mode.
2615 302 : if (state.dataIPShortCut->cAlphaArgs(14).empty() && state.dataIPShortCut->cAlphaArgs(15).empty() &&
2616 302 : state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty()) {
2617 47 : Tank.StandAlone = true;
2618 : }
2619 :
2620 180 : if (!state.dataIPShortCut->lNumericFieldBlanks(20)) {
2621 119 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(20);
2622 119 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
2623 113 : Tank.UseDesignVolFlowRateWasAutoSized = true;
2624 : }
2625 : } else {
2626 61 : Tank.UseDesignVolFlowRate = 0.0;
2627 : }
2628 180 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2629 :
2630 180 : if (!state.dataIPShortCut->lNumericFieldBlanks(21)) {
2631 111 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(21);
2632 111 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
2633 101 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
2634 : }
2635 : } else {
2636 69 : Tank.SourceDesignVolFlowRate = 0.0;
2637 : }
2638 180 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2639 :
2640 180 : if (!state.dataIPShortCut->lNumericFieldBlanks(22)) {
2641 97 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(22);
2642 : } else {
2643 83 : Tank.SizingRecoveryTime = 1.5;
2644 : }
2645 :
2646 180 : if ((!state.dataIPShortCut->cAlphaArgs(14).empty()) || (!state.dataIPShortCut->cAlphaArgs(15).empty())) {
2647 119 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
2648 119 : state.dataIPShortCut->cAlphaArgs(14),
2649 : ErrorsFound,
2650 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2651 119 : state.dataIPShortCut->cAlphaArgs(1),
2652 : DataLoopNode::NodeFluidType::Water,
2653 : DataLoopNode::ConnectionType::Inlet,
2654 : NodeInputManager::CompFluidStream::Primary,
2655 : DataLoopNode::ObjectIsNotParent);
2656 119 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(14);
2657 119 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
2658 119 : state.dataIPShortCut->cAlphaArgs(15),
2659 : ErrorsFound,
2660 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2661 119 : state.dataIPShortCut->cAlphaArgs(1),
2662 : DataLoopNode::NodeFluidType::Water,
2663 : DataLoopNode::ConnectionType::Outlet,
2664 : NodeInputManager::CompFluidStream::Primary,
2665 : DataLoopNode::ObjectIsNotParent);
2666 119 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(15);
2667 :
2668 119 : if (state.dataIPShortCut->rNumericArgs(17) > 0) {
2669 0 : ShowWarningError(state,
2670 0 : format("{} = {}: Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used",
2671 0 : state.dataIPShortCut->cCurrentModuleObject,
2672 0 : state.dataIPShortCut->cAlphaArgs(1)));
2673 : }
2674 :
2675 119 : if (Tank.flowRateSched != nullptr) {
2676 0 : ShowWarningError(state,
2677 0 : format("{} = {}: Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used",
2678 0 : state.dataIPShortCut->cCurrentModuleObject,
2679 0 : state.dataIPShortCut->cAlphaArgs(1)));
2680 : }
2681 :
2682 119 : if (Tank.useInletTempSched != nullptr) {
2683 0 : ShowWarningError(state,
2684 0 : format("{} = {}: Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used",
2685 0 : state.dataIPShortCut->cCurrentModuleObject,
2686 0 : state.dataIPShortCut->cAlphaArgs(1)));
2687 : }
2688 : }
2689 :
2690 180 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
2691 33 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
2692 33 : state.dataIPShortCut->cAlphaArgs(16),
2693 : ErrorsFound,
2694 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2695 33 : state.dataIPShortCut->cAlphaArgs(1),
2696 : DataLoopNode::NodeFluidType::Water,
2697 : DataLoopNode::ConnectionType::Inlet,
2698 : NodeInputManager::CompFluidStream::Secondary,
2699 : DataLoopNode::ObjectIsNotParent);
2700 33 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(16);
2701 33 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
2702 33 : state.dataIPShortCut->cAlphaArgs(17),
2703 : ErrorsFound,
2704 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2705 33 : state.dataIPShortCut->cAlphaArgs(1),
2706 : DataLoopNode::NodeFluidType::Water,
2707 : DataLoopNode::ConnectionType::Outlet,
2708 : NodeInputManager::CompFluidStream::Secondary,
2709 : DataLoopNode::ObjectIsNotParent);
2710 33 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(17);
2711 : }
2712 :
2713 180 : if (!state.dataIPShortCut->lAlphaFieldBlanks(18)) {
2714 3 : Tank.SourceSideControlMode =
2715 3 : static_cast<SourceSideControl>(getEnumValue(SourceSideControlNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(18))));
2716 3 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
2717 0 : ShowSevereError(state,
2718 0 : format("{} = {}: Invalid Control Mode entered={}",
2719 0 : state.dataIPShortCut->cCurrentModuleObject,
2720 0 : state.dataIPShortCut->cAlphaArgs(1),
2721 0 : state.dataIPShortCut->cAlphaArgs(18)));
2722 0 : ErrorsFound = true;
2723 : }
2724 : } else {
2725 177 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
2726 : }
2727 :
2728 180 : if (state.dataIPShortCut->lAlphaFieldBlanks(19)) {
2729 0 : } else if ((Tank.sourceSideAltSetpointSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(19))) == nullptr) {
2730 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(19), state.dataIPShortCut->cAlphaArgs(19));
2731 0 : ErrorsFound = true;
2732 : }
2733 :
2734 180 : if (NumAlphas > 19) {
2735 3 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(20);
2736 : }
2737 :
2738 : } // WaterThermalTankNum
2739 :
2740 127 : return ErrorsFound;
2741 : }
2742 :
2743 10 : bool getWaterHeaterStratifiedInput(EnergyPlusData &state)
2744 : {
2745 10 : bool ErrorsFound = false;
2746 : static constexpr std::string_view routineName = "getWaterHeaterStratifiedInput";
2747 :
2748 10 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedWHModuleObj; //'WaterHeater:Stratified'
2749 :
2750 26 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + 1;
2751 26 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified;
2752 : ++WaterThermalTankNum) {
2753 : int NumAlphas;
2754 : int NumNums;
2755 : int IOStat;
2756 32 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2757 16 : state.dataIPShortCut->cCurrentModuleObject,
2758 16 : WaterThermalTankNum - state.dataWaterThermalTanks->numWaterHeaterMixed,
2759 16 : state.dataIPShortCut->cAlphaArgs,
2760 : NumAlphas,
2761 16 : state.dataIPShortCut->rNumericArgs,
2762 : NumNums,
2763 : IOStat,
2764 16 : state.dataIPShortCut->lNumericFieldBlanks,
2765 16 : state.dataIPShortCut->lAlphaFieldBlanks,
2766 16 : state.dataIPShortCut->cAlphaFieldNames,
2767 16 : state.dataIPShortCut->cNumericFieldNames);
2768 :
2769 16 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
2770 :
2771 16 : GlobalNames::VerifyUniqueInterObjectName(state,
2772 16 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2773 16 : state.dataIPShortCut->cAlphaArgs(1),
2774 16 : state.dataIPShortCut->cCurrentModuleObject,
2775 16 : state.dataIPShortCut->cAlphaFieldNames(1),
2776 : ErrorsFound);
2777 :
2778 16 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2779 :
2780 16 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2781 16 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2782 16 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterStratified;
2783 :
2784 16 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
2785 0 : ShowSevereError(state, "Fluid Properties for WATER not found.");
2786 0 : ErrorsFound = true;
2787 : }
2788 :
2789 : // default to always on
2790 16 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2791 16 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2792 :
2793 16 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(2);
2794 :
2795 16 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2796 16 : if (Tank.Volume == DataSizing::AutoSize) {
2797 0 : Tank.VolumeWasAutoSized = true;
2798 : }
2799 16 : Real64 rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
2800 16 : Tank.Mass = Tank.Volume * rho;
2801 16 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
2802 16 : if (Tank.Height == DataSizing::AutoSize) {
2803 0 : Tank.HeightWasAutoSized = true;
2804 : }
2805 :
2806 16 : Tank.Shape = static_cast<TankShape>(getEnumValue(TankShapeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
2807 16 : switch (Tank.Shape) {
2808 16 : case TankShape::HorizCylinder:
2809 : case TankShape::VertCylinder: {
2810 16 : break;
2811 : }
2812 0 : case TankShape::Other: {
2813 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2814 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
2815 : } else {
2816 0 : ShowSevereError(state,
2817 0 : format("{} = {}: Tank Perimeter must be greater than zero for Tank Shape=OTHER",
2818 0 : state.dataIPShortCut->cCurrentModuleObject,
2819 0 : state.dataIPShortCut->cAlphaArgs(1)));
2820 0 : ErrorsFound = true;
2821 : }
2822 :
2823 0 : break;
2824 : }
2825 0 : default: {
2826 0 : ShowSevereError(state,
2827 0 : format("{} = {}: Invalid Tank Shape entered={}",
2828 0 : state.dataIPShortCut->cCurrentModuleObject,
2829 0 : state.dataIPShortCut->cAlphaArgs(1),
2830 0 : state.dataIPShortCut->cAlphaArgs(3)));
2831 0 : Tank.Shape = TankShape::VertCylinder;
2832 0 : ErrorsFound = true;
2833 0 : break;
2834 : }
2835 : }
2836 :
2837 16 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
2838 16 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(4);
2839 : } else {
2840 : // Default to very large number
2841 0 : Tank.TankTempLimit = 1.0e9;
2842 : }
2843 :
2844 : // Validate Heater Priority Control
2845 16 : Tank.StratifiedControlMode =
2846 16 : static_cast<PriorityControlMode>(getEnumValue(PriorityControlModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(4))));
2847 16 : if (Tank.StratifiedControlMode == PriorityControlMode::Invalid) {
2848 0 : ShowSevereError(state,
2849 0 : format("{} = {}: Invalid Heater Priority Control entered={}",
2850 0 : state.dataIPShortCut->cCurrentModuleObject,
2851 0 : state.dataIPShortCut->cAlphaArgs(1),
2852 0 : state.dataIPShortCut->cAlphaArgs(4)));
2853 0 : ErrorsFound = true;
2854 : }
2855 :
2856 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(5)) {
2857 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5));
2858 0 : ErrorsFound = true;
2859 16 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(5))) == nullptr) {
2860 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5));
2861 0 : ErrorsFound = true;
2862 : }
2863 :
2864 16 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2865 16 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(5);
2866 : } else {
2867 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2868 0 : Tank.DeadBandDeltaTemp = 0.0001;
2869 : }
2870 :
2871 16 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(6);
2872 16 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2873 2 : Tank.MaxCapacityWasAutoSized = true;
2874 : }
2875 :
2876 16 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(7);
2877 :
2878 : // adjust tank height used in these calculations for testing heater height
2879 : Real64 tankHeightForTesting;
2880 16 : if (Tank.Shape == TankShape::HorizCylinder) {
2881 0 : tankHeightForTesting = 2.0 * sqrt((Tank.Volume / Tank.Height) / Constant::Pi);
2882 : } else {
2883 16 : tankHeightForTesting = Tank.Height;
2884 : }
2885 :
2886 : // Test if Heater height is within range
2887 16 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight1 > tankHeightForTesting)) {
2888 0 : ShowSevereError(state,
2889 0 : format("{} = {}: Heater 1 is located higher than overall tank height.",
2890 0 : state.dataIPShortCut->cCurrentModuleObject,
2891 0 : state.dataIPShortCut->cAlphaArgs(1)));
2892 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2893 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(7), state.dataIPShortCut->rNumericArgs(7)));
2894 0 : ErrorsFound = true;
2895 : }
2896 :
2897 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(6)) {
2898 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(6));
2899 0 : ErrorsFound = true;
2900 16 : } else if ((Tank.setptTemp2Sched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(6))) == nullptr) {
2901 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6));
2902 0 : ErrorsFound = true;
2903 : }
2904 :
2905 16 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2906 16 : Tank.DeadBandDeltaTemp2 = state.dataIPShortCut->rNumericArgs(8);
2907 : } else {
2908 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2909 0 : Tank.DeadBandDeltaTemp2 = 0.0001;
2910 : }
2911 :
2912 16 : Tank.MaxCapacity2 = state.dataIPShortCut->rNumericArgs(9);
2913 16 : Tank.HeaterHeight2 = state.dataIPShortCut->rNumericArgs(10);
2914 :
2915 : // Test if Heater height is within range
2916 16 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight2 > tankHeightForTesting)) {
2917 0 : ShowSevereError(state,
2918 0 : format("{} = {}: Heater 2 is located higher than overall tank height.",
2919 0 : state.dataIPShortCut->cCurrentModuleObject,
2920 0 : state.dataIPShortCut->cAlphaArgs(1)));
2921 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2922 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
2923 0 : ErrorsFound = true;
2924 : }
2925 :
2926 : // Validate Heater Fuel Type
2927 16 : Tank.FuelType = static_cast<Constant::eFuel>(
2928 16 : getEnumValue(Constant::eFuelNamesUC,
2929 16 : state.dataIPShortCut->cAlphaArgs(
2930 : 7))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
2931 16 : if (Tank.FuelType == Constant::eFuel::Invalid) {
2932 0 : ShowSevereError(state,
2933 0 : format("{} = {}: Invalid Heater Fuel Type entered={}",
2934 0 : state.dataIPShortCut->cCurrentModuleObject,
2935 0 : state.dataIPShortCut->cAlphaArgs(1),
2936 0 : state.dataIPShortCut->cAlphaArgs(7)));
2937 : // Set to Electric to avoid errors when setting up output variables
2938 0 : Tank.FuelType = Constant::eFuel::Electricity;
2939 0 : ErrorsFound = true;
2940 : }
2941 :
2942 16 : if (state.dataIPShortCut->rNumericArgs(11) > 0.0) {
2943 16 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(11);
2944 16 : if (state.dataIPShortCut->rNumericArgs(11) > 1.0) {
2945 0 : ShowWarningError(state,
2946 0 : fmt::format("{} = {}: {}={} should not typically be greater than 1.",
2947 0 : state.dataIPShortCut->cCurrentModuleObject,
2948 0 : state.dataIPShortCut->cAlphaArgs(1),
2949 0 : state.dataIPShortCut->cNumericFieldNames(11),
2950 0 : state.dataIPShortCut->rNumericArgs(11)));
2951 : }
2952 : } else {
2953 0 : ShowSevereError(state,
2954 0 : format("{} = {}: Heater Thermal Efficiency must be greater than zero",
2955 0 : state.dataIPShortCut->cCurrentModuleObject,
2956 0 : state.dataIPShortCut->cAlphaArgs(1)));
2957 0 : ErrorsFound = true;
2958 : }
2959 :
2960 16 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(12);
2961 :
2962 : // Validate Off-Cycle Parasitic Fuel Type
2963 16 : Tank.OffCycParaFuelType = static_cast<Constant::eFuel>(
2964 16 : getEnumValue(Constant::eFuelNamesUC,
2965 16 : state.dataIPShortCut->cAlphaArgs(
2966 : 8))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
2967 16 : if (Tank.OffCycParaFuelType == Constant::eFuel::Invalid) {
2968 0 : if (state.dataIPShortCut->cAlphaArgs(8).empty()) {
2969 0 : Tank.OffCycParaFuelType = Tank.FuelType;
2970 : } else {
2971 0 : ShowSevereError(state,
2972 0 : format("{} = {}: Invalid Off-Cycle Parasitic Fuel Type entered={}",
2973 0 : state.dataIPShortCut->cCurrentModuleObject,
2974 0 : state.dataIPShortCut->cAlphaArgs(1),
2975 0 : state.dataIPShortCut->cAlphaArgs(8)));
2976 : // Set to Electric to avoid errors when setting up output variables
2977 0 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
2978 0 : ErrorsFound = true;
2979 : }
2980 : }
2981 :
2982 16 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(13);
2983 16 : Tank.OffCycParaHeight = state.dataIPShortCut->rNumericArgs(14);
2984 :
2985 16 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(15);
2986 :
2987 : // Validate On-Cycle Parasitic Fuel Type
2988 16 : Tank.OnCycParaFuelType = static_cast<Constant::eFuel>(
2989 16 : getEnumValue(Constant::eFuelNamesUC,
2990 16 : state.dataIPShortCut->cAlphaArgs(
2991 : 9))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported/empty
2992 16 : if (Tank.OnCycParaFuelType == Constant::eFuel::Invalid) {
2993 0 : if (state.dataIPShortCut->cAlphaArgs(9).empty()) {
2994 0 : Tank.OnCycParaFuelType = Tank.FuelType;
2995 : } else {
2996 0 : ShowSevereError(state,
2997 0 : format("{} = {}: Invalid On-Cycle Parasitic Fuel Type entered={}",
2998 0 : state.dataIPShortCut->cCurrentModuleObject,
2999 0 : state.dataIPShortCut->cAlphaArgs(1),
3000 0 : state.dataIPShortCut->cAlphaArgs(9)));
3001 : // Set to Electric to avoid errors when setting up output variables
3002 0 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3003 0 : ErrorsFound = true;
3004 : }
3005 : }
3006 :
3007 16 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(16);
3008 16 : Tank.OnCycParaHeight = state.dataIPShortCut->rNumericArgs(17);
3009 :
3010 16 : Tank.AmbientTempIndicator =
3011 16 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(10))));
3012 16 : switch (Tank.AmbientTempIndicator) {
3013 :
3014 8 : case WTTAmbientTemp::Schedule: {
3015 8 : if (state.dataIPShortCut->lAlphaFieldBlanks(11)) {
3016 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(11));
3017 0 : ErrorsFound = true;
3018 8 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(11))) == nullptr) {
3019 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(11), state.dataIPShortCut->cAlphaArgs(11));
3020 0 : ErrorsFound = true;
3021 : }
3022 8 : } break;
3023 :
3024 6 : case WTTAmbientTemp::TempZone: {
3025 6 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(12), state.dataHeatBal->Zone);
3026 6 : if (Tank.AmbientTempZone == 0) {
3027 0 : ShowSevereError(state,
3028 0 : format("{} = {}: Ambient Temperature Zone not found = {}",
3029 0 : state.dataIPShortCut->cCurrentModuleObject,
3030 0 : state.dataIPShortCut->cAlphaArgs(1),
3031 0 : state.dataIPShortCut->cAlphaArgs(12)));
3032 0 : ErrorsFound = true;
3033 : }
3034 :
3035 6 : break;
3036 : }
3037 2 : case WTTAmbientTemp::OutsideAir: {
3038 2 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3039 2 : state.dataIPShortCut->cAlphaArgs(13),
3040 : ErrorsFound,
3041 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3042 2 : state.dataIPShortCut->cAlphaArgs(1),
3043 : DataLoopNode::NodeFluidType::Air,
3044 : DataLoopNode::ConnectionType::Inlet,
3045 : NodeInputManager::CompFluidStream::Primary,
3046 : DataLoopNode::ObjectIsNotParent);
3047 2 : if (!state.dataIPShortCut->cAlphaArgs(13).empty()) {
3048 2 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3049 0 : ShowSevereError(state,
3050 0 : format("{} = {}: Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node",
3051 0 : state.dataIPShortCut->cCurrentModuleObject,
3052 0 : state.dataIPShortCut->cAlphaArgs(1)));
3053 0 : ShowContinueError(state, format("...Referenced Node Name={}", state.dataIPShortCut->cAlphaArgs(13)));
3054 0 : ErrorsFound = true;
3055 : }
3056 : } else {
3057 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3058 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3059 0 : ErrorsFound = true;
3060 : }
3061 :
3062 2 : break;
3063 : }
3064 0 : default: {
3065 0 : ShowSevereError(state,
3066 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3067 0 : state.dataIPShortCut->cCurrentModuleObject,
3068 0 : state.dataIPShortCut->cAlphaArgs(1),
3069 0 : state.dataIPShortCut->cAlphaArgs(10)));
3070 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3071 0 : ErrorsFound = true;
3072 0 : break;
3073 : }
3074 : }
3075 :
3076 16 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(18);
3077 16 : Tank.SkinLossFracToZone = state.dataIPShortCut->rNumericArgs(19);
3078 16 : Tank.OffCycFlueLossCoeff = state.dataIPShortCut->rNumericArgs(20);
3079 16 : Tank.OffCycFlueLossFracToZone = state.dataIPShortCut->rNumericArgs(21);
3080 :
3081 : // this is temporary until we know fluid type
3082 16 : rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
3083 :
3084 16 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(22) * rho;
3085 :
3086 16 : if ((state.dataIPShortCut->cAlphaArgs(16).empty()) && (state.dataIPShortCut->cAlphaArgs(17).empty())) {
3087 7 : if (state.dataIPShortCut->lAlphaFieldBlanks(14)) {
3088 7 : } else if ((Tank.flowRateSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(14))) == nullptr) {
3089 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(14), state.dataIPShortCut->cAlphaArgs(14));
3090 0 : ErrorsFound = true;
3091 : }
3092 : }
3093 :
3094 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(15)) {
3095 0 : } else if ((Tank.useInletTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(15))) == nullptr) {
3096 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(15), state.dataIPShortCut->cAlphaArgs(15));
3097 0 : ErrorsFound = true;
3098 : }
3099 :
3100 16 : if (NumNums > 22) {
3101 16 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(23);
3102 : } else {
3103 0 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
3104 : }
3105 :
3106 16 : if (NumNums > 23) {
3107 16 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(24);
3108 : } else {
3109 : // Defaults to bottom of tank
3110 0 : Tank.UseInletHeight = 0.0;
3111 : }
3112 16 : if ((!Tank.HeightWasAutoSized) && (Tank.UseInletHeight > Tank.Height)) {
3113 0 : ShowSevereError(state,
3114 0 : format("{} = {}: Use inlet is located higher than overall tank height.",
3115 0 : state.dataIPShortCut->cCurrentModuleObject,
3116 0 : state.dataIPShortCut->cAlphaArgs(1)));
3117 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3118 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(24), state.dataIPShortCut->rNumericArgs(24)));
3119 0 : ErrorsFound = true;
3120 : }
3121 :
3122 16 : if ((NumNums > 24) && (state.dataIPShortCut->rNumericArgs(25) != Constant::AutoCalculate)) {
3123 1 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(25);
3124 : } else {
3125 : // Defaults to top of tank
3126 15 : Tank.UseOutletHeight = Tank.Height;
3127 : }
3128 16 : if (Tank.UseOutletHeight == DataSizing::AutoSize) {
3129 0 : Tank.UseOutletHeightWasAutoSized = true;
3130 : }
3131 16 : if ((!Tank.HeightWasAutoSized) && (Tank.UseOutletHeight > Tank.Height)) {
3132 0 : ShowSevereError(state,
3133 0 : format("{} = {}: Use outlet is located higher than overall tank height.",
3134 0 : state.dataIPShortCut->cCurrentModuleObject,
3135 0 : state.dataIPShortCut->cAlphaArgs(1)));
3136 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3137 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(25), state.dataIPShortCut->rNumericArgs(25)));
3138 0 : ErrorsFound = true;
3139 : }
3140 :
3141 16 : if (NumNums > 25) {
3142 16 : if ((state.dataIPShortCut->rNumericArgs(26) > 1) || (state.dataIPShortCut->rNumericArgs(26) <= 0)) {
3143 0 : ShowSevereError(state,
3144 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3145 0 : state.dataIPShortCut->cCurrentModuleObject,
3146 0 : state.dataIPShortCut->cAlphaArgs(1)));
3147 0 : ErrorsFound = true;
3148 : }
3149 16 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(26);
3150 : } else {
3151 0 : Tank.SourceEffectiveness = 1.0;
3152 : }
3153 :
3154 16 : if ((NumNums > 26) && (state.dataIPShortCut->rNumericArgs(27) != Constant::AutoCalculate)) {
3155 11 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(27);
3156 : } else {
3157 : // Defaults to top of tank
3158 5 : Tank.SourceInletHeight = Tank.Height;
3159 : }
3160 16 : if (Tank.SourceInletHeight == DataSizing::AutoSize) {
3161 0 : Tank.SourceInletHeightWasAutoSized = true;
3162 : }
3163 16 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceInletHeight > Tank.Height)) {
3164 0 : ShowSevereError(state,
3165 0 : format("{} = {}: Source inlet is located higher than overall tank height.",
3166 0 : state.dataIPShortCut->cCurrentModuleObject,
3167 0 : state.dataIPShortCut->cAlphaArgs(1)));
3168 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3169 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(27), state.dataIPShortCut->rNumericArgs(27)));
3170 0 : ErrorsFound = true;
3171 : }
3172 :
3173 16 : if ((NumNums > 27) && (state.dataIPShortCut->rNumericArgs(28) != Constant::AutoCalculate)) {
3174 16 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(28);
3175 : } else {
3176 : // Defaults to bottom of tank
3177 0 : Tank.SourceOutletHeight = 0.0;
3178 : }
3179 16 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceOutletHeight > Tank.Height)) {
3180 0 : ShowSevereError(state,
3181 0 : format("{} = {}: Source outlet is located higher than overall tank height.",
3182 0 : state.dataIPShortCut->cCurrentModuleObject,
3183 0 : state.dataIPShortCut->cAlphaArgs(1)));
3184 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3185 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(28), state.dataIPShortCut->rNumericArgs(28)));
3186 0 : ErrorsFound = true;
3187 : }
3188 :
3189 : // If no plant nodes are connected, simulate in stand-alone mode.
3190 30 : if (state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty() &&
3191 30 : state.dataIPShortCut->cAlphaArgs(18).empty() && state.dataIPShortCut->cAlphaArgs(19).empty()) {
3192 2 : Tank.StandAlone = true;
3193 : }
3194 :
3195 16 : if (!state.dataIPShortCut->lNumericFieldBlanks(29)) {
3196 9 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(29);
3197 9 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3198 9 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3199 : }
3200 : } else {
3201 7 : Tank.UseDesignVolFlowRate = 0.0;
3202 : }
3203 :
3204 16 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3205 :
3206 16 : if (!state.dataIPShortCut->lNumericFieldBlanks(30)) {
3207 6 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(30);
3208 6 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3209 4 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3210 : }
3211 : } else {
3212 10 : Tank.SourceDesignVolFlowRate = 0.0;
3213 : }
3214 :
3215 16 : if (NumNums > 30) {
3216 16 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(31);
3217 : } else {
3218 0 : Tank.SizingRecoveryTime = 1.5;
3219 : }
3220 :
3221 16 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3222 :
3223 16 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
3224 9 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3225 9 : state.dataIPShortCut->cAlphaArgs(16),
3226 : ErrorsFound,
3227 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3228 9 : state.dataIPShortCut->cAlphaArgs(1),
3229 : DataLoopNode::NodeFluidType::Water,
3230 : DataLoopNode::ConnectionType::Inlet,
3231 : NodeInputManager::CompFluidStream::Primary,
3232 : DataLoopNode::ObjectIsNotParent);
3233 9 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(16);
3234 9 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3235 9 : state.dataIPShortCut->cAlphaArgs(17),
3236 : ErrorsFound,
3237 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3238 9 : state.dataIPShortCut->cAlphaArgs(1),
3239 : DataLoopNode::NodeFluidType::Water,
3240 : DataLoopNode::ConnectionType::Outlet,
3241 : NodeInputManager::CompFluidStream::Primary,
3242 : DataLoopNode::ObjectIsNotParent);
3243 9 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(17);
3244 :
3245 9 : if (state.dataIPShortCut->rNumericArgs(22) > 0) {
3246 0 : ShowWarningError(state,
3247 0 : format("{} = {}: Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used",
3248 0 : state.dataIPShortCut->cCurrentModuleObject,
3249 0 : state.dataIPShortCut->cAlphaArgs(1)));
3250 : }
3251 :
3252 9 : if (Tank.flowRateSched != nullptr) {
3253 0 : ShowWarningError(state,
3254 0 : format("{} = {}: Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used",
3255 0 : state.dataIPShortCut->cCurrentModuleObject,
3256 0 : state.dataIPShortCut->cAlphaArgs(1)));
3257 : }
3258 :
3259 9 : if (Tank.useInletTempSched != nullptr) {
3260 0 : ShowWarningError(state,
3261 0 : format("{} = {}: Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used",
3262 0 : state.dataIPShortCut->cCurrentModuleObject,
3263 0 : state.dataIPShortCut->cAlphaArgs(1)));
3264 : }
3265 : }
3266 :
3267 16 : if ((!state.dataIPShortCut->cAlphaArgs(18).empty()) || (!state.dataIPShortCut->cAlphaArgs(19).empty())) {
3268 10 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3269 10 : state.dataIPShortCut->cAlphaArgs(18),
3270 : ErrorsFound,
3271 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3272 10 : state.dataIPShortCut->cAlphaArgs(1),
3273 : DataLoopNode::NodeFluidType::Water,
3274 : DataLoopNode::ConnectionType::Inlet,
3275 : NodeInputManager::CompFluidStream::Secondary,
3276 : DataLoopNode::ObjectIsNotParent);
3277 10 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(18);
3278 10 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3279 10 : state.dataIPShortCut->cAlphaArgs(19),
3280 : ErrorsFound,
3281 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3282 10 : state.dataIPShortCut->cAlphaArgs(1),
3283 : DataLoopNode::NodeFluidType::Water,
3284 : DataLoopNode::ConnectionType::Outlet,
3285 : NodeInputManager::CompFluidStream::Secondary,
3286 : DataLoopNode::ObjectIsNotParent);
3287 10 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(19);
3288 : }
3289 :
3290 : // Validate inlet mode
3291 16 : Tank.InletMode =
3292 16 : static_cast<InletPositionMode>(getEnumValue(InletPositionModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(20))));
3293 :
3294 16 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(32);
3295 16 : int specifiedNodes = 0;
3296 16 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(33);
3297 :
3298 16 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
3299 16 : Tank.AdditionalLossCoeff = 0.0;
3300 44 : for (int NodeNum = 1; NodeNum <= 12; ++NodeNum) {
3301 44 : int index = 33 + NodeNum;
3302 44 : if (NumNums >= index) {
3303 28 : if (NodeNum <= Tank.Nodes) {
3304 28 : ++specifiedNodes;
3305 28 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(index);
3306 0 : } else if (!state.dataIPShortCut->lNumericFieldBlanks(index) && (state.dataIPShortCut->rNumericArgs(index) != 0)) {
3307 : // If either blank, or zero (default), then do not warn
3308 0 : ++specifiedNodes;
3309 : }
3310 : } else {
3311 16 : break;
3312 : }
3313 : }
3314 :
3315 16 : if (specifiedNodes > Tank.Nodes) {
3316 0 : ShowWarningError(
3317 : state,
3318 0 : format("{} = {}: More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used",
3319 0 : state.dataIPShortCut->cCurrentModuleObject,
3320 0 : state.dataIPShortCut->cAlphaArgs(1)));
3321 : }
3322 :
3323 16 : Tank.SetupStratifiedNodes(state);
3324 :
3325 16 : if (!state.dataIPShortCut->lAlphaFieldBlanks(21)) {
3326 0 : Tank.SourceSideControlMode =
3327 0 : static_cast<SourceSideControl>(getEnumValue(SourceSideControlNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(21))));
3328 0 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
3329 0 : ShowSevereError(state,
3330 0 : format("{} = {}: Invalid Control Mode entered={}",
3331 0 : state.dataIPShortCut->cCurrentModuleObject,
3332 0 : state.dataIPShortCut->cAlphaArgs(1),
3333 0 : state.dataIPShortCut->cAlphaArgs(21)));
3334 0 : ErrorsFound = true;
3335 : }
3336 : } else {
3337 16 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
3338 : }
3339 :
3340 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(22)) {
3341 0 : } else if ((Tank.sourceSideAltSetpointSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(22))) == nullptr) {
3342 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(22), state.dataIPShortCut->cAlphaArgs(22));
3343 0 : ErrorsFound = true;
3344 : }
3345 : }
3346 :
3347 10 : return ErrorsFound;
3348 : }
3349 :
3350 4 : bool getWaterTankMixedInput(EnergyPlusData &state)
3351 : {
3352 : static constexpr std::string_view routineName = "getWaterTankMixedInput";
3353 4 : bool ErrorsFound = false;
3354 :
3355 4 : state.dataIPShortCut->cCurrentModuleObject = cMixedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Mixed'
3356 8 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified + 1;
3357 8 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3358 8 : state.dataWaterThermalTanks->numChilledWaterMixed;
3359 : ++WaterThermalTankNum) {
3360 : int NumAlphas;
3361 : int NumNums;
3362 : int IOStat;
3363 8 : state.dataInputProcessing->inputProcessor->getObjectItem(
3364 : state,
3365 4 : state.dataIPShortCut->cCurrentModuleObject,
3366 4 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified),
3367 4 : state.dataIPShortCut->cAlphaArgs,
3368 : NumAlphas,
3369 4 : state.dataIPShortCut->rNumericArgs,
3370 : NumNums,
3371 : IOStat,
3372 4 : state.dataIPShortCut->lNumericFieldBlanks,
3373 4 : state.dataIPShortCut->lAlphaFieldBlanks,
3374 4 : state.dataIPShortCut->cAlphaFieldNames,
3375 4 : state.dataIPShortCut->cNumericFieldNames);
3376 :
3377 4 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
3378 :
3379 4 : GlobalNames::VerifyUniqueInterObjectName(state,
3380 4 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3381 4 : state.dataIPShortCut->cAlphaArgs(1),
3382 4 : state.dataIPShortCut->cCurrentModuleObject,
3383 4 : state.dataIPShortCut->cAlphaFieldNames(1),
3384 : ErrorsFound);
3385 :
3386 4 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3387 :
3388 4 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3389 4 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3390 4 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankMixed;
3391 :
3392 4 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
3393 0 : ShowSevereError(state, "Fluid Properties for WATER not found");
3394 0 : ErrorsFound = true;
3395 : }
3396 :
3397 4 : Tank.IsChilledWaterTank = true;
3398 4 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3399 :
3400 4 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3401 4 : if (Tank.Volume == DataSizing::AutoSize) {
3402 0 : Tank.VolumeWasAutoSized = true;
3403 : }
3404 :
3405 4 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
3406 : // Set volume to a really small number to continue simulation
3407 0 : Tank.Volume = 0.000001; // = 1 cm3
3408 : }
3409 :
3410 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
3411 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2));
3412 4 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
3413 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
3414 0 : ErrorsFound = true;
3415 : }
3416 :
3417 4 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
3418 4 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
3419 : } else {
3420 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3421 0 : Tank.DeadBandDeltaTemp = 0.5;
3422 : }
3423 :
3424 4 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3425 4 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
3426 : } else {
3427 : // default to just above freezing
3428 0 : Tank.TankTempLimit = 1.0;
3429 : }
3430 :
3431 4 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
3432 4 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3433 0 : Tank.MaxCapacityWasAutoSized = true;
3434 : }
3435 :
3436 4 : Tank.MinCapacity = 0.0;
3437 4 : Tank.ControlType = HeaterControlMode::Cycle;
3438 :
3439 4 : Tank.MassFlowRateMin = 0.0;
3440 4 : Tank.IgnitionDelay = 0.0;
3441 4 : Tank.FuelType = Constant::eFuel::Electricity;
3442 4 : Tank.Efficiency = 1.0;
3443 4 : Tank.PLFCurve = 0;
3444 4 : Tank.OffCycParaLoad = 0.0;
3445 4 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
3446 4 : Tank.OffCycParaFracToTank = 0.0;
3447 4 : Tank.OnCycParaLoad = 0.0;
3448 4 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3449 4 : Tank.OnCycParaFracToTank = 0.0;
3450 :
3451 4 : Tank.AmbientTempIndicator =
3452 4 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
3453 4 : switch (Tank.AmbientTempIndicator) {
3454 :
3455 0 : case WTTAmbientTemp::Schedule: {
3456 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(4)) {
3457 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(4));
3458 0 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(4))) == nullptr) {
3459 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4));
3460 0 : ErrorsFound = true;
3461 : }
3462 0 : } break;
3463 :
3464 3 : case WTTAmbientTemp::TempZone: {
3465 3 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(5), state.dataHeatBal->Zone);
3466 3 : if (Tank.AmbientTempZone == 0) {
3467 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
3468 0 : ShowContinueError(state,
3469 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3470 0 : ShowContinueError(state, "Zone was not found.");
3471 0 : ErrorsFound = true;
3472 : }
3473 :
3474 3 : break;
3475 : }
3476 1 : case WTTAmbientTemp::OutsideAir: {
3477 1 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3478 1 : state.dataIPShortCut->cAlphaArgs(6),
3479 : ErrorsFound,
3480 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3481 1 : state.dataIPShortCut->cAlphaArgs(1),
3482 : DataLoopNode::NodeFluidType::Air,
3483 : DataLoopNode::ConnectionType::OutsideAirReference,
3484 : NodeInputManager::CompFluidStream::Primary,
3485 : DataLoopNode::ObjectIsNotParent);
3486 1 : if (!state.dataIPShortCut->lAlphaFieldBlanks(6)) {
3487 1 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3488 0 : ShowSevereError(state,
3489 0 : format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)));
3490 0 : ShowContinueError(state,
3491 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3492 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3493 0 : ErrorsFound = true;
3494 : }
3495 : } else {
3496 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3497 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3498 0 : ErrorsFound = true;
3499 : }
3500 :
3501 1 : break;
3502 : }
3503 0 : default: {
3504 0 : ShowSevereError(state,
3505 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3506 0 : state.dataIPShortCut->cCurrentModuleObject,
3507 0 : state.dataIPShortCut->cAlphaArgs(1),
3508 0 : state.dataIPShortCut->cAlphaArgs(3)));
3509 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3510 0 : ErrorsFound = true;
3511 0 : break;
3512 : }
3513 : }
3514 :
3515 4 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3516 4 : Tank.OffCycLossFracToZone = 1.0;
3517 :
3518 4 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3519 4 : Tank.OnCycLossFracToZone = 1.0;
3520 :
3521 4 : Tank.MassFlowRateMax = 0.0;
3522 4 : Tank.flowRateSched = nullptr;
3523 4 : Tank.useInletTempSched = nullptr;
3524 :
3525 : // default to always on
3526 4 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3527 4 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3528 :
3529 4 : if ((state.dataIPShortCut->rNumericArgs(6) > 1) || (state.dataIPShortCut->rNumericArgs(6) < 0)) {
3530 0 : ShowSevereError(state,
3531 0 : format("{} = {}: Use Side Effectiveness is out of bounds (0 to 1)",
3532 0 : state.dataIPShortCut->cCurrentModuleObject,
3533 0 : state.dataIPShortCut->cAlphaArgs(1)));
3534 0 : ErrorsFound = true;
3535 : }
3536 4 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(6);
3537 :
3538 4 : if ((state.dataIPShortCut->rNumericArgs(8) > 1) || (state.dataIPShortCut->rNumericArgs(8) <= 0)) {
3539 0 : ShowSevereError(state,
3540 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3541 0 : state.dataIPShortCut->cCurrentModuleObject,
3542 0 : state.dataIPShortCut->cAlphaArgs(1)));
3543 0 : ErrorsFound = true;
3544 : }
3545 4 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(8);
3546 :
3547 4 : if (state.dataIPShortCut->lNumericFieldBlanks(7)) {
3548 0 : Tank.UseDesignVolFlowRate = 0.0;
3549 : } else {
3550 4 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(7);
3551 4 : if (Tank.UseDesignVolFlowRate) {
3552 4 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3553 : }
3554 : }
3555 :
3556 4 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3557 :
3558 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(9)) {
3559 0 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3560 4 : } else if ((Tank.useSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(9))) == nullptr) {
3561 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(9), state.dataIPShortCut->cAlphaArgs(9));
3562 0 : ErrorsFound = true;
3563 : }
3564 :
3565 4 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3566 :
3567 4 : if (state.dataIPShortCut->lNumericFieldBlanks(9)) {
3568 1 : Tank.SourceDesignVolFlowRate = 0.0;
3569 : } else {
3570 3 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(9);
3571 3 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3572 3 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3573 : }
3574 : }
3575 :
3576 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(12)) {
3577 1 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3578 3 : } else if ((Tank.sourceSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(12))) == nullptr) {
3579 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(12), state.dataIPShortCut->cAlphaArgs(12));
3580 0 : ErrorsFound = true;
3581 : }
3582 :
3583 4 : if (state.dataIPShortCut->lNumericFieldBlanks(10)) {
3584 1 : Tank.SizingRecoveryTime = 4.0;
3585 : } else {
3586 3 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(10);
3587 : }
3588 :
3589 4 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(7)) || (!state.dataIPShortCut->lAlphaFieldBlanks(8))) {
3590 4 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3591 4 : state.dataIPShortCut->cAlphaArgs(7),
3592 : ErrorsFound,
3593 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3594 4 : state.dataIPShortCut->cAlphaArgs(1),
3595 : DataLoopNode::NodeFluidType::Water,
3596 : DataLoopNode::ConnectionType::Inlet,
3597 : NodeInputManager::CompFluidStream::Primary,
3598 : DataLoopNode::ObjectIsNotParent);
3599 4 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(7);
3600 4 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3601 4 : state.dataIPShortCut->cAlphaArgs(8),
3602 : ErrorsFound,
3603 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3604 4 : state.dataIPShortCut->cAlphaArgs(1),
3605 : DataLoopNode::NodeFluidType::Water,
3606 : DataLoopNode::ConnectionType::Outlet,
3607 : NodeInputManager::CompFluidStream::Primary,
3608 : DataLoopNode::ObjectIsNotParent);
3609 4 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
3610 : }
3611 :
3612 4 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(10)) || (!state.dataIPShortCut->lAlphaFieldBlanks(11))) {
3613 3 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3614 3 : state.dataIPShortCut->cAlphaArgs(10),
3615 : ErrorsFound,
3616 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3617 3 : state.dataIPShortCut->cAlphaArgs(1),
3618 : DataLoopNode::NodeFluidType::Water,
3619 : DataLoopNode::ConnectionType::Inlet,
3620 : NodeInputManager::CompFluidStream::Secondary,
3621 : DataLoopNode::ObjectIsNotParent);
3622 3 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(10);
3623 3 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3624 3 : state.dataIPShortCut->cAlphaArgs(11),
3625 : ErrorsFound,
3626 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3627 3 : state.dataIPShortCut->cAlphaArgs(1),
3628 : DataLoopNode::NodeFluidType::Water,
3629 : DataLoopNode::ConnectionType::Outlet,
3630 : NodeInputManager::CompFluidStream::Secondary,
3631 : DataLoopNode::ObjectIsNotParent);
3632 3 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
3633 : }
3634 :
3635 4 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
3636 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
3637 : }
3638 :
3639 : } // WaterThermalTankNum
3640 :
3641 4 : return ErrorsFound;
3642 : }
3643 :
3644 2 : bool getWaterTankStratifiedInput(EnergyPlusData &state)
3645 : {
3646 2 : bool ErrorsFound = false;
3647 : static constexpr std::string_view routineName = "getWaterTankStratifiedInput";
3648 :
3649 2 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Stratified'
3650 :
3651 4 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3652 2 : state.dataWaterThermalTanks->numChilledWaterMixed + 1;
3653 4 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3654 4 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
3655 : ++WaterThermalTankNum) {
3656 : int NumNums;
3657 : int NumAlphas;
3658 : int IOStat;
3659 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3660 2 : state.dataIPShortCut->cCurrentModuleObject,
3661 2 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed +
3662 2 : state.dataWaterThermalTanks->numWaterHeaterStratified +
3663 2 : state.dataWaterThermalTanks->numChilledWaterMixed),
3664 2 : state.dataIPShortCut->cAlphaArgs,
3665 : NumAlphas,
3666 2 : state.dataIPShortCut->rNumericArgs,
3667 : NumNums,
3668 : IOStat,
3669 2 : state.dataIPShortCut->lNumericFieldBlanks,
3670 2 : state.dataIPShortCut->lAlphaFieldBlanks,
3671 2 : state.dataIPShortCut->cAlphaFieldNames,
3672 2 : state.dataIPShortCut->cNumericFieldNames);
3673 :
3674 2 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
3675 :
3676 2 : GlobalNames::VerifyUniqueInterObjectName(state,
3677 2 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3678 2 : state.dataIPShortCut->cAlphaArgs(1),
3679 2 : state.dataIPShortCut->cCurrentModuleObject,
3680 2 : state.dataIPShortCut->cAlphaFieldNames(1),
3681 : ErrorsFound);
3682 :
3683 2 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3684 :
3685 2 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3686 2 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3687 2 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankStratified;
3688 :
3689 2 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
3690 0 : ShowSevereError(state, "Fluid properties for WATER not found");
3691 0 : ErrorsFound = true;
3692 : }
3693 :
3694 2 : Tank.IsChilledWaterTank = true;
3695 2 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3696 :
3697 2 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3698 2 : if (Tank.Volume == DataSizing::AutoSize) {
3699 0 : Tank.VolumeWasAutoSized = true;
3700 : }
3701 :
3702 2 : Real64 rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
3703 :
3704 2 : Tank.Mass = Tank.Volume * rho;
3705 2 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
3706 2 : if (Tank.Height == DataSizing::AutoSize) {
3707 0 : Tank.HeightWasAutoSized = true;
3708 : }
3709 :
3710 2 : Tank.Shape = static_cast<TankShape>(getEnumValue(TankShapeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(2))));
3711 2 : switch (Tank.Shape) {
3712 2 : case TankShape::HorizCylinder:
3713 : case TankShape::VertCylinder: {
3714 2 : break;
3715 : }
3716 0 : case TankShape::Other: {
3717 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3718 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
3719 : } else {
3720 0 : ShowSevereError(state,
3721 0 : format("{} = {}: Tank Perimeter must be greater than zero for Tank Shape=OTHER",
3722 0 : state.dataIPShortCut->cCurrentModuleObject,
3723 0 : state.dataIPShortCut->cAlphaArgs(1)));
3724 0 : ErrorsFound = true;
3725 : }
3726 0 : break;
3727 : }
3728 0 : default: {
3729 0 : ShowSevereError(state,
3730 0 : format("{} = {}: Invalid Tank Shape entered={}",
3731 0 : state.dataIPShortCut->cCurrentModuleObject,
3732 0 : state.dataIPShortCut->cAlphaArgs(1),
3733 0 : state.dataIPShortCut->cAlphaArgs(2)));
3734 0 : Tank.Shape = TankShape::VertCylinder;
3735 0 : ErrorsFound = true;
3736 0 : break;
3737 : }
3738 : }
3739 :
3740 2 : if (state.dataIPShortCut->rNumericArgs(6) > 0.0) {
3741 2 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(6);
3742 : } else {
3743 : // default to just above freezing
3744 0 : Tank.TankTempLimit = 1.0;
3745 : }
3746 :
3747 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(3)) {
3748 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(3));
3749 0 : ErrorsFound = true;
3750 2 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(3))) == nullptr) {
3751 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3));
3752 0 : ErrorsFound = true;
3753 : }
3754 :
3755 2 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
3756 2 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(4);
3757 : } else {
3758 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3759 0 : Tank.DeadBandDeltaTemp = 0.0001;
3760 : }
3761 :
3762 2 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(5);
3763 2 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(7);
3764 2 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3765 0 : Tank.MaxCapacityWasAutoSized = true;
3766 : }
3767 :
3768 2 : Tank.Efficiency = 1.0;
3769 2 : Tank.setptTemp2Sched = nullptr;
3770 2 : Tank.MaxCapacity2 = 0.0;
3771 2 : Tank.HeaterHeight2 = 0.0;
3772 2 : Tank.FuelType = Constant::eFuel::Electricity;
3773 :
3774 2 : Tank.OffCycParaLoad = 0.0;
3775 2 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
3776 2 : Tank.OffCycParaFracToTank = 0.0;
3777 2 : Tank.OffCycParaHeight = 0.0;
3778 2 : Tank.OnCycParaLoad = 0.0;
3779 2 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3780 2 : Tank.OnCycParaFracToTank = 0.0;
3781 2 : Tank.OnCycParaHeight = 0.0;
3782 :
3783 2 : Tank.AmbientTempIndicator =
3784 2 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(4))));
3785 2 : switch (Tank.AmbientTempIndicator) {
3786 :
3787 0 : case WTTAmbientTemp::Schedule: {
3788 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(5)) {
3789 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5));
3790 0 : ErrorsFound = true;
3791 0 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(5))) == nullptr) {
3792 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5));
3793 0 : ErrorsFound = true;
3794 : }
3795 0 : } break;
3796 :
3797 2 : case WTTAmbientTemp::TempZone: {
3798 2 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(6), state.dataHeatBal->Zone);
3799 2 : if (Tank.AmbientTempZone == 0) {
3800 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)));
3801 0 : ShowContinueError(state,
3802 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3803 0 : ShowContinueError(state, "Zone was not found.");
3804 0 : ErrorsFound = true;
3805 : }
3806 2 : Tank.OffCycLossFracToZone = 1.0;
3807 :
3808 2 : break;
3809 : }
3810 0 : case WTTAmbientTemp::OutsideAir: {
3811 0 : Tank.AmbientTempOutsideAirNode =
3812 0 : NodeInputManager::GetOnlySingleNode(state,
3813 0 : state.dataIPShortCut->cAlphaArgs(7),
3814 : ErrorsFound,
3815 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3816 0 : state.dataIPShortCut->cAlphaArgs(1),
3817 : DataLoopNode::NodeFluidType::Air,
3818 : DataLoopNode::ConnectionType::Inlet,
3819 : NodeInputManager::CompFluidStream::Primary,
3820 : DataLoopNode::ObjectIsNotParent);
3821 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(7)) {
3822 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3823 0 : ShowSevereError(state,
3824 0 : format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(7), state.dataIPShortCut->cAlphaArgs(7)));
3825 0 : ShowContinueError(state,
3826 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3827 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3828 0 : ErrorsFound = true;
3829 : }
3830 : } else {
3831 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3832 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3833 0 : ErrorsFound = true;
3834 : }
3835 :
3836 0 : break;
3837 : }
3838 0 : default: {
3839 0 : ShowSevereError(state,
3840 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3841 0 : state.dataIPShortCut->cCurrentModuleObject,
3842 0 : state.dataIPShortCut->cAlphaArgs(1),
3843 0 : state.dataIPShortCut->cAlphaArgs(4)));
3844 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3845 0 : ErrorsFound = true;
3846 0 : break;
3847 : }
3848 : }
3849 :
3850 2 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(8);
3851 2 : Tank.SkinLossFracToZone = 1.0;
3852 2 : Tank.OffCycFlueLossCoeff = 0.0;
3853 2 : Tank.OffCycFlueLossFracToZone = 0.0;
3854 :
3855 2 : Tank.MassFlowRateMax = 0.0;
3856 2 : Tank.flowRateSched = nullptr;
3857 2 : Tank.useInletTempSched = nullptr;
3858 2 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(9);
3859 2 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(10);
3860 :
3861 : // default to always on
3862 2 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3863 2 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3864 :
3865 2 : if (state.dataIPShortCut->rNumericArgs(10) == Constant::AutoCalculate) {
3866 0 : Tank.UseInletHeight = Tank.Height; // top of tank
3867 : }
3868 2 : if (Tank.UseInletHeight > Tank.Height) {
3869 0 : ShowSevereError(state,
3870 0 : format("{} = {}: Use inlet is located higher than overall tank height.",
3871 0 : state.dataIPShortCut->cCurrentModuleObject,
3872 0 : state.dataIPShortCut->cAlphaArgs(1)));
3873 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3874 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
3875 0 : ErrorsFound = true;
3876 : }
3877 :
3878 2 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(11);
3879 2 : if (Tank.UseOutletHeight > Tank.Height) {
3880 0 : ShowSevereError(state,
3881 0 : format("{} = {}: Use outlet is located higher than overall tank height.",
3882 0 : state.dataIPShortCut->cCurrentModuleObject,
3883 0 : state.dataIPShortCut->cAlphaArgs(1)));
3884 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3885 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(11), state.dataIPShortCut->rNumericArgs(11)));
3886 0 : ErrorsFound = true;
3887 : }
3888 :
3889 2 : if ((state.dataIPShortCut->rNumericArgs(13) > 1) || (state.dataIPShortCut->rNumericArgs(13) <= 0)) {
3890 0 : ShowSevereError(state,
3891 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3892 0 : state.dataIPShortCut->cCurrentModuleObject,
3893 0 : state.dataIPShortCut->cAlphaArgs(1)));
3894 0 : ErrorsFound = true;
3895 : }
3896 2 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(13);
3897 :
3898 2 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(14);
3899 2 : if (Tank.SourceInletHeight > Tank.Height) {
3900 0 : ShowSevereError(state,
3901 0 : format("{} = {}: Source inlet is located higher than overall tank height.",
3902 0 : state.dataIPShortCut->cCurrentModuleObject,
3903 0 : state.dataIPShortCut->cAlphaArgs(1)));
3904 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3905 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(14), state.dataIPShortCut->rNumericArgs(14)));
3906 0 : ErrorsFound = true;
3907 : }
3908 :
3909 2 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(15);
3910 2 : if (state.dataIPShortCut->rNumericArgs(15) == Constant::AutoCalculate) {
3911 0 : Tank.SourceOutletHeight = Tank.Height; // top of tank
3912 : }
3913 2 : if (Tank.SourceOutletHeight > Tank.Height) {
3914 0 : ShowSevereError(state,
3915 0 : format("{} = {}: Source outlet is located higher than overall tank height.",
3916 0 : state.dataIPShortCut->cCurrentModuleObject,
3917 0 : state.dataIPShortCut->cAlphaArgs(1)));
3918 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3919 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(15), state.dataIPShortCut->rNumericArgs(15)));
3920 0 : ErrorsFound = true;
3921 : }
3922 :
3923 2 : Tank.StandAlone = false;
3924 :
3925 2 : if (state.dataIPShortCut->lNumericFieldBlanks(12)) {
3926 0 : Tank.UseDesignVolFlowRate = 0.0;
3927 : } else {
3928 2 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(12);
3929 2 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3930 2 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3931 : }
3932 : }
3933 :
3934 2 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3935 :
3936 2 : if (state.dataIPShortCut->lNumericFieldBlanks(16)) {
3937 0 : Tank.SourceDesignVolFlowRate = 0.0;
3938 : } else {
3939 2 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(16);
3940 2 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3941 0 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3942 : }
3943 : }
3944 :
3945 2 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(17);
3946 :
3947 2 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3948 :
3949 2 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(8)) || (!state.dataIPShortCut->lAlphaFieldBlanks(9))) {
3950 2 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3951 2 : state.dataIPShortCut->cAlphaArgs(8),
3952 : ErrorsFound,
3953 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3954 2 : state.dataIPShortCut->cAlphaArgs(1),
3955 : DataLoopNode::NodeFluidType::Water,
3956 : DataLoopNode::ConnectionType::Inlet,
3957 : NodeInputManager::CompFluidStream::Primary,
3958 : DataLoopNode::ObjectIsNotParent);
3959 2 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
3960 2 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3961 2 : state.dataIPShortCut->cAlphaArgs(9),
3962 : ErrorsFound,
3963 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3964 2 : state.dataIPShortCut->cAlphaArgs(1),
3965 : DataLoopNode::NodeFluidType::Water,
3966 : DataLoopNode::ConnectionType::Outlet,
3967 : NodeInputManager::CompFluidStream::Primary,
3968 : DataLoopNode::ObjectIsNotParent);
3969 2 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(9);
3970 : }
3971 :
3972 2 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(11)) || (!state.dataIPShortCut->lAlphaFieldBlanks(12))) {
3973 1 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3974 1 : state.dataIPShortCut->cAlphaArgs(11),
3975 : ErrorsFound,
3976 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3977 1 : state.dataIPShortCut->cAlphaArgs(1),
3978 : DataLoopNode::NodeFluidType::Water,
3979 : DataLoopNode::ConnectionType::Inlet,
3980 : NodeInputManager::CompFluidStream::Secondary,
3981 : DataLoopNode::ObjectIsNotParent);
3982 1 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
3983 1 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3984 1 : state.dataIPShortCut->cAlphaArgs(12),
3985 : ErrorsFound,
3986 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3987 1 : state.dataIPShortCut->cAlphaArgs(1),
3988 : DataLoopNode::NodeFluidType::Water,
3989 : DataLoopNode::ConnectionType::Outlet,
3990 : NodeInputManager::CompFluidStream::Secondary,
3991 : DataLoopNode::ObjectIsNotParent);
3992 1 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(12);
3993 : }
3994 :
3995 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(10)) {
3996 0 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3997 2 : } else if ((Tank.useSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(10))) == nullptr) {
3998 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(10), state.dataIPShortCut->cAlphaArgs(10));
3999 0 : ErrorsFound = true;
4000 : }
4001 :
4002 2 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
4003 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
4004 : }
4005 :
4006 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(13)) {
4007 0 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
4008 2 : } else if ((Tank.sourceSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(13))) == nullptr) {
4009 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(13), state.dataIPShortCut->cAlphaArgs(13));
4010 0 : ErrorsFound = true;
4011 : }
4012 :
4013 : // Validate inlet mode
4014 2 : Tank.InletMode =
4015 2 : static_cast<InletPositionMode>(getEnumValue(InletPositionModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(14))));
4016 :
4017 2 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(18);
4018 2 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(19);
4019 :
4020 2 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
4021 2 : Tank.AdditionalLossCoeff = 0.0;
4022 2 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4023 2 : if (NumNums > 19 + NodeNum) {
4024 0 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(19 + NodeNum);
4025 : } else {
4026 2 : break;
4027 : }
4028 : }
4029 :
4030 2 : if (NumNums > 19 + Tank.Nodes) {
4031 0 : ShowWarningError(
4032 : state,
4033 0 : format("{} = {}: More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used",
4034 0 : state.dataIPShortCut->cCurrentModuleObject,
4035 0 : state.dataIPShortCut->cAlphaArgs(1)));
4036 : }
4037 :
4038 2 : Tank.SetupStratifiedNodes(state);
4039 : }
4040 :
4041 2 : return ErrorsFound;
4042 : }
4043 :
4044 450 : bool GetWaterThermalTankInput(EnergyPlusData &state)
4045 : {
4046 :
4047 : // SUBROUTINE INFORMATION:
4048 : // AUTHOR Dan Fisher and Brandon Anderson
4049 : // DATE WRITTEN May 2000
4050 : // MODIFIED R. Raustad, June 2005, added HPWH and desuperheater water heating coils
4051 : // B. Griffith, Oct. 2007 extensions for indirect water heaters
4052 : // B. Griffith, Feb. 2008 extensions for autosizing water heaters
4053 : // BG Mar 2009. Trap for bad heater height input for stratified water heater CR7718
4054 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
4055 :
4056 : // PURPOSE OF THIS SUBROUTINE:
4057 : // Gets the water heater, HPWH, and/or desuperheater heating coil input from the input file.
4058 :
4059 450 : bool ErrorsFound = false;
4060 :
4061 : // Make sure refrigeration input is gotten before this input
4062 450 : RefrigeratedCase::CheckRefrigerationInput(state);
4063 :
4064 450 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
4065 450 : state.dataWaterThermalTanks->numWaterHeaterMixed = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedWHModuleObj);
4066 900 : state.dataWaterThermalTanks->numWaterHeaterStratified =
4067 450 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedWHModuleObj);
4068 900 : state.dataWaterThermalTanks->numChilledWaterMixed =
4069 450 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedCWTankModuleObj);
4070 900 : state.dataWaterThermalTanks->numChilledWaterStratified =
4071 450 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedCWTankModuleObj);
4072 900 : state.dataWaterThermalTanks->numWaterThermalTank =
4073 450 : state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
4074 450 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
4075 900 : state.dataWaterThermalTanks->numHeatPumpWaterHeater =
4076 450 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHPumpedCondenser) +
4077 450 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHWrappedCondenser);
4078 900 : state.dataWaterThermalTanks->numWaterHeaterDesuperheater =
4079 450 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCoilDesuperheater);
4080 :
4081 450 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4082 : static constexpr std::string_view Format_720(
4083 : "! <Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery Efficiency, "
4084 : "Standard Rated Energy Factor\n");
4085 : static constexpr std::string_view Format_721(
4086 : "! <Heat Pump Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery "
4087 : "Efficiency,Standard Rated Energy Factor,DX Coil Total Cooling Rate {{W}}\n");
4088 : static constexpr std::string_view Format_722(
4089 : "! <Water Heater Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},Maximum Capacity "
4090 : "{{W}},Off-Cycle UA {{W/K}},On-Cycle UA {{W/K}},Number Of Inlets,Number Of Outlets\n");
4091 : static constexpr std::string_view Format_725(
4092 : "! <Chilled Water Tank Information>,Type,Name,Volume {{m3}},Use Side Design Flow Rate {{m3/s}}, "
4093 : "Source Side Design Flow Rate {{m3/s}}\n");
4094 : static constexpr std::string_view Format_726(
4095 : "! <Chilled Water Tank Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},UA {{W/K}},Number Of "
4096 : "Inlets,Number Of Outlets\n");
4097 :
4098 : // Write water heater header for EIO
4099 136 : if ((state.dataWaterThermalTanks->numWaterHeaterMixed > 0) || (state.dataWaterThermalTanks->numWaterHeaterStratified > 0)) {
4100 136 : print(state.files.eio, Format_720);
4101 : }
4102 136 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4103 9 : print(state.files.eio, Format_721);
4104 : }
4105 136 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) {
4106 10 : print(state.files.eio, Format_722);
4107 : }
4108 136 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) {
4109 4 : print(state.files.eio, Format_725);
4110 : }
4111 136 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) {
4112 2 : print(state.files.eio, Format_726);
4113 : }
4114 : }
4115 :
4116 450 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4117 136 : state.dataWaterThermalTanks->WaterThermalTank.allocate(state.dataWaterThermalTanks->numWaterThermalTank);
4118 136 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames.reserve(static_cast<unsigned>(state.dataWaterThermalTanks->numWaterThermalTank));
4119 : }
4120 450 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4121 9 : state.dataWaterThermalTanks->HPWaterHeater.allocate(state.dataWaterThermalTanks->numHeatPumpWaterHeater);
4122 : }
4123 :
4124 450 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4125 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
4126 : }
4127 :
4128 : // ======= Get Coil:WaterHeating:Desuperheater ======================================================================
4129 450 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4130 6 : ErrorsFound |= getDesuperHtrInput(state);
4131 : }
4132 :
4133 : // ======= Get HEAT PUMP:WATER HEATER ===============================================================================
4134 450 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4135 9 : ErrorsFound |= getHPWaterHeaterInput(state);
4136 : }
4137 :
4138 : // ======= Get WATER HEATER:MIXED ===================================================================================
4139 450 : if (state.dataWaterThermalTanks->numWaterHeaterMixed > 0) {
4140 127 : ErrorsFound |= getWaterHeaterMixedInputs(state);
4141 : }
4142 :
4143 : // ======= Get WATER HEATER:STRATIFIED ==============================================================================
4144 450 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) {
4145 10 : ErrorsFound |= getWaterHeaterStratifiedInput(state);
4146 : }
4147 :
4148 : // ======= Get Chilled Water :MIXED ===================================================================================
4149 450 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) {
4150 4 : ErrorsFound |= getWaterTankMixedInput(state);
4151 : }
4152 :
4153 : // ======= Get 'ThermalStorage:ChilledWater:Stratified' =======================================================
4154 450 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) {
4155 2 : ErrorsFound |= getWaterTankStratifiedInput(state);
4156 : }
4157 :
4158 : // Loop through all desuperheating coils and then search all water heaters for the tank connected to the desuperheating coil
4159 450 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4160 6 : state.dataIPShortCut->cCurrentModuleObject = cCoilDesuperheater;
4161 12 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
4162 6 : auto &DesuperHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
4163 12 : for (int WtrHtrNum = 1; WtrHtrNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WtrHtrNum) {
4164 6 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WtrHtrNum);
4165 6 : if (!Util::SameString(DesuperHtr.TankName, Tank.Name) || !Util::SameString(DesuperHtr.TankType, Tank.Type)) {
4166 0 : continue;
4167 : }
4168 6 : Tank.DesuperheaterNum = DesuperheaterNum;
4169 6 : DesuperHtr.WaterHeaterTankNum = WtrHtrNum;
4170 6 : DesuperHtr.TankTypeNum = Tank.WaterThermalTankType;
4171 6 : DesuperHtr.BackupElementCapacity = Tank.MaxCapacity;
4172 6 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) {
4173 6 : DesuperHtr.StandAlone = true;
4174 : }
4175 :
4176 : // verify Desuperheater/tank source node connections
4177 6 : if (DesuperHtr.WaterInletNode != Tank.SourceOutletNode) {
4178 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4179 0 : ShowContinueError(state, "Desuperheater inlet node name does not match thermal tank source outlet node name.");
4180 0 : ShowContinueError(state,
4181 0 : format("Desuperheater water inlet and outlet node names = {} and {}",
4182 0 : DesuperHtr.InletNodeName1,
4183 0 : DesuperHtr.OutletNodeName1));
4184 0 : ShowContinueError(state,
4185 0 : format("Thermal tank source side inlet and outlet node names = {} and {}",
4186 0 : Tank.InletNodeName2,
4187 0 : Tank.OutletNodeName2));
4188 0 : ErrorsFound = true;
4189 : }
4190 :
4191 6 : if (DesuperHtr.WaterOutletNode != Tank.SourceInletNode) {
4192 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4193 0 : ShowContinueError(state, "Desuperheater water outlet node name does not match thermal tank source inlet node name.");
4194 0 : ShowContinueError(state,
4195 0 : format("Desuperheater water inlet and outlet node names = {} and {}",
4196 0 : DesuperHtr.InletNodeName1,
4197 0 : DesuperHtr.OutletNodeName1));
4198 0 : ShowContinueError(state,
4199 0 : format("Thermal tank source side inlet and outlet node names = {} and {}",
4200 0 : Tank.InletNodeName2,
4201 0 : Tank.OutletNodeName2));
4202 0 : ErrorsFound = true;
4203 : }
4204 : }
4205 :
4206 6 : if (DesuperHtr.WaterHeaterTankNum == 0) {
4207 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4208 0 : ShowContinueError(state, format(" Water heater tank = {} not found.", DesuperHtr.TankName));
4209 0 : ErrorsFound = true;
4210 : }
4211 : }
4212 : }
4213 :
4214 : // Loop through HPWH's and then search all water heaters for the tank connected to the HPWH
4215 450 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4216 :
4217 9 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
4218 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
4219 32 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
4220 :
4221 : // Create reference to current HPWH object in array.
4222 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
4223 23 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
4224 : // Pumped Condenser
4225 20 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
4226 : } else {
4227 : // Wrapped Condenser
4228 3 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
4229 : }
4230 :
4231 : // find the tank associated with the heat pump water heater and change its %TYPE to HEAT PUMP:WATER HEATER
4232 143 : for (int CheckWaterHeaterNum = 1; CheckWaterHeaterNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++CheckWaterHeaterNum) {
4233 :
4234 120 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(CheckWaterHeaterNum);
4235 :
4236 120 : if (!(Util::SameString(HPWH.TankName, Tank.Name) && Util::SameString(HPWH.TankType, Tank.Type))) {
4237 97 : continue;
4238 : }
4239 :
4240 : // save backup element and on/off-cycle parasitic properties for use during standard rating procedure
4241 23 : HPWH.BackupElementCapacity = Tank.MaxCapacity;
4242 23 : HPWH.BackupElementEfficiency = Tank.Efficiency;
4243 23 : HPWH.WHOnCycParaLoad = Tank.OnCycParaLoad;
4244 23 : HPWH.WHOffCycParaLoad = Tank.OffCycParaLoad;
4245 23 : HPWH.WHOnCycParaFracToTank = Tank.OnCycParaFracToTank;
4246 23 : HPWH.WHOffCycParaFracToTank = Tank.OffCycParaFracToTank;
4247 23 : HPWH.WHPLFCurve = Tank.PLFCurve;
4248 :
4249 23 : if (((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4250 12 : (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) ||
4251 11 : (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified)) {
4252 23 : HPWH.TankType = Tank.Type;
4253 23 : HPWH.HPWHTankType = Tank.WaterThermalTankType;
4254 : } else {
4255 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4256 0 : ShowContinueError(state, format("Invalid water heater tank type = {}", Tank.Type));
4257 0 : ErrorsFound = true;
4258 : }
4259 :
4260 : // Set up comp set for condenser water side nodes (reverse inlet/outlet for water heater)
4261 23 : if (HPWH.bIsIHP) {
4262 2 : BranchNodeConnections::SetUpCompSets(state,
4263 : HPWH.Type,
4264 : HPWH.Name,
4265 : HPWH.DXCoilType,
4266 2 : HPWH.DXCoilName + " Water Coil",
4267 : HPWH.InletNodeName1,
4268 : HPWH.OutletNodeName1,
4269 : "HPWH To Coil");
4270 : } else {
4271 22 : BranchNodeConnections::SetUpCompSets(
4272 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.InletNodeName1, HPWH.OutletNodeName1, "HPWH To Coil");
4273 : }
4274 23 : BranchNodeConnections::SetUpCompSets(
4275 : state, HPWH.Type, HPWH.Name, HPWH.TankType, HPWH.TankName, HPWH.OutletNodeName1, HPWH.InletNodeName1, "HPWH To Tank");
4276 :
4277 : // If WaterHeaterMixed: do not allow modulating control for HPWH's (i.e. modulating control usually used for tankless WH's)
4278 23 : if ((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4279 12 : (Tank.ControlType == HeaterControlMode::Modulate)) {
4280 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4281 0 : ShowContinueError(state, format("Heater Control Type for {} = {} must be CYCLE.", Tank.Type, Tank.Name));
4282 0 : ErrorsFound = true;
4283 : }
4284 :
4285 23 : Tank.HeatPumpNum = HPWaterHeaterNum;
4286 23 : HPWH.WaterHeaterTankNum = CheckWaterHeaterNum;
4287 23 : HPWH.FoundTank = true;
4288 :
4289 23 : if (Tank.DesuperheaterNum > 0) {
4290 0 : ShowSevereError(
4291 : state,
4292 0 : format("{} = {}and Coil:WaterHeating:Desuperheater = {}: cannot be connected to the same water heater tank = {}",
4293 0 : state.dataIPShortCut->cCurrentModuleObject,
4294 0 : HPWH.Name,
4295 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(CheckWaterHeaterNum).Name,
4296 0 : Tank.Name));
4297 : }
4298 :
4299 : // check that water heater source side effectiveness is greater than 0
4300 23 : if (Tank.SourceEffectiveness <= 0.0) {
4301 0 : ShowSevereError(state,
4302 0 : format("{} = {}: Invalid source side effectiveness for heat pump water heater = {:.3T}",
4303 0 : state.dataIPShortCut->cCurrentModuleObject,
4304 0 : HPWH.Name,
4305 0 : Tank.SourceEffectiveness));
4306 0 : ShowContinueError(state, " water heater source effectiveness will default to 1.0 and simulation continues.");
4307 0 : Tank.SourceEffectiveness = 1.0;
4308 : }
4309 :
4310 : // Set up the source side nodes for wrapped condensers
4311 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4312 3 : if (Tank.SourceInletNode > 0 || Tank.SourceOutletNode > 0) {
4313 0 : ShowSevereError(state, format("{} = {} has a source inlet or outlet node specified,", Tank.Type, Tank.Name));
4314 0 : ShowContinueError(
4315 0 : state, format("but it is attached to {} = {}, which doesn't permit source side connections.", HPWH.Type, HPWH.Name));
4316 0 : ShowContinueError(state, "Please leave the source side inlet and outlet fields blank.");
4317 0 : ErrorsFound = true;
4318 : } else {
4319 :
4320 : DataLoopNode::ConnectionObjectType objType = static_cast<DataLoopNode::ConnectionObjectType>(
4321 3 : getEnumValue(BranchNodeConnections::ConnectionObjectTypeNamesUC, Util::makeUPPER(Tank.Type)));
4322 :
4323 3 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
4324 3 : HPWH.OutletNodeName1,
4325 : ErrorsFound,
4326 : objType,
4327 3 : Tank.Name,
4328 : DataLoopNode::NodeFluidType::Water,
4329 : DataLoopNode::ConnectionType::Inlet,
4330 : NodeInputManager::CompFluidStream::Secondary,
4331 : DataLoopNode::ObjectIsNotParent);
4332 3 : Tank.InletNodeName2 = HPWH.OutletNodeName1;
4333 3 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
4334 3 : HPWH.InletNodeName1,
4335 : ErrorsFound,
4336 : objType,
4337 3 : Tank.Name,
4338 : DataLoopNode::NodeFluidType::Water,
4339 : DataLoopNode::ConnectionType::Outlet,
4340 : NodeInputManager::CompFluidStream::Secondary,
4341 : DataLoopNode::ObjectIsNotParent);
4342 3 : Tank.OutletNodeName2 = HPWH.InletNodeName1;
4343 : }
4344 :
4345 : // Mark the tank as not stand alone because it is connected now.
4346 3 : Tank.StandAlone = false;
4347 : }
4348 :
4349 : // Set HPWH structure variable StandAlone to TRUE if use nodes are not connected
4350 23 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) {
4351 13 : HPWH.StandAlone = true;
4352 : }
4353 :
4354 23 : if (HPWH.WHUseInletNode != Tank.UseInletNode || HPWH.WHUseOutletNode != Tank.UseOutletNode) {
4355 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4356 0 : ShowContinueError(state,
4357 0 : format("Heat pump water heater tank use side inlet and outlet node names must match the use side inlet and "
4358 : "outlet node names for water heater tank = {}: {}",
4359 0 : HPWH.TankType,
4360 0 : HPWH.TankName));
4361 0 : ShowContinueError(state,
4362 0 : format("Heat pump water heater use side inlet and outlet node names = {} and {}",
4363 0 : HPWH.InletNodeName2,
4364 0 : HPWH.OutletNodeName2));
4365 0 : ShowContinueError(state,
4366 0 : format("Water heater tank use side inlet and outlet node names = {} and {}",
4367 0 : Tank.InletNodeName1,
4368 0 : Tank.OutletNodeName1));
4369 0 : ErrorsFound = true;
4370 : } else {
4371 23 : if (!HPWH.StandAlone) {
4372 20 : BranchNodeConnections::TestCompSet(state, HPWH.Type, HPWH.Name, Tank.InletNodeName1, Tank.OutletNodeName1, "Water Nodes");
4373 : }
4374 : }
4375 :
4376 : // verify HP/tank source node connections
4377 23 : if (HPWH.CondWaterInletNode != Tank.SourceOutletNode) {
4378 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4379 0 : ShowContinueError(state,
4380 : "Heat Pump condenser water inlet node name does not match water heater tank source outlet node name.");
4381 0 : ShowContinueError(
4382 : state,
4383 0 : format("Heat pump condenser water inlet and outlet node names = {} and {}", HPWH.InletNodeName1, HPWH.OutletNodeName1));
4384 0 : ShowContinueError(state,
4385 0 : format("Water heater tank source side inlet and outlet node names = {} and {}",
4386 0 : Tank.InletNodeName2,
4387 0 : Tank.OutletNodeName2));
4388 0 : ErrorsFound = true;
4389 : }
4390 :
4391 23 : if (HPWH.CondWaterOutletNode != Tank.SourceInletNode) {
4392 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4393 0 : ShowContinueError(state,
4394 : "Heat Pump condenser water outlet node name does not match water heater tank source inlet node name.");
4395 0 : ShowContinueError(
4396 : state,
4397 0 : format("Heat pump condenser water inlet and outlet node names = {} and {}", HPWH.InletNodeName1, HPWH.OutletNodeName1));
4398 0 : ShowContinueError(state,
4399 0 : format("Water heater tank source side inlet and outlet node names = {} and {}",
4400 0 : Tank.InletNodeName2,
4401 0 : Tank.OutletNodeName2));
4402 0 : ErrorsFound = true;
4403 : }
4404 :
4405 : // verify wrapped condenser location
4406 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4407 : // make sure the top of the condenser is not above the tank height.
4408 3 : if (HPWH.WrappedCondenserTopLocation > Tank.Height) {
4409 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4410 0 : ShowContinueError(state, "The height of the top of the wrapped condenser is greater than the height of the tank.");
4411 0 : ErrorsFound = true;
4412 : }
4413 : }
4414 :
4415 : // Verify tank name is in a zone equipment list if HPWH Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
4416 23 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
4417 10 : if (allocated(state.dataZoneEquip->ZoneEquipConfig) && allocated(state.dataZoneEquip->ZoneEquipList)) {
4418 10 : bool FoundTankInList = false;
4419 10 : bool TankNotLowestPriority = false;
4420 10 : int ZoneEquipConfigNum = HPWH.AmbientTempZone;
4421 10 : int ZoneEquipListNum = state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigNum).EquipListIndex;
4422 10 : int TankCoolingPriority = 0;
4423 10 : int TankHeatingPriority = 0;
4424 10 : auto const &zoneEquipList = state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum);
4425 19 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= zoneEquipList.NumOfEquipTypes; ++EquipmentTypeNum) {
4426 19 : if (zoneEquipList.EquipName(EquipmentTypeNum) != HPWH.Name) {
4427 9 : continue;
4428 : }
4429 10 : FoundTankInList = true;
4430 10 : TankCoolingPriority = zoneEquipList.CoolingPriority(EquipmentTypeNum);
4431 10 : TankHeatingPriority = zoneEquipList.HeatingPriority(EquipmentTypeNum);
4432 10 : break;
4433 : } // EquipmentTypeNum
4434 10 : if (!FoundTankInList) {
4435 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4436 0 : ShowContinueError(state,
4437 : "Heat pump water heater type and name must be listed in the correct "
4438 : "ZoneHVAC:EquipmentList object when Inlet Air Configuration is equal to "
4439 : "ZoneAirOnly or ZoneAndOutdoorAir.");
4440 0 : ErrorsFound = true;
4441 : }
4442 : // check that tank has lower priority than all other non-HPWH objects in Zone
4443 : // Equipment List
4444 39 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= zoneEquipList.NumOfEquipTypes; ++EquipmentTypeNum) {
4445 29 : if (Util::SameString(zoneEquipList.EquipTypeName(EquipmentTypeNum), state.dataIPShortCut->cCurrentModuleObject)) {
4446 14 : continue;
4447 : }
4448 30 : if (TankCoolingPriority > zoneEquipList.CoolingPriority(EquipmentTypeNum) ||
4449 15 : TankHeatingPriority > zoneEquipList.HeatingPriority(EquipmentTypeNum)) {
4450 0 : TankNotLowestPriority = true;
4451 : }
4452 : } // EquipmentTypeNum
4453 10 : if (TankNotLowestPriority && FoundTankInList) {
4454 0 : ShowWarningError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4455 0 : ShowContinueError(state,
4456 : "Heat pump water heaters should be simulated first, before other space "
4457 : "conditioning equipment.");
4458 0 : ShowContinueError(state,
4459 : "Poor temperature control may result if the Heating/Cooling sequence number is "
4460 : "not 1 in the ZoneHVAC:EquipmentList.");
4461 : }
4462 : } else {
4463 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4464 0 : ShowContinueError(state,
4465 : "ZoneHVAC:EquipmentList and ZoneHVAC:EquipmentConnections objects are required when Inlet Air "
4466 : "Configuration is either ZoneAirOnly or ZoneAndOutdoorAir.");
4467 0 : ErrorsFound = true;
4468 : } // ALLOCATED
4469 : } // InletAirConfiguration
4470 :
4471 23 : if (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
4472 :
4473 : // Nodal heat distribution fraction for stratified tank wrapped condensers
4474 11 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4475 3 : if (Tank.Shape == TankShape::HorizCylinder) {
4476 0 : ShowWarningError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4477 0 : ShowContinueError(state, "A wrapped condenser HPWH model should not be used with a horizontal stratified tank.");
4478 0 : ShowContinueError(
4479 : state, "Ignoring condenser location and distributing heat evenly throughout the tank. Simulation continues.");
4480 0 : Real64 const SameFrac = 1.0 / Tank.Nodes;
4481 0 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4482 0 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac = SameFrac;
4483 : }
4484 : } else {
4485 3 : Real64 H0 = Tank.Height; // height of top of node
4486 : Real64 H; // height of bottom of node
4487 3 : Real64 SumFrac(0.0);
4488 : // Get the fraction of each stratified node that is wrapped by the condenser
4489 17 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4490 14 : StratifiedNodeData &CurNode = Tank.Node(NodeNum);
4491 14 : if (NodeNum == Tank.Nodes) {
4492 3 : H = 0.0;
4493 : } else {
4494 11 : H = H0 - CurNode.Height;
4495 : }
4496 14 : if (H < HPWH.WrappedCondenserBottomLocation && H0 > HPWH.WrappedCondenserBottomLocation) {
4497 : // The bottom of the condenser starts partway through this node.
4498 3 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0 - (HPWH.WrappedCondenserBottomLocation - H) / CurNode.Height;
4499 11 : } else if (H >= HPWH.WrappedCondenserBottomLocation && H <= HPWH.WrappedCondenserTopLocation) {
4500 6 : if (H0 > HPWH.WrappedCondenserTopLocation) {
4501 : // the top of the condenser ends partway through this node.
4502 1 : CurNode.HPWHWrappedCondenserHeatingFrac = (HPWH.WrappedCondenserTopLocation - H) / CurNode.Height;
4503 : } else {
4504 : // the entire node is wrapped by the condenser
4505 5 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0;
4506 : }
4507 : } else {
4508 5 : CurNode.HPWHWrappedCondenserHeatingFrac = 0.0;
4509 : }
4510 14 : SumFrac += CurNode.HPWHWrappedCondenserHeatingFrac;
4511 14 : H0 = H;
4512 : }
4513 : // Normalize the fractions so they sum to 1.
4514 17 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4515 14 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac /= SumFrac;
4516 : }
4517 : }
4518 : }
4519 :
4520 : // Stratified Tank HPWH control sensor node locations
4521 11 : if (HPWH.ControlSensor1Height < 0.0) {
4522 : // default to heater 1
4523 0 : HPWH.ControlSensor1Height = Tank.HeaterHeight1;
4524 : }
4525 11 : if (HPWH.ControlSensor2Height < 0.0) {
4526 : // default to heater 2
4527 0 : HPWH.ControlSensor2Height = Tank.HeaterHeight2;
4528 : }
4529 :
4530 : // Get the vertical tank height depending on the type of tank
4531 : Real64 TankHeight;
4532 11 : if (Tank.Shape == TankShape::VertCylinder || Tank.Shape == TankShape::Other) {
4533 11 : TankHeight = Tank.Height;
4534 : } else {
4535 0 : assert(Tank.Shape == TankShape::HorizCylinder);
4536 : // For horizontal cylinders, the tank "height" is actually the length.
4537 : // We need to calculate the height.
4538 0 : Real64 EndArea = Tank.Volume / Tank.Height;
4539 0 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
4540 0 : TankHeight = 2.0 * Radius; // actual vertical height
4541 : }
4542 :
4543 : // Make sure the control sensor locations are in the tank
4544 11 : if (HPWH.ControlSensor1Height < 0.0 || HPWH.ControlSensor1Height > TankHeight) {
4545 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4546 0 : ShowContinueError(state, "Control Sensor 1 is located outside the tank.");
4547 0 : ErrorsFound = true;
4548 : }
4549 11 : if (HPWH.ControlSensor2Height < 0.0 || HPWH.ControlSensor2Height > TankHeight) {
4550 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4551 0 : ShowContinueError(state, "Control Sensor 2 is located outside the tank.");
4552 0 : ErrorsFound = true;
4553 : }
4554 :
4555 : // Assign the control sensors to the appropriate nodes
4556 11 : Real64 H0 = TankHeight;
4557 : Real64 H;
4558 97 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4559 86 : StratifiedNodeData const &TankNode = Tank.Node(NodeNum);
4560 86 : if (NodeNum == Tank.Nodes) {
4561 11 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
4562 : } else {
4563 75 : H = H0 - TankNode.Height;
4564 : }
4565 :
4566 : // Control Sensor 1 Node
4567 86 : if (HPWH.ControlSensor1Height <= H0 && HPWH.ControlSensor1Height > H) {
4568 11 : HPWH.ControlSensor1Node = NodeNum;
4569 : }
4570 :
4571 : // Control Sensor 2 Node
4572 86 : if (HPWH.ControlSensor2Height <= H0 && HPWH.ControlSensor2Height > H) {
4573 11 : HPWH.ControlSensor2Node = NodeNum;
4574 : }
4575 :
4576 86 : H0 = H;
4577 : }
4578 : }
4579 :
4580 : } // DO CheckWaterHeaterNum = 1, NumWaterHeater
4581 :
4582 23 : if (!HPWH.FoundTank) {
4583 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4584 0 : ShowContinueError(state, format("Water heater tank object not found = {}, {}", HPWH.TankType, HPWH.TankName));
4585 0 : ErrorsFound = true;
4586 : }
4587 :
4588 : } // DO HPWaterHeaterNum = 1, NumHeatPumpWaterHeater
4589 : }
4590 :
4591 : // Get water heater sizing input.
4592 450 : state.dataIPShortCut->cCurrentModuleObject = "WaterHeater:Sizing";
4593 900 : state.dataWaterThermalTanks->numWaterHeaterSizing =
4594 450 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
4595 :
4596 450 : if (state.dataWaterThermalTanks->numWaterHeaterSizing > 0) {
4597 :
4598 14 : for (int WHsizingNum = 1; WHsizingNum <= state.dataWaterThermalTanks->numWaterHeaterSizing; ++WHsizingNum) {
4599 : int NumAlphas;
4600 : int NumNums;
4601 : int IOStat;
4602 14 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4603 7 : state.dataIPShortCut->cCurrentModuleObject,
4604 : WHsizingNum,
4605 7 : state.dataIPShortCut->cAlphaArgs,
4606 : NumAlphas,
4607 7 : state.dataIPShortCut->rNumericArgs,
4608 : NumNums,
4609 : IOStat);
4610 :
4611 : // find which water heater this object is for
4612 7 : int WaterThermalTankNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(1), state.dataWaterThermalTanks->WaterThermalTank);
4613 7 : if (WaterThermalTankNum == 0) {
4614 : // did not match name throw warning.
4615 0 : ShowSevereError(state,
4616 0 : format("{} object name: {} does not match any of the water heaters defined in the file",
4617 0 : state.dataIPShortCut->cCurrentModuleObject,
4618 0 : state.dataIPShortCut->cAlphaArgs(1)));
4619 0 : ErrorsFound = true;
4620 0 : continue;
4621 : } else { // we have a match
4622 : // store the sizing data in "sizing" nested derived type for the correct water heater
4623 :
4624 7 : if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PeakDraw")) {
4625 4 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PeakDraw;
4626 3 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "ResidentialHUD-FHAMinimum")) {
4627 3 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::ResidentialMin;
4628 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerPerson")) {
4629 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerPerson;
4630 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerFloorArea")) {
4631 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerFloorArea;
4632 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerUnit")) {
4633 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerUnit;
4634 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerSolarCollectorArea")) {
4635 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerSolarColArea;
4636 : } else {
4637 : // wrong design mode entered, throw error
4638 0 : ShowSevereError(state,
4639 0 : format("{} object named: {} contains an incorrect Design Mode of: {}",
4640 0 : state.dataIPShortCut->cCurrentModuleObject,
4641 0 : state.dataIPShortCut->cAlphaArgs(1),
4642 0 : state.dataIPShortCut->cAlphaArgs(2)));
4643 0 : ErrorsFound = true;
4644 : }
4645 :
4646 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime = state.dataIPShortCut->rNumericArgs(1);
4647 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryTime = state.dataIPShortCut->rNumericArgs(2);
4648 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow =
4649 7 : state.dataIPShortCut->rNumericArgs(3);
4650 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms =
4651 7 : int(state.dataIPShortCut->rNumericArgs(4));
4652 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBathrooms =
4653 7 : int(state.dataIPShortCut->rNumericArgs(5));
4654 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson =
4655 7 : state.dataIPShortCut->rNumericArgs(6);
4656 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson =
4657 7 : state.dataIPShortCut->rNumericArgs(7);
4658 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea =
4659 7 : state.dataIPShortCut->rNumericArgs(8);
4660 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea =
4661 7 : state.dataIPShortCut->rNumericArgs(9);
4662 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits = state.dataIPShortCut->rNumericArgs(10);
4663 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit =
4664 7 : state.dataIPShortCut->rNumericArgs(11);
4665 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit =
4666 7 : state.dataIPShortCut->rNumericArgs(12);
4667 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea =
4668 7 : state.dataIPShortCut->rNumericArgs(13);
4669 7 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.HeightAspectRatio =
4670 7 : state.dataIPShortCut->rNumericArgs(14);
4671 :
4672 7 : switch (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode) {
4673 :
4674 0 : case SizingMode::Invalid: {
4675 : // do nothing, error thrown if design mode not found
4676 0 : break;
4677 : }
4678 4 : case SizingMode::PeakDraw: { // need to have entered a reasonable value for TankDrawTime
4679 4 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime <= 0.0) {
4680 0 : ShowSevereError(state,
4681 0 : format("{}, named {}, design mode set to Peak Draw but needs a positive value for tank draw time",
4682 0 : state.dataIPShortCut->cCurrentModuleObject,
4683 0 : state.dataIPShortCut->cAlphaArgs(1)));
4684 0 : ErrorsFound = true;
4685 : }
4686 : // constrain crazy sizes by limiting to 10 years or 8760*10
4687 4 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime > 87600.0) {
4688 0 : ShowWarningError(state,
4689 0 : format("{}, named {}, has input with an unreasonably large Tank Draw Time, more than 10 years",
4690 0 : state.dataIPShortCut->cCurrentModuleObject,
4691 0 : state.dataIPShortCut->cAlphaArgs(1)));
4692 0 : ErrorsFound = true;
4693 : }
4694 : // if both volume and demand side flow connections are autosized, must be a good NominalVolForSizingDemandSideFlow
4695 4 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseSidePlantLoc.loopSideNum ==
4696 4 : DataPlant::LoopSideLocation::Demand) &&
4697 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseDesignVolFlowRateWasAutoSized)) {
4698 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4699 0 : ShowWarningError(state,
4700 0 : format("{}, named {} needs a value for Nominal Tank Volume for Autosizing Plant Connections",
4701 0 : state.dataIPShortCut->cCurrentModuleObject,
4702 0 : state.dataIPShortCut->cAlphaArgs(1)));
4703 0 : ErrorsFound = true;
4704 : }
4705 : }
4706 4 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SrcSidePlantLoc.loopSideNum ==
4707 4 : DataPlant::LoopSideLocation::Demand) &&
4708 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceDesignVolFlowRateWasAutoSized)) {
4709 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4710 0 : ShowWarningError(state,
4711 0 : format("{}, named {} needs a value for Nominal Tank Volume for Autosizing Plant Connections",
4712 0 : state.dataIPShortCut->cCurrentModuleObject,
4713 0 : state.dataIPShortCut->cAlphaArgs(1)));
4714 0 : ErrorsFound = true;
4715 : }
4716 : }
4717 :
4718 4 : break;
4719 : }
4720 3 : case SizingMode::ResidentialMin: {
4721 : // it would have to have at least on bedroom and any more than 10 is crazy for this mode
4722 3 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms < 1) {
4723 0 : ShowSevereError(state,
4724 0 : format("{}, named {}, mode needs at least one bedroom",
4725 0 : state.dataIPShortCut->cCurrentModuleObject,
4726 0 : state.dataIPShortCut->cAlphaArgs(1)));
4727 0 : ErrorsFound = true;
4728 : }
4729 3 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms > 10) {
4730 0 : ShowWarningError(state,
4731 0 : format("{}, named {}, probably has too many bedrooms for the selected design mode",
4732 0 : state.dataIPShortCut->cCurrentModuleObject,
4733 0 : state.dataIPShortCut->cAlphaArgs(1)));
4734 : }
4735 :
4736 3 : break;
4737 : }
4738 0 : case SizingMode::PerPerson: {
4739 :
4740 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4741 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson <= 0.0)) {
4742 0 : ShowSevereError(state,
4743 0 : format("{}, named {}, PerPerson mode needs positive value input for storage capacity per person",
4744 0 : state.dataIPShortCut->cCurrentModuleObject,
4745 0 : state.dataIPShortCut->cAlphaArgs(1)));
4746 0 : ErrorsFound = true;
4747 : }
4748 :
4749 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4750 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson <= 0.0)) {
4751 0 : ShowSevereError(state,
4752 0 : format("{}, named {}, PerPerson mode needs positive value input for recovery capacity per person",
4753 0 : state.dataIPShortCut->cCurrentModuleObject,
4754 0 : state.dataIPShortCut->cAlphaArgs(1)));
4755 0 : ErrorsFound = true;
4756 : }
4757 :
4758 0 : break;
4759 : }
4760 0 : case SizingMode::PerFloorArea: {
4761 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4762 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea <= 0.0)) {
4763 0 : ShowSevereError(state,
4764 0 : format("{}, named {}, PerArea mode needs positive value input for storage capacity per floor area",
4765 0 : state.dataIPShortCut->cCurrentModuleObject,
4766 0 : state.dataIPShortCut->cAlphaArgs(1)));
4767 0 : ErrorsFound = true;
4768 : }
4769 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4770 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea <= 0.0)) {
4771 0 : ShowSevereError(state,
4772 0 : format("{}, named {}, PerArea mode needs positive value input for recovery capacity per floor area",
4773 0 : state.dataIPShortCut->cCurrentModuleObject,
4774 0 : state.dataIPShortCut->cAlphaArgs(1)));
4775 0 : ErrorsFound = true;
4776 : }
4777 :
4778 0 : break;
4779 : }
4780 0 : case SizingMode::PerUnit: {
4781 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4782 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit <= 0.0)) {
4783 0 : ShowSevereError(state,
4784 0 : format("{}, named {}, PerUnit mode needs positive value input for storage capacity per unit",
4785 0 : state.dataIPShortCut->cCurrentModuleObject,
4786 0 : state.dataIPShortCut->cAlphaArgs(1)));
4787 0 : ErrorsFound = true;
4788 : }
4789 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4790 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4791 0 : ShowSevereError(state,
4792 0 : format("{}, named {}, PerUnit mode needs positive value input for number of units",
4793 0 : state.dataIPShortCut->cCurrentModuleObject,
4794 0 : state.dataIPShortCut->cAlphaArgs(1)));
4795 0 : ErrorsFound = true;
4796 : }
4797 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4798 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit <= 0.0)) {
4799 0 : ShowSevereError(state,
4800 0 : format("{}, named {}, PerUnit mode needs positive value input for recovery capacity per unit",
4801 0 : state.dataIPShortCut->cCurrentModuleObject,
4802 0 : state.dataIPShortCut->cAlphaArgs(1)));
4803 0 : ErrorsFound = true;
4804 : }
4805 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4806 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4807 0 : ShowSevereError(state,
4808 0 : format("{}, named {}, PerUnit mode needs positive value input for number of units",
4809 0 : state.dataIPShortCut->cCurrentModuleObject,
4810 0 : state.dataIPShortCut->cAlphaArgs(1)));
4811 0 : ErrorsFound = true;
4812 : }
4813 0 : break;
4814 : }
4815 0 : case SizingMode::PerSolarColArea: {
4816 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4817 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea <= 0.0)) {
4818 0 : ShowSevereError(
4819 : state,
4820 0 : format("{}, named {}, PerSolarCollectorArea mode needs positive value input for storage capacity per collector area",
4821 0 : state.dataIPShortCut->cCurrentModuleObject,
4822 0 : state.dataIPShortCut->cAlphaArgs(1)));
4823 0 : ErrorsFound = true;
4824 : }
4825 0 : break;
4826 : }
4827 0 : default:
4828 0 : break;
4829 : }
4830 :
4831 : } // found water heater num okay
4832 : } // loop over sizing objects
4833 :
4834 : } // any water heater sizing objects
4835 :
4836 : // now check that if water heater fields were autosized, that there was also a sizing object for that water heater
4837 450 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4838 338 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4839 :
4840 204 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4841 2 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4842 0 : ShowWarningError(
4843 : state,
4844 0 : format("Water heater named {}has tank volume set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4845 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4846 0 : ErrorsFound = true;
4847 : }
4848 205 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4849 3 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4850 0 : ShowWarningError(
4851 : state,
4852 0 : format("Water heater named {}has heater capacity set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4853 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4854 0 : ErrorsFound = true;
4855 : }
4856 202 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeightWasAutoSized) &&
4857 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4858 0 : ShowWarningError(
4859 : state,
4860 0 : format("Water heater named {}has tank height set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4861 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4862 0 : ErrorsFound = true;
4863 : }
4864 : }
4865 : }
4866 :
4867 : // now do calls to TestCompSet for tanks, depending on nodes and heat pump water heater
4868 450 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4869 338 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4870 336 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseInletNode > 0 &&
4871 134 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseOutletNode > 0) {
4872 134 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeatPumpNum > 0) {
4873 : // do nothing, Use nodes are tested for HeatPump:WaterHeater not tank
4874 : } else {
4875 372 : BranchNodeConnections::TestCompSet(state,
4876 124 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4877 124 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4878 124 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName1,
4879 124 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName1,
4880 : "Use Side Water Nodes");
4881 : }
4882 : }
4883 252 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceInletNode > 0 &&
4884 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceOutletNode > 0) {
4885 :
4886 150 : BranchNodeConnections::TestCompSet(state,
4887 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4888 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4889 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName2,
4890 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName2,
4891 : "Source Side Water Nodes");
4892 : }
4893 : }
4894 : }
4895 :
4896 450 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4897 338 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4898 :
4899 202 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).setupZoneInternalGains(state);
4900 :
4901 : } // WaterThermalTankNum
4902 : }
4903 : } // get input flag
4904 :
4905 450 : return ErrorsFound;
4906 : }
4907 :
4908 201 : void WaterThermalTankData::setupOutputVars(EnergyPlusData &state)
4909 : {
4910 201 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) ||
4911 197 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
4912 6 : this->setupChilledWaterTankOutputVars(state);
4913 : } else {
4914 : // moving setupWaterHeaterOutputVars to here causes big table diffs...
4915 195 : this->setupWaterHeaterOutputVars(state);
4916 : }
4917 : // moving setupZoneInternalGains to here causes math and table diffs...
4918 : // this->setupZoneInternalGains();
4919 201 : }
4920 :
4921 6 : void WaterThermalTankData::setupChilledWaterTankOutputVars(EnergyPlusData &state)
4922 : {
4923 :
4924 : // CurrentModuleObject='ThermalStorage:ChilledWater:Mixed/ThermalStorage:ChilledWater:Stratified'
4925 12 : SetupOutputVariable(state,
4926 : "Chilled Water Thermal Storage Tank Temperature",
4927 : Constant::Units::C,
4928 6 : this->TankTempAvg,
4929 : OutputProcessor::TimeStepType::System,
4930 : OutputProcessor::StoreType::Average,
4931 6 : this->Name);
4932 :
4933 12 : SetupOutputVariable(state,
4934 : "Chilled Water Thermal Storage Final Tank Temperature",
4935 : Constant::Units::C,
4936 6 : this->TankTemp,
4937 : OutputProcessor::TimeStepType::System,
4938 : OutputProcessor::StoreType::Average,
4939 6 : this->Name);
4940 :
4941 12 : SetupOutputVariable(state,
4942 : "Chilled Water Thermal Storage Tank Heat Gain Rate",
4943 : Constant::Units::W,
4944 6 : this->LossRate,
4945 : OutputProcessor::TimeStepType::System,
4946 : OutputProcessor::StoreType::Average,
4947 6 : this->Name);
4948 12 : SetupOutputVariable(state,
4949 : "Chilled Water Thermal Storage Tank Heat Gain Energy",
4950 : Constant::Units::J,
4951 6 : this->LossEnergy,
4952 : OutputProcessor::TimeStepType::System,
4953 : OutputProcessor::StoreType::Sum,
4954 6 : this->Name);
4955 :
4956 12 : SetupOutputVariable(state,
4957 : "Chilled Water Thermal Storage Use Side Mass Flow Rate",
4958 : Constant::Units::kg_s,
4959 6 : this->UseMassFlowRate,
4960 : OutputProcessor::TimeStepType::System,
4961 : OutputProcessor::StoreType::Average,
4962 6 : this->Name);
4963 :
4964 12 : SetupOutputVariable(state,
4965 : "Chilled Water Thermal Storage Use Side Inlet Temperature",
4966 : Constant::Units::C,
4967 6 : this->UseInletTemp,
4968 : OutputProcessor::TimeStepType::System,
4969 : OutputProcessor::StoreType::Average,
4970 6 : this->Name);
4971 :
4972 12 : SetupOutputVariable(state,
4973 : "Chilled Water Thermal Storage Use Side Outlet Temperature",
4974 : Constant::Units::C,
4975 6 : this->UseOutletTemp,
4976 : OutputProcessor::TimeStepType::System,
4977 : OutputProcessor::StoreType::Average,
4978 6 : this->Name);
4979 :
4980 12 : SetupOutputVariable(state,
4981 : "Chilled Water Thermal Storage Use Side Heat Transfer Rate",
4982 : Constant::Units::W,
4983 6 : this->UseRate,
4984 : OutputProcessor::TimeStepType::System,
4985 : OutputProcessor::StoreType::Average,
4986 6 : this->Name);
4987 12 : SetupOutputVariable(state,
4988 : "Chilled Water Thermal Storage Use Side Heat Transfer Energy",
4989 : Constant::Units::J,
4990 6 : this->UseEnergy,
4991 : OutputProcessor::TimeStepType::System,
4992 : OutputProcessor::StoreType::Sum,
4993 6 : this->Name);
4994 :
4995 12 : SetupOutputVariable(state,
4996 : "Chilled Water Thermal Storage Source Side Mass Flow Rate",
4997 : Constant::Units::kg_s,
4998 6 : this->SourceMassFlowRate,
4999 : OutputProcessor::TimeStepType::System,
5000 : OutputProcessor::StoreType::Average,
5001 6 : this->Name);
5002 :
5003 12 : SetupOutputVariable(state,
5004 : "Chilled Water Thermal Storage Source Side Inlet Temperature",
5005 : Constant::Units::C,
5006 6 : this->SourceInletTemp,
5007 : OutputProcessor::TimeStepType::System,
5008 : OutputProcessor::StoreType::Average,
5009 6 : this->Name);
5010 :
5011 12 : SetupOutputVariable(state,
5012 : "Chilled Water Thermal Storage Source Side Outlet Temperature",
5013 : Constant::Units::C,
5014 6 : this->SourceOutletTemp,
5015 : OutputProcessor::TimeStepType::System,
5016 : OutputProcessor::StoreType::Average,
5017 6 : this->Name);
5018 :
5019 12 : SetupOutputVariable(state,
5020 : "Chilled Water Thermal Storage Source Side Heat Transfer Rate",
5021 : Constant::Units::W,
5022 6 : this->SourceRate,
5023 : OutputProcessor::TimeStepType::System,
5024 : OutputProcessor::StoreType::Average,
5025 6 : this->Name);
5026 12 : SetupOutputVariable(state,
5027 : "Chilled Water Thermal Storage Source Side Heat Transfer Energy",
5028 : Constant::Units::J,
5029 6 : this->SourceEnergy,
5030 : OutputProcessor::TimeStepType::System,
5031 : OutputProcessor::StoreType::Sum,
5032 6 : this->Name);
5033 :
5034 6 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
5035 :
5036 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5037 36 : SetupOutputVariable(state,
5038 24 : format("Chilled Water Thermal Storage Temperature Node {}", NodeNum),
5039 : Constant::Units::C,
5040 12 : this->Node(NodeNum).TempAvg,
5041 : OutputProcessor::TimeStepType::System,
5042 : OutputProcessor::StoreType::Average,
5043 12 : this->Name);
5044 : }
5045 :
5046 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5047 36 : SetupOutputVariable(state,
5048 24 : format("Chilled Water Thermal Storage Final Temperature Node {}", NodeNum),
5049 : Constant::Units::C,
5050 12 : this->Node(NodeNum).Temp,
5051 : OutputProcessor::TimeStepType::System,
5052 : OutputProcessor::StoreType::Average,
5053 12 : this->Name);
5054 : }
5055 : }
5056 :
5057 6 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
5058 :
5059 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5060 : static constexpr std::string_view Format_724("Chilled Water Tank Stratified Node Information,{},{:.4T},{:.4T},{:.4T},{},{}\n");
5061 :
5062 12 : print(state.files.eio,
5063 : Format_724,
5064 : NodeNum,
5065 12 : this->Node(NodeNum).Height,
5066 12 : this->Node(NodeNum).Volume,
5067 12 : this->Node(NodeNum).OffCycLossCoeff,
5068 12 : this->Node(NodeNum).Inlets,
5069 12 : this->Node(NodeNum).Outlets);
5070 : }
5071 : }
5072 6 : }
5073 :
5074 202 : void WaterThermalTankData::setupZoneInternalGains(EnergyPlusData &state)
5075 : {
5076 : // set up internal gains if tank is in a thermal zone
5077 202 : if (this->AmbientTempZone > 0) {
5078 83 : switch (this->WaterThermalTankType) {
5079 72 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
5080 72 : SetupZoneInternalGain(state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterMixed, &this->AmbientZoneGain);
5081 72 : break;
5082 : }
5083 6 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
5084 6 : SetupZoneInternalGain(
5085 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterStratified, &this->AmbientZoneGain);
5086 6 : break;
5087 : }
5088 3 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
5089 3 : SetupZoneInternalGain(
5090 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterMixed, &this->AmbientZoneGain);
5091 3 : break;
5092 : }
5093 2 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
5094 2 : SetupZoneInternalGain(
5095 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterStratified, &this->AmbientZoneGain);
5096 2 : break;
5097 : }
5098 0 : default:
5099 0 : break;
5100 : }
5101 : }
5102 202 : }
5103 :
5104 195 : void WaterThermalTankData::setupWaterHeaterOutputVars(EnergyPlusData &state)
5105 : {
5106 :
5107 : // Setup report variables for WaterHeater:Mixed
5108 : // CurrentModuleObject='WaterHeater:Mixed'
5109 390 : SetupOutputVariable(state,
5110 : "Water Heater Tank Temperature",
5111 : Constant::Units::C,
5112 195 : this->TankTempAvg,
5113 : OutputProcessor::TimeStepType::System,
5114 : OutputProcessor::StoreType::Average,
5115 195 : this->Name);
5116 :
5117 390 : SetupOutputVariable(state,
5118 : "Water Heater Final Tank Temperature",
5119 : Constant::Units::C,
5120 195 : this->TankTemp,
5121 : OutputProcessor::TimeStepType::System,
5122 : OutputProcessor::StoreType::Average,
5123 195 : this->Name);
5124 :
5125 390 : SetupOutputVariable(state,
5126 : "Water Heater Heat Loss Rate",
5127 : Constant::Units::W,
5128 195 : this->LossRate,
5129 : OutputProcessor::TimeStepType::System,
5130 : OutputProcessor::StoreType::Average,
5131 195 : this->Name);
5132 390 : SetupOutputVariable(state,
5133 : "Water Heater Heat Loss Energy",
5134 : Constant::Units::J,
5135 195 : this->LossEnergy,
5136 : OutputProcessor::TimeStepType::System,
5137 : OutputProcessor::StoreType::Sum,
5138 195 : this->Name);
5139 :
5140 390 : SetupOutputVariable(state,
5141 : "Water Heater Use Side Mass Flow Rate",
5142 : Constant::Units::kg_s,
5143 195 : this->UseMassFlowRate,
5144 : OutputProcessor::TimeStepType::System,
5145 : OutputProcessor::StoreType::Average,
5146 195 : this->Name);
5147 :
5148 390 : SetupOutputVariable(state,
5149 : "Water Heater Use Side Inlet Temperature",
5150 : Constant::Units::C,
5151 195 : this->UseInletTemp,
5152 : OutputProcessor::TimeStepType::System,
5153 : OutputProcessor::StoreType::Average,
5154 195 : this->Name);
5155 :
5156 390 : SetupOutputVariable(state,
5157 : "Water Heater Use Side Outlet Temperature",
5158 : Constant::Units::C,
5159 195 : this->UseOutletTemp,
5160 : OutputProcessor::TimeStepType::System,
5161 : OutputProcessor::StoreType::Average,
5162 195 : this->Name);
5163 :
5164 390 : SetupOutputVariable(state,
5165 : "Water Heater Use Side Heat Transfer Rate",
5166 : Constant::Units::W,
5167 195 : this->UseRate,
5168 : OutputProcessor::TimeStepType::System,
5169 : OutputProcessor::StoreType::Average,
5170 195 : this->Name);
5171 390 : SetupOutputVariable(state,
5172 : "Water Heater Use Side Heat Transfer Energy",
5173 : Constant::Units::J,
5174 195 : this->UseEnergy,
5175 : OutputProcessor::TimeStepType::System,
5176 : OutputProcessor::StoreType::Sum,
5177 195 : this->Name);
5178 :
5179 390 : SetupOutputVariable(state,
5180 : "Water Heater Source Side Mass Flow Rate",
5181 : Constant::Units::kg_s,
5182 195 : this->SourceMassFlowRate,
5183 : OutputProcessor::TimeStepType::System,
5184 : OutputProcessor::StoreType::Average,
5185 195 : this->Name);
5186 :
5187 390 : SetupOutputVariable(state,
5188 : "Water Heater Source Side Inlet Temperature",
5189 : Constant::Units::C,
5190 195 : this->SourceInletTemp,
5191 : OutputProcessor::TimeStepType::System,
5192 : OutputProcessor::StoreType::Average,
5193 195 : this->Name);
5194 :
5195 390 : SetupOutputVariable(state,
5196 : "Water Heater Source Side Outlet Temperature",
5197 : Constant::Units::C,
5198 195 : this->SourceOutletTemp,
5199 : OutputProcessor::TimeStepType::System,
5200 : OutputProcessor::StoreType::Average,
5201 195 : this->Name);
5202 :
5203 390 : SetupOutputVariable(state,
5204 : "Water Heater Source Side Heat Transfer Rate",
5205 : Constant::Units::W,
5206 195 : this->SourceRate,
5207 : OutputProcessor::TimeStepType::System,
5208 : OutputProcessor::StoreType::Average,
5209 195 : this->Name);
5210 390 : SetupOutputVariable(state,
5211 : "Water Heater Source Side Heat Transfer Energy",
5212 : Constant::Units::J,
5213 195 : this->SourceEnergy,
5214 : OutputProcessor::TimeStepType::System,
5215 : OutputProcessor::StoreType::Sum,
5216 195 : this->Name,
5217 : Constant::eResource::PlantLoopHeatingDemand,
5218 : OutputProcessor::Group::Plant,
5219 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5220 : this->EndUseSubcategoryName);
5221 :
5222 390 : SetupOutputVariable(state,
5223 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Rate",
5224 : Constant::Units::W,
5225 195 : this->OffCycParaRateToTank,
5226 : OutputProcessor::TimeStepType::System,
5227 : OutputProcessor::StoreType::Average,
5228 195 : this->Name);
5229 390 : SetupOutputVariable(state,
5230 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Energy",
5231 : Constant::Units::J,
5232 195 : this->OffCycParaEnergyToTank,
5233 : OutputProcessor::TimeStepType::System,
5234 : OutputProcessor::StoreType::Sum,
5235 195 : this->Name);
5236 :
5237 390 : SetupOutputVariable(state,
5238 : "Water Heater On Cycle Parasitic Tank Heat Transfer Rate",
5239 : Constant::Units::W,
5240 195 : this->OnCycParaRateToTank,
5241 : OutputProcessor::TimeStepType::System,
5242 : OutputProcessor::StoreType::Average,
5243 195 : this->Name);
5244 390 : SetupOutputVariable(state,
5245 : "Water Heater On Cycle Parasitic Tank Heat Transfer Energy",
5246 : Constant::Units::J,
5247 195 : this->OnCycParaEnergyToTank,
5248 : OutputProcessor::TimeStepType::System,
5249 : OutputProcessor::StoreType::Sum,
5250 195 : this->Name);
5251 :
5252 390 : SetupOutputVariable(state,
5253 : "Water Heater Total Demand Heat Transfer Rate",
5254 : Constant::Units::W,
5255 195 : this->TotalDemandRate,
5256 : OutputProcessor::TimeStepType::System,
5257 : OutputProcessor::StoreType::Average,
5258 195 : this->Name);
5259 390 : SetupOutputVariable(state,
5260 : "Water Heater Total Demand Heat Transfer Energy",
5261 : Constant::Units::J,
5262 195 : this->TotalDemandEnergy,
5263 : OutputProcessor::TimeStepType::System,
5264 : OutputProcessor::StoreType::Sum,
5265 195 : this->Name);
5266 :
5267 390 : SetupOutputVariable(state,
5268 : "Water Heater Heating Rate",
5269 : Constant::Units::W,
5270 195 : this->HeaterRate,
5271 : OutputProcessor::TimeStepType::System,
5272 : OutputProcessor::StoreType::Average,
5273 195 : this->Name);
5274 390 : SetupOutputVariable(state,
5275 : "Water Heater Heating Energy",
5276 : Constant::Units::J,
5277 195 : this->HeaterEnergy,
5278 : OutputProcessor::TimeStepType::System,
5279 : OutputProcessor::StoreType::Sum,
5280 195 : this->Name);
5281 :
5282 390 : SetupOutputVariable(state,
5283 : "Water Heater Unmet Demand Heat Transfer Rate",
5284 : Constant::Units::W,
5285 195 : this->UnmetRate,
5286 : OutputProcessor::TimeStepType::System,
5287 : OutputProcessor::StoreType::Average,
5288 195 : this->Name);
5289 390 : SetupOutputVariable(state,
5290 : "Water Heater Unmet Demand Heat Transfer Energy",
5291 : Constant::Units::J,
5292 195 : this->UnmetEnergy,
5293 : OutputProcessor::TimeStepType::System,
5294 : OutputProcessor::StoreType::Sum,
5295 195 : this->Name);
5296 :
5297 390 : SetupOutputVariable(state,
5298 : "Water Heater Venting Heat Transfer Rate",
5299 : Constant::Units::W,
5300 195 : this->VentRate,
5301 : OutputProcessor::TimeStepType::System,
5302 : OutputProcessor::StoreType::Average,
5303 195 : this->Name);
5304 390 : SetupOutputVariable(state,
5305 : "Water Heater Venting Heat Transfer Energy",
5306 : Constant::Units::J,
5307 195 : this->VentEnergy,
5308 : OutputProcessor::TimeStepType::System,
5309 : OutputProcessor::StoreType::Sum,
5310 195 : this->Name);
5311 :
5312 390 : SetupOutputVariable(state,
5313 : "Water Heater Net Heat Transfer Rate",
5314 : Constant::Units::W,
5315 195 : this->NetHeatTransferRate,
5316 : OutputProcessor::TimeStepType::System,
5317 : OutputProcessor::StoreType::Average,
5318 195 : this->Name);
5319 390 : SetupOutputVariable(state,
5320 : "Water Heater Net Heat Transfer Energy",
5321 : Constant::Units::J,
5322 195 : this->NetHeatTransferEnergy,
5323 : OutputProcessor::TimeStepType::System,
5324 : OutputProcessor::StoreType::Sum,
5325 195 : this->Name);
5326 :
5327 195 : SetupOutputVariable(state,
5328 : "Water Heater Cycle On Count",
5329 : Constant::Units::None,
5330 195 : this->CycleOnCount,
5331 : OutputProcessor::TimeStepType::System,
5332 : OutputProcessor::StoreType::Sum,
5333 195 : this->Name);
5334 390 : SetupOutputVariable(state,
5335 : "Water Heater Runtime Fraction",
5336 : Constant::Units::None,
5337 195 : this->RuntimeFraction,
5338 : OutputProcessor::TimeStepType::System,
5339 : OutputProcessor::StoreType::Average,
5340 195 : this->Name);
5341 390 : SetupOutputVariable(state,
5342 : "Water Heater Part Load Ratio",
5343 : Constant::Units::None,
5344 195 : this->PartLoadRatio,
5345 : OutputProcessor::TimeStepType::System,
5346 : OutputProcessor::StoreType::Average,
5347 195 : this->Name);
5348 :
5349 585 : SetupOutputVariable(state,
5350 390 : format("Water Heater {} Rate", Constant::eFuelNames[static_cast<int>(this->FuelType)]),
5351 : Constant::Units::W,
5352 195 : this->FuelRate,
5353 : OutputProcessor::TimeStepType::System,
5354 : OutputProcessor::StoreType::Average,
5355 195 : this->Name);
5356 585 : SetupOutputVariable(state,
5357 390 : format("Water Heater {} Energy", Constant::eFuelNames[static_cast<int>(this->FuelType)]),
5358 : Constant::Units::J,
5359 195 : this->FuelEnergy,
5360 : OutputProcessor::TimeStepType::System,
5361 : OutputProcessor::StoreType::Sum,
5362 195 : this->Name,
5363 195 : Constant::eFuel2eResource[(int)this->FuelType],
5364 : OutputProcessor::Group::Plant,
5365 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5366 : this->EndUseSubcategoryName);
5367 :
5368 585 : SetupOutputVariable(state,
5369 390 : format("Water Heater Off Cycle Parasitic {} Rate", Constant::eFuelNames[static_cast<int>(this->OffCycParaFuelType)]),
5370 : Constant::Units::W,
5371 195 : this->OffCycParaFuelRate,
5372 : OutputProcessor::TimeStepType::System,
5373 : OutputProcessor::StoreType::Average,
5374 195 : this->Name);
5375 585 : SetupOutputVariable(state,
5376 390 : format("Water Heater Off Cycle Parasitic {} Energy", Constant::eFuelNames[static_cast<int>(this->OffCycParaFuelType)]),
5377 : Constant::Units::J,
5378 195 : this->OffCycParaFuelEnergy,
5379 : OutputProcessor::TimeStepType::System,
5380 : OutputProcessor::StoreType::Sum,
5381 195 : this->Name,
5382 195 : Constant::eFuel2eResource[(int)this->OffCycParaFuelType],
5383 : OutputProcessor::Group::Plant,
5384 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5385 : this->EndUseSubcategoryName);
5386 :
5387 585 : SetupOutputVariable(state,
5388 390 : format("Water Heater On Cycle Parasitic {} Rate", Constant::eFuelNames[static_cast<int>(this->OnCycParaFuelType)]),
5389 : Constant::Units::W,
5390 195 : this->OnCycParaFuelRate,
5391 : OutputProcessor::TimeStepType::System,
5392 : OutputProcessor::StoreType::Average,
5393 195 : this->Name);
5394 585 : SetupOutputVariable(state,
5395 390 : format("Water Heater On Cycle Parasitic {} Energy", Constant::eFuelNames[static_cast<int>(this->OnCycParaFuelType)]),
5396 : Constant::Units::J,
5397 195 : this->OnCycParaFuelEnergy,
5398 : OutputProcessor::TimeStepType::System,
5399 : OutputProcessor::StoreType::Sum,
5400 195 : this->Name,
5401 195 : Constant::eFuel2eResource[(int)this->OnCycParaFuelType],
5402 : OutputProcessor::Group::Plant,
5403 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5404 : this->EndUseSubcategoryName);
5405 :
5406 390 : SetupOutputVariable(state,
5407 : "Water Heater Water Volume Flow Rate",
5408 : Constant::Units::m3_s,
5409 195 : this->VolFlowRate,
5410 : OutputProcessor::TimeStepType::System,
5411 : OutputProcessor::StoreType::Average,
5412 195 : this->Name);
5413 390 : SetupOutputVariable(state,
5414 : "Water Heater Water Volume",
5415 : Constant::Units::m3,
5416 195 : this->VolumeConsumed,
5417 : OutputProcessor::TimeStepType::System,
5418 : OutputProcessor::StoreType::Sum,
5419 195 : this->Name,
5420 : Constant::eResource::Water,
5421 : OutputProcessor::Group::Plant,
5422 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5423 : this->EndUseSubcategoryName);
5424 390 : SetupOutputVariable(state,
5425 : "Water Heater Mains Water Volume",
5426 : Constant::Units::m3,
5427 195 : this->VolumeConsumed,
5428 : OutputProcessor::TimeStepType::System,
5429 : OutputProcessor::StoreType::Sum,
5430 195 : this->Name,
5431 : Constant::eResource::MainsWater,
5432 : OutputProcessor::Group::Plant,
5433 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5434 : this->EndUseSubcategoryName);
5435 :
5436 195 : if (this->HeatPumpNum > 0) {
5437 : // CurrentModuleObject='WaterHeater:HeatPump:PumpedCondenser'
5438 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
5439 46 : SetupOutputVariable(state,
5440 : "Water Heater Compressor Part Load Ratio",
5441 : Constant::Units::None,
5442 23 : HPWH.HeatingPLR,
5443 : OutputProcessor::TimeStepType::System,
5444 : OutputProcessor::StoreType::Average,
5445 23 : HPWH.Name);
5446 46 : SetupOutputVariable(state,
5447 : "Water Heater Off Cycle Ancillary Electricity Rate",
5448 : Constant::Units::W,
5449 23 : HPWH.OffCycParaFuelRate,
5450 : OutputProcessor::TimeStepType::System,
5451 : OutputProcessor::StoreType::Average,
5452 23 : HPWH.Name);
5453 46 : SetupOutputVariable(state,
5454 : "Water Heater Off Cycle Ancillary Electricity Energy",
5455 : Constant::Units::J,
5456 23 : HPWH.OffCycParaFuelEnergy,
5457 : OutputProcessor::TimeStepType::System,
5458 : OutputProcessor::StoreType::Sum,
5459 23 : HPWH.Name,
5460 : Constant::eResource::Electricity,
5461 : OutputProcessor::Group::Plant,
5462 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5463 : "Water Heater Parasitic");
5464 46 : SetupOutputVariable(state,
5465 : "Water Heater On Cycle Ancillary Electricity Rate",
5466 : Constant::Units::W,
5467 23 : HPWH.OnCycParaFuelRate,
5468 : OutputProcessor::TimeStepType::System,
5469 : OutputProcessor::StoreType::Average,
5470 23 : HPWH.Name);
5471 46 : SetupOutputVariable(state,
5472 : "Water Heater On Cycle Ancillary Electricity Energy",
5473 : Constant::Units::J,
5474 23 : HPWH.OnCycParaFuelEnergy,
5475 : OutputProcessor::TimeStepType::System,
5476 : OutputProcessor::StoreType::Sum,
5477 23 : HPWH.Name,
5478 : Constant::eResource::Electricity,
5479 : OutputProcessor::Group::Plant,
5480 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5481 : "Water Heater Parasitic");
5482 46 : SetupOutputVariable(state,
5483 : "Water Heater Heat Pump Control Tank Temperature",
5484 : Constant::Units::C,
5485 23 : HPWH.ControlTempAvg,
5486 : OutputProcessor::TimeStepType::System,
5487 : OutputProcessor::StoreType::Average,
5488 23 : HPWH.Name);
5489 46 : SetupOutputVariable(state,
5490 : "Water Heater Heat Pump Control Tank Final Temperature",
5491 : Constant::Units::C,
5492 23 : HPWH.ControlTempFinal,
5493 : OutputProcessor::TimeStepType::System,
5494 : OutputProcessor::StoreType::Average,
5495 23 : HPWH.Name);
5496 : }
5497 :
5498 195 : if (this->DesuperheaterNum > 0) {
5499 : // CurrentModuleObject='Coil:WaterHeating:Desuperheater'
5500 12 : SetupOutputVariable(state,
5501 : "Water Heater Part Load Ratio",
5502 : Constant::Units::None,
5503 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR,
5504 : OutputProcessor::TimeStepType::System,
5505 : OutputProcessor::StoreType::Average,
5506 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5507 12 : SetupOutputVariable(state,
5508 : "Water Heater On Cycle Parasitic Electricity Rate",
5509 : Constant::Units::W,
5510 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelRate,
5511 : OutputProcessor::TimeStepType::System,
5512 : OutputProcessor::StoreType::Average,
5513 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5514 12 : SetupOutputVariable(state,
5515 : "Water Heater On Cycle Parasitic Electricity Energy",
5516 : Constant::Units::J,
5517 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelEnergy,
5518 : OutputProcessor::TimeStepType::System,
5519 : OutputProcessor::StoreType::Sum,
5520 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5521 : Constant::eResource::Electricity,
5522 : OutputProcessor::Group::Plant,
5523 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5524 : "Water Heater Parasitic");
5525 12 : SetupOutputVariable(state,
5526 : "Water Heater Off Cycle Parasitic Electricity Rate",
5527 : Constant::Units::W,
5528 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelRate,
5529 : OutputProcessor::TimeStepType::System,
5530 : OutputProcessor::StoreType::Average,
5531 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5532 12 : SetupOutputVariable(state,
5533 : "Water Heater Off Cycle Parasitic Electricity Energy",
5534 : Constant::Units::J,
5535 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelEnergy,
5536 : OutputProcessor::TimeStepType::System,
5537 : OutputProcessor::StoreType::Sum,
5538 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5539 : Constant::eResource::Electricity,
5540 : OutputProcessor::Group::Plant,
5541 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5542 : "Water Heater Parasitic");
5543 12 : SetupOutputVariable(state,
5544 : "Water Heater Heat Reclaim Efficiency Modifier Multiplier",
5545 : Constant::Units::None,
5546 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HEffFTempOutput,
5547 : OutputProcessor::TimeStepType::System,
5548 : OutputProcessor::StoreType::Average,
5549 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5550 12 : SetupOutputVariable(state,
5551 : "Water Heater Pump Electricity Rate",
5552 : Constant::Units::W,
5553 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpPower,
5554 : OutputProcessor::TimeStepType::System,
5555 : OutputProcessor::StoreType::Average,
5556 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5557 12 : SetupOutputVariable(state,
5558 : "Water Heater Pump Electricity Energy",
5559 : Constant::Units::J,
5560 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpEnergy,
5561 : OutputProcessor::TimeStepType::System,
5562 : OutputProcessor::StoreType::Sum,
5563 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5564 : Constant::eResource::Electricity,
5565 : OutputProcessor::Group::Plant,
5566 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5567 : "Desuperheater Pump");
5568 12 : SetupOutputVariable(state,
5569 : "Water Heater Heating Rate",
5570 : Constant::Units::W,
5571 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate,
5572 : OutputProcessor::TimeStepType::System,
5573 : OutputProcessor::StoreType::Average,
5574 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5575 12 : SetupOutputVariable(state,
5576 : "Water Heater Heating Energy",
5577 : Constant::Units::J,
5578 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterEnergy,
5579 : OutputProcessor::TimeStepType::System,
5580 : OutputProcessor::StoreType::Sum,
5581 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5582 : Constant::eResource::EnergyTransfer,
5583 : OutputProcessor::Group::Plant,
5584 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5585 : "Water Heater");
5586 : }
5587 :
5588 : // Setup report variables for WaterHeater:Stratified
5589 : // CurrentModuleObject='WaterHeater:Stratified'
5590 195 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5591 :
5592 32 : SetupOutputVariable(state,
5593 : "Water Heater Heater 1 Heating Rate",
5594 : Constant::Units::W,
5595 16 : this->HeaterRate1,
5596 : OutputProcessor::TimeStepType::System,
5597 : OutputProcessor::StoreType::Average,
5598 16 : this->Name);
5599 32 : SetupOutputVariable(state,
5600 : "Water Heater Heater 2 Heating Rate",
5601 : Constant::Units::W,
5602 16 : this->HeaterRate2,
5603 : OutputProcessor::TimeStepType::System,
5604 : OutputProcessor::StoreType::Average,
5605 16 : this->Name);
5606 :
5607 32 : SetupOutputVariable(state,
5608 : "Water Heater Heater 1 Heating Energy",
5609 : Constant::Units::J,
5610 16 : this->HeaterEnergy1,
5611 : OutputProcessor::TimeStepType::System,
5612 : OutputProcessor::StoreType::Sum,
5613 16 : this->Name);
5614 32 : SetupOutputVariable(state,
5615 : "Water Heater Heater 2 Heating Energy",
5616 : Constant::Units::J,
5617 16 : this->HeaterEnergy2,
5618 : OutputProcessor::TimeStepType::System,
5619 : OutputProcessor::StoreType::Sum,
5620 16 : this->Name);
5621 :
5622 16 : SetupOutputVariable(state,
5623 : "Water Heater Heater 1 Cycle On Count",
5624 : Constant::Units::None,
5625 16 : this->CycleOnCount1,
5626 : OutputProcessor::TimeStepType::System,
5627 : OutputProcessor::StoreType::Sum,
5628 16 : this->Name);
5629 16 : SetupOutputVariable(state,
5630 : "Water Heater Heater 2 Cycle On Count",
5631 : Constant::Units::None,
5632 16 : this->CycleOnCount2,
5633 : OutputProcessor::TimeStepType::System,
5634 : OutputProcessor::StoreType::Sum,
5635 16 : this->Name);
5636 :
5637 32 : SetupOutputVariable(state,
5638 : "Water Heater Heater 1 Runtime Fraction",
5639 : Constant::Units::None,
5640 16 : this->RuntimeFraction1,
5641 : OutputProcessor::TimeStepType::System,
5642 : OutputProcessor::StoreType::Average,
5643 16 : this->Name);
5644 32 : SetupOutputVariable(state,
5645 : "Water Heater Heater 2 Runtime Fraction",
5646 : Constant::Units::None,
5647 16 : this->RuntimeFraction2,
5648 : OutputProcessor::TimeStepType::System,
5649 : OutputProcessor::StoreType::Average,
5650 16 : this->Name);
5651 :
5652 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5653 384 : SetupOutputVariable(state,
5654 256 : format("Water Heater Temperature Node {}", NodeNum),
5655 : Constant::Units::C,
5656 128 : this->Node(NodeNum).TempAvg,
5657 : OutputProcessor::TimeStepType::System,
5658 : OutputProcessor::StoreType::Average,
5659 128 : this->Name);
5660 : }
5661 :
5662 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5663 384 : SetupOutputVariable(state,
5664 256 : format("Water Heater Final Temperature Node {}", NodeNum),
5665 : Constant::Units::C,
5666 128 : this->Node(NodeNum).Temp,
5667 : OutputProcessor::TimeStepType::System,
5668 : OutputProcessor::StoreType::Average,
5669 128 : this->Name);
5670 : }
5671 : }
5672 :
5673 195 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5674 :
5675 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5676 : static constexpr std::string_view Format_723("Water Heater Stratified Node Information,{},{:.4T},{:.4T},{:.3T},{:.4T},{:.4T},{},{}\n");
5677 128 : print(state.files.eio,
5678 : Format_723,
5679 : NodeNum,
5680 128 : this->Node(NodeNum).Height,
5681 128 : this->Node(NodeNum).Volume,
5682 128 : this->Node(NodeNum).MaxCapacity,
5683 128 : this->Node(NodeNum).OffCycLossCoeff,
5684 128 : this->Node(NodeNum).OnCycLossCoeff,
5685 128 : this->Node(NodeNum).Inlets,
5686 128 : this->Node(NodeNum).Outlets);
5687 : }
5688 : }
5689 195 : }
5690 :
5691 0 : void WaterThermalTankData::ValidatePLFCurve(EnergyPlusData &state, int const CurveIndex, bool &IsValid)
5692 : {
5693 :
5694 : // SUBROUTINE INFORMATION:
5695 : // AUTHOR Peter Graham Ellis
5696 : // DATE WRITTEN February 2005
5697 : // MODIFIED na
5698 : // RE-ENGINEERED na
5699 :
5700 : // PURPOSE OF THIS SUBROUTINE:
5701 : // Validates the Part Load Factor curve by making sure it can never be less than or equal to zero
5702 : // over the domain of Part Load Ratio inputs from 0 to 1.
5703 :
5704 : // METHODOLOGY EMPLOYED:
5705 : // Currently can only check 0 and 1. Need changes in CurveManager to be able to check minimums and
5706 : // maximums.
5707 :
5708 0 : IsValid = true;
5709 :
5710 : // Check 0 and 1
5711 0 : if (Curve::CurveValue(state, CurveIndex, 0.0) <= 0) {
5712 0 : IsValid = false;
5713 : }
5714 0 : if (Curve::CurveValue(state, CurveIndex, 1.0) <= 0) {
5715 0 : IsValid = false;
5716 : }
5717 0 : }
5718 :
5719 18 : void WaterThermalTankData::SetupStratifiedNodes(EnergyPlusData &state)
5720 : {
5721 :
5722 : // SUBROUTINE INFORMATION:
5723 : // AUTHOR Peter Graham Ellis
5724 : // DATE WRITTEN January 2007
5725 : // MODIFIED na
5726 : // RE-ENGINEERED na
5727 :
5728 : // PURPOSE OF THIS SUBROUTINE:
5729 : // Sets up node properties based on the tank shape, i.e., vertical cylinder, horizontal cylinder, or other.
5730 : // Node height, skin area, vertical conduction area, and loss coefficients are calculated and assigned.
5731 : // Heating elements, parasitics, and fluid inlet and outlet flows are assigned according to node height.
5732 :
5733 : // METHODOLOGY EMPLOYED:
5734 : // Tank is divided into nodes of equal mass. For horizontal cylinders, node heights are calculated using
5735 : // the Newton-Raphson iterative method. For vertical cylinders and other shapes, the node heights are calculated
5736 : // using basic geometry.
5737 :
5738 : static constexpr std::string_view RoutineName("GetWaterThermalTankInput");
5739 :
5740 18 : constexpr Real64 Tolerance(1.0e-8); // Tolerance for Newton-Raphson solution
5741 18 : constexpr Real64 FluidCond(0.6); // Conductivity of water (W/m-K)
5742 :
5743 18 : int NumNodes = this->Nodes;
5744 18 : this->Node.allocate(NumNodes);
5745 : Real64 rho;
5746 18 : if ((this->UseSidePlantLoc.loopNum > 0) && allocated(state.dataPlnt->PlantLoop)) {
5747 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
5748 : } else {
5749 18 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
5750 : }
5751 :
5752 18 : Real64 NodeMass = this->Volume * rho / NumNodes;
5753 : Real64 TankHeight;
5754 :
5755 : // Mixing rate set to 50% of the max value for dt = 1.0
5756 18 : this->InversionMixingRate = NodeMass * 0.5 * 1.0;
5757 :
5758 18 : if ((this->Shape == TankShape::VertCylinder) || (this->Shape == TankShape::Other)) {
5759 18 : TankHeight = this->Height;
5760 18 : Real64 EndArea = this->Volume / TankHeight;
5761 18 : Real64 NodeHeight = TankHeight / NumNodes;
5762 18 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * EndArea / NodeHeight;
5763 :
5764 : Real64 Perimeter_loc;
5765 18 : if (this->Shape == TankShape::VertCylinder) {
5766 18 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
5767 18 : Perimeter_loc = 2.0 * Constant::Pi * Radius;
5768 : } else { // TankShapeOther
5769 0 : Perimeter_loc = this->Perimeter;
5770 : }
5771 :
5772 158 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5773 140 : this->Node(NodeNum).Mass = NodeMass;
5774 140 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5775 140 : this->Node(NodeNum).Height = NodeHeight;
5776 140 : this->Node(NodeNum).CondCoeffUp = CondCoeff;
5777 140 : this->Node(NodeNum).CondCoeffDn = CondCoeff;
5778 :
5779 : Real64 SkinArea;
5780 140 : if ((NodeNum == 1) || (NodeNum == NumNodes)) {
5781 34 : SkinArea = Perimeter_loc * NodeHeight + EndArea;
5782 : } else {
5783 106 : SkinArea = Perimeter_loc * NodeHeight;
5784 : }
5785 :
5786 140 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5787 :
5788 140 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5789 :
5790 : } // NodeNum
5791 :
5792 18 : this->Node(1).CondCoeffUp = 0.0;
5793 18 : this->Node(NumNodes).CondCoeffDn = 0.0;
5794 :
5795 18 : } else { // Tank%Shape == TankShapeHorizCylinder
5796 0 : Real64 TankLength = this->Height; // Height is the length in the axial direction
5797 0 : Real64 EndArea = this->Volume / TankLength;
5798 0 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
5799 0 : TankHeight = 2.0 * Radius; // Actual vertical height
5800 0 : Real64 NodeEndArea = EndArea / NumNodes;
5801 :
5802 0 : Real64 R = Radius;
5803 0 : Real64 H0 = 0.0;
5804 0 : Real64 ChordLength = 0.0;
5805 0 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5806 0 : this->Node(NodeNum).Mass = NodeMass;
5807 0 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5808 : Real64 H;
5809 0 : if (NodeNum == NumNodes) {
5810 0 : H = TankHeight;
5811 : } else {
5812 : // Use the Newton-Raphson method to solve the nonlinear algebraic equation for node height
5813 0 : H = H0 + TankHeight / NumNodes; // Initial guess
5814 :
5815 : while (true) {
5816 0 : Real64 a = std::sqrt(H);
5817 0 : Real64 b = std::sqrt(2.0 * R - H);
5818 0 : Real64 c = 2.0 * R * R * std::atan(a / b) - (2.0 * R * R - 3.0 * H * R + H * H) * (a / b);
5819 : Real64 c0;
5820 0 : if (H0 > 0.0) {
5821 0 : Real64 a0 = std::sqrt(H0);
5822 0 : Real64 b0 = std::sqrt(2.0 * R - H0);
5823 0 : c0 = 2.0 * R * R * std::atan(a0 / b0) - (2.0 * R * R - 3.0 * H0 * R + H0 * H0) * (a0 / b0);
5824 : } else {
5825 0 : c0 = 0.0;
5826 : }
5827 :
5828 0 : Real64 ApproxEndArea = c - c0; // Area approximated by iteration
5829 0 : Real64 G = ApproxEndArea - NodeEndArea; // G is the function that should converge to zero
5830 :
5831 0 : if (std::abs(G) < Tolerance) {
5832 0 : break; // Converged !!!
5833 : } else {
5834 0 : H -= G / (2.0 * a * b); // Calculate next guess: H = Hprev - G/G'
5835 : }
5836 0 : } // Newton-Raphson
5837 : }
5838 :
5839 0 : this->Node(NodeNum).Height = H - H0;
5840 :
5841 0 : if (NodeNum > 1) {
5842 0 : Real64 CrossArea = 2.0 * ChordLength * TankLength; // Use old ChordLength from previous node
5843 0 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * CrossArea / (0.5 * (H - H0) + 0.5 * this->Node(NodeNum - 1).Height);
5844 0 : this->Node(NodeNum - 1).CondCoeffUp = CondCoeff; // Set for previous node
5845 0 : this->Node(NodeNum).CondCoeffDn = CondCoeff; // Set for this node
5846 : }
5847 :
5848 0 : ChordLength = std::sqrt(2.0 * R * H - H * H); // Calc new ChordLength to be used with next node
5849 :
5850 0 : Real64 Perimeter_loc = 2.0 * R * (std::acos((R - H) / R) - std::acos((R - H0) / R)); // Segments of circular perimeter
5851 0 : Real64 SkinArea = Perimeter_loc * TankLength + 2.0 * NodeEndArea;
5852 :
5853 0 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5854 :
5855 0 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5856 : // Although it doesn't make much sense to have a flue in a horizontal tank, keep it in anyway
5857 :
5858 0 : H0 = H;
5859 : } // NodeNum
5860 :
5861 0 : this->Node(1).CondCoeffUp = 0.0;
5862 0 : this->Node(NumNodes).CondCoeffDn = 0.0;
5863 : }
5864 :
5865 : // Loop through nodes again (from top to bottom this time) and assign heating elements, parasitics, flow inlets/outlets
5866 : // according to their vertical heights in the tank
5867 18 : Real64 H0 = TankHeight;
5868 158 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5869 : Real64 H;
5870 140 : if (NodeNum == NumNodes) {
5871 18 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
5872 : } else {
5873 122 : H = H0 - this->Node(NodeNum).Height;
5874 : }
5875 :
5876 : // Assign heater elements to the nodes at the specified heights
5877 140 : if ((this->HeaterHeight1 <= H0) && (this->HeaterHeight1 > H)) {
5878 : // sensor node will not get set if user enters 0 for this heater capacity
5879 : // (Tank%MaxCapacity > 0.0d0)) THEN
5880 18 : this->HeaterNode1 = NodeNum;
5881 18 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity;
5882 : }
5883 :
5884 140 : if ((this->HeaterHeight2 <= H0) && (this->HeaterHeight2 > H)) {
5885 : // sensor node will not get set if user enters 0 for this heater capacity
5886 : // .AND. (Tank%MaxCapacity2 > 0.0d0)) THEN
5887 18 : this->HeaterNode2 = NodeNum;
5888 :
5889 18 : if ((NodeNum == this->HeaterNode1) && (this->StratifiedControlMode == PriorityControlMode::Simultaneous)) {
5890 0 : this->Node(NodeNum).MaxCapacity += this->MaxCapacity2;
5891 : } else {
5892 18 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity2;
5893 : }
5894 : }
5895 :
5896 : // Assign parasitic heat gains to the nodes at the specified heights
5897 140 : if ((this->OffCycParaHeight <= H0) && (this->OffCycParaHeight > H)) {
5898 18 : this->Node(NodeNum).OffCycParaLoad = this->OffCycParaFracToTank * this->OffCycParaLoad;
5899 : }
5900 :
5901 140 : if ((this->OnCycParaHeight <= H0) && (this->OnCycParaHeight > H)) {
5902 18 : this->Node(NodeNum).OnCycParaLoad = this->OnCycParaFracToTank * this->OnCycParaLoad;
5903 : }
5904 :
5905 : // Assign inlets and outlets to the nodes at the specified heights
5906 140 : if ((this->UseInletHeight <= H0) && (this->UseInletHeight > H)) {
5907 18 : this->UseInletStratNode = NodeNum;
5908 :
5909 18 : if ((this->UseInletNode > 0) || (this->MassFlowRateMax > 0.0)) {
5910 18 : ++this->Node(NodeNum).Inlets;
5911 : }
5912 : }
5913 :
5914 140 : if ((this->UseOutletHeight <= H0) && (this->UseOutletHeight > H)) {
5915 18 : this->UseOutletStratNode = NodeNum;
5916 :
5917 18 : if ((this->UseOutletNode > 0) || (this->MassFlowRateMax > 0.0)) {
5918 18 : ++this->Node(NodeNum).Outlets;
5919 : }
5920 : }
5921 :
5922 140 : if ((this->SourceInletHeight <= H0) && (this->SourceInletHeight > H) && (this->SourceInletNode > 0)) {
5923 :
5924 11 : this->SourceInletStratNode = NodeNum;
5925 11 : ++this->Node(NodeNum).Inlets;
5926 : }
5927 :
5928 140 : if ((this->SourceOutletHeight <= H0) && (this->SourceOutletHeight > H) && (this->SourceOutletNode > 0)) {
5929 :
5930 11 : this->SourceOutletStratNode = NodeNum;
5931 11 : ++this->Node(NodeNum).Outlets;
5932 : }
5933 :
5934 140 : H0 = H;
5935 : } // NodeNum
5936 18 : }
5937 :
5938 6337420 : void WaterThermalTankData::initialize(EnergyPlusData &state, bool const FirstHVACIteration)
5939 : {
5940 :
5941 : // SUBROUTINE INFORMATION:
5942 : // AUTHOR Peter Graham Ellis
5943 : // DATE WRITTEN February 2004
5944 : // MODIFIED FSEC, July 2005
5945 : // Brent Griffith, October 2007 indirect fired water heater
5946 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
5947 : // RE-ENGINEERED na
5948 :
5949 : // PURPOSE OF THIS SUBROUTINE:
5950 : // Initialize the water heater, heat pump water heater, or desuperheater heating coil objects during the simulation.
5951 : // determine flow rates thru use side and source side plant connections (if any)
5952 :
5953 : // METHODOLOGY EMPLOYED:
5954 : // Inlet and outlet nodes are initialized. Scheduled values are retrieved for the current timestep.
5955 :
5956 6337420 : auto &ZoneEqSizing = state.dataSize->ZoneEqSizing;
5957 :
5958 : static constexpr std::string_view RoutineName("InitWaterThermalTank");
5959 : static constexpr std::string_view GetWaterThermalTankInput("GetWaterThermalTankInput");
5960 : static constexpr std::string_view SizeTankForDemand("SizeTankForDemandSide");
5961 :
5962 6337420 : if (this->scanPlantLoopsFlag && allocated(state.dataPlnt->PlantLoop)) {
5963 201 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
5964 123 : bool errFlag = false;
5965 369 : PlantUtilities::ScanPlantLoopsForObject(
5966 246 : state, this->Name, this->WaterThermalTankType, this->UseSidePlantLoc, errFlag, _, _, _, this->UseInletNode, _);
5967 123 : if (errFlag) {
5968 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5969 : }
5970 : }
5971 201 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
5972 : // this is a heat pump water heater, need a separate block because TypeOf_HeatPumpWtrHeater shows up on Branch
5973 : // (input should probably have been the associated tank )
5974 10 : bool errFlag = false;
5975 30 : PlantUtilities::ScanPlantLoopsForObject(state,
5976 10 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
5977 10 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType,
5978 10 : this->UseSidePlantLoc,
5979 : errFlag,
5980 : _,
5981 : _,
5982 : _,
5983 10 : this->UseInletNode,
5984 : _);
5985 10 : if (errFlag) {
5986 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5987 : }
5988 : }
5989 201 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
5990 21 : bool errFlag = false;
5991 63 : PlantUtilities::ScanPlantLoopsForObject(
5992 42 : state, this->Name, this->WaterThermalTankType, this->SrcSidePlantLoc, errFlag, _, _, _, this->SourceInletNode, _);
5993 21 : if (this->UseInletNode > 0) {
5994 21 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->UseSidePlantLoc, this->SrcSidePlantLoc, this->WaterThermalTankType, true);
5995 : }
5996 21 : if (errFlag) {
5997 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5998 : }
5999 : }
6000 201 : this->scanPlantLoopsFlag = false;
6001 : }
6002 :
6003 6337420 : if (this->SetLoopIndexFlag && allocated(state.dataPlnt->PlantLoop)) {
6004 1474 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
6005 : Real64 rho =
6006 1396 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
6007 1396 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6008 1396 : this->Mass = this->Volume * rho;
6009 1396 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
6010 1396 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
6011 0 : ShowSevereError(state,
6012 0 : format("InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = {}", this->Name));
6013 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6014 : }
6015 : }
6016 1474 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
6017 : Real64 rho =
6018 10 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
6019 10 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6020 10 : this->Mass = this->Volume * rho;
6021 10 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
6022 10 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
6023 0 : ShowSevereError(state,
6024 0 : format("InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = {}", this->Name));
6025 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6026 : }
6027 : }
6028 1474 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
6029 : Real64 rho =
6030 379 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
6031 379 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6032 379 : this->SourceSidePlantSizNum = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).PlantSizNum;
6033 379 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0)) {
6034 0 : ShowSevereError(
6035 0 : state, format("InitWaterThermalTank: Did not find Sizing:Plant object for source side of plant thermal tank = {}", this->Name));
6036 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6037 : }
6038 : }
6039 1474 : if (((this->SourceInletNode > 0) && (this->DesuperheaterNum > 0)) || (this->HeatPumpNum > 0)) {
6040 29 : this->SetLoopIndexFlag = false;
6041 : }
6042 1474 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
6043 123 : this->SetLoopIndexFlag = false;
6044 : }
6045 1474 : if (this->StandAlone) {
6046 49 : this->SizeStandAloneWaterHeater(state);
6047 49 : this->SetLoopIndexFlag = false;
6048 : }
6049 6335946 : } else if (this->SetLoopIndexFlag && !state.dataGlobal->AnyPlantInModel) {
6050 0 : if (this->StandAlone) {
6051 0 : this->SizeStandAloneWaterHeater(state);
6052 : }
6053 0 : this->SetLoopIndexFlag = false;
6054 : }
6055 :
6056 6337420 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag && !this->SetLoopIndexFlag) {
6057 :
6058 49777 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
6059 :
6060 48873 : if (this->ControlType == HeaterControlMode::Cycle) {
6061 48355 : this->MinCapacity = this->MaxCapacity;
6062 : }
6063 :
6064 : // check for sizing issues that model can not support
6065 :
6066 : // if stratified tank model, ensure that nominal change over rate is greater than one minute, avoid numerical problems.
6067 :
6068 48873 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
6069 46547 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
6070 3004 : Real64 MaxSideVolFlow = max(this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
6071 :
6072 3004 : if (MaxSideVolFlow > 0.0) { // protect div by zero
6073 2827 : Real64 TankChangeRateScale = this->Volume / MaxSideVolFlow;
6074 2827 : if (TankChangeRateScale < 60.0) { // nominal change over in less than one minute
6075 0 : ShowSevereError(state, "InitWaterThermalTank: Detected problem for stratified tank model. Model cannot be applied.");
6076 0 : ShowContinueError(state, format("Occurs for stratified tank name = {}", this->Name));
6077 0 : ShowContinueError(state, format("Tank volume = {:.4R} [m3]", this->Volume));
6078 0 : ShowContinueError(state, format("Tank use side volume flow rate = {:.4R} [m3/s]", this->UseDesignVolFlowRate));
6079 0 : ShowContinueError(state, format("Tank source side volume flow rate = {:.4R} [m3/s]", this->SourceDesignVolFlowRate));
6080 0 : ShowContinueError(state, format("Nominal tank change over rate = {:.2R} [s]", TankChangeRateScale));
6081 0 : ShowContinueError(
6082 : state, "Change over rate is too fast, increase tank volume, decrease connection flow rates or use mixed tank model");
6083 :
6084 0 : ShowFatalError(state, "InitWaterThermalTank: Simulation halted because of sizing problem in stratified tank model.");
6085 : }
6086 : }
6087 : }
6088 : }
6089 :
6090 : // Clear node initial conditions
6091 49777 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
6092 48284 : state.dataLoopNodes->Node(this->UseInletNode).Temp = 0.0;
6093 : Real64 rho =
6094 48284 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
6095 48284 : this->MassFlowRateMin = this->VolFlowRateMin * rho;
6096 48284 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6097 48284 : PlantUtilities::InitComponentNodes(state, this->MassFlowRateMin, this->PlantUseMassFlowRateMax, this->UseInletNode, this->UseOutletNode);
6098 48284 : this->UseOutletTemp = 0.0;
6099 48284 : this->UseMassFlowRate = 0.0;
6100 48284 : this->SavedUseOutletTemp = 0.0;
6101 :
6102 48284 : this->Mass = this->Volume * rho;
6103 48284 : this->UseBranchControlType = DataPlant::CompData::getPlantComponent(state, this->UseSidePlantLoc).FlowCtrl;
6104 : }
6105 :
6106 49777 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
6107 : Real64 rho =
6108 14760 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
6109 14760 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6110 14760 : PlantUtilities::InitComponentNodes(state, 0.0, this->PlantSourceMassFlowRateMax, this->SourceInletNode, this->SourceOutletNode);
6111 :
6112 14760 : this->SourceOutletTemp = 0.0;
6113 14760 : this->SourceMassFlowRate = 0.0;
6114 14760 : this->SavedSourceOutletTemp = 0.0;
6115 :
6116 14760 : this->SourceBranchControlType = DataPlant::CompData::getPlantComponent(state, this->SrcSidePlantLoc).FlowCtrl;
6117 : }
6118 :
6119 49777 : if ((this->SourceInletNode > 0) && ((this->DesuperheaterNum > 0) || (this->HeatPumpNum > 0))) {
6120 2410 : state.dataLoopNodes->Node(this->SourceInletNode).Temp = 0.0;
6121 2410 : this->SourceOutletTemp = 0.0;
6122 2410 : this->SourceMassFlowRate = 0.0;
6123 2410 : this->SavedSourceOutletTemp = 0.0;
6124 2410 : Real64 rho = this->water->getDensity(state, Constant::InitConvTemp, SizeTankForDemand);
6125 2410 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6126 : }
6127 :
6128 : // Initialize tank temperature to setpoint of first hour of warm up period
6129 : // (use HPWH or Desuperheater heating coil set point if applicable)
6130 49777 : Sched::Schedule *sched = nullptr;
6131 49777 : if (this->HeatPumpNum > 0) {
6132 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6133 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveMode = TankOperatingMode::Floating;
6134 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveWHMode = TankOperatingMode::Floating;
6135 2278 : sched = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).setptTempSched;
6136 47499 : } else if (this->DesuperheaterNum > 0) {
6137 132 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6138 132 : sched = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).setptTempSched;
6139 : } else {
6140 47367 : sched = this->setptTempSched;
6141 : }
6142 :
6143 49777 : if (sched != nullptr) {
6144 49777 : this->TankTemp = sched->getCurrentVal();
6145 49777 : this->SavedTankTemp = this->TankTemp;
6146 :
6147 49777 : if (this->Nodes > 0) {
6148 27013 : for (auto &e : this->Node) {
6149 23926 : e.Temp = e.SavedTemp = this->TankTemp;
6150 : }
6151 : }
6152 : } else {
6153 0 : this->TankTemp = 20.0;
6154 0 : this->SavedTankTemp = this->TankTemp;
6155 :
6156 0 : if (this->Nodes > 0) {
6157 0 : for (auto &e : this->Node) {
6158 0 : e.Temp = e.SavedTemp = this->TankTemp;
6159 : }
6160 : }
6161 : }
6162 49777 : this->SourceOutletTemp = this->SavedTankTemp;
6163 49777 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6164 49777 : this->UseOutletTemp = this->SavedTankTemp;
6165 49777 : this->SavedUseOutletTemp = this->SavedTankTemp;
6166 49777 : this->TankTempAvg = this->SavedTankTemp;
6167 :
6168 49777 : this->SavedHeaterOn1 = false;
6169 49777 : this->SavedHeaterOn2 = false;
6170 49777 : this->Mode = TankOperatingMode::Floating;
6171 49777 : this->SavedMode = TankOperatingMode::Floating;
6172 49777 : this->FirstRecoveryDone = false;
6173 49777 : this->FirstRecoveryFuel = 0.0;
6174 49777 : this->UnmetEnergy = 0.0;
6175 49777 : this->LossEnergy = 0.0;
6176 49777 : this->FlueLossEnergy = 0.0;
6177 49777 : this->UseEnergy = 0.0;
6178 49777 : this->TotalDemandEnergy = 0.0;
6179 49777 : this->SourceEnergy = 0.0;
6180 49777 : this->HeaterEnergy = 0.0;
6181 49777 : this->HeaterEnergy1 = 0.0;
6182 49777 : this->HeaterEnergy2 = 0.0;
6183 49777 : this->FuelEnergy = 0.0;
6184 49777 : this->FuelEnergy1 = 0.0;
6185 49777 : this->FuelEnergy2 = 0.0;
6186 49777 : this->VentEnergy = 0.0;
6187 49777 : this->OffCycParaFuelEnergy = 0.0;
6188 49777 : this->OffCycParaEnergyToTank = 0.0;
6189 49777 : this->OnCycParaFuelEnergy = 0.0;
6190 49777 : this->OnCycParaEnergyToTank = 0.0;
6191 49777 : this->NetHeatTransferEnergy = 0.0;
6192 : }
6193 :
6194 6337420 : if (!state.dataGlobal->BeginEnvrnFlag) {
6195 6286370 : this->MyEnvrnFlag = true;
6196 : }
6197 :
6198 6337420 : if (this->WarmupFlag && (!state.dataGlobal->WarmupFlag)) {
6199 : // reInitialize tank temperature to setpoint of first hour (use HPWH or Desuperheater heating coil set point if applicable)
6200 : // BG's interpretation here is that its better to reset initial condition to setpoint once warm up is over.
6201 : // (otherwise with a dynamic storage model it is difficult for the user to see the initial performance if it isn't periodic.)
6202 445 : Sched::Schedule *sched = nullptr;
6203 445 : if (this->HeatPumpNum > 0) {
6204 46 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6205 46 : sched = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).setptTempSched;
6206 399 : } else if (this->DesuperheaterNum > 0) {
6207 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6208 12 : sched = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).setptTempSched;
6209 : } else {
6210 387 : sched = this->setptTempSched;
6211 : }
6212 :
6213 445 : if (sched != nullptr) {
6214 445 : this->TankTemp = sched->getCurrentVal();
6215 445 : this->SavedTankTemp = this->TankTemp;
6216 :
6217 445 : if (this->Nodes > 0) {
6218 316 : for (auto &e : this->Node) {
6219 280 : e.Temp = e.SavedTemp = this->TankTemp;
6220 : }
6221 : }
6222 : } else {
6223 0 : this->TankTemp = 20.0;
6224 0 : this->SavedTankTemp = this->TankTemp;
6225 :
6226 0 : if (this->Nodes > 0) {
6227 0 : for (auto &e : this->Node) {
6228 0 : e.Temp = e.SavedTemp = this->TankTemp;
6229 : }
6230 : }
6231 : }
6232 445 : this->SourceOutletTemp = this->SavedTankTemp;
6233 445 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6234 445 : this->UseOutletTemp = this->SavedTankTemp;
6235 445 : this->SavedUseOutletTemp = this->SavedTankTemp;
6236 445 : this->SavedHeaterOn1 = false;
6237 445 : this->SavedHeaterOn2 = false;
6238 445 : this->Mode = TankOperatingMode::Floating;
6239 445 : this->SavedMode = TankOperatingMode::Floating;
6240 445 : this->WarmupFlag = false;
6241 : }
6242 6337420 : if (state.dataGlobal->WarmupFlag) {
6243 5395346 : this->WarmupFlag = true;
6244 : }
6245 :
6246 6337420 : if (FirstHVACIteration) {
6247 : // Get all scheduled values
6248 3036687 : this->SetPointTemp = this->setptTempSched->getCurrentVal();
6249 :
6250 3036687 : if (!this->IsChilledWaterTank) {
6251 2766386 : if (this->SetPointTemp > this->TankTempLimit) {
6252 : // Setpoint temperature scheduled higher than maximum tank temperature limit
6253 0 : this->SetPointTemp = this->TankTempLimit - 1.0;
6254 :
6255 0 : if (this->ShowSetPointWarning) {
6256 0 : ShowSevereError(
6257 : state,
6258 0 : format("Water heater = {}: Water heater tank set point temperature is greater than the maximum tank temperature limit.",
6259 0 : this->Name));
6260 0 : ShowContinueErrorTimeStamp(state,
6261 0 : format("Water heater tank set point temperature is reset to Tank Temperature Limit minus 1 C "
6262 : "({:.2T}) and simulation continues.",
6263 0 : this->SetPointTemp));
6264 0 : this->ShowSetPointWarning = false;
6265 : }
6266 : }
6267 : } else {
6268 270301 : if (this->SetPointTemp < this->TankTempLimit) {
6269 : // Setpoint temperature scheduled lower than minimum tank temperature limit
6270 0 : this->SetPointTemp = this->TankTempLimit + 1.0;
6271 :
6272 0 : if (this->ShowSetPointWarning) {
6273 0 : ShowSevereError(
6274 : state,
6275 0 : format("Chilled Water Tank = {}: Water heater tank set point temperature is lower than the minimum tank temperature limit.",
6276 0 : this->Name));
6277 0 : ShowContinueErrorTimeStamp(state,
6278 0 : format("Chilled water tank set point temperature is reset to Tank Temperature Limit plus 1 C "
6279 : "({:.2T}) and simulation continues.",
6280 0 : this->SetPointTemp));
6281 0 : this->ShowSetPointWarning = false;
6282 : }
6283 : }
6284 : }
6285 :
6286 3036687 : if (this->setptTemp2Sched != nullptr) {
6287 159273 : this->SetPointTemp2 = this->setptTemp2Sched->getCurrentVal();
6288 : }
6289 :
6290 3036687 : switch (this->AmbientTempIndicator) {
6291 2273391 : case WTTAmbientTemp::Schedule: {
6292 2273391 : this->AmbientTemp = this->ambientTempSched->getCurrentVal();
6293 2273391 : } break;
6294 :
6295 628642 : case WTTAmbientTemp::TempZone: {
6296 628642 : this->AmbientTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->AmbientTempZone).MAT;
6297 :
6298 628642 : break;
6299 : }
6300 134654 : case WTTAmbientTemp::OutsideAir: {
6301 134654 : this->AmbientTemp = state.dataLoopNodes->Node(this->AmbientTempOutsideAirNode).Temp;
6302 134654 : break;
6303 : }
6304 0 : default:
6305 0 : break;
6306 : }
6307 :
6308 3036687 : if (this->UseInletNode == 0) { // Stand-alone operation
6309 :
6310 203028 : this->UseInletTemp = (this->useInletTempSched != nullptr) ? this->useInletTempSched->getCurrentVal() : state.dataEnvrn->WaterMainsTemp;
6311 :
6312 203028 : this->UseMassFlowRate = this->MassFlowRateMax;
6313 203028 : if (this->flowRateSched != nullptr) {
6314 203028 : this->UseMassFlowRate *= this->flowRateSched->getCurrentVal();
6315 : }
6316 203028 : this->VolFlowRate = this->UseMassFlowRate / Psychrometrics::RhoH2O(Constant::InitConvTemp);
6317 : }
6318 :
6319 3036687 : if (this->HeatPumpNum > 0) {
6320 206578 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp =
6321 206578 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).setptTempSched->getCurrentVal();
6322 206578 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp >= this->TankTempLimit) {
6323 : // HP setpoint temperature scheduled equal to or higher than tank temperature limit
6324 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp = this->TankTempLimit - 1.0;
6325 :
6326 0 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning) {
6327 0 : ShowSevereError(state,
6328 0 : format("Heat Pump Water Heater = {}: Heat Pump water heater set point temperature is equal to or greater than "
6329 : "the maximum tank temperature limit.",
6330 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name));
6331 0 : ShowContinueErrorTimeStamp(state,
6332 0 : format("Heat Pump water heater tank set point temperature is reset to Tank Temperature Limit "
6333 : "minus 1 C ({:.2T}) and simulation continues.",
6334 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp));
6335 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning = false;
6336 : }
6337 : }
6338 : }
6339 :
6340 3036687 : if (this->DesuperheaterNum > 0) {
6341 14742 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTemp =
6342 14742 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).setptTempSched->getCurrentVal();
6343 : }
6344 :
6345 : } // first HVAC Iteration
6346 :
6347 6337420 : if (this->UseInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6348 : Real64 DeadBandTemp;
6349 5934824 : if (this->IsChilledWaterTank) {
6350 545258 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6351 : } else {
6352 5389566 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6353 : }
6354 :
6355 11869648 : Real64 mdotUse = this->PlantMassFlowRatesFunc(state,
6356 : this->UseInletNode,
6357 : FirstHVACIteration,
6358 : WaterHeaterSide::Use,
6359 : this->UseSidePlantLoc.loopSideNum,
6360 5934824 : this->UseSideSeries,
6361 : this->UseBranchControlType,
6362 : this->SavedUseOutletTemp,
6363 : DeadBandTemp,
6364 5934824 : this->SetPointTemp);
6365 5934824 : PlantUtilities::SetComponentFlowRate(state, mdotUse, this->UseInletNode, this->UseOutletNode, this->UseSidePlantLoc);
6366 :
6367 5934824 : this->UseInletTemp = state.dataLoopNodes->Node(this->UseInletNode).Temp;
6368 5934824 : this->UseMassFlowRate = mdotUse;
6369 : }
6370 :
6371 6337420 : if (this->SourceInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6372 : Real64 DeadBandTemp;
6373 1800858 : if (this->IsChilledWaterTank) {
6374 410016 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6375 : } else {
6376 1390842 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6377 : }
6378 :
6379 : Real64 sensedTemp;
6380 1800858 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
6381 104044 : int tmpNodeNum = this->HeaterNode1;
6382 104044 : sensedTemp = this->Node(tmpNodeNum).SavedTemp;
6383 : } else {
6384 1696814 : sensedTemp = this->SavedSourceOutletTemp;
6385 : }
6386 :
6387 3601716 : Real64 mdotSource = this->PlantMassFlowRatesFunc(state,
6388 : this->SourceInletNode,
6389 : FirstHVACIteration,
6390 : WaterHeaterSide::Source,
6391 : this->SrcSidePlantLoc.loopSideNum,
6392 1800858 : this->SourceSideSeries,
6393 : this->SourceBranchControlType,
6394 : sensedTemp,
6395 : DeadBandTemp,
6396 1800858 : this->SetPointTemp);
6397 1800858 : if (this->SrcSidePlantLoc.loopNum > 0) {
6398 1362856 : PlantUtilities::SetComponentFlowRate(state, mdotSource, this->SourceInletNode, this->SourceOutletNode, this->SrcSidePlantLoc);
6399 : } else { // not really plant connected (desuperheater or heat pump)
6400 438002 : state.dataLoopNodes->Node(this->SourceInletNode).MassFlowRate = mdotSource;
6401 438002 : state.dataLoopNodes->Node(this->SourceOutletNode).MassFlowRate = mdotSource;
6402 : }
6403 :
6404 1800858 : this->SourceInletTemp = state.dataLoopNodes->Node(this->SourceInletNode).Temp;
6405 1800858 : this->SourceMassFlowRate = mdotSource;
6406 : }
6407 :
6408 : // initialize HPWHs each iteration
6409 6337420 : if (this->HeatPumpNum > 0) {
6410 :
6411 408518 : int HPNum = this->HeatPumpNum;
6412 :
6413 408518 : if (this->MyHPSizeFlag) {
6414 : // autosize info must be calculated in GetWaterThermalTankInputFlag for use in StandardRating procedure
6415 : // (called at end of GetWaterThermalTankInputFlag)
6416 : // report autosizing information here (must be done after GetWaterThermalTankInputFlag is complete)
6417 61 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).WaterFlowRateAutoSized &&
6418 16 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6419 16 : BaseSizer::reportSizerOutput(state,
6420 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6421 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6422 : "Condenser water flow rate [m3/s]",
6423 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate);
6424 : }
6425 65 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).AirFlowRateAutoSized &&
6426 20 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6427 20 : BaseSizer::reportSizerOutput(state,
6428 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6429 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6430 : "Evaporator air flow rate [m3/s]",
6431 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate);
6432 : }
6433 45 : state.dataSize->DataNonZoneNonAirloopValue = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate;
6434 45 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate =
6435 45 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate * state.dataEnvrn->StdRhoAir;
6436 45 : if (state.dataSize->CurZoneEqNum > 0) {
6437 12 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirFlow = true;
6438 12 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirVolFlow = state.dataSize->DataNonZoneNonAirloopValue;
6439 : }
6440 45 : if (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated) {
6441 23 : this->MyHPSizeFlag = false;
6442 : }
6443 : }
6444 :
6445 408518 : int HPAirInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode;
6446 408518 : int HPAirOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirOutletNode;
6447 408518 : int OutdoorAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode;
6448 408518 : int ExhaustAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ExhaustAirNode;
6449 408518 : int HPWaterInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode;
6450 408518 : int HPWaterOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterOutletNode;
6451 408518 : int InletAirMixerNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode;
6452 408518 : int OutletAirSplitterNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutletAirSplitterNode;
6453 :
6454 408518 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).CrankcaseTempIndicator) {
6455 183624 : case CrankcaseHeaterControlTemp::Zone: {
6456 367248 : state.dataHVACGlobal->HPWHCrankcaseDBTemp =
6457 183624 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientTempZone).MAT;
6458 183624 : break;
6459 : }
6460 208978 : case CrankcaseHeaterControlTemp::Outdoors: {
6461 208978 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = state.dataEnvrn->OutDryBulbTemp;
6462 208978 : break;
6463 : }
6464 15916 : case CrankcaseHeaterControlTemp::Schedule: {
6465 15916 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = state.dataWaterThermalTanks->HPWaterHeater(HPNum).crankcaseTempSched->getCurrentVal();
6466 15916 : break;
6467 : }
6468 0 : default:
6469 0 : break;
6470 : }
6471 :
6472 : // initialize HPWH report variables to 0 and set tank inlet node equal to outlet node
6473 408518 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterSensibleCapacity = 0.0;
6474 408518 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterLatentCapacity = 0.0;
6475 408518 : this->SourceMassFlowRate = 0.0;
6476 408518 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatingPLR = 0.0;
6477 408518 : this->SourceInletTemp = this->SourceOutletTemp;
6478 :
6479 : // determine HPWH inlet air conditions based on inlet air configuration (Zone, ZoneAndOA, OutdoorAir, or Schedule)
6480 408518 : Real64 HPInletDryBulbTemp = 0.0;
6481 408518 : Real64 HPInletHumRat = 0.0;
6482 : Real64 HPInletRelHum;
6483 408518 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirConfiguration) {
6484 167586 : case WTTAmbientTemp::TempZone: {
6485 167586 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6486 167586 : HPInletDryBulbTemp = state.dataLoopNodes->Node(HPAirInletNode).Temp;
6487 167586 : HPInletHumRat = state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6488 167586 : break;
6489 : }
6490 16038 : case WTTAmbientTemp::ZoneAndOA: {
6491 16038 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).inletAirMixerSched != nullptr) {
6492 : // schedule values are checked for boundary of 0 and 1 in GetWaterThermalTankInputFlag
6493 16038 : state.dataWaterThermalTanks->mixerInletAirSchedule =
6494 16038 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).inletAirMixerSched->getCurrentVal();
6495 : } else {
6496 0 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6497 : }
6498 16038 : HPInletDryBulbTemp = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).Temp +
6499 16038 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).Temp;
6500 16038 : HPInletHumRat = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).HumRat +
6501 16038 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6502 16038 : break;
6503 : }
6504 208978 : case WTTAmbientTemp::OutsideAir: {
6505 208978 : state.dataWaterThermalTanks->mixerInletAirSchedule = 1.0;
6506 208978 : HPInletDryBulbTemp = state.dataLoopNodes->Node(OutdoorAirNode).Temp;
6507 208978 : HPInletHumRat = state.dataLoopNodes->Node(OutdoorAirNode).HumRat;
6508 :
6509 208978 : break;
6510 : }
6511 15916 : case WTTAmbientTemp::Schedule: {
6512 15916 : HPInletDryBulbTemp = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ambientTempSched->getCurrentVal();
6513 15916 : HPInletRelHum = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ambientRHSched->getCurrentVal();
6514 15916 : HPInletHumRat = Psychrometrics::PsyWFnTdbRhPb(state, HPInletDryBulbTemp, HPInletRelHum, state.dataEnvrn->OutBaroPress, RoutineName);
6515 15916 : state.dataLoopNodes->Node(HPAirInletNode).Temp = HPInletDryBulbTemp;
6516 15916 : state.dataLoopNodes->Node(HPAirInletNode).HumRat = HPInletHumRat;
6517 15916 : state.dataLoopNodes->Node(HPAirInletNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6518 15916 : state.dataLoopNodes->Node(HPAirInletNode).Press = state.dataEnvrn->OutBaroPress;
6519 :
6520 15916 : break;
6521 : }
6522 0 : default:
6523 0 : assert(false);
6524 : break;
6525 : }
6526 :
6527 408518 : state.dataWaterThermalTanks->mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
6528 :
6529 : // set up initial conditions on nodes
6530 408518 : if (InletAirMixerNode > 0) {
6531 16038 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate = 0.0;
6532 16038 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6533 16038 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6534 16038 : state.dataLoopNodes->Node(InletAirMixerNode).Temp = HPInletDryBulbTemp;
6535 16038 : state.dataLoopNodes->Node(InletAirMixerNode).HumRat = HPInletHumRat;
6536 16038 : state.dataLoopNodes->Node(InletAirMixerNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6537 16038 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6538 16038 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6539 16038 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6540 16038 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6541 : } else {
6542 392480 : if (OutdoorAirNode == 0) {
6543 183502 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6544 183502 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6545 183502 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6546 183502 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6547 : } else {
6548 208978 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6549 208978 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6550 208978 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6551 208978 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6552 : }
6553 : }
6554 :
6555 408518 : if (OutletAirSplitterNode > 0) {
6556 16038 : state.dataLoopNodes->Node(OutletAirSplitterNode).MassFlowRate = 0.0;
6557 : }
6558 : // these are water nodes are not managed by plant. the HP connects
6559 : // directly to the WH without using plant.
6560 408518 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
6561 304563 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
6562 304563 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
6563 : }
6564 :
6565 : // set the max mass flow rate for outdoor fans
6566 408518 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6567 408518 : state.dataWaterThermalTanks->mdotAir;
6568 :
6569 : // Curve objects in DXCoils::CalcHPWHDXCoil will use inlet conditions to HPWH not inlet air conditions to DX Coil
6570 : // HPWHInletDBTemp and HPWHInletWBTemp are DataHVACGlobals to pass info to HPWHDXCoil
6571 408518 : state.dataHVACGlobal->HPWHInletDBTemp = HPInletDryBulbTemp;
6572 817036 : state.dataHVACGlobal->HPWHInletWBTemp =
6573 408518 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, HPInletHumRat, state.dataEnvrn->OutBaroPress);
6574 :
6575 : // initialize flow rates at speed levels for variable-speed HPWH
6576 418564 : if ((state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) &&
6577 10046 : (0 == state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) // use SCWH coil represents
6578 : {
6579 1 : IntegratedHeatPump::SizeIHP(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum); //
6580 : // IntegratedHeatPump::SimIHP(modBlankString, HPWaterHeater(HPNum).DXCoilNum,
6581 : // 0, EMP1, EMP2, EMP3, 0, 0.0, 1, 0.0, 0.0, 0.0, false, 0.0); //conduct the sizing operation in the IHP
6582 1 : int VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6583 1 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6584 :
6585 408517 : } else if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
6586 463795 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") &&
6587 55278 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed == 0)) {
6588 6 : VariableSpeedCoils::SimVariableSpeedCoils(state,
6589 6 : std::string(),
6590 6 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
6591 : HVAC::FanOp::Invalid, // Invalid instead of off?
6592 : HVAC::CompressorOp::Off,
6593 : 0.0,
6594 : 1,
6595 : 0.0,
6596 : 0.0,
6597 : 0.0,
6598 : 0.0); // conduct the sizing operation in the VS WSHP
6599 6 : int VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6600 6 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6601 : // below pass the flow rates from the VS coil to the water heater object
6602 : }
6603 :
6604 408518 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed > 0) {
6605 : int VSCoilID;
6606 65324 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
6607 10046 : VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6608 : } else {
6609 55278 : VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6610 : }
6611 :
6612 : // scale air flow rates
6613 65324 : Real64 MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedAirVolFlowRate /
6614 65324 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(
6615 65324 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6616 718564 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6617 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6618 653240 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(Iter) * MulSpeedFlowScale;
6619 : }
6620 :
6621 : // check fan flow rate, should be larger than the max flow rate of the VS coil
6622 65324 : Real64 FanVolFlow = state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->maxAirFlowRate;
6623 :
6624 130648 : if (FanVolFlow < state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6625 65324 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) { // but this is the not the scaled mas flow
6626 : // if ( FanVolFlow < HPWaterHeater( HPNum ).HPWHAirVolFlowRate( HPWaterHeater( HPNum ).NumofSpeed ) ) {
6627 :
6628 0 : ShowWarningError(state,
6629 0 : format("InitWaterThermalTank: -air flow rate = {:.7T} in fan object is less than the MSHP system air flow rate "
6630 : "when waterheating is required({:.7T}).",
6631 : FanVolFlow,
6632 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6633 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)));
6634 0 : ShowContinueError(state,
6635 : " The MSHP system flow rate when waterheating is required is reset to the"
6636 : " fan flow rate and the simulation continues.");
6637 0 : ShowContinueError(state, format(" Occurs in {}", state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name));
6638 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed) =
6639 : FanVolFlow;
6640 : // Check flow rates in other speeds and ensure flow rates are not above the max flow rate
6641 0 : for (int Iter = state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed - 1; Iter >= 1; --Iter) {
6642 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) >
6643 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1)) {
6644 0 : ShowContinueError(state,
6645 0 : format(" The MSHP system flow rate when waterheating is required is reset to the flow rate at higher "
6646 : "speed and the simulation continues at Speed{}.",
6647 : Iter));
6648 0 : ShowContinueError(state, format(" Occurs in {}", state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name));
6649 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6650 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1);
6651 : }
6652 : }
6653 : }
6654 :
6655 718564 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6656 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSAirSpeedRatio(Iter) =
6657 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) /
6658 1306480 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6659 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6660 : }
6661 :
6662 : // scale water flow rates
6663 65324 : MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedWaterVolFlowRate /
6664 65324 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6665 65324 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6666 718564 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6667 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterVolFlowRate(Iter) =
6668 653240 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) * MulSpeedFlowScale;
6669 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterMassFlowRate(Iter) =
6670 653240 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterMassFlowRate(Iter) * MulSpeedFlowScale;
6671 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSWaterSpeedRatio(Iter) =
6672 653240 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) /
6673 1306480 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6674 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6675 : }
6676 :
6677 65324 : Real64 rhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, HPInletDryBulbTemp, HPInletHumRat);
6678 :
6679 718564 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6680 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(Iter) =
6681 653240 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) * rhoAir;
6682 : }
6683 :
6684 : // set the max mass flow rate for outdoor fans
6685 65324 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6686 65324 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6687 : }
6688 :
6689 : } // IF(WaterThermalTank(WaterThermalTankNum)%HeatPumpNum .GT. 0)THEN
6690 :
6691 : // calling CalcStandardRatings early bypasses fan sizing since DataSizing::DataNonZoneNonAirloopValue has not been set yet
6692 6337420 : if (!this->AlreadyRated) {
6693 297 : if (this->IsChilledWaterTank) {
6694 6 : this->AlreadyRated = true;
6695 : } else {
6696 395 : if (!state.dataGlobal->AnyPlantInModel || state.dataPlnt->PlantFirstSizesOkayToReport || this->MaxCapacity > 0.0 ||
6697 104 : this->HeatPumpNum > 0) {
6698 189 : this->CalcStandardRatings(state);
6699 : }
6700 : }
6701 : }
6702 6337420 : }
6703 :
6704 6551906 : void WaterThermalTankData::CalcWaterThermalTankMixed(EnergyPlusData &state) // Water Heater being simulated
6705 : {
6706 :
6707 : // SUBROUTINE INFORMATION:
6708 : // AUTHOR Peter Graham Ellis
6709 : // DATE WRITTEN January 2005
6710 : // MODIFIED na
6711 : // RE-ENGINEERED na
6712 :
6713 : // PURPOSE OF THIS SUBROUTINE:
6714 : // Simulates a well-mixed, single node water heater tank.
6715 :
6716 : // METHODOLOGY EMPLOYED:
6717 : // This model uses analytical calculations based on the differential equation describing the tank energy
6718 : // balance. The model operates in three different modes: heating, floating, and venting. Temperatures and
6719 : // energies change dynamically over the timestep. The final reported tank temperature is the average over
6720 : // the timestep. The final reported heat rates are averages based on the total energy transfer over the
6721 : // timestep.
6722 :
6723 : static constexpr std::string_view RoutineName("CalcWaterThermalTankMixed");
6724 :
6725 : Real64 TimeElapsed_loc =
6726 6551906 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
6727 :
6728 6551906 : if (this->TimeElapsed != TimeElapsed_loc) {
6729 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
6730 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
6731 594372 : this->SavedTankTemp = this->TankTemp;
6732 594372 : this->SavedMode = this->Mode;
6733 :
6734 : // Save outlet temperatures for demand-side flow control
6735 594372 : this->SavedUseOutletTemp = this->UseOutletTemp;
6736 594372 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
6737 :
6738 594372 : this->TimeElapsed = TimeElapsed_loc;
6739 : }
6740 :
6741 6551906 : Real64 TankTemp_loc = this->SavedTankTemp;
6742 6551906 : TankOperatingMode Mode_loc = this->SavedMode;
6743 :
6744 6551906 : Real64 Qmaxcap = this->MaxCapacity;
6745 6551906 : Real64 Qmincap = this->MinCapacity;
6746 6551906 : Real64 Qoffcycfuel = this->OffCycParaLoad;
6747 6551906 : Real64 Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
6748 6551906 : Real64 Qoncycfuel = this->OnCycParaLoad;
6749 6551906 : Real64 Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
6750 :
6751 6551906 : Real64 SetPointTemp_loc = this->SetPointTemp;
6752 6551906 : Real64 DeadBandTemp = this->getDeadBandTemp();
6753 6551906 : Real64 MaxTemp = this->TankTempLimit;
6754 6551906 : Real64 AmbientTemp_loc = this->AmbientTemp;
6755 :
6756 6551906 : Real64 UseInletTemp_loc = this->UseInletTemp;
6757 6551906 : Real64 UseMassFlowRate_loc = this->UseMassFlowRate * this->UseEffectiveness;
6758 6551906 : Real64 SourceInletTemp_loc = this->SourceInletTemp;
6759 6551906 : Real64 SourceMassFlowRate_loc = this->SourceMassFlowRate * this->SourceEffectiveness;
6760 :
6761 : Real64 rho;
6762 6551906 : if (this->UseSidePlantLoc.loopNum > 0) {
6763 6040675 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, TankTemp_loc, RoutineName);
6764 : } else {
6765 511231 : rho = this->water->getDensity(state, TankTemp_loc, RoutineName);
6766 : }
6767 :
6768 6551906 : Real64 TankMass = rho * this->Volume;
6769 :
6770 : Real64 Cp;
6771 6551906 : if (this->UseSidePlantLoc.loopNum > 0) {
6772 6040675 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, TankTemp_loc, RoutineName);
6773 : } else {
6774 511231 : Cp = this->water->getSpecificHeat(state, TankTemp_loc, RoutineName);
6775 : }
6776 :
6777 6551906 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
6778 6551906 : Real64 TimeRemaining = SecInTimeStep;
6779 6551906 : int CycleOnCount_loc = 0;
6780 6551906 : int MaxCycles = SecInTimeStep;
6781 6551906 : Real64 Runtime = 0.0;
6782 6551906 : bool SetPointRecovered = false;
6783 :
6784 6551906 : Real64 Tsum = 0.0;
6785 6551906 : Real64 Eloss = 0.0;
6786 6551906 : Real64 Elosszone = 0.0;
6787 6551906 : Real64 Euse = 0.0;
6788 6551906 : Real64 Esource = 0.0;
6789 6551906 : Real64 Eheater = 0.0;
6790 6551906 : Real64 Event = 0.0;
6791 6551906 : Real64 Eneeded = 0.0;
6792 6551906 : Real64 Eunmet = 0.0;
6793 6551906 : Real64 Efuel = 0.0;
6794 6551906 : Real64 Eoncycfuel = 0.0;
6795 6551906 : Real64 Eoffcycfuel = 0.0;
6796 6551906 : Real64 PLR = 0.0;
6797 6551906 : Real64 PLRsum = 0.0;
6798 :
6799 6551906 : Real64 Qheat = 0.0;
6800 6551906 : Real64 Qheater = 0.0;
6801 6551906 : Real64 Qvent = 0.0;
6802 6551906 : Real64 Qneeded = 0.0;
6803 6551906 : Real64 Qunmet = 0.0;
6804 6551906 : Real64 Qfuel = 0.0;
6805 :
6806 : // Calculate the heating rate from the heat pump.
6807 6551906 : Real64 HPWHCondenserDeltaT = 0.0;
6808 :
6809 6551906 : if (this->HeatPumpNum > 0) {
6810 811623 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
6811 811623 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
6812 811623 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
6813 811623 : HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
6814 : }
6815 6551906 : assert(HPWHCondenserDeltaT >= 0);
6816 :
6817 : Real64 Qheatpump;
6818 : Real64 Qsource;
6819 6551906 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
6820 : HPWHCondenserDeltaT, SourceInletTemp_loc, Cp, SetPointTemp_loc, SourceMassFlowRate_loc, Qheatpump, Qsource);
6821 :
6822 : // Calculate steady-state use heat rate.
6823 6551906 : Real64 Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - SetPointTemp_loc);
6824 6551906 : Real64 Qloss = 0.0, PLF = 0.0;
6825 :
6826 24956299 : while (TimeRemaining > 0.0) {
6827 :
6828 18404393 : Real64 TimeNeeded = 0.0;
6829 :
6830 18404393 : Real64 NewTankTemp = TankTemp_loc;
6831 18404393 : Real64 LossCoeff_loc = 0.0;
6832 18404393 : Real64 LossFracToZone = 0.0;
6833 :
6834 18404393 : switch (Mode_loc) {
6835 :
6836 8308633 : case TankOperatingMode::Heating: // Heater is on
6837 :
6838 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
6839 8308633 : LossCoeff_loc = this->OnCycLossCoeff;
6840 8308633 : LossFracToZone = this->OnCycLossFracToZone;
6841 8308633 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
6842 8308633 : Qneeded = -Quse - Qsource - Qloss - Qoncycheat;
6843 :
6844 8308633 : if (TankTemp_loc > SetPointTemp_loc) {
6845 : // Heater is not needed after all, possibly due to step change in scheduled SetPointTemp
6846 :
6847 8 : Qheater = 0.0;
6848 8 : Qunmet = 0.0;
6849 8 : Mode_loc = TankOperatingMode::Floating;
6850 8 : continue;
6851 :
6852 8308625 : } else if (TankTemp_loc < SetPointTemp_loc) {
6853 : // Attempt to recover to the setpoint as quickly as possible by using maximum heater capacity
6854 :
6855 : // Qneeded is calculated above
6856 : // Qneeded does not account for the extra energy needed to recover to the setpoint
6857 4390322 : Qheater = Qmaxcap;
6858 4390322 : Qunmet = max(Qneeded - Qheater, 0.0);
6859 4390322 : Qheat = Qoncycheat + Qheater + Qheatpump;
6860 :
6861 : // Calculate time needed to recover to the setpoint at maximum heater capacity
6862 4390322 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
6863 : SetPointTemp_loc,
6864 : AmbientTemp_loc,
6865 : UseInletTemp_loc,
6866 : SourceInletTemp_loc,
6867 : TankMass,
6868 : Cp,
6869 : UseMassFlowRate_loc,
6870 : SourceMassFlowRate_loc,
6871 : LossCoeff_loc,
6872 : Qheat);
6873 :
6874 4390322 : if (TimeNeeded > TimeRemaining) {
6875 : // Heater is at maximum capacity and heats for all of the remaining time
6876 : // Setpoint temperature WILL NOT be recovered
6877 :
6878 523901 : TimeNeeded = TimeRemaining;
6879 :
6880 523901 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6881 : AmbientTemp_loc,
6882 : UseInletTemp_loc,
6883 : SourceInletTemp_loc,
6884 : TankMass,
6885 : Cp,
6886 : UseMassFlowRate_loc,
6887 : SourceMassFlowRate_loc,
6888 : LossCoeff_loc,
6889 : Qheat,
6890 : TimeNeeded);
6891 :
6892 : } else { // TimeNeeded <= TimeRemaining
6893 : // Heater is at maximum capacity but will not heat for all of the remaining time (at maximum anyway)
6894 : // Setpoint temperature WILL be recovered
6895 :
6896 3866421 : NewTankTemp = SetPointTemp_loc;
6897 :
6898 3866421 : SetPointRecovered = true;
6899 :
6900 : } // TimeNeeded > TimeRemaining
6901 :
6902 : } else { // TankTemp == SetPointTemp
6903 : // Attempt to maintain the setpoint by using the needed heater capacity (modulating, if allowed)
6904 :
6905 3918303 : if (Qneeded <= 0.0) {
6906 : // Heater is not needed
6907 :
6908 9256 : Qneeded = 0.0;
6909 9256 : Qheater = 0.0;
6910 9256 : Qunmet = 0.0;
6911 9256 : Mode_loc = TankOperatingMode::Floating;
6912 9256 : continue;
6913 :
6914 3909047 : } else if (Qneeded < Qmincap) {
6915 : // Heater is required at less than the minimum capacity
6916 : // If cycling, Qmincap = Qmaxcap. Once the setpoint is reached, heater will almost always be shut off here
6917 :
6918 3856502 : if (this->ControlType == HeaterControlMode::Cycle) {
6919 : // Control will cycle on and off based on DeadBandTemp
6920 3856502 : Qheater = 0.0;
6921 3856502 : Qunmet = 0.0;
6922 3856502 : Mode_loc = TankOperatingMode::Floating;
6923 3856502 : continue;
6924 :
6925 0 : } else if (this->ControlType == HeaterControlMode::Modulate) {
6926 : // Control will cycle on and off based on DeadBandTemp until Qneeded > Qmincap again
6927 0 : Qheater = 0.0;
6928 0 : Qunmet = Qneeded;
6929 0 : Mode_loc = TankOperatingMode::Floating;
6930 0 : continue;
6931 :
6932 : // CASE (ControlTypeModulateWithOverheat) ! Not yet implemented
6933 : // Calculate time to reach steady-state temp; check for venting at MaxTemp limit
6934 : // Qheater = Qmincap
6935 :
6936 : // CASE (ControlTypeModulateWithUnderheat) ! Not yet implemented
6937 : // Heater must not come back on until Qneeded >= Qmincap
6938 : // Mode = modfloatMode
6939 : }
6940 :
6941 52545 : } else if (Qneeded <= Qmaxcap) {
6942 : // Heater can exactly meet the needed heat rate (usually by modulating) and heats for all of the remaining time
6943 : // Setpoint temperature WILL be maintained
6944 :
6945 52503 : TimeNeeded = TimeRemaining;
6946 :
6947 52503 : Qheater = Qneeded;
6948 52503 : Qunmet = 0.0;
6949 :
6950 52503 : NewTankTemp = SetPointTemp_loc;
6951 :
6952 : } else { // Qneeded > Qmaxcap
6953 : // Heater is at maximum capacity and heats for all of the remaining time
6954 : // Setpoint temperature WILL NOT be maintained
6955 :
6956 42 : TimeNeeded = TimeRemaining;
6957 :
6958 42 : Qheater = Qmaxcap;
6959 42 : Qunmet = Qneeded - Qheater;
6960 42 : Qheat = Qoncycheat + Qheater + Qheatpump;
6961 :
6962 42 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6963 : AmbientTemp_loc,
6964 : UseInletTemp_loc,
6965 : SourceInletTemp_loc,
6966 : TankMass,
6967 : Cp,
6968 : UseMassFlowRate_loc,
6969 : SourceMassFlowRate_loc,
6970 : LossCoeff_loc,
6971 : Qheat,
6972 : TimeNeeded);
6973 :
6974 : } // Qneeded > Qmaxcap
6975 :
6976 : } // TankTemp > SetPointTemp
6977 :
6978 : // Update summed values
6979 4442867 : Eneeded += Qneeded * TimeNeeded;
6980 4442867 : Eheater += Qheater * TimeNeeded;
6981 4442867 : Eunmet += Qunmet * TimeNeeded;
6982 4442867 : Eoncycfuel += Qoncycfuel * TimeNeeded;
6983 :
6984 4442867 : if (Qmaxcap > 0.0) {
6985 4345931 : PLR = Qheater / Qmaxcap;
6986 : }
6987 4442867 : PLF = this->PartLoadFactor(state, PLR);
6988 4442867 : Efuel += Qheater * TimeNeeded / (PLF * this->Efficiency);
6989 :
6990 4442867 : Runtime += TimeNeeded;
6991 4442867 : PLRsum += PLR * TimeNeeded;
6992 :
6993 4442867 : if (!this->FirstRecoveryDone) {
6994 108874 : this->FirstRecoveryFuel += Efuel + Eoffcycfuel + Eoncycfuel;
6995 108874 : if (SetPointRecovered) {
6996 7456 : this->FirstRecoveryDone = true;
6997 : }
6998 : }
6999 4442867 : break;
7000 :
7001 9959094 : case TankOperatingMode::Floating:
7002 : case TankOperatingMode::Cooling: // Heater is off
7003 :
7004 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
7005 9959094 : LossCoeff_loc = this->OffCycLossCoeff;
7006 9959094 : LossFracToZone = this->OffCycLossFracToZone;
7007 9959094 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
7008 9959094 : Qneeded = -Quse - Qsource - Qloss - Qoffcycheat;
7009 :
7010 : // This section really needs to work differently depending on ControlType
7011 : // CYCLE will look at TankTemp, MODULATE will look at Qneeded
7012 :
7013 9959094 : if ((TankTemp_loc < DeadBandTemp) && (!this->IsChilledWaterTank)) {
7014 : // Tank temperature is already below the minimum, possibly due to step change in scheduled SetPointTemp
7015 :
7016 455 : Mode_loc = TankOperatingMode::Heating;
7017 455 : ++CycleOnCount_loc;
7018 455 : continue;
7019 :
7020 9958639 : } else if ((TankTemp_loc >= DeadBandTemp) && (!this->IsChilledWaterTank)) {
7021 :
7022 9568979 : Qheat = Qoffcycheat + Qheatpump;
7023 :
7024 : // Calculate time needed for tank temperature to fall to minimum (setpoint - deadband)
7025 9568979 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
7026 : DeadBandTemp,
7027 : AmbientTemp_loc,
7028 : UseInletTemp_loc,
7029 : SourceInletTemp_loc,
7030 : TankMass,
7031 : Cp,
7032 : UseMassFlowRate_loc,
7033 : SourceMassFlowRate_loc,
7034 : LossCoeff_loc,
7035 : Qheat);
7036 :
7037 9568979 : if (TimeNeeded <= TimeRemaining) {
7038 : // Heating will be needed in this DataGlobals::TimeStep
7039 :
7040 4058811 : NewTankTemp = DeadBandTemp;
7041 4058811 : Mode_loc = TankOperatingMode::Heating;
7042 4058811 : ++CycleOnCount_loc;
7043 :
7044 : } else { // TimeNeeded > TimeRemaining
7045 : // Heating will not be needed for all of the remaining time
7046 :
7047 5510168 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7048 : AmbientTemp_loc,
7049 : UseInletTemp_loc,
7050 : SourceInletTemp_loc,
7051 : TankMass,
7052 : Cp,
7053 : UseMassFlowRate_loc,
7054 : SourceMassFlowRate_loc,
7055 : LossCoeff_loc,
7056 : Qheat,
7057 : TimeRemaining);
7058 :
7059 5510168 : if ((NewTankTemp < MaxTemp) || (this->IsChilledWaterTank)) {
7060 : // Neither heating nor venting is needed for all of the remaining time
7061 :
7062 5491087 : TimeNeeded = TimeRemaining;
7063 :
7064 : } else { // NewTankTemp >= MaxTemp
7065 : // Venting will be needed in this DataGlobals::TimeStep
7066 :
7067 : // Calculate time needed for tank temperature to rise to the maximum
7068 19081 : TimeNeeded = CalcTimeNeeded(TankTemp_loc,
7069 : MaxTemp,
7070 : AmbientTemp_loc,
7071 : UseInletTemp_loc,
7072 : SourceInletTemp_loc,
7073 : TankMass,
7074 : Cp,
7075 : UseMassFlowRate_loc,
7076 : SourceMassFlowRate_loc,
7077 : LossCoeff_loc,
7078 : Qheat);
7079 :
7080 : // if limit NewTankTemp >= MaxTemp
7081 19081 : if (TankTemp_loc >= MaxTemp) {
7082 0 : TimeNeeded = TimeRemaining;
7083 : }
7084 19081 : NewTankTemp = MaxTemp;
7085 19081 : Mode_loc = TankOperatingMode::Venting;
7086 :
7087 : } // NewTankTemp >= MaxTemp
7088 :
7089 : } // TimeNeeded <= TimeRemaining
7090 :
7091 389660 : } else if ((TankTemp_loc > DeadBandTemp) && (this->IsChilledWaterTank)) {
7092 77910 : Mode_loc = TankOperatingMode::Cooling;
7093 77910 : Qheat = Qheatpump;
7094 :
7095 77910 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7096 : AmbientTemp_loc,
7097 : UseInletTemp_loc,
7098 : SourceInletTemp_loc,
7099 : TankMass,
7100 : Cp,
7101 : UseMassFlowRate_loc,
7102 : SourceMassFlowRate_loc,
7103 : LossCoeff_loc,
7104 : Qheat,
7105 : TimeRemaining);
7106 77910 : TimeNeeded = TimeRemaining;
7107 311750 : } else if ((TankTemp_loc <= DeadBandTemp) && (this->IsChilledWaterTank)) {
7108 :
7109 311750 : if (TankTemp_loc < SetPointTemp_loc) {
7110 38959 : Mode_loc = TankOperatingMode::Floating;
7111 : }
7112 :
7113 311750 : Qheat = Qheatpump;
7114 :
7115 311750 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7116 : AmbientTemp_loc,
7117 : UseInletTemp_loc,
7118 : SourceInletTemp_loc,
7119 : TankMass,
7120 : Cp,
7121 : UseMassFlowRate_loc,
7122 : SourceMassFlowRate_loc,
7123 : LossCoeff_loc,
7124 : Qheat,
7125 : TimeRemaining);
7126 311750 : TimeNeeded = TimeRemaining;
7127 : } // TankTemp vs DeadBandTemp for heaters and chilled water tanks
7128 :
7129 : // Update summed values
7130 9958639 : Eneeded += Qneeded * TimeNeeded;
7131 9958639 : Eunmet += Qunmet * TimeNeeded; // Qunmet may be propagated thru from the previous iteration
7132 9958639 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7133 9958639 : break;
7134 :
7135 136666 : case TankOperatingMode::Venting: // Excess heat is vented
7136 :
7137 136666 : LossCoeff_loc = this->OffCycLossCoeff;
7138 136666 : LossFracToZone = this->OffCycLossFracToZone;
7139 136666 : Qheat = Qoffcycheat + Qheatpump;
7140 :
7141 136666 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7142 : AmbientTemp_loc,
7143 : UseInletTemp_loc,
7144 : SourceInletTemp_loc,
7145 : TankMass,
7146 : Cp,
7147 : UseMassFlowRate_loc,
7148 : SourceMassFlowRate_loc,
7149 : LossCoeff_loc,
7150 : Qheat,
7151 : TimeRemaining);
7152 :
7153 136666 : if (NewTankTemp < MaxTemp) {
7154 : // Venting is no longer needed because conditions have changed
7155 :
7156 41953 : Mode_loc = TankOperatingMode::Floating;
7157 41953 : continue;
7158 :
7159 : } else { // NewTankTemp >= MaxTemp
7160 :
7161 94713 : TimeNeeded = TimeRemaining;
7162 :
7163 : // Calculate the steady-state venting rate needed to maintain the tank at maximum temperature
7164 94713 : Real64 Qloss = LossCoeff_loc * (AmbientTemp_loc - MaxTemp);
7165 94713 : Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - MaxTemp);
7166 94713 : Qsource = SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc - MaxTemp);
7167 94713 : Qvent = -Quse - Qsource - Qloss - Qoffcycheat;
7168 :
7169 94713 : NewTankTemp = MaxTemp;
7170 :
7171 : } // NewTankTemp < MaxTemp
7172 :
7173 : // Update summed values
7174 94713 : Event += Qvent * TimeNeeded;
7175 94713 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7176 94713 : break;
7177 0 : default:
7178 0 : assert(false); // should never get here
7179 : }
7180 :
7181 14496219 : Real64 deltaTsum = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTempIntegral(TankTemp_loc,
7182 : NewTankTemp,
7183 : AmbientTemp_loc,
7184 : UseInletTemp_loc,
7185 : SourceInletTemp_loc,
7186 : TankMass,
7187 : Cp,
7188 : UseMassFlowRate_loc,
7189 : SourceMassFlowRate_loc,
7190 : LossCoeff_loc,
7191 : Qheat,
7192 : TimeNeeded);
7193 :
7194 : // Update summed values
7195 14496219 : Tsum += deltaTsum;
7196 14496219 : Eloss += LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7197 14496219 : Elosszone += LossFracToZone * LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7198 14496219 : Euse += UseMassFlowRate_loc * Cp * (UseInletTemp_loc * TimeNeeded - deltaTsum);
7199 14496219 : if (this->HeatPumpNum > 0) {
7200 895538 : Esource += Qheatpump * TimeNeeded;
7201 : } else {
7202 13600681 : Esource += SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc * TimeNeeded - deltaTsum);
7203 : }
7204 :
7205 14496219 : TankTemp_loc = NewTankTemp; // Update tank temperature
7206 :
7207 14496219 : TimeRemaining -= TimeNeeded;
7208 :
7209 14496219 : if (CycleOnCount_loc > MaxCycles) {
7210 :
7211 0 : if (!state.dataGlobal->WarmupFlag) {
7212 0 : if (this->MaxCycleErrorIndex == 0) {
7213 0 : ShowWarningError(state, format("WaterHeater:Mixed = {}: Heater is cycling on and off more than once per second.", this->Name));
7214 0 : ShowContinueError(state, "Try increasing Deadband Temperature Difference or Tank Volume");
7215 0 : ShowContinueErrorTimeStamp(state, "");
7216 : }
7217 0 : ShowRecurringWarningErrorAtEnd(state,
7218 0 : "WaterHeater:Mixed = " + this->Name + " Heater is cycling on and off more than once per second:",
7219 0 : this->MaxCycleErrorIndex);
7220 : }
7221 :
7222 0 : break;
7223 :
7224 : } // CycleOnCount > MaxCycles
7225 :
7226 : } // TimeRemaining > 0.0
7227 :
7228 : // 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
7229 6551906 : Real64 TankTempAvg_loc = Tsum / SecInTimeStep;
7230 6551906 : Qloss = Eloss / SecInTimeStep;
7231 6551906 : Real64 Qlosszone = Elosszone / SecInTimeStep;
7232 6551906 : Quse = Euse / SecInTimeStep;
7233 6551906 : Qsource = Esource / SecInTimeStep;
7234 6551906 : Qheater = Eheater / SecInTimeStep;
7235 6551906 : Qoffcycfuel = Eoffcycfuel / SecInTimeStep;
7236 6551906 : Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
7237 6551906 : Qoncycfuel = Eoncycfuel / SecInTimeStep;
7238 6551906 : Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
7239 6551906 : Qvent = Event / SecInTimeStep;
7240 6551906 : Qneeded = Eneeded / SecInTimeStep;
7241 6551906 : Qunmet = Eunmet / SecInTimeStep;
7242 6551906 : Real64 RTF = Runtime / SecInTimeStep;
7243 6551906 : PLR = PLRsum / SecInTimeStep;
7244 :
7245 6551906 : if (this->ControlType == HeaterControlMode::Cycle) {
7246 : // Recalculate Part Load Factor and fuel energy based on Runtime Fraction, instead of Part Load Ratio
7247 6498118 : PLF = this->PartLoadFactor(state, RTF);
7248 6498118 : Efuel = Eheater / (PLF * this->Efficiency);
7249 : }
7250 :
7251 6551906 : Qfuel = Efuel / SecInTimeStep;
7252 :
7253 6551906 : this->Mode = Mode_loc; // Operating mode for carry-over to next DataGlobals::TimeStep
7254 6551906 : this->TankTemp = TankTemp_loc; // Final tank temperature for carry-over to next DataGlobals::TimeStep
7255 6551906 : this->TankTempAvg = TankTempAvg_loc; // Average tank temperature over the DataGlobals::TimeStep for reporting
7256 :
7257 6551906 : if (!state.dataGlobal->WarmupFlag) {
7258 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
7259 951848 : if (this->TankTemp < 2) {
7260 0 : if (this->FreezingErrorIndex == 0) {
7261 0 : ShowWarningError(state,
7262 0 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
7263 : RoutineName,
7264 0 : this->Type,
7265 0 : this->Name,
7266 0 : this->TankTemp));
7267 0 : ShowContinueErrorTimeStamp(state, "");
7268 : }
7269 0 : ShowRecurringWarningErrorAtEnd(state,
7270 0 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
7271 0 : this->FreezingErrorIndex,
7272 0 : this->TankTemp, // Report Max
7273 0 : this->TankTemp, // Report Min
7274 : _, // Don't report Sum
7275 : "{C}", // Max Unit
7276 : "{C}"); // Min Unit
7277 : }
7278 : }
7279 6551906 : this->UseOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7280 6551906 : this->SourceOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7281 6551906 : if (this->HeatPumpNum > 0) {
7282 811623 : this->SourceInletTemp =
7283 811623 : TankTempAvg_loc + HPWHCondenserDeltaT; // Update the source inlet temperature to the average over the DataGlobals::TimeStep
7284 : }
7285 :
7286 6551906 : this->LossRate = Qloss;
7287 6551906 : this->UseRate = Quse;
7288 6551906 : this->SourceRate = Qsource;
7289 6551906 : this->OffCycParaRateToTank = Qoffcycheat;
7290 6551906 : this->OnCycParaRateToTank = Qoncycheat;
7291 6551906 : this->TotalDemandRate = -Quse - Qsource - Qloss - Qoffcycheat - Qoncycheat;
7292 6551906 : this->HeaterRate = Qheater;
7293 6551906 : this->UnmetRate = Qunmet;
7294 6551906 : this->VentRate = Qvent;
7295 6551906 : this->NetHeatTransferRate = Quse + Qsource + Qloss + Qoffcycheat + Qoncycheat + Qheater + Qvent;
7296 :
7297 6551906 : this->CycleOnCount = CycleOnCount_loc;
7298 6551906 : this->RuntimeFraction = RTF;
7299 6551906 : this->PartLoadRatio = PLR;
7300 :
7301 6551906 : this->FuelRate = Qfuel;
7302 6551906 : this->OffCycParaFuelRate = Qoffcycfuel;
7303 6551906 : this->OnCycParaFuelRate = Qoncycfuel;
7304 :
7305 : // Add water heater skin losses and venting losses to ambient zone, if specified
7306 6551906 : if (this->AmbientTempZone > 0) {
7307 1296253 : this->AmbientZoneGain = -Qlosszone - Qvent;
7308 : }
7309 6551906 : }
7310 :
7311 6551906 : void WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
7312 : Real64 HPWHCondenserDeltaT, // input, The temperature difference (C) across the heat pump, zero if
7313 : // there is no heat pump or if the heat pump is off
7314 : Real64 SourceInletTemp, // input, Source inlet temperature (C)
7315 : Real64 Cp, // Specific heat of fluid (J/kg deltaC)
7316 : Real64 SetPointTemp, // input, Mixed tank set point temperature
7317 : Real64 &SourceMassFlowRate, // source mass flow rate (kg/s)
7318 : Real64 &Qheatpump, // heat transfer rate from heat pump
7319 : Real64 &Qsource // steady state heat transfer rate from a constant temperature source side flow
7320 : )
7321 : {
7322 : // Function Information:
7323 : // Author: Noel Merket
7324 : // Date Written: January 2015
7325 : // Modified: na
7326 : // Re-engineered: na
7327 :
7328 : // Purpose of this function:
7329 : // Determines if the source side heat transfer is coming from a heat pump.
7330 : // If so it treats the source side heat transfer as a constant heat source
7331 : // If it is not coming from a heat pump it treats the source side heat transfer
7332 : // as a constant temperature.
7333 :
7334 : // Determine if the source side heating is coming from a heat pump.
7335 6551906 : Qheatpump = SourceMassFlowRate * Cp * HPWHCondenserDeltaT;
7336 6551906 : if (Qheatpump > 0.0) {
7337 546752 : SourceMassFlowRate = 0.0; // Handle this heating as a constant heat source
7338 546752 : Qsource = Qheatpump;
7339 : } else {
7340 6005154 : Qsource = SourceMassFlowRate * Cp * (SourceInletTemp - SetPointTemp);
7341 : }
7342 6551906 : }
7343 :
7344 13978382 : Real64 WaterThermalTankData::CalcTimeNeeded(Real64 const Ti, // Initial tank temperature (C)
7345 : Real64 const Tf, // Final tank temperature (C)
7346 : Real64 const Ta, // Ambient environment temperature (C)
7347 : Real64 const T1, // Temperature of flow 1 (C)
7348 : Real64 const T2, // Temperature of flow 2 (C)
7349 : Real64 const m, // Mass of tank fluid (kg)
7350 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7351 : Real64 const m1, // Mass flow rate 1 (kg/s)
7352 : Real64 const m2, // Mass flow rate 2 (kg/s)
7353 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7354 : Real64 const Q // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7355 : )
7356 : {
7357 :
7358 : // SUBROUTINE INFORMATION:
7359 : // AUTHOR Peter Graham Ellis
7360 : // DATE WRITTEN February 2005
7361 : // MODIFIED na
7362 : // RE-ENGINEERED na
7363 :
7364 : // PURPOSE OF THIS SUBROUTINE:
7365 : // Calculates the time needed for the tank temperature to change from Ti to Tf given heat loss,
7366 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7367 :
7368 : // METHODOLOGY EMPLOYED:
7369 : // Equations are derived by solving the differential equation governing the tank energy balance.
7370 : // Special cases which cause the natural logarithm to blow up are trapped and interpreted as
7371 : // requiring an infinite amount of time because Tf can never be reached under the given conditions.
7372 :
7373 : // Return value
7374 : Real64 CalcTimeNeeded;
7375 :
7376 : // SUBROUTINE PARAMETER DEFINITIONS:
7377 13978382 : Real64 constexpr Infinity(99999999.9); // A time interval much larger than any single DataGlobals::TimeStep (s)
7378 :
7379 : Real64 t; // Time elapsed from Ti to Tf (s)
7380 :
7381 13978382 : if (Tf == Ti) {
7382 : // Already at Tf; no time is needed
7383 0 : t = 0.0;
7384 :
7385 : } else {
7386 : Real64 a; // Intermediate variable
7387 : Real64 b; // Intermediate variable
7388 : Real64 Tm; // Mixed temperature after an infinite amount of time has passed (C)
7389 : Real64 quotient; // Intermediate variable
7390 :
7391 13978382 : if (UA / Cp + m1 + m2 == 0.0) {
7392 :
7393 22891 : if (Q == 0.0) {
7394 : // With no mass flow and no heat flow and Tf<>Ti, then Tf can never be reached
7395 21952 : t = Infinity;
7396 :
7397 : } else {
7398 939 : a = Q / (m * Cp);
7399 :
7400 939 : t = (Tf - Ti) / a;
7401 : }
7402 :
7403 : } else {
7404 13955491 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7405 13955491 : b = -(UA / Cp + m1 + m2) / m;
7406 :
7407 : // Calculate the mixed temperature Tm of the tank after an infinite amount of time has passed
7408 13955491 : Tm = -a / b;
7409 :
7410 13955491 : if (Tm == Ti) {
7411 : // Mixed temperature is the same as Ti; if Tf<>Ti, then Tf can never be reached
7412 1515 : t = Infinity;
7413 :
7414 13953976 : } else if (Tm == Tf) {
7415 : // Tf only approaches Tm; it can never actually get there in finite time (also avoids divide by zero error)
7416 0 : t = Infinity;
7417 :
7418 : } else {
7419 13953976 : quotient = (Tf - Tm) / (Ti - Tm);
7420 :
7421 13953976 : if (quotient <= 0.0) { // Autodesk:Num Changed < to <= to elim poss floating point error in LOG call
7422 : // Tm is in between Ti and Tf; Tf can never be reached
7423 538083 : t = Infinity;
7424 :
7425 : } else {
7426 13415893 : t = std::log(quotient) / b;
7427 : }
7428 : }
7429 : }
7430 :
7431 13978382 : if (t < 0.0) {
7432 583368 : t = Infinity; // If negative time, Tf can never be reached in the future
7433 : }
7434 : }
7435 :
7436 13978382 : CalcTimeNeeded = t;
7437 :
7438 13978382 : return CalcTimeNeeded;
7439 : }
7440 :
7441 6577212 : Real64 WaterThermalTankData::CalcTankTemp(Real64 const Ti, // Initial tank temperature (C)
7442 : Real64 const Ta, // Ambient environment temperature (C)
7443 : Real64 const T1, // Temperature of flow 1 (C)
7444 : Real64 const T2, // Temperature of flow 2 (C)
7445 : Real64 const m, // Mass of tank fluid (kg)
7446 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7447 : Real64 const m1, // Mass flow rate 1 (kg/s)
7448 : Real64 const m2, // Mass flow rate 2 (kg/s)
7449 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7450 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7451 : Real64 const t // Time elapsed from Ti to Tf (s)
7452 : )
7453 : {
7454 :
7455 : // SUBROUTINE INFORMATION:
7456 : // AUTHOR Peter Graham Ellis
7457 : // DATE WRITTEN February 2005
7458 : // MODIFIED na
7459 : // RE-ENGINEERED na
7460 :
7461 : // PURPOSE OF THIS SUBROUTINE:
7462 : // Calculates the final tank temperature Tf after time t has elapsed given heat loss,
7463 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7464 :
7465 : // METHODOLOGY EMPLOYED:
7466 : // Equations are derived by solving the differential equation governing the tank energy balance.
7467 :
7468 : // Return value
7469 : Real64 CalcTankTemp;
7470 :
7471 : // Locals
7472 : // SUBROUTINE ARGUMENT DEFINITIONS:
7473 :
7474 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7475 : Real64 a; // Intermediate variable
7476 : Real64 b; // Intermediate variable
7477 : Real64 Tf; // Final tank temperature (C)
7478 :
7479 6577212 : if (UA / Cp + m1 + m2 == 0.0) {
7480 22819 : a = Q / (m * Cp);
7481 :
7482 22819 : Tf = a * t + Ti;
7483 :
7484 : } else {
7485 6554393 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7486 6554393 : b = -(UA / Cp + m1 + m2) / m;
7487 :
7488 6554393 : Tf = (a / b + Ti) * std::exp(b * t) - a / b;
7489 : }
7490 :
7491 6577212 : CalcTankTemp = Tf;
7492 :
7493 6577212 : return CalcTankTemp;
7494 : }
7495 :
7496 14512994 : Real64 WaterThermalTankData::CalcTempIntegral(Real64 const Ti, // Initial tank temperature (C)
7497 : Real64 const Tf, // Final tank temperature (C)
7498 : Real64 const Ta, // Ambient environment temperature (C)
7499 : Real64 const T1, // Temperature of flow 1 (C)
7500 : Real64 const T2, // Temperature of flow 2 (C)
7501 : Real64 const m, // Mass of tank fluid (kg)
7502 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7503 : Real64 const m1, // Mass flow rate 1 (kg/s)
7504 : Real64 const m2, // Mass flow rate 2 (kg/s)
7505 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7506 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7507 : Real64 const t // Time elapsed from Ti to Tf (s)
7508 : )
7509 : {
7510 :
7511 : // SUBROUTINE INFORMATION:
7512 : // AUTHOR Peter Graham Ellis
7513 : // DATE WRITTEN February 2005
7514 : // MODIFIED na
7515 : // RE-ENGINEERED na
7516 :
7517 : // PURPOSE OF THIS SUBROUTINE:
7518 : // Calculates the integral of the tank temperature from Ti to Tf. The integral is added to a sum which is
7519 : // later divided by the elapsed time to yield the average tank temperature over the DataGlobals::TimeStep.
7520 :
7521 : // METHODOLOGY EMPLOYED:
7522 : // Equations are the mathematical integrals of the governing differential equations.
7523 :
7524 : // Return value
7525 : Real64 CalcTempIntegral;
7526 :
7527 : // Locals
7528 : // SUBROUTINE ARGUMENT DEFINITIONS:
7529 :
7530 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7531 : Real64 a; // Intermediate variable
7532 : Real64 b; // Intermediate variable
7533 : Real64 dTsum; // Integral of tank temperature (C s)
7534 :
7535 14512994 : if (t == 0.0) {
7536 0 : dTsum = 0.0;
7537 :
7538 14512994 : } else if (Tf == Ti) { // Steady-state conditions
7539 187500 : dTsum = Tf * t;
7540 :
7541 14325494 : } else if (UA / Cp + m1 + m2 == 0.0) {
7542 939 : a = Q / (m * Cp);
7543 :
7544 : // Integral of T(t) = a * t + Ti, evaluated from 0 to t
7545 939 : dTsum = 0.5 * a * t * t + Ti * t;
7546 :
7547 : } else {
7548 14324555 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7549 14324555 : b = -(UA / Cp + m1 + m2) / m;
7550 :
7551 : // Integral of T(t) = (a / b + Ti) * EXP(b * t) - a / b, evaluated from 0 to t
7552 14324555 : dTsum = (a / b + Ti) * (std::exp(b * t) - 1.0) / b - a * t / b;
7553 : }
7554 :
7555 14512994 : CalcTempIntegral = dTsum;
7556 :
7557 14512994 : return CalcTempIntegral;
7558 : }
7559 :
7560 10940985 : Real64 WaterThermalTankData::PartLoadFactor(EnergyPlusData &state, Real64 const PartLoadRatio_loc)
7561 : {
7562 :
7563 : // SUBROUTINE INFORMATION:
7564 : // AUTHOR Peter Graham Ellis
7565 : // DATE WRITTEN January 2005
7566 : // MODIFIED na
7567 : // RE-ENGINEERED na
7568 :
7569 : // PURPOSE OF THIS SUBROUTINE:
7570 : // Calculates the Part Load Factor (PLF) based on a curve correlated to Part Load Ratio, if Heater Control Type
7571 : // is MODULATE, or correlated to Runtime Fraction, if Heater Control Type is CYCLE.
7572 :
7573 10940985 : if (this->PLFCurve > 0) {
7574 1260 : return max(Curve::CurveValue(state, this->PLFCurve, PartLoadRatio_loc), 0.1);
7575 : } else {
7576 10939725 : return 1.0;
7577 : }
7578 : }
7579 :
7580 1146020 : void WaterThermalTankData::CalcWaterThermalTankStratified(EnergyPlusData &state)
7581 : {
7582 : // SUBROUTINE INFORMATION:
7583 : // AUTHOR Noel Merket, originally by Peter Graham Ellis
7584 : // DATE WRITTEN January 2007
7585 : // MODIFIED Nov 2011, BAN; modified the use and source outlet temperature calculation
7586 : // RE-ENGINEERED Noel Merket, November 2018
7587 :
7588 : // PURPOSE OF THIS SUBROUTINE:
7589 : // Simulates a stratified, multi-node water heater tank with up to two heating elements.
7590 :
7591 : // METHODOLOGY EMPLOYED:
7592 : // This model uses a numerical calculation based on an analytical solution of the ODE dT/dt = a*T + b.
7593 : // A heat balance is calculated for each node.
7594 : // Temperatures and energies change dynamically over the system time step.
7595 : // Final node temperatures are reported as final instantaneous values as well as averages over the
7596 : // time step. Heat transfer rates are averages over the time step.
7597 :
7598 : static constexpr std::string_view RoutineName("CalcWaterThermalTankStratified");
7599 1146020 : constexpr Real64 TemperatureConvergenceCriteria = 0.0001;
7600 1146020 : constexpr Real64 SubTimestepMax = 60.0 * 10.0; // seconds
7601 1146020 : constexpr Real64 SubTimestepMin = 10.0; // seconds
7602 : Real64 dt;
7603 :
7604 : // Tank object reference
7605 1146020 : const Real64 &nTankNodes = this->Nodes;
7606 :
7607 : // Fraction of the current hour that has elapsed (h)
7608 : const Real64 TimeElapsed_loc =
7609 1146020 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
7610 :
7611 : // Seconds in one DataGlobals::TimeStep (s)
7612 1146020 : const Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
7613 :
7614 : // Advance tank simulation to the next system DataGlobals::TimeStep, if applicable
7615 1146020 : if (this->TimeElapsed != TimeElapsed_loc) {
7616 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
7617 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
7618 407354 : for (auto &e : this->Node) {
7619 362412 : e.SavedTemp = e.Temp;
7620 : }
7621 :
7622 44942 : this->SavedHeaterOn1 = this->HeaterOn1;
7623 44942 : this->SavedHeaterOn2 = this->HeaterOn2;
7624 :
7625 : // Save outlet temperatures for demand-side flow control
7626 44942 : this->SavedUseOutletTemp = this->UseOutletTemp;
7627 44942 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
7628 :
7629 44942 : this->TimeElapsed = TimeElapsed_loc;
7630 : }
7631 :
7632 : // Reset node temperatures to what they were at the beginning of the system DataGlobals::TimeStep.
7633 9263262 : for (auto &e : this->Node) {
7634 8117242 : e.Temp = e.SavedTemp;
7635 : }
7636 :
7637 1146020 : this->HeaterOn1 = this->SavedHeaterOn1;
7638 1146020 : this->HeaterOn2 = this->SavedHeaterOn2;
7639 :
7640 : // Condenser configuration of heat pump water heater
7641 : const DataPlant::PlantEquipmentType HPWHCondenserConfig =
7642 1146020 : this->HeatPumpNum > 0 ? state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType : DataPlant::PlantEquipmentType::Invalid;
7643 :
7644 : // Heat rate from the heat pump (W)
7645 0 : const Real64 Qheatpump = [&, this] { // BLB
7646 1146020 : if (this->HeatPumpNum == 0) {
7647 244578 : return 0.0;
7648 : }
7649 901442 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
7650 : Real64 CoilTotalHeatingEnergyRate;
7651 901442 : if (HPWH.NumofSpeed > 0) {
7652 : // VSHPWH
7653 70625 : VariableSpeedCoils::VariableSpeedCoilData const &Coil = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum);
7654 70625 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7655 : } else {
7656 : // Single speed HPWH
7657 830817 : DXCoils::DXCoilData const &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
7658 830817 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7659 : }
7660 901442 : return CoilTotalHeatingEnergyRate * this->SourceEffectiveness;
7661 1146020 : }();
7662 :
7663 : // Minimum tank temperatures
7664 1146020 : const Real64 MinTemp1 = this->SetPointTemp - this->DeadBandDeltaTemp;
7665 1146020 : const Real64 MinTemp2 = this->SetPointTemp2 - this->DeadBandDeltaTemp2;
7666 :
7667 : // Specific Heat of water (J/kg K)
7668 0 : const Real64 Cp = [&] {
7669 1146020 : if (this->UseSidePlantLoc.loopNum > 0) {
7670 1076885 : return state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, this->TankTemp, RoutineName);
7671 : } else {
7672 69135 : return this->water->getSpecificHeat(state, this->TankTemp, RoutineName);
7673 : }
7674 1146020 : }();
7675 :
7676 1146020 : Real64 Eloss = 0.0; // Energy change due to ambient losses over the DataGlobals::TimeStep (J)
7677 1146020 : Real64 Euse = 0.0; // Energy change due to use side mass flow over the DataGlobals::TimeStep (J)
7678 1146020 : Real64 Esource = 0.0; // Energy change due to source side mass flow over the DataGlobals::TimeStep (J)
7679 1146020 : Real64 Eheater1 = 0.0; // Energy change due to heater 1 over the DataGlobals::TimeStep (J)
7680 1146020 : Real64 Eheater2 = 0.0; // Energy change due to heater 2 over the DataGlobals::TimeStep (J)
7681 1146020 : Real64 Eunmet = 0.0; // Energy change unmet over the DataGlobals::TimeStep (J)
7682 1146020 : Real64 Event = 0.0; // Energy change due to venting over the DataGlobals::TimeStep (J)
7683 1146020 : int CycleOnCount1_loc = 0; // Number of times heater 1 cycles on in the current time step
7684 1146020 : int CycleOnCount2_loc = 0; // Number of times heater 2 cycles on in the current time step
7685 1146020 : Real64 Runtime = 0.0; // Time that either heater is running (s)
7686 1146020 : Real64 Runtime1 = 0.0; // Time that heater 1 is running (s)
7687 1146020 : Real64 Runtime2 = 0.0; // Time that heater 2 is running (s)
7688 1146020 : bool SetPointRecovered = false; // Flag to indicate when set point is recovered for the first time
7689 : // Added three variables for desuperheater sourceinlet temperature update
7690 : Real64 MdotDesuperheaterWater; // mass flow rate of desuperheater source side water, kg/s
7691 1146020 : Real64 DesuperheaterPLR = 0.0; // Desuperheater part load ratio
7692 1146020 : Real64 DesuperheaterHeaterRate = 0.0; // Desuperheater heater rate (W)
7693 1146020 : Real64 SourceInletTempSum = 0.0; // Sum the source inlet temperature in sub time step to calculate average tempearature
7694 : Real64 Qheater1; // Heating rate of burner or electric heating element 1 (W)
7695 : Real64 Qheater2; // Heating rate of burner or electric heating element 2 (W)
7696 :
7697 1146020 : if (this->InletMode == InletPositionMode::Fixed) {
7698 990387 : CalcNodeMassFlows(InletPositionMode::Fixed);
7699 : }
7700 :
7701 : // Time remaining in the current DataGlobals::TimeStep (s)
7702 1146020 : Real64 TimeRemaining = SecInTimeStep;
7703 :
7704 : // Diff Eq. Coefficients for each node
7705 1146020 : std::vector<Real64> A;
7706 1146020 : A.resize(nTankNodes);
7707 1146020 : std::vector<Real64> B;
7708 1146020 : B.resize(nTankNodes);
7709 :
7710 : // Temperature at the end of the internal DataGlobals::TimeStep
7711 1146020 : std::vector<Real64> Tfinal;
7712 1146020 : Tfinal.resize(nTankNodes);
7713 :
7714 : // Average temperature of each node over the internal DataGlobals::TimeStep
7715 1146020 : std::vector<Real64> Tavg;
7716 1146020 : Tavg.resize(nTankNodes);
7717 :
7718 1146020 : int SubTimestepCount = 0;
7719 :
7720 6321905 : while (TimeRemaining > 0.0) {
7721 :
7722 5175885 : ++SubTimestepCount;
7723 :
7724 5175885 : bool PrevHeaterOn1 = this->HeaterOn1;
7725 5175885 : bool PrevHeaterOn2 = this->HeaterOn2;
7726 :
7727 5175885 : if (this->InletMode == InletPositionMode::Seeking) {
7728 311442 : CalcNodeMassFlows(InletPositionMode::Seeking);
7729 : }
7730 :
7731 : // Heater control logic
7732 5175885 : if (this->IsChilledWaterTank) {
7733 : // Chilled Water Tank, no heating
7734 311442 : Qheater1 = 0.0;
7735 311442 : Qheater2 = 0.0;
7736 : } else {
7737 : // Control the first heater element (master)
7738 4864443 : if (this->MaxCapacity > 0.0) {
7739 2927138 : const Real64 &NodeTemp = this->Node(this->HeaterNode1).Temp;
7740 :
7741 2927138 : if (this->HeaterOn1) {
7742 307687 : if (NodeTemp >= this->SetPointTemp) {
7743 47340 : this->HeaterOn1 = false;
7744 47340 : SetPointRecovered = true;
7745 : }
7746 : } else { // Heater is off
7747 2619451 : if (NodeTemp < MinTemp1) {
7748 89293 : this->HeaterOn1 = true;
7749 89293 : ++CycleOnCount1_loc;
7750 : }
7751 : }
7752 : }
7753 :
7754 4864443 : if (this->HeaterOn1) {
7755 349677 : Qheater1 = this->MaxCapacity;
7756 : } else {
7757 4514766 : Qheater1 = 0.0;
7758 : }
7759 :
7760 : // Control the second heater element (slave)
7761 4864443 : if (this->MaxCapacity2 > 0.0) {
7762 756904 : if ((this->StratifiedControlMode == PriorityControlMode::MasterSlave) && this->HeaterOn1) {
7763 30937 : this->HeaterOn2 = false;
7764 :
7765 : } else {
7766 725967 : const Real64 &NodeTemp = this->Node(this->HeaterNode2).Temp;
7767 :
7768 725967 : if (this->HeaterOn2) {
7769 422778 : if (NodeTemp >= this->SetPointTemp2) {
7770 7028 : this->HeaterOn2 = false;
7771 7028 : SetPointRecovered = true;
7772 : }
7773 : } else { // Heater is off
7774 303189 : if (NodeTemp < MinTemp2) {
7775 58361 : this->HeaterOn2 = true;
7776 58361 : ++CycleOnCount2_loc;
7777 : }
7778 : }
7779 : }
7780 : }
7781 :
7782 4864443 : if (this->HeaterOn2) {
7783 474111 : Qheater2 = this->MaxCapacity2;
7784 : } else {
7785 4390332 : Qheater2 = 0.0;
7786 : }
7787 : }
7788 :
7789 5175885 : if (SubTimestepCount == 1) {
7790 1146020 : dt = SubTimestepMin;
7791 : } else {
7792 :
7793 : // Set the maximum tank temperature change allowed
7794 4029865 : Real64 dT_max = std::numeric_limits<Real64>::max();
7795 4029865 : if (this->HeaterOn1) {
7796 339991 : if (this->Node(this->HeaterNode1).Temp < this->SetPointTemp) {
7797 : // Node temperature is less than setpoint and heater is on
7798 339975 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7799 : } else {
7800 : // Node temperature is greater than or equal to setpoint and heater is on
7801 : // Heater will turn off next time around, calculate assuming that
7802 16 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7803 : }
7804 : } else { // Heater off
7805 3689874 : if (this->Node(this->HeaterNode1).Temp >= MinTemp1) {
7806 : // Node temperature is greater than or equal to cut in temperature and heater is off
7807 2907526 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7808 : } else {
7809 : // Heater will turn on next time around, calculate to setpoint
7810 782348 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7811 : }
7812 : }
7813 4029865 : if (this->HeaterOn2) {
7814 454824 : if (this->Node(this->HeaterNode2).Temp < this->SetPointTemp2) {
7815 : // Node temperature is less than setpoint and heater is on
7816 454824 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7817 : } else {
7818 : // Node temperature is greater than or equal to setpoint and heater is on
7819 : // Heater will turn off next time around, calculate assuming that
7820 0 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7821 : }
7822 : } else { // Heater off
7823 3575041 : if (this->Node(this->HeaterNode2).Temp >= MinTemp2) {
7824 : // Node temperature is greater than or equal to cut in temperature and heater is off
7825 1890064 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7826 : } else {
7827 : // Heater will turn on next time around, calculate to setpoint
7828 1684977 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7829 : }
7830 : }
7831 :
7832 : // Make adjustments to A and B to account for heaters being on or off now
7833 4029865 : if (this->HeaterOn1 && !PrevHeaterOn1) {
7834 : // If heater 1 is on now and wasn't before add the heat rate to the B term
7835 88617 : B[this->HeaterNode1 - 1] += Qheater1 / (this->Node(this->HeaterNode1).Mass * Cp);
7836 3941248 : } else if (!this->HeaterOn1 && PrevHeaterOn1) {
7837 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7838 46511 : B[this->HeaterNode1 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode1).Mass * Cp);
7839 : }
7840 4029865 : if (this->HeaterOn2 && !PrevHeaterOn2) {
7841 : // If heater 2 is on now and wasn't before add the heat rate to the B term
7842 57921 : B[this->HeaterNode2 - 1] += Qheater2 / (this->Node(this->HeaterNode2).Mass * Cp);
7843 3971944 : } else if (!this->HeaterOn2 && PrevHeaterOn2) {
7844 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7845 12215 : B[this->HeaterNode2 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode2).Mass * Cp);
7846 : }
7847 :
7848 4029865 : if ((this->HeaterOn1 || this->HeaterOn2) && !(PrevHeaterOn1 || PrevHeaterOn2)) {
7849 : // Remove off cycle loads
7850 : // Apply on cycle loads
7851 1322647 : for (int i = 0; i < nTankNodes; i++) {
7852 1182048 : auto const &node = this->Node[i];
7853 1182048 : Real64 NodeCapacitance = node.Mass * Cp;
7854 1182048 : A[i] += (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7855 1182048 : B[i] += (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7856 : NodeCapacitance;
7857 : }
7858 4029865 : } else if (!(this->HeaterOn1 || this->HeaterOn2) && (PrevHeaterOn1 || PrevHeaterOn2)) {
7859 : // Remove on cycle loads
7860 : // Apply off cycle loads
7861 554773 : for (int i = 0; i < nTankNodes; i++) {
7862 501986 : auto const &node = this->Node[i];
7863 501986 : Real64 NodeCapacitance = node.Mass * Cp;
7864 501986 : A[i] -= (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7865 501986 : B[i] -= (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7866 : NodeCapacitance;
7867 : }
7868 : }
7869 :
7870 : // Set the sub DataGlobals::TimeStep (dt)
7871 4029865 : dt = TimeRemaining;
7872 30580865 : for (int i = 0; i < nTankNodes; ++i) {
7873 26551000 : const Real64 Denominator = fabs(A[i] * Tavg[i] + B[i]);
7874 26551000 : if (Denominator != 0.0) {
7875 26513977 : dt = min(dt, dT_max / Denominator);
7876 : }
7877 : }
7878 4029865 : dt = max(min(SubTimestepMin, TimeRemaining), dt);
7879 4029865 : dt = min(SubTimestepMax, dt);
7880 : }
7881 :
7882 : // Make initial guess that average and final temperatures over the DataGlobals::TimeStep are equal to the starting temperatures
7883 39844127 : for (int i = 0; i < nTankNodes; i++) {
7884 34668242 : const auto &NodeTemp = this->Node[i].Temp;
7885 34668242 : Tfinal[i] = NodeTemp;
7886 34668242 : Tavg[i] = NodeTemp;
7887 : }
7888 :
7889 22992092 : for (int ConvergenceCounter = 1; ConvergenceCounter <= 10; ConvergenceCounter++) {
7890 :
7891 22634590 : std::fill(A.begin(), A.end(), 0.0);
7892 22634590 : std::fill(B.begin(), B.end(), 0.0);
7893 :
7894 : // Heater Coefficients
7895 22634590 : B[this->HeaterNode1 - 1] += Qheater1;
7896 22634590 : B[this->HeaterNode2 - 1] += Qheater2;
7897 :
7898 209030656 : for (int i = 0; i < nTankNodes; i++) {
7899 186396066 : const int NodeNum = i + 1;
7900 186396066 : const auto &tank_node = this->Node(NodeNum);
7901 :
7902 : // Parasitic Loads and Losses to Ambient
7903 186396066 : if (this->HeaterOn1 || this->HeaterOn2) {
7904 : // Parasitic Loads
7905 32625970 : B[i] += tank_node.OnCycParaLoad;
7906 : // Losses to Ambient
7907 32625970 : A[i] += -tank_node.OnCycLossCoeff;
7908 32625970 : B[i] += tank_node.OnCycLossCoeff * this->AmbientTemp;
7909 : } else {
7910 : // Parasitic Loads
7911 153770096 : B[i] += tank_node.OffCycParaLoad;
7912 : // Losses to Ambient
7913 153770096 : A[i] += -tank_node.OffCycLossCoeff;
7914 153770096 : B[i] += tank_node.OffCycLossCoeff * this->AmbientTemp;
7915 : }
7916 :
7917 : // Conduction to adjacent nodes
7918 186396066 : A[i] += -(tank_node.CondCoeffDn + tank_node.CondCoeffUp);
7919 186396066 : if (NodeNum > 1) {
7920 163761476 : B[i] += tank_node.CondCoeffUp * Tavg[i - 1];
7921 : }
7922 186396066 : if (NodeNum < nTankNodes) {
7923 163761476 : B[i] += tank_node.CondCoeffDn * Tavg[i + 1];
7924 : }
7925 :
7926 : // Use side plant connection
7927 186396066 : const Real64 use_e_mdot_cp = tank_node.UseMassFlowRate * Cp;
7928 186396066 : A[i] += -use_e_mdot_cp;
7929 186396066 : B[i] += use_e_mdot_cp * this->UseInletTemp;
7930 :
7931 : // Source side heat transfer rate
7932 186396066 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
7933 : // Pumped Condenser Heat Pump Water Heater
7934 126820756 : if (tank_node.SourceMassFlowRate > 0.0) {
7935 10512880 : B[i] += Qheatpump;
7936 : }
7937 : } else {
7938 : // Source side plant connection (constant temperature)
7939 59575310 : const Real64 src_e_mdot_cp = tank_node.SourceMassFlowRate * Cp;
7940 59575310 : A[i] += -src_e_mdot_cp;
7941 59575310 : B[i] += src_e_mdot_cp * this->SourceInletTemp;
7942 : }
7943 :
7944 : // Wrapped condenser heat pump water heater
7945 186396066 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped)) {
7946 27582976 : B[i] += Qheatpump * tank_node.HPWHWrappedCondenserHeatingFrac;
7947 : }
7948 :
7949 : // Internodal flow
7950 186396066 : A[i] += -(tank_node.MassFlowFromUpper + tank_node.MassFlowFromLower) * Cp;
7951 186396066 : if (NodeNum > 1) {
7952 163761476 : B[i] += tank_node.MassFlowFromUpper * Cp * Tavg[i - 1];
7953 : }
7954 186396066 : if (NodeNum < nTankNodes) {
7955 163761476 : B[i] += tank_node.MassFlowFromLower * Cp * Tavg[i + 1];
7956 : }
7957 :
7958 : // Divide by mass and specific heat
7959 : // m * cp * dT/dt = q_net => dT/dt = a * T + b
7960 186396066 : A[i] /= tank_node.Mass * Cp;
7961 186396066 : B[i] /= tank_node.Mass * Cp;
7962 :
7963 : } // end for each node
7964 :
7965 : // Calculate the average and final temperatures over the interval
7966 22634590 : Real64 TfinalDiff = 0.0;
7967 209030656 : for (int i = 0; i < nTankNodes; ++i) {
7968 186396066 : const Real64 Tstart = this->Node[i].Temp;
7969 186396066 : const Real64 b_a = B[i] / A[i];
7970 186396066 : const Real64 e_a_dt = exp(A[i] * dt);
7971 186396066 : Tavg[i] = (Tstart + b_a) * (e_a_dt - 1.0) / (A[i] * dt) - b_a;
7972 186396066 : const Real64 Tfinal_old = Tfinal[i];
7973 186396066 : Tfinal[i] = (Tstart + b_a) * e_a_dt - b_a;
7974 186396066 : TfinalDiff = max(fabs(Tfinal[i] - Tfinal_old), TfinalDiff);
7975 : }
7976 :
7977 22634590 : if (TfinalDiff < TemperatureConvergenceCriteria) {
7978 4818383 : break;
7979 : }
7980 :
7981 17816207 : if (this->DesuperheaterNum > 0) {
7982 47893 : DesuperheaterPLR = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR;
7983 47893 : DesuperheaterHeaterRate = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate;
7984 47893 : MdotDesuperheaterWater = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OperatingWaterFlowRate *
7985 47893 : Psychrometrics::RhoH2O(Tavg[this->SourceOutletStratNode - 1]);
7986 47893 : if (DesuperheaterPLR > 0.0 && MdotDesuperheaterWater > 0.0) {
7987 34540 : this->SourceInletTemp =
7988 34540 : Tavg[this->SourceOutletStratNode - 1] + (DesuperheaterHeaterRate / DesuperheaterPLR) / (MdotDesuperheaterWater * Cp);
7989 : } else {
7990 13353 : this->SourceInletTemp = Tavg[this->SourceOutletStratNode - 1];
7991 : }
7992 : }
7993 : } // end temperature convergence loop
7994 :
7995 : // Inversion mixing
7996 : bool HasInversion;
7997 8346126 : do {
7998 8346126 : HasInversion = false;
7999 : // Starting from the top of the tank check if the node below has a temperature inversion.
8000 48430959 : for (int j = 0; j < nTankNodes - 1; ++j) {
8001 43255074 : if (Tfinal[j] < Tfinal[j + 1]) {
8002 :
8003 : // Temperature inversion!
8004 3170241 : HasInversion = true;
8005 :
8006 : // From the node above the inversion, move down calculating a weighted average
8007 : // of node temperatures until the node below the group of mixed nodes isn't hotter
8008 : // or we hit the bottom of the tank.
8009 3170241 : Real64 Tmixed = 0.0;
8010 3170241 : Real64 MassMixed = 0.0;
8011 : int m;
8012 15598481 : for (m = j; m < nTankNodes; ++m) {
8013 15598481 : Tmixed += Tfinal[m] * this->Node[m].Mass;
8014 15598481 : MassMixed += this->Node[m].Mass;
8015 15598481 : if ((m == nTankNodes - 1) || (Tmixed / MassMixed > Tfinal[m + 1])) {
8016 3170241 : break;
8017 : }
8018 : }
8019 3170241 : Tmixed /= MassMixed;
8020 :
8021 : // Now we have a range of nodes (j = top, m = bottom) that are mixed
8022 : // and the mixed temperature (Tmixed).
8023 : // Move through the mixed nodes and set the final temperature to the mixed temperature.
8024 : // Also calculate a corrected average temperature for each node.
8025 18768722 : for (int k = j; k <= m; ++k) {
8026 : Real64 FinalFactorMixing;
8027 : Real64 AvgFactorMixing;
8028 15598481 : const Real64 NodeCapacitance = this->Node[k].Mass * Cp;
8029 15598481 : if (A[k] == 0.0) {
8030 0 : FinalFactorMixing = dt / NodeCapacitance;
8031 0 : AvgFactorMixing = FinalFactorMixing / 2.0;
8032 : } else {
8033 15598481 : FinalFactorMixing = (exp(A[k] * dt) - 1.0) / A[k] / NodeCapacitance;
8034 15598481 : AvgFactorMixing = ((exp(A[k] * dt) - 1.0) / A[k] / dt - 1.0) / A[k] / NodeCapacitance;
8035 : }
8036 15598481 : const Real64 Q_AdiabaticMixing = (Tmixed - Tfinal[k]) / FinalFactorMixing;
8037 15598481 : Tfinal[k] = Tmixed;
8038 15598481 : Tavg[k] += Q_AdiabaticMixing * AvgFactorMixing;
8039 : }
8040 :
8041 : // Since we mixed, get out of here and start from the top to check again for mixing.
8042 3170241 : break;
8043 : }
8044 : }
8045 : } while (HasInversion);
8046 :
8047 : // Venting
8048 5175885 : if (!this->IsChilledWaterTank) {
8049 4864443 : if (Tfinal[0] > this->TankTempLimit) {
8050 1056270 : for (int i = 0; i < nTankNodes; ++i) {
8051 912834 : if (Tfinal[i] > this->TankTempLimit) {
8052 364737 : Event += this->Node[i].Mass * Cp * (this->TankTempLimit - Tfinal[i]);
8053 364737 : Tfinal[i] = this->TankTempLimit;
8054 : }
8055 : }
8056 : }
8057 : }
8058 :
8059 : // Increment to next internal time step
8060 5175885 : TimeRemaining -= dt;
8061 5175885 : Real64 Qloss = 0.0;
8062 39844127 : for (int i = 0; i < nTankNodes; ++i) {
8063 34668242 : auto &node = this->Node[i];
8064 34668242 : node.Temp = Tfinal[i];
8065 34668242 : node.TempSum += Tavg[i] * dt;
8066 :
8067 : // Bookkeeping for reporting variables, mostly for Qunmet.
8068 34668242 : Real64 Qloss_node = (this->AmbientTemp - Tavg[i]);
8069 : Real64 Qheat_node;
8070 34668242 : const Real64 Quse_node = node.UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[i]);
8071 104004726 : const Real64 Qsource_node = [&] {
8072 34668242 : if (this->HeatPumpNum > 0) {
8073 28440288 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8074 21912506 : if (node.SourceMassFlowRate > 0.0) {
8075 1594749 : return Qheatpump;
8076 : } else {
8077 20317757 : return 0.0;
8078 : }
8079 : } else {
8080 6527782 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
8081 6527782 : return Qheatpump * node.HPWHWrappedCondenserHeatingFrac;
8082 : }
8083 : } else {
8084 6227954 : return node.SourceMassFlowRate * Cp * (this->SourceInletTemp - Tavg[i]);
8085 : }
8086 34668242 : }();
8087 :
8088 34668242 : if (this->HeaterOn1 || this->HeaterOn2) {
8089 6279800 : Qloss_node *= node.OnCycLossCoeff;
8090 6279800 : Qheat_node = node.OnCycParaLoad * this->OnCycParaFracToTank;
8091 : } else {
8092 28388442 : Qloss_node *= node.OffCycLossCoeff;
8093 28388442 : Qheat_node = node.OffCycParaLoad * this->OffCycParaFracToTank;
8094 : }
8095 34668242 : Qloss += Qloss_node;
8096 34668242 : const Real64 Qneeded_node = max(-Quse_node - Qsource_node - Qloss_node - Qheat_node, 0.0);
8097 34668242 : const Real64 Qunmet_node = max(Qneeded_node - Qheater1 - Qheater2, 0.0);
8098 34668242 : Eunmet += Qunmet_node * dt;
8099 : }
8100 5175885 : SourceInletTempSum += this->SourceInletTemp * dt;
8101 : // More bookkeeping for reporting variables
8102 5175885 : Eloss += Qloss * dt;
8103 5175885 : const Real64 Quse = (this->UseOutletStratNode > 0)
8104 5175885 : ? this->UseEffectiveness * this->UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[this->UseOutletStratNode - 1])
8105 5175885 : : 0.0;
8106 5175885 : Euse += Quse * dt;
8107 15527655 : const Real64 Qsource = [&] {
8108 5175885 : if (this->HeatPumpNum > 0) {
8109 4392702 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8110 2459147 : return Qheatpump;
8111 : } else {
8112 1933555 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
8113 1933555 : return 0.0;
8114 : }
8115 : } else {
8116 783183 : if (this->SourceOutletStratNode > 0) {
8117 575437 : return this->SourceEffectiveness * this->SourceMassFlowRate * Cp *
8118 575437 : (this->SourceInletTemp - Tavg[this->SourceOutletStratNode - 1]);
8119 : } else {
8120 207746 : return 0.0;
8121 : }
8122 : }
8123 5175885 : }();
8124 5175885 : Esource += Qsource * dt;
8125 5175885 : if (this->HeaterOn1) {
8126 349677 : Runtime1 += dt;
8127 : }
8128 5175885 : if (this->HeaterOn2) {
8129 474111 : Runtime2 += dt;
8130 : }
8131 5175885 : if (this->HeaterOn1 || this->HeaterOn2) {
8132 823788 : Runtime += dt;
8133 : }
8134 5175885 : Eheater1 += Qheater1 * dt;
8135 5175885 : Eheater2 += Qheater2 * dt;
8136 :
8137 : // Calculation for standard ratings
8138 5175885 : if (!this->FirstRecoveryDone) {
8139 : Real64 Qrecovery;
8140 3142507 : if (this->HeaterOn1 || this->HeaterOn2) {
8141 110333 : Qrecovery = (Qheater1 + Qheater1) / this->Efficiency + this->OnCycParaLoad;
8142 : } else {
8143 3032174 : Qrecovery = this->OffCycParaLoad;
8144 : }
8145 3142507 : this->FirstRecoveryFuel += Qrecovery * dt;
8146 3142507 : if (SetPointRecovered) {
8147 29 : this->FirstRecoveryDone = true;
8148 : }
8149 : }
8150 : } // end while TimeRemaining > 0.0
8151 :
8152 9263262 : for (auto &e : this->Node) {
8153 8117242 : e.TempAvg = e.TempSum / SecInTimeStep;
8154 8117242 : e.TempSum = 0.0;
8155 : }
8156 :
8157 1146020 : this->TankTemp = sum(this->Node, &StratifiedNodeData::Temp) / this->Nodes;
8158 1146020 : this->TankTempAvg = sum(this->Node, &StratifiedNodeData::TempAvg) / this->Nodes;
8159 :
8160 1146020 : if (!state.dataGlobal->WarmupFlag) {
8161 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
8162 251621 : if (this->TankTemp < 2) {
8163 0 : if (this->FreezingErrorIndex == 0) {
8164 0 : ShowWarningError(state,
8165 0 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
8166 : RoutineName,
8167 0 : this->Type,
8168 0 : this->Name,
8169 0 : this->TankTemp));
8170 0 : ShowContinueErrorTimeStamp(state, "");
8171 : }
8172 0 : ShowRecurringWarningErrorAtEnd(state,
8173 0 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
8174 0 : this->FreezingErrorIndex,
8175 0 : this->TankTemp, // Report Max
8176 0 : this->TankTemp, // Report Min
8177 : _, // Don't report Sum
8178 : "{C}", // Max Unit
8179 : "{C}"); // Min Unit
8180 : }
8181 : }
8182 :
8183 1146020 : if (this->UseOutletStratNode > 0) {
8184 1146020 : this->UseOutletTemp = this->Node(this->UseOutletStratNode).TempAvg;
8185 : // Revised use outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8186 1146020 : if (this->UseMassFlowRate > 0.0) {
8187 885741 : this->UseOutletTemp = this->UseInletTemp * (1.0 - this->UseEffectiveness) + this->UseOutletTemp * this->UseEffectiveness;
8188 : }
8189 : }
8190 :
8191 1146020 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
8192 : // If we have a wrapped condenser HPWH, set the source outlet to the weighted average of the node
8193 : // temperatures the condenser sees
8194 476567 : Real64 WeightedAverageSourceOutletTemp(0.0);
8195 2912883 : for (int i = 1; i <= this->Nodes; ++i) {
8196 2436316 : WeightedAverageSourceOutletTemp += this->Node(i).TempAvg * this->Node(i).HPWHWrappedCondenserHeatingFrac;
8197 : }
8198 476567 : this->SourceOutletTemp = WeightedAverageSourceOutletTemp;
8199 669453 : } else if (this->SourceOutletStratNode > 0) {
8200 : // otherwise set it to the temperature of the source outlet node
8201 605659 : this->SourceOutletTemp = this->Node(this->SourceOutletStratNode).TempAvg;
8202 : // Output the average inlet temperature for the DataGlobals::TimeStep
8203 605659 : this->SourceInletTemp = SourceInletTempSum / SecInTimeStep;
8204 : }
8205 1146020 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8206 : // For pumped condensers, set the source inlet and outlets to match the delta T
8207 : // across the water side of the DX coil.
8208 424875 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8209 424875 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
8210 424875 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
8211 424875 : Real64 const HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
8212 424875 : this->SourceInletTemp = this->SourceOutletTemp + HPWHCondenserDeltaT;
8213 : }
8214 :
8215 : // Revised source outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8216 1146020 : if (this->SourceOutletStratNode > 0) {
8217 605659 : if (this->SourceMassFlowRate > 0.0) {
8218 277242 : this->SourceOutletTemp = this->SourceInletTemp * (1.0 - this->SourceEffectiveness) + this->SourceOutletTemp * this->SourceEffectiveness;
8219 : }
8220 : }
8221 :
8222 1146020 : this->LossRate = Eloss / SecInTimeStep;
8223 1146020 : this->UseRate = Euse / SecInTimeStep;
8224 1146020 : Real64 WrappedCondenserHeatPumpRate = 0.0;
8225 1146020 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
8226 424875 : this->SourceRate = Qheatpump;
8227 : } else {
8228 721145 : this->SourceRate = Esource / SecInTimeStep;
8229 721145 : WrappedCondenserHeatPumpRate = Qheatpump;
8230 : }
8231 :
8232 1146020 : this->OffCycParaFuelRate = this->OffCycParaLoad * (SecInTimeStep - Runtime) / SecInTimeStep;
8233 1146020 : this->OnCycParaFuelRate = this->OnCycParaLoad * Runtime / SecInTimeStep;
8234 1146020 : this->OffCycParaRateToTank = this->OffCycParaFuelRate * this->OffCycParaFracToTank;
8235 1146020 : this->OnCycParaRateToTank = this->OnCycParaFuelRate * this->OnCycParaFracToTank;
8236 1146020 : this->TotalDemandRate =
8237 1146020 : -this->UseRate - this->SourceRate - this->LossRate - this->OffCycParaRateToTank - this->OnCycParaRateToTank - WrappedCondenserHeatPumpRate;
8238 1146020 : this->HeaterRate1 = Eheater1 / SecInTimeStep;
8239 1146020 : this->HeaterRate2 = Eheater2 / SecInTimeStep;
8240 1146020 : this->HeaterRate = this->HeaterRate1 + this->HeaterRate2;
8241 :
8242 1146020 : this->UnmetRate = Eunmet / SecInTimeStep;
8243 1146020 : this->VentRate = Event / SecInTimeStep;
8244 1146020 : this->NetHeatTransferRate = this->UseRate + this->SourceRate + this->LossRate + this->OffCycParaRateToTank + this->OnCycParaRateToTank +
8245 1146020 : this->HeaterRate + this->VentRate + WrappedCondenserHeatPumpRate;
8246 :
8247 1146020 : this->CycleOnCount = CycleOnCount1_loc + CycleOnCount2_loc;
8248 1146020 : this->CycleOnCount1 = CycleOnCount1_loc;
8249 1146020 : this->CycleOnCount2 = CycleOnCount2_loc;
8250 :
8251 1146020 : this->RuntimeFraction = Runtime / SecInTimeStep;
8252 1146020 : this->RuntimeFraction1 = Runtime1 / SecInTimeStep;
8253 1146020 : this->RuntimeFraction2 = Runtime2 / SecInTimeStep;
8254 :
8255 1146020 : this->FuelRate = (Eheater1 + Eheater2) / this->Efficiency / SecInTimeStep;
8256 :
8257 : // Add water heater skin losses and venting losses to ambient zone, if specified
8258 1146020 : if (this->AmbientTempZone > 0) {
8259 633391 : this->AmbientZoneGain = -this->LossRate * this->SkinLossFracToZone - this->VentRate;
8260 : }
8261 1146020 : }
8262 :
8263 1301829 : void WaterThermalTankData::CalcNodeMassFlows(InletPositionMode inletMode)
8264 : {
8265 :
8266 : // SUBROUTINE INFORMATION:
8267 : // AUTHOR Peter Graham Ellis
8268 : // DATE WRITTEN January 2007
8269 : // MODIFIED na
8270 : // RE-ENGINEERED na
8271 :
8272 : // PURPOSE OF THIS SUBROUTINE:
8273 : // Determines mass flow rates between nodes according to the locations of the use- and source-side inlet and outlet
8274 : // nodes.
8275 :
8276 : // METHODOLOGY EMPLOYED:
8277 : // In 'Seeking' mode, nodes are searched between the user-specified inlet and outlet nodes to find the node closest
8278 : // in temperature to the inlet fluid temperature. In 'Fixed' mode, the user-specified nodes are always used.
8279 : // Upward and downward flows are added to each node between an inlet and outlet. Flows in both directions cancel out
8280 : // to leave only the net flow in one direction.
8281 :
8282 1301829 : int useInletStratNod = this->UseInletStratNode;
8283 1301829 : int useOutletStratNode = this->UseOutletStratNode;
8284 1301829 : int sourceInletStratNode = this->SourceInletStratNode;
8285 1301829 : int sourceOutletStratNode = this->SourceOutletStratNode;
8286 :
8287 1301829 : Real64 useMassFlowRate = this->UseMassFlowRate * this->UseEffectiveness;
8288 1301829 : Real64 sourceMassFlowRate = this->SourceMassFlowRate * this->SourceEffectiveness;
8289 :
8290 10353925 : for (auto &e : this->Node) {
8291 9052096 : e.UseMassFlowRate = 0.0;
8292 9052096 : e.SourceMassFlowRate = 0.0;
8293 9052096 : e.MassFlowFromUpper = 0.0;
8294 9052096 : e.MassFlowFromLower = 0.0;
8295 9052096 : e.MassFlowToUpper = 0.0;
8296 9052096 : e.MassFlowToLower = 0.0;
8297 : }
8298 :
8299 1301829 : if (inletMode == InletPositionMode::Seeking) {
8300 : // 'Seek' the node with the temperature closest to the inlet temperature
8301 : // Start at the user-specified inlet node and search to the user-specified outlet node
8302 : int Step;
8303 311442 : if (useMassFlowRate > 0.0) {
8304 143208 : if (useInletStratNod > useOutletStratNode) {
8305 0 : Step = -1;
8306 : } else {
8307 143208 : Step = 1;
8308 : }
8309 143208 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8310 143208 : int const NodeNum_stop(floop_end(useInletStratNod, useOutletStratNode, Step));
8311 456902 : for (int NodeNum = useInletStratNod; NodeNum != NodeNum_stop; NodeNum += Step) {
8312 423205 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->UseInletTemp);
8313 423205 : if (DeltaTemp < MinDeltaTemp) {
8314 229690 : MinDeltaTemp = DeltaTemp;
8315 229690 : useInletStratNod = NodeNum;
8316 193515 : } else if (DeltaTemp > MinDeltaTemp) {
8317 109511 : break;
8318 : }
8319 : }
8320 : }
8321 :
8322 311442 : if (sourceMassFlowRate > 0.0) {
8323 46870 : if (sourceInletStratNode > sourceOutletStratNode) {
8324 46870 : Step = -1;
8325 : } else {
8326 0 : Step = 1;
8327 : }
8328 46870 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8329 46870 : int const NodeNum_stop(floop_end(sourceInletStratNode, sourceOutletStratNode, Step));
8330 94488 : for (int NodeNum = sourceInletStratNode; NodeNum != NodeNum_stop; NodeNum += Step) {
8331 94488 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->SourceInletTemp);
8332 94488 : if (DeltaTemp < MinDeltaTemp) {
8333 46870 : MinDeltaTemp = DeltaTemp;
8334 46870 : sourceInletStratNode = NodeNum;
8335 47618 : } else if (DeltaTemp > MinDeltaTemp) {
8336 46870 : break;
8337 : }
8338 : }
8339 : }
8340 : }
8341 :
8342 1301829 : if (useInletStratNod > 0) {
8343 1301829 : this->Node(useInletStratNod).UseMassFlowRate = useMassFlowRate;
8344 : }
8345 1301829 : if (sourceInletStratNode > 0) {
8346 709773 : this->Node(sourceInletStratNode).SourceMassFlowRate = sourceMassFlowRate;
8347 : }
8348 :
8349 1301829 : if (useMassFlowRate > 0.0) {
8350 957384 : if (useOutletStratNode > useInletStratNod) {
8351 : // Use-side flow is down
8352 770408 : for (int NodeNum = useInletStratNod; NodeNum <= useOutletStratNode - 1; ++NodeNum) {
8353 629558 : this->Node(NodeNum).MassFlowToLower += useMassFlowRate;
8354 : }
8355 770408 : for (int NodeNum = useInletStratNod + 1; NodeNum <= useOutletStratNode; ++NodeNum) {
8356 629558 : this->Node(NodeNum).MassFlowFromUpper += useMassFlowRate;
8357 : }
8358 :
8359 816534 : } else if (useOutletStratNode < useInletStratNod) {
8360 : // Use-side flow is up
8361 5148062 : for (int NodeNum = useOutletStratNode; NodeNum <= useInletStratNod - 1; ++NodeNum) {
8362 4631310 : this->Node(NodeNum).MassFlowFromLower += useMassFlowRate;
8363 : }
8364 5148062 : for (int NodeNum = useOutletStratNode + 1; NodeNum <= useInletStratNod; ++NodeNum) {
8365 4631310 : this->Node(NodeNum).MassFlowToUpper += useMassFlowRate;
8366 : }
8367 :
8368 : } else {
8369 : // Use-side flow is across the node; no flow to other nodes
8370 : }
8371 : }
8372 :
8373 1301829 : if (sourceMassFlowRate > 0.0) {
8374 300677 : if (sourceOutletStratNode > sourceInletStratNode) {
8375 : // Source-side flow is down
8376 672770 : for (int NodeNum = sourceInletStratNode; NodeNum <= sourceOutletStratNode - 1; ++NodeNum) {
8377 574385 : this->Node(NodeNum).MassFlowToLower += sourceMassFlowRate;
8378 : }
8379 672770 : for (int NodeNum = sourceInletStratNode + 1; NodeNum <= sourceOutletStratNode; ++NodeNum) {
8380 574385 : this->Node(NodeNum).MassFlowFromUpper += sourceMassFlowRate;
8381 : }
8382 :
8383 202292 : } else if (sourceOutletStratNode < sourceInletStratNode) {
8384 : // Source-side flow is up
8385 1135967 : for (int NodeNum = sourceOutletStratNode; NodeNum <= sourceInletStratNode - 1; ++NodeNum) {
8386 933675 : this->Node(NodeNum).MassFlowFromLower += sourceMassFlowRate;
8387 : }
8388 1135967 : for (int NodeNum = sourceOutletStratNode + 1; NodeNum <= sourceInletStratNode; ++NodeNum) {
8389 933675 : this->Node(NodeNum).MassFlowToUpper += sourceMassFlowRate;
8390 : }
8391 :
8392 : } else {
8393 : // Source-side flow is across the node; no flow to other nodes
8394 : }
8395 : }
8396 :
8397 : // Cancel out any up and down flows
8398 10353925 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
8399 9052096 : this->Node(NodeNum).MassFlowFromUpper = max((this->Node(NodeNum).MassFlowFromUpper - this->Node(NodeNum).MassFlowToUpper), 0.0);
8400 9052096 : this->Node(NodeNum).MassFlowFromLower = max((this->Node(NodeNum).MassFlowFromLower - this->Node(NodeNum).MassFlowToLower), 0.0);
8401 : }
8402 1301829 : }
8403 :
8404 29484 : void WaterThermalTankData::CalcDesuperheaterWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8405 : {
8406 :
8407 : // SUBROUTINE INFORMATION:
8408 : // AUTHOR Richard Raustad
8409 : // DATE WRITTEN July 2005
8410 : // MODIFIED na
8411 : // RE-ENGINEERED na
8412 :
8413 : // PURPOSE OF THIS SUBROUTINE:
8414 : // Simulates a refrigerant desuperheater to heat water
8415 :
8416 : // METHODOLOGY EMPLOYED:
8417 : // This model uses the rated heat reclaim recovery efficiency, recovery efficiency modifier curve,
8418 : // set point temperature, and dead band temperature difference to simulate the desuperheater coil
8419 : // and sets up inputs to the tank model associated with the desuperheater coil
8420 :
8421 29484 : int constexpr MaxIte(500); // Maximum number of iterations for RegulaFalsi
8422 :
8423 29484 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum);
8424 :
8425 29484 : int WaterInletNode = DesupHtr.WaterInletNode;
8426 29484 : int WaterOutletNode = DesupHtr.WaterOutletNode;
8427 :
8428 : // store first iteration tank temperature and desuperheater mode of operation
8429 29484 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && DesupHtr.FirstTimeThroughFlag) {
8430 : // Save conditions from end of previous system timestep
8431 : // Every iteration that does not advance time should reset to these values
8432 13796 : this->SavedTankTemp = this->TankTemp;
8433 13796 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
8434 13796 : DesupHtr.SaveMode = DesupHtr.Mode;
8435 13796 : DesupHtr.FirstTimeThroughFlag = false;
8436 : }
8437 :
8438 15688 : else if (!FirstHVACIteration) {
8439 14742 : DesupHtr.FirstTimeThroughFlag = true;
8440 : }
8441 :
8442 : // initialize variables before invoking any RETURN statement
8443 29484 : this->SourceMassFlowRate = 0.0;
8444 : // reset tank inlet temp from previous time step
8445 29484 : this->SourceInletTemp = this->SavedSourceOutletTemp;
8446 29484 : DesupHtr.DesuperheaterPLR = 0.0;
8447 :
8448 29484 : state.dataLoopNodes->Node(WaterInletNode).MassFlowRate = 0.0;
8449 29484 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = 0.0;
8450 29484 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SavedSourceOutletTemp;
8451 :
8452 29484 : DesupHtr.DesuperheaterPLR = 0.0;
8453 29484 : DesupHtr.OnCycParaFuelRate = 0.0;
8454 29484 : DesupHtr.OnCycParaFuelEnergy = 0.0;
8455 29484 : DesupHtr.OffCycParaFuelRate = 0.0;
8456 29484 : DesupHtr.OffCycParaFuelEnergy = 0.0;
8457 29484 : DesupHtr.HEffFTempOutput = 0.0;
8458 29484 : DesupHtr.HeaterRate = 0.0;
8459 29484 : DesupHtr.HeaterEnergy = 0.0;
8460 29484 : DesupHtr.PumpPower = 0.0;
8461 29484 : DesupHtr.PumpEnergy = 0.0;
8462 :
8463 : // simulate only the water heater tank if the desuperheater coil is scheduled off
8464 29484 : Real64 AvailSchedule = DesupHtr.availSched->getCurrentVal();
8465 29484 : if (AvailSchedule == 0.0) {
8466 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8467 0 : this->CalcWaterThermalTank(state);
8468 18199 : return;
8469 : }
8470 :
8471 : // simulate only the water heater tank if the lowest temperature available from the desuperheater coil
8472 : // is less than water inlet temperature if the reclaim source is a refrigeration condenser
8473 29484 : if (DesupHtr.ValidSourceType) {
8474 29484 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8475 29484 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8476 4602 : if (state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailTemperature <= this->SourceInletTemp) {
8477 24 : DesupHtr.Mode = TankOperatingMode::Floating;
8478 24 : this->CalcWaterThermalTank(state);
8479 168 : ShowRecurringWarningErrorAtEnd(state,
8480 48 : "WaterHeating:Desuperheater " + DesupHtr.Name +
8481 : " - Waste heat source temperature was too low to be useful.",
8482 24 : DesupHtr.InsuffTemperatureWarn);
8483 24 : return;
8484 : } // Temp too low
8485 : } // desuperheater source is condenser_refrigeration
8486 : } // validsourcetype
8487 :
8488 29460 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad;
8489 29460 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8490 :
8491 : // check that water heater tank cut-in temp is greater than desuperheater cut-in temp
8492 29460 : Real64 desupHtrSetPointTemp = DesupHtr.SetPointTemp;
8493 29460 : Real64 DeadBandTempDiff = DesupHtr.DeadBandTempDiff;
8494 29460 : if ((desupHtrSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
8495 4 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
8496 0 : Real64 MinTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8497 0 : ++DesupHtr.SetPointError;
8498 0 : if (DesupHtr.SetPointError < 5) {
8499 0 : ShowWarningError(state,
8500 0 : format("{} \"{}\": Water heater tank set point temperature is greater than or equal to the cut-in temperature of "
8501 : "the desuperheater. Desuperheater will be disabled.",
8502 0 : DesupHtr.Type,
8503 0 : DesupHtr.Name));
8504 0 : ShowContinueErrorTimeStamp(state, format(" ...Desuperheater cut-in temperature = {:.2R}", MinTemp));
8505 : } else {
8506 0 : ShowRecurringWarningErrorAtEnd(state,
8507 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8508 : "\": Water heater tank set point temperature is greater than or equal to the cut-in "
8509 : "temperature of the desuperheater. Desuperheater will be disabled warning continues...",
8510 0 : DesupHtr.SetPointErrIndex1,
8511 : MinTemp,
8512 : MinTemp);
8513 : }
8514 : }
8515 :
8516 : // Simulate tank if desuperheater unavailable for water heating
8517 4 : this->CalcWaterThermalTank(state);
8518 4 : return;
8519 : }
8520 :
8521 29456 : Real64 Effic = DesupHtr.HeatReclaimRecoveryEff;
8522 :
8523 29456 : state.dataLoopNodes->Node(WaterInletNode).Temp = this->SavedSourceOutletTemp;
8524 29456 : DesupHtr.Mode = DesupHtr.SaveMode;
8525 :
8526 : Real64 HEffFTemp;
8527 29456 : if (DesupHtr.HEffFTemp > 0) {
8528 29456 : HEffFTemp = max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, this->SavedTankTemp, state.dataEnvrn->OutDryBulbTemp));
8529 : } else {
8530 0 : HEffFTemp = 1.0;
8531 : }
8532 :
8533 : // set limits on heat recovery efficiency
8534 29456 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8535 4578 : if ((HEffFTemp * Effic) > 0.9) {
8536 0 : HEffFTemp = 0.9 / Effic;
8537 : }
8538 : } else { // max is 0.3 for all other sources
8539 24878 : if ((HEffFTemp * Effic) > 0.3) {
8540 0 : HEffFTemp = 0.3 / Effic;
8541 : }
8542 : } // setting limits on heat recovery efficiency
8543 :
8544 : // Access the appropriate structure to find the average heating capacity of the desuperheater heating coil
8545 29456 : Real64 AverageWasteHeat = 0.0;
8546 29456 : if (DesupHtr.ValidSourceType) {
8547 29456 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8548 29456 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8549 : // Refrigeration systems are solved outside the time step iteration, so the
8550 : // appropriate decrement for other waste heat applications is handled differently
8551 0 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).AvailCapacity -
8552 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8553 0 : DesupHtr.DXSysPLR = 1.0;
8554 29456 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8555 4578 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailCapacity -
8556 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8557 4578 : DesupHtr.DXSysPLR = 1.0;
8558 24878 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8559 12978 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8560 12978 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8561 11900 : AverageWasteHeat = state.dataHeatBal->HeatReclaimDXCoil(SourceID).AvailCapacity -
8562 11900 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8563 11900 : DesupHtr.DXSysPLR = state.dataDXCoils->DXCoil(SourceID).PartLoadRatio;
8564 12978 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling ||
8565 9196 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpVSEQ) {
8566 3782 : AverageWasteHeat = state.dataHeatBal->HeatReclaimVS_Coil(SourceID).AvailCapacity -
8567 3782 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8568 3782 : DesupHtr.DXSysPLR = state.dataVariableSpeedCoils->VarSpeedCoil(SourceID).PartLoadRatio;
8569 9196 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8570 3246 : AverageWasteHeat = state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).AvailCapacity -
8571 3246 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8572 3246 : DesupHtr.DXSysPLR = state.dataWaterToAirHeatPumpSimple->SimpleWatertoAirHP(SourceID).PartLoadRatio;
8573 5950 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8574 5950 : AverageWasteHeat =
8575 5950 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.AvailCapacity -
8576 5950 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.HVACDesuperheaterReclaimedHeatTotal;
8577 5950 : DesupHtr.DXSysPLR = state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].partLoadRatioReport;
8578 : }
8579 : } else {
8580 0 : AverageWasteHeat = 0.0;
8581 : }
8582 :
8583 : // simulate only water heater tank if reclaim heating source is off
8584 29456 : if (DesupHtr.DXSysPLR == 0.0) {
8585 18171 : this->CalcWaterThermalTank(state);
8586 18171 : return;
8587 : }
8588 :
8589 : // If the set point is higher than the maximum water temp, reset both the set point and the dead band temperature difference
8590 11285 : if (desupHtrSetPointTemp > DesupHtr.MaxInletWaterTemp) {
8591 11285 : Real64 CutInTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8592 11285 : desupHtrSetPointTemp = DesupHtr.MaxInletWaterTemp;
8593 11285 : DeadBandTempDiff = max(0.0, (desupHtrSetPointTemp - CutInTemp));
8594 : }
8595 :
8596 : Real64 Acc; // Accuracy of result from RegulaFalsi
8597 11285 : if (DesupHtr.TankTypeNum == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8598 951 : Acc = 0.001;
8599 : } else {
8600 10334 : Acc = 0.00001;
8601 : }
8602 :
8603 : // set the water-side mass flow rate
8604 11285 : Real64 CpWater = Psychrometrics::CPHW(state.dataLoopNodes->Node(WaterInletNode).Temp);
8605 11285 : Real64 MdotWater = DesupHtr.OperatingWaterFlowRate * Psychrometrics::RhoH2O(state.dataLoopNodes->Node(WaterInletNode).Temp);
8606 11285 : Real64 QHeatRate = 0.0;
8607 11285 : if (state.dataLoopNodes->Node(WaterInletNode).Temp <= DesupHtr.MaxInletWaterTemp + Acc) {
8608 10099 : QHeatRate = ((AverageWasteHeat * Effic * HEffFTemp) / DesupHtr.DXSysPLR) + (DesupHtr.PumpElecPower * DesupHtr.PumpFracToWater);
8609 : }
8610 :
8611 : // change to tanktypenum using parameters?
8612 11285 : Real64 partLoadRatio = 0.0;
8613 : Real64 NewTankTemp;
8614 : {
8615 11285 : DataPlant::PlantEquipmentType const TankType = DesupHtr.TankTypeNum;
8616 :
8617 11285 : if (TankType == DataPlant::PlantEquipmentType::WtrHeaterMixed || TankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8618 :
8619 11285 : DesupHtr.SaveWHMode = this->Mode;
8620 11285 : Real64 PreTankAvgTemp = this->TankTempAvg;
8621 11285 : Real64 NewTankAvgTemp = 0.0; // Initialization
8622 11285 : int max_count = 200;
8623 11285 : int count = 0;
8624 11285 : bool firstThrough = true;
8625 11285 : switch (DesupHtr.Mode) {
8626 10391 : case TankOperatingMode::Heating:
8627 : // Calculate until consistency of desuperheater and tank source side energy transfer achieved
8628 30514 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > HVAC::SmallTempDiff || firstThrough) && count < max_count) {
8629 20123 : count++;
8630 20123 : firstThrough = false;
8631 20123 : PreTankAvgTemp = this->TankTempAvg;
8632 20123 : partLoadRatio = DesupHtr.DXSysPLR;
8633 20123 : if (MdotWater > 0.0) {
8634 20123 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8635 : } else {
8636 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8637 : }
8638 :
8639 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8640 20123 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8641 :
8642 : // set the source mass flow rate for the tank
8643 20123 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8644 :
8645 20123 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8646 20123 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8647 20123 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8648 20123 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8649 20123 : this->CalcWaterThermalTank(state);
8650 20123 : Real64 NewTankTemp = this->TankTemp;
8651 :
8652 20123 : if (NewTankTemp > desupHtrSetPointTemp) {
8653 : // Only revert to floating mode if the tank temperature is higher than the cut out temperature
8654 6809 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8655 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8656 : }
8657 : int SolFla;
8658 6809 : std::string IterNum;
8659 27227 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8660 27227 : this->Mode = DesupHtr.SaveWHMode;
8661 27227 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8662 27227 : this->CalcWaterThermalTank(state);
8663 27227 : Real64 NewTankTemp = this->TankTemp;
8664 27227 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8665 27227 : return PLRResidualWaterThermalTank;
8666 6809 : };
8667 6809 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8668 6809 : if (SolFla == -1) {
8669 0 : IterNum = fmt::to_string(MaxIte);
8670 0 : if (!state.dataGlobal->WarmupFlag) {
8671 0 : ++DesupHtr.IterLimitExceededNum1;
8672 0 : if (DesupHtr.IterLimitExceededNum1 == 1) {
8673 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8674 0 : ShowContinueError(state,
8675 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8676 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8677 : IterNum,
8678 : partLoadRatio));
8679 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8680 : } else {
8681 0 : ShowRecurringWarningErrorAtEnd(state,
8682 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8683 : "\": Iteration limit exceeded in heating mode warning continues. "
8684 : "Part-load ratio statistics follow.",
8685 0 : DesupHtr.IterLimitErrIndex1,
8686 : partLoadRatio,
8687 : partLoadRatio);
8688 : }
8689 : }
8690 6809 : } else if (SolFla == -2) {
8691 0 : partLoadRatio =
8692 0 : max(0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8693 0 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8694 0 : this->CalcWaterThermalTank(state);
8695 0 : if (!state.dataGlobal->WarmupFlag) {
8696 0 : ++DesupHtr.RegulaFalsiFailedNum1;
8697 0 : if (DesupHtr.RegulaFalsiFailedNum1 == 1) {
8698 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8699 0 : ShowContinueError(state,
8700 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to 1 "
8701 : "exceeded. Part-load ratio used = {:.3R}",
8702 : partLoadRatio));
8703 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8704 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8705 : } else {
8706 0 : ShowRecurringWarningErrorAtEnd(state,
8707 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8708 : "\": Part-load ratio calculation failed in heating mode warning "
8709 : "continues. Part-load ratio statistics follow.",
8710 0 : DesupHtr.RegulaFalsiFailedIndex1,
8711 : partLoadRatio,
8712 : partLoadRatio);
8713 : }
8714 : }
8715 : }
8716 6809 : } else {
8717 13314 : partLoadRatio = DesupHtr.DXSysPLR;
8718 : }
8719 20123 : NewTankAvgTemp = this->TankTempAvg;
8720 : }
8721 10391 : break;
8722 894 : case TankOperatingMode::Floating:
8723 894 : if (MdotWater > 0.0) {
8724 894 : state.dataLoopNodes->Node(WaterOutletNode).Temp =
8725 894 : state.dataLoopNodes->Node(WaterInletNode).Temp + QHeatRate / (MdotWater * CpWater);
8726 : } else {
8727 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = state.dataLoopNodes->Node(WaterInletNode).Temp;
8728 : }
8729 : // check tank temperature by setting source inlet mass flow rate to zero
8730 894 : partLoadRatio = 0.0;
8731 :
8732 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8733 894 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8734 :
8735 : // check tank temperature by setting source inlet mass flow rate to zero
8736 894 : this->SourceMassFlowRate = 0.0;
8737 :
8738 : // disable the tank heater to find PLR of the HPWH
8739 894 : this->MaxCapacity = 0.0;
8740 894 : this->MinCapacity = 0.0;
8741 894 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8742 894 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8743 894 : this->CalcWaterThermalTank(state);
8744 894 : NewTankTemp = this->TankTemp;
8745 :
8746 894 : if (NewTankTemp <= (desupHtrSetPointTemp - DeadBandTempDiff)) {
8747 44 : this->Mode = DesupHtr.SaveWHMode;
8748 44 : if ((this->SavedTankTemp - NewTankTemp) != 0.0) {
8749 44 : partLoadRatio =
8750 44 : min(DesupHtr.DXSysPLR,
8751 44 : max(0.0, ((desupHtrSetPointTemp - DeadBandTempDiff) - NewTankTemp) / (this->SavedTankTemp - NewTankTemp)));
8752 : } else {
8753 0 : partLoadRatio = DesupHtr.DXSysPLR;
8754 : }
8755 201 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > HVAC::SmallTempDiff || firstThrough) && count < max_count) {
8756 157 : count++;
8757 157 : firstThrough = false;
8758 157 : PreTankAvgTemp = this->TankTempAvg;
8759 157 : DesupHtr.Mode = TankOperatingMode::Heating;
8760 157 : if (MdotWater > 0.0) {
8761 157 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8762 : } else {
8763 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8764 : }
8765 :
8766 : // set the full load outlet temperature on the water heater source inlet node
8767 157 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8768 :
8769 : // set the source mass flow rate for the tank and enable backup heating element
8770 157 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8771 157 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8772 157 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8773 157 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8774 157 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8775 157 : this->CalcWaterThermalTank(state);
8776 157 : NewTankTemp = this->TankTemp;
8777 :
8778 157 : if (NewTankTemp > desupHtrSetPointTemp) {
8779 : // Only revert to floating mode if the tank temperature is higher than the cut-out temperature
8780 0 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8781 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8782 : }
8783 : int SolFla;
8784 0 : std::string IterNum;
8785 0 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8786 0 : this->Mode = DesupHtr.SaveWHMode;
8787 0 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8788 0 : this->CalcWaterThermalTank(state);
8789 0 : Real64 NewTankTemp = this->TankTemp;
8790 0 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8791 0 : return PLRResidualWaterThermalTank;
8792 0 : };
8793 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8794 0 : if (SolFla == -1) {
8795 0 : IterNum = fmt::to_string(MaxIte);
8796 0 : if (!state.dataGlobal->WarmupFlag) {
8797 0 : ++DesupHtr.IterLimitExceededNum2;
8798 0 : if (DesupHtr.IterLimitExceededNum2 == 1) {
8799 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8800 0 : ShowContinueError(state,
8801 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8802 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8803 : IterNum,
8804 : partLoadRatio));
8805 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8806 : } else {
8807 0 : ShowRecurringWarningErrorAtEnd(state,
8808 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8809 : "\": Iteration limit exceeded in float mode warning continues. "
8810 : "Part-load ratio statistics follow.",
8811 0 : DesupHtr.IterLimitErrIndex2,
8812 : partLoadRatio,
8813 : partLoadRatio);
8814 : }
8815 : }
8816 0 : } else if (SolFla == -2) {
8817 0 : partLoadRatio = max(
8818 0 : 0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8819 0 : if (!state.dataGlobal->WarmupFlag) {
8820 0 : ++DesupHtr.RegulaFalsiFailedNum2;
8821 0 : if (DesupHtr.RegulaFalsiFailedNum2 == 1) {
8822 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8823 0 : ShowContinueError(state,
8824 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to "
8825 : "1 exceeded. Part-load ratio used = {:.3R}",
8826 : partLoadRatio));
8827 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8828 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8829 : } else {
8830 0 : ShowRecurringWarningErrorAtEnd(
8831 : state,
8832 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8833 : "\": Part-load ratio calculation failed in float mode warning "
8834 : "continues. Part-load ratio statistics follow.",
8835 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).RegulaFalsiFailedIndex2,
8836 : partLoadRatio,
8837 : partLoadRatio);
8838 : }
8839 : }
8840 : }
8841 0 : }
8842 157 : NewTankAvgTemp = this->TankTempAvg;
8843 : }
8844 : } else {
8845 850 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8846 850 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8847 : }
8848 894 : break;
8849 0 : default:
8850 0 : break;
8851 : }
8852 :
8853 : // should never get here, case is checked in GetWaterThermalTankInput
8854 11285 : } else {
8855 0 : ShowFatalError(state,
8856 0 : format("Coil:WaterHeating:Desuperheater = {}: invalid water heater tank type and name entered = {}, {}",
8857 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).Name,
8858 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankType,
8859 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankName));
8860 : }
8861 : }
8862 :
8863 11285 : if (QHeatRate == 0) {
8864 1186 : partLoadRatio = 0.0;
8865 : }
8866 :
8867 11285 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = MdotWater * partLoadRatio;
8868 11285 : DesupHtr.HEffFTempOutput = HEffFTemp;
8869 11285 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8870 11285 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8871 :
8872 11285 : if (partLoadRatio == 0) {
8873 1186 : this->SourceInletTemp = this->SourceOutletTemp;
8874 1186 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8875 1186 : DesupHtr.HEffFTempOutput = 0.0;
8876 1186 : DesupHtr.HeaterRate = 0.0;
8877 : }
8878 :
8879 11285 : DesupHtr.HeaterEnergy = DesupHtr.HeaterRate * state.dataHVACGlobal->TimeStepSysSec;
8880 11285 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8881 11285 : DesupHtr.OnCycParaFuelRate = DesupHtr.OnCycParaLoad * partLoadRatio;
8882 11285 : DesupHtr.OnCycParaFuelEnergy = DesupHtr.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8883 11285 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad * (1 - partLoadRatio);
8884 11285 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8885 11285 : DesupHtr.PumpPower = DesupHtr.PumpElecPower * (partLoadRatio);
8886 11285 : DesupHtr.PumpEnergy = DesupHtr.PumpPower * state.dataHVACGlobal->TimeStepSysSec;
8887 :
8888 : // Update used waste heat (just in case multiple users of waste heat use same source)
8889 11285 : if (DesupHtr.ValidSourceType) {
8890 11285 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8891 11285 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8892 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8893 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8894 0 : for (auto const &num : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat) {
8895 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8896 : }
8897 11285 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8898 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8899 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8900 9156 : for (auto const &num : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat) {
8901 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8902 : }
8903 6707 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8904 4467 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8905 4467 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8906 2240 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8907 2240 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8908 4480 : for (auto const &num : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat) {
8909 2240 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8910 : }
8911 6707 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling ||
8912 2071 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpVSEQ) {
8913 2396 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8914 2396 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8915 4792 : for (auto const &num : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeat) {
8916 2396 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8917 : }
8918 4467 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8919 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8920 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8921 1902 : for (auto const &num : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat) {
8922 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8923 : }
8924 1120 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8925 1120 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat(
8926 1120 : DesuperheaterNum) = DesupHtr.HeaterRate;
8927 1120 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal =
8928 : 0.0;
8929 1120 : for (auto const &num :
8930 3360 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat) {
8931 1120 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum]
8932 1120 : .reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8933 : }
8934 : }
8935 : }
8936 : }
8937 :
8938 408468 : void WaterThermalTankData::CalcHeatPumpWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8939 : {
8940 :
8941 : // SUBROUTINE INFORMATION:
8942 : // AUTHOR Richard Raustad
8943 : // DATE WRITTEN March 2005
8944 : // MODIFIED B. Griffith, Jan 2012 for stratified tank
8945 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
8946 : // RE-ENGINEERED na
8947 :
8948 : // PURPOSE OF THIS SUBROUTINE:
8949 : // Simulates a heat pump water heater
8950 :
8951 : // METHODOLOGY EMPLOYED:
8952 : // Simulate the water heater tank, DX coil, and fan to meet the water heating requirements.
8953 :
8954 408468 : int constexpr MaxIte(500); // maximum number of iterations
8955 408468 : Real64 constexpr Acc(0.001); // Accuracy of result from RegulaFalsi
8956 :
8957 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8958 : Real64 MdotWater; // mass flow rate of condenser water, kg/s
8959 408468 : IntegratedHeatPump::IHPOperationMode IHPMode(IntegratedHeatPump::IHPOperationMode::Idle); // IHP working mode
8960 :
8961 : // References to objects used in this function
8962 408468 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8963 :
8964 : // initialize local variables
8965 408468 : int AvailSchedule = HeatPump.availSched->getCurrentVal();
8966 408468 : int HPAirInletNode = HeatPump.HeatPumpAirInletNode;
8967 408468 : int HPAirOutletNode = HeatPump.HeatPumpAirOutletNode;
8968 408468 : int OutdoorAirNode = HeatPump.OutsideAirNode;
8969 408468 : int ExhaustAirNode = HeatPump.ExhaustAirNode;
8970 408468 : int HPWaterInletNode = HeatPump.CondWaterInletNode;
8971 408468 : int HPWaterOutletNode = HeatPump.CondWaterOutletNode;
8972 408468 : int InletAirMixerNode = HeatPump.InletAirMixerNode;
8973 408468 : int OutletAirSplitterNode = HeatPump.OutletAirSplitterNode;
8974 408468 : int DXCoilAirInletNode = HeatPump.DXCoilAirInletNode;
8975 408468 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
8976 408468 : HVAC::CompressorOp compressorOp = HVAC::CompressorOp::Off; // DX compressor operation; 1=on, 0=off
8977 408468 : HeatPump.OnCycParaFuelRate = 0.0;
8978 408468 : HeatPump.OnCycParaFuelEnergy = 0.0;
8979 408468 : HeatPump.OffCycParaFuelRate = 0.0;
8980 408468 : HeatPump.OffCycParaFuelEnergy = 0.0;
8981 408468 : state.dataLoopNodes->Node(HPWaterOutletNode) = state.dataLoopNodes->Node(HPWaterInletNode);
8982 408468 : int MaxSpeedNum = HeatPump.NumofSpeed; // speed number of variable speed HPWH coil
8983 :
8984 : // assign set point temperature (cut-out) and dead band temp diff (cut-in = cut-out minus dead band temp diff)
8985 408468 : Real64 HPSetPointTemp = HeatPump.SetPointTemp;
8986 408468 : Real64 DeadBandTempDiff = HeatPump.DeadBandTempDiff;
8987 408468 : Real64 RhoWater = Psychrometrics::RhoH2O(HPSetPointTemp); // initialize
8988 :
8989 : // store first iteration tank temperature and HP mode of operation
8990 : // this code can be called more than once with FirstHVACIteration = .TRUE., use FirstTimeThroughFlag to control save
8991 408468 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && HeatPump.FirstTimeThroughFlag) {
8992 55851 : this->SavedTankTemp = this->TankTemp;
8993 55851 : HeatPump.SaveMode = HeatPump.Mode;
8994 55851 : HeatPump.SaveWHMode = this->Mode;
8995 55851 : HeatPump.FirstTimeThroughFlag = false;
8996 : }
8997 :
8998 408468 : if (!FirstHVACIteration) {
8999 201940 : HeatPump.FirstTimeThroughFlag = true;
9000 : }
9001 :
9002 : // check if HPWH is off for some reason and simulate HPWH air- and water-side mass flow rates of 0
9003 : // simulate only water heater tank if HP compressor is scheduled off
9004 : // simulate only water heater tank if HP compressor cut-out temperature is lower than the tank's cut-in temperature
9005 : // simulate only water heater tank if HP inlet air temperature is below minimum temperature for HP compressor operation
9006 : // if the tank maximum temperature limit is less than the HPWH set point temp, disable HPWH
9007 408468 : if (AvailSchedule == 0.0 || (HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp ||
9008 408468 : state.dataHVACGlobal->HPWHInletDBTemp < HeatPump.MinAirTempForHPOperation ||
9009 328948 : state.dataHVACGlobal->HPWHInletDBTemp > HeatPump.MaxAirTempForHPOperation || HPSetPointTemp >= this->TankTempLimit ||
9010 328948 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime && this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed &&
9011 816936 : this->SavedMode == TankOperatingMode::Heating) ||
9012 328948 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime &&
9013 103940 : this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified && (this->SavedHeaterOn1 || this->SavedHeaterOn2))) {
9014 : // revert to float mode any time HPWH compressor is OFF
9015 81640 : HeatPump.Mode = TankOperatingMode::Floating;
9016 81640 : if (InletAirMixerNode > 0) {
9017 0 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
9018 : }
9019 : // pass node info and simulate crankcase heater
9020 81640 : if (MaxSpeedNum > 0) {
9021 25630 : int VSCoilNum = HeatPump.DXCoilNum;
9022 :
9023 25630 : if (HeatPump.bIsIHP) {
9024 4976 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(VSCoilNum).SCWHCoilIndex;
9025 : }
9026 : // set the SCWH mode
9027 25630 : Real64 SpeedRatio = 1.0; // speed ratio for interpolating between two speed levels
9028 25630 : int SpeedNum = 1;
9029 25630 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9030 25630 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9031 :
9032 25630 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9033 25630 : if (HeatPump.bIsIHP) {
9034 4976 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9035 : "",
9036 : VSCoilNum,
9037 : HVAC::FanOp::Cycling,
9038 : HVAC::CompressorOp::On,
9039 4976 : state.dataWaterThermalTanks->hpPartLoadRatio,
9040 : SpeedNum,
9041 : SpeedRatio,
9042 : 0.0,
9043 : 0.0,
9044 : 1.0);
9045 : } else {
9046 20654 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9047 : HeatPump.DXCoilName,
9048 : VSCoilNum,
9049 : HVAC::FanOp::Cycling,
9050 : HVAC::CompressorOp::On,
9051 20654 : state.dataWaterThermalTanks->hpPartLoadRatio,
9052 : SpeedNum,
9053 : SpeedRatio,
9054 : 0.0,
9055 : 0.0,
9056 : 1.0);
9057 : }
9058 : } else {
9059 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9060 0 : if (HeatPump.bIsIHP) {
9061 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9062 : "",
9063 : VSCoilNum,
9064 : HVAC::FanOp::Cycling,
9065 : HVAC::CompressorOp::On,
9066 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9067 : SpeedNum,
9068 : SpeedRatio,
9069 : 0.0,
9070 : 0.0,
9071 : 1.0);
9072 : } else {
9073 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9074 : HeatPump.DXCoilName,
9075 : VSCoilNum,
9076 : HVAC::FanOp::Cycling,
9077 : HVAC::CompressorOp::On,
9078 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9079 : SpeedNum,
9080 : SpeedRatio,
9081 : 0.0,
9082 : 0.0,
9083 : 1.0);
9084 : }
9085 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9086 : }
9087 :
9088 : // set the DWH mode
9089 25630 : if (HeatPump.bIsIHP) {
9090 4976 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
9091 :
9092 4976 : if (VSCoilNum > 0) // if DWH coil exists
9093 : {
9094 4976 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9095 4976 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9096 :
9097 4976 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9098 4976 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9099 : "",
9100 : VSCoilNum,
9101 : HVAC::FanOp::Cycling,
9102 : HVAC::CompressorOp::On,
9103 4976 : state.dataWaterThermalTanks->hpPartLoadRatio,
9104 : SpeedNum,
9105 : SpeedRatio,
9106 : 0.0,
9107 : 0.0,
9108 : 1.0);
9109 : } else {
9110 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9111 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9112 : "",
9113 : VSCoilNum,
9114 : HVAC::FanOp::Cycling,
9115 : HVAC::CompressorOp::On,
9116 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9117 : SpeedNum,
9118 : SpeedRatio,
9119 : 0.0,
9120 : 0.0,
9121 : 1.0);
9122 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9123 : }
9124 : }
9125 : }
9126 :
9127 : } else {
9128 56010 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9129 53890 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9130 :
9131 107780 : DXCoils::SimDXCoil(state,
9132 : HeatPump.DXCoilName,
9133 : compressorOp,
9134 : FirstHVACIteration,
9135 53890 : HeatPump.DXCoilNum,
9136 : HVAC::FanOp::Cycling,
9137 53890 : state.dataWaterThermalTanks->hpPartLoadRatio);
9138 : } else {
9139 4240 : DXCoils::SimDXCoil(state,
9140 : HeatPump.DXCoilName,
9141 : compressorOp,
9142 : FirstHVACIteration,
9143 2120 : HeatPump.DXCoilNum,
9144 : HVAC::FanOp::Cycling,
9145 2120 : state.dataWaterThermalTanks->hpPartLoadRatio);
9146 2120 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9147 : }
9148 : }
9149 :
9150 81640 : if (OutletAirSplitterNode > 0) {
9151 0 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
9152 : }
9153 :
9154 : // Simulate tank if HP compressor unavailable for water heating
9155 81640 : this->CalcWaterThermalTank(state);
9156 :
9157 : // If HPWH compressor is available and unit is off for another reason, off-cycle parasitics are calculated
9158 81640 : if (AvailSchedule != 0) {
9159 81640 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
9160 81640 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
9161 : }
9162 :
9163 : // Warn if HPWH compressor cut-in temperature is less than the water heater tank's set point temp
9164 81640 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
9165 13926 : if ((HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
9166 0 : Real64 HPMinTemp = HPSetPointTemp - DeadBandTempDiff;
9167 0 : const std::string HPMinTempChar = fmt::to_string(HPMinTemp);
9168 0 : ++HeatPump.HPSetPointError;
9169 : // add logic for warmup, DataGlobals::KickOffSimulation and doing sizing here
9170 0 : if (HeatPump.HPSetPointError == 1) {
9171 0 : ShowWarningError(state,
9172 0 : format("{} \"{}: Water heater tank set point temperature is greater than or equal to the cut-in temperature of "
9173 : "the heat pump water heater. Heat Pump will be disabled and simulation continues.",
9174 0 : HeatPump.Type,
9175 0 : HeatPump.Name));
9176 0 : ShowContinueErrorTimeStamp(state, format(" ...Heat Pump cut-in temperature={}", HPMinTempChar));
9177 : } else {
9178 0 : ShowRecurringWarningErrorAtEnd(state,
9179 0 : HeatPump.Type + " \"" + HeatPump.Name +
9180 : ": Water heater tank set point temperature is greater than or equal to the cut-in "
9181 : "temperature of the heat pump water heater. Heat Pump will be disabled error continues...",
9182 0 : HeatPump.HPSetPointErrIndex1,
9183 : HPMinTemp,
9184 : HPMinTemp);
9185 : }
9186 0 : }
9187 : }
9188 81640 : return;
9189 : }
9190 326828 : Real64 savedTankTemp = this->SavedTankTemp;
9191 326828 : HeatPump.Mode = HeatPump.SaveMode;
9192 :
9193 326828 : RhoWater = Psychrometrics::RhoH2O(savedTankTemp); // update water density using tank temp
9194 :
9195 : // set the heat pump air- and water-side mass flow rate
9196 326828 : MdotWater = HeatPump.OperatingWaterFlowRate * Psychrometrics::RhoH2O(savedTankTemp);
9197 :
9198 : // Select mode of operation (float mode or heat mode) from last iteration.
9199 : // Determine if heating will occur this iteration and get an estimate of the PLR
9200 326828 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9201 : // HPWH was heating last iteration and will continue to heat until the set point is reached
9202 108536 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9203 108536 : if (savedTankTemp > HPSetPointTemp) { // tank set point temp may have been reduced since last iteration and float mode may be needed
9204 110 : HeatPump.Mode = TankOperatingMode::Floating;
9205 110 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9206 : // check to see if HP needs to operate
9207 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9208 110 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9209 110 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9210 110 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9211 0 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9212 0 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9213 0 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9214 : }
9215 110 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9216 110 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9217 :
9218 : // Check tank temperature by setting source inlet mass flow rate to zero.
9219 110 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9220 :
9221 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9222 110 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9223 :
9224 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9225 110 : this->MaxCapacity = 0.0;
9226 110 : this->MinCapacity = 0.0;
9227 110 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9228 110 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9229 110 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9230 110 : this->CalcWaterThermalTank(state);
9231 110 : this->SourceEffectiveness = SourceEffectivenessBackup;
9232 110 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9233 :
9234 : // Reset the tank's internal heating element capacity.
9235 110 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9236 110 : this->MinCapacity = HeatPump.BackupElementCapacity;
9237 :
9238 : // Check to see if the tank drifts below set point if no heating happens.
9239 110 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9240 :
9241 : // HPWH is now in heating mode
9242 72 : HeatPump.Mode = TankOperatingMode::Heating;
9243 :
9244 : // Reset the water heater's mode (call above may have changed modes)
9245 72 : this->Mode = HeatPump.SaveWHMode;
9246 :
9247 72 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9248 : }
9249 : } else { // or use side nodes may meet set point without need for heat pump compressor operation
9250 : // check to see if HP needs to operate
9251 108426 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9252 30832 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9253 30832 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9254 77594 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9255 77594 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9256 77594 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9257 : }
9258 : // Check tank temperature by setting source inlet mass flow rate to zero.
9259 108426 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9260 108426 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9261 :
9262 108426 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9263 :
9264 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9265 108426 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9266 :
9267 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9268 108426 : this->MaxCapacity = 0.0;
9269 108426 : this->MinCapacity = 0.0;
9270 108426 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9271 108426 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9272 108426 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9273 108426 : this->CalcWaterThermalTank(state);
9274 108426 : this->SourceEffectiveness = SourceEffectivenessBackup;
9275 108426 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9276 :
9277 : // Reset the tank's internal heating element capacity.
9278 108426 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9279 108426 : this->MinCapacity = HeatPump.BackupElementCapacity;
9280 :
9281 : // Check to see if the tank meets set point if no heating happens.
9282 108426 : if (NewTankTemp > HPSetPointTemp) {
9283 :
9284 : // HPWH is now in floating mode
9285 0 : HeatPump.Mode = TankOperatingMode::Floating;
9286 :
9287 : } else {
9288 :
9289 : // HPWH remains in heating mode
9290 108426 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9291 : }
9292 :
9293 : // Reset the water heater's mode (call above may have changed modes)
9294 108426 : this->Mode = HeatPump.SaveWHMode;
9295 : }
9296 : } else {
9297 218292 : assert(HeatPump.Mode == TankOperatingMode::Floating);
9298 : // HPWH was floating last iteration and will continue to float until the cut-in temperature is reached
9299 :
9300 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9301 218292 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9302 95996 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9303 95996 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9304 122296 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9305 122296 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9306 122296 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9307 : }
9308 218292 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9309 218292 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9310 :
9311 : // Check tank temperature by setting source inlet mass flow rate to zero.
9312 218292 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9313 :
9314 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9315 218292 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9316 :
9317 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9318 218292 : this->MaxCapacity = 0.0;
9319 218292 : this->MinCapacity = 0.0;
9320 218292 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9321 218292 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9322 218292 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9323 218292 : this->CalcWaterThermalTank(state);
9324 218292 : this->SourceEffectiveness = SourceEffectivenessBackup;
9325 218292 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9326 :
9327 : // Reset the tank's internal heating element capacity.
9328 218292 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9329 218292 : this->MinCapacity = HeatPump.BackupElementCapacity;
9330 :
9331 : // Check to see if the tank drifts below set point if no heating happens.
9332 218292 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9333 :
9334 : // HPWH is now in heating mode
9335 45808 : HeatPump.Mode = TankOperatingMode::Heating;
9336 :
9337 : // Reset the water heater's mode (call above may have changed modes)
9338 45808 : this->Mode = HeatPump.SaveWHMode;
9339 :
9340 45808 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9341 : }
9342 : }
9343 :
9344 326828 : if (HeatPump.bIsIHP) // mark the water heating call, if existing
9345 : {
9346 5070 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9347 2534 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9348 1258 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = true;
9349 : } else {
9350 1276 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = false;
9351 : }
9352 : }
9353 : }
9354 :
9355 : // If the HPWH was in heating mode during the last DataGlobals::TimeStep or if it was determined that
9356 : // heating would be needed during this DataGlobals::TimeStep to maintain setpoint, do the heating calculation.
9357 326828 : int SpeedNum = 0;
9358 326828 : Real64 SpeedRatio = 0.0;
9359 326828 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9360 :
9361 : // set up air flow on DX coil inlet node
9362 154306 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate =
9363 154306 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9364 :
9365 : // set the condenser inlet node mass flow rate prior to calling the DXCoils::CalcHPWHDXCoil
9366 154306 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9367 154306 : this->SourceMassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9368 :
9369 : // Do the coil and tank calculations at full PLR to see if it overshoots setpoint.
9370 154306 : bool bIterSpeed = false;
9371 154306 : if (MaxSpeedNum > 0) { // lowest speed of VS HPWH coil
9372 20581 : SpeedRatio = 1.0;
9373 20581 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9374 20581 : bIterSpeed = true; // prepare for iterating between speed levels
9375 20581 : SpeedNum = 1;
9376 20581 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9377 :
9378 20581 : if (HeatPump.bIsIHP) {
9379 2517 : bIterSpeed = false; // don't iterate speed unless match conditions below
9380 2517 : IHPMode = IntegratedHeatPump::GetCurWorkMode(state, HeatPump.DXCoilNum);
9381 :
9382 2517 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9383 : int VSCoilNum;
9384 1258 : if (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) {
9385 48 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
9386 48 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode =
9387 : IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg;
9388 : } else {
9389 1210 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).SCWHCoilIndex;
9390 1210 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IntegratedHeatPump::IHPOperationMode::SCWHMatchWH;
9391 : }
9392 :
9393 1258 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9394 :
9395 1258 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9396 : "",
9397 : VSCoilNum,
9398 : HVAC::FanOp::Cycling,
9399 : HVAC::CompressorOp::On,
9400 1258 : state.dataWaterThermalTanks->hpPartLoadRatio,
9401 : SpeedNum,
9402 : SpeedRatio,
9403 : 0.0,
9404 : 0.0,
9405 : 1.0);
9406 :
9407 1258 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IHPMode;
9408 1258 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9409 : } else {
9410 1259 : SpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9411 :
9412 1259 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9413 1259 : IntegratedHeatPump::SimIHP(state,
9414 : HeatPump.DXCoilName,
9415 1259 : HeatPump.DXCoilNum,
9416 : HVAC::FanOp::Cycling,
9417 : HVAC::CompressorOp::On,
9418 1259 : state.dataWaterThermalTanks->hpPartLoadRatio,
9419 : SpeedNum,
9420 : SpeedRatio,
9421 : 0.0,
9422 : 0.0,
9423 : true,
9424 : false,
9425 1259 : 1.0);
9426 :
9427 1259 : if ((IntegratedHeatPump::IHPOperationMode::SCWHMatchWH == IHPMode) ||
9428 : (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode)) {
9429 112 : bIterSpeed = true;
9430 : } else {
9431 1147 : this->SourceMassFlowRate = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TankSourceWaterMassFlowRate;
9432 1147 : MdotWater = this->SourceMassFlowRate;
9433 : }
9434 :
9435 1259 : if (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) // turn off heater element
9436 : {
9437 0 : this->MaxCapacity = 0.0;
9438 0 : this->MinCapacity = 0.0;
9439 : }
9440 : }
9441 : } else {
9442 18064 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9443 : HeatPump.DXCoilName,
9444 18064 : HeatPump.DXCoilNum,
9445 : HVAC::FanOp::Cycling,
9446 : HVAC::CompressorOp::On,
9447 18064 : state.dataWaterThermalTanks->hpPartLoadRatio,
9448 : SpeedNum,
9449 : SpeedRatio,
9450 : 0.0,
9451 : 0.0,
9452 : 1.0);
9453 : }
9454 :
9455 20581 : this->CalcWaterThermalTank(state);
9456 : } else {
9457 133725 : this->ConvergeSingleSpeedHPWHCoilAndTank(state, state.dataWaterThermalTanks->hpPartLoadRatio);
9458 : }
9459 :
9460 154306 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9461 154306 : Real64 LowSpeedTankTemp = NewTankTemp;
9462 154306 : Real64 HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9463 :
9464 154306 : if (NewTankTemp > HPSetPointTemp) {
9465 11261 : HeatPump.Mode = TankOperatingMode::Floating;
9466 11261 : TankOperatingMode tmpMode = HeatPump.SaveWHMode;
9467 55028 : auto f = [&state, this, HPSetPointTemp, tmpMode, MdotWater](Real64 const HPPartLoadRatio) {
9468 55028 : return this->PLRResidualHPWH(state, HPPartLoadRatio, HPSetPointTemp, tmpMode, MdotWater);
9469 11261 : };
9470 11261 : Real64 zeroResidual = 1.0;
9471 11261 : if (MaxSpeedNum > 0) {
9472 : // square the solving, and avoid warning
9473 : // due to very small capacity at lowest speed of VSHPWH coil
9474 931 : if (bIterSpeed) {
9475 915 : zeroResidual = this->PLRResidualHPWH(state, 0.0, HPSetPointTemp, tmpMode, MdotWater);
9476 : } else {
9477 16 : zeroResidual = -1.0;
9478 : }
9479 : }
9480 :
9481 11261 : if (zeroResidual > 0.0) { // then iteration
9482 : int SolFla;
9483 10888 : General::SolveRoot(state, Acc, MaxIte, SolFla, state.dataWaterThermalTanks->hpPartLoadRatio, f, 0.0, 1.0);
9484 10888 : if (SolFla == -1) {
9485 0 : std::string IterNum;
9486 0 : IterNum = fmt::to_string(MaxIte);
9487 0 : if (!state.dataGlobal->WarmupFlag) {
9488 0 : ++HeatPump.IterLimitExceededNum2;
9489 0 : if (HeatPump.IterLimitExceededNum2 == 1) {
9490 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9491 0 : ShowContinueError(state,
9492 0 : format("Iteration limit exceeded calculating heat pump water heater compressor part-load ratio, "
9493 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
9494 : IterNum,
9495 0 : state.dataWaterThermalTanks->hpPartLoadRatio));
9496 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9497 : } else {
9498 0 : ShowRecurringWarningErrorAtEnd(
9499 : state,
9500 0 : HeatPump.Type + " \"" + HeatPump.Name +
9501 : "\": Iteration limit exceeded in float mode warning continues. Part-load ratio statistics follow.",
9502 0 : HeatPump.IterLimitErrIndex2,
9503 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9504 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9505 : }
9506 : }
9507 10888 : } else if (SolFla == -2) {
9508 304 : state.dataWaterThermalTanks->hpPartLoadRatio =
9509 304 : max(0.0, min(1.0, (HPSetPointTemp - savedTankTemp) / (NewTankTemp - savedTankTemp)));
9510 304 : if (!state.dataGlobal->WarmupFlag) {
9511 152 : ++HeatPump.RegulaFalsiFailedNum2;
9512 152 : if (HeatPump.RegulaFalsiFailedNum2 == 1) {
9513 1 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9514 2 : ShowContinueError(state,
9515 2 : format("Heat pump water heater compressor part-load ratio calculation failed: PLR limits of 0 to 1 "
9516 : "exceeded. Part-load ratio used = {:.3R}",
9517 1 : state.dataWaterThermalTanks->hpPartLoadRatio));
9518 2 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9519 3 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9520 : } else {
9521 1208 : ShowRecurringWarningErrorAtEnd(
9522 : state,
9523 302 : HeatPump.Type + " \"" + HeatPump.Name +
9524 : "\": Part-load ratio calculation failed in float mode warning continues. Part-load ratio statistics follow.",
9525 151 : HeatPump.RegulaFalsiFailedIndex2,
9526 151 : state.dataWaterThermalTanks->hpPartLoadRatio,
9527 151 : state.dataWaterThermalTanks->hpPartLoadRatio);
9528 : }
9529 : }
9530 : }
9531 : } else {
9532 373 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9533 : }
9534 :
9535 : // Re-calculate the HPWH Coil to get the correct heat transfer rate.
9536 11261 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9537 11261 : if (MaxSpeedNum > 0) {
9538 931 : SpeedRatio = 1.0;
9539 931 : SpeedNum = 1;
9540 :
9541 931 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9542 :
9543 931 : if (HeatPump.bIsIHP) {
9544 32 : if (bIterSpeed) {
9545 16 : IntegratedHeatPump::SimIHP(state,
9546 : HeatPump.DXCoilName,
9547 16 : HeatPump.DXCoilNum,
9548 : HVAC::FanOp::Cycling,
9549 : HVAC::CompressorOp::On,
9550 16 : state.dataWaterThermalTanks->hpPartLoadRatio,
9551 : SpeedNum,
9552 : SpeedRatio,
9553 : 0.0,
9554 : 0.0,
9555 : true,
9556 : false,
9557 32 : 1.0);
9558 : }
9559 : } else {
9560 899 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9561 : HeatPump.DXCoilName,
9562 899 : HeatPump.DXCoilNum,
9563 : HVAC::FanOp::Cycling,
9564 : HVAC::CompressorOp::On,
9565 899 : state.dataWaterThermalTanks->hpPartLoadRatio,
9566 : SpeedNum,
9567 : SpeedRatio,
9568 : 0.0,
9569 : 0.0,
9570 : 1.0);
9571 : }
9572 :
9573 : } else {
9574 10330 : DXCoils::CalcHPWHDXCoil(state, HeatPump.DXCoilNum, state.dataWaterThermalTanks->hpPartLoadRatio);
9575 : }
9576 143045 : } else if (bIterSpeed) {
9577 43656 : for (int loopIter = 1; loopIter <= 4; ++loopIter) {
9578 43656 : HeatPump.Mode = TankOperatingMode::Heating; // modHeatMode is important for system convergence
9579 43656 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9580 43656 : SpeedRatio = 1.0;
9581 43656 : int LowSpeedNum = 2;
9582 43656 : if (HeatPump.bIsIHP) {
9583 272 : LowSpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9584 272 : MaxSpeedNum = IntegratedHeatPump::GetMaxSpeedNumIHP(state, HeatPump.DXCoilNum);
9585 : }
9586 :
9587 351730 : for (int i = LowSpeedNum; i <= MaxSpeedNum; ++i) {
9588 319936 : SpeedNum = i;
9589 319936 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9590 319936 : if (HeatPump.bIsIHP) {
9591 2624 : IntegratedHeatPump::SimIHP(state,
9592 : HeatPump.DXCoilName,
9593 2624 : HeatPump.DXCoilNum,
9594 : HVAC::FanOp::Cycling,
9595 : HVAC::CompressorOp::On,
9596 2624 : state.dataWaterThermalTanks->hpPartLoadRatio,
9597 : SpeedNum,
9598 : SpeedRatio,
9599 : 0.0,
9600 : 0.0,
9601 : true,
9602 : false,
9603 5248 : 1.0);
9604 : } else {
9605 317312 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9606 : HeatPump.DXCoilName,
9607 317312 : HeatPump.DXCoilNum,
9608 : HVAC::FanOp::Cycling,
9609 : HVAC::CompressorOp::On,
9610 317312 : state.dataWaterThermalTanks->hpPartLoadRatio,
9611 : SpeedNum,
9612 : SpeedRatio,
9613 : 0.0,
9614 : 0.0,
9615 : 1.0);
9616 : }
9617 :
9618 : // HPWH condenser water temperature difference
9619 319936 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9620 :
9621 : // move the full load outlet temperature rate to the water heater structure variables
9622 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has
9623 : // been called)
9624 319936 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9625 : // this CALL does not update node temps, must use WaterThermalTank variables
9626 : // select tank type
9627 319936 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9628 312974 : this->CalcWaterThermalTankMixed(state);
9629 312974 : NewTankTemp = this->TankTemp;
9630 6962 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9631 6962 : this->CalcWaterThermalTankStratified(state);
9632 6962 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9633 : }
9634 :
9635 319936 : if (NewTankTemp > HPSetPointTemp) {
9636 11862 : SpeedNum = i;
9637 11862 : break;
9638 : } else {
9639 308074 : LowSpeedTankTemp = NewTankTemp;
9640 : }
9641 : }
9642 :
9643 43656 : if (NewTankTemp > HPSetPointTemp) {
9644 : int SolFla;
9645 11862 : std::string IterNum;
9646 76295 : auto f = [&state, this, SpeedNum, HPWaterInletNode, HPWaterOutletNode, RhoWater, HPSetPointTemp, &HeatPump, FirstHVACIteration](
9647 : Real64 const SpeedRatio) {
9648 152590 : return this->PLRResidualIterSpeed(state,
9649 : SpeedRatio,
9650 76295 : this->HeatPumpNum,
9651 : SpeedNum,
9652 : HPWaterInletNode,
9653 : HPWaterOutletNode,
9654 : RhoWater,
9655 : HPSetPointTemp,
9656 76295 : HeatPump.SaveWHMode,
9657 76295 : FirstHVACIteration);
9658 11862 : };
9659 11862 : General::SolveRoot(state, Acc, MaxIte, SolFla, SpeedRatio, f, 1.0e-10, 1.0);
9660 :
9661 11862 : if (SolFla == -1) {
9662 0 : IterNum = fmt::to_string(MaxIte);
9663 0 : if (!state.dataGlobal->WarmupFlag) {
9664 0 : ++HeatPump.IterLimitExceededNum1;
9665 0 : if (HeatPump.IterLimitExceededNum1 == 1) {
9666 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9667 0 : ShowContinueError(state,
9668 0 : format("Iteration limit exceeded calculating heat pump water heater speed speed ratio ratio, "
9669 : "maximum iterations = {}. speed ratio returned = {:.3R}",
9670 : IterNum,
9671 : SpeedRatio));
9672 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9673 : } else {
9674 0 : ShowRecurringWarningErrorAtEnd(
9675 : state,
9676 0 : HeatPump.Type + " \"" + HeatPump.Name +
9677 : "\": Iteration limit exceeded in heating mode warning continues. speed ratio statistics follow.",
9678 0 : HeatPump.IterLimitErrIndex1,
9679 : SpeedRatio,
9680 : SpeedRatio);
9681 : }
9682 : }
9683 11862 : } else if (SolFla == -2) {
9684 0 : SpeedRatio = max(0.0, min(1.0, (HPSetPointTemp - LowSpeedTankTemp) / (NewTankTemp - LowSpeedTankTemp)));
9685 0 : if (!state.dataGlobal->WarmupFlag) {
9686 0 : ++HeatPump.RegulaFalsiFailedNum1;
9687 0 : if (HeatPump.RegulaFalsiFailedNum1 == 1) {
9688 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9689 0 : ShowContinueError(state,
9690 0 : format("Heat pump water heater speed ratio calculation failed: speed ratio limits of 0 to 1 "
9691 : "exceeded. speed ratio used = {:.3R}",
9692 : SpeedRatio));
9693 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9694 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9695 : } else {
9696 0 : ShowRecurringWarningErrorAtEnd(
9697 : state,
9698 0 : HeatPump.Type + " \"" + HeatPump.Name +
9699 : "\": Speed ratio calculation failed in heating mode warning continues. Speed ratio statistics follow.",
9700 0 : HeatPump.RegulaFalsiFailedIndex1,
9701 : SpeedRatio,
9702 : SpeedRatio);
9703 : }
9704 : }
9705 : }
9706 11862 : } else {
9707 31794 : SpeedNum = MaxSpeedNum;
9708 31794 : SpeedRatio = 1.0;
9709 : }
9710 :
9711 43656 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9712 43656 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9713 :
9714 43656 : if (HeatPump.bIsIHP) {
9715 272 : IntegratedHeatPump::SimIHP(state,
9716 : HeatPump.DXCoilName,
9717 272 : HeatPump.DXCoilNum,
9718 : HVAC::FanOp::Cycling,
9719 : HVAC::CompressorOp::On,
9720 272 : state.dataWaterThermalTanks->hpPartLoadRatio,
9721 : SpeedNum,
9722 : SpeedRatio,
9723 : 0.0,
9724 : 0.0,
9725 : true,
9726 : false,
9727 544 : 1.0);
9728 : } else {
9729 43384 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9730 : HeatPump.DXCoilName,
9731 43384 : HeatPump.DXCoilNum,
9732 : HVAC::FanOp::Cycling,
9733 : HVAC::CompressorOp::On,
9734 43384 : state.dataWaterThermalTanks->hpPartLoadRatio,
9735 : SpeedNum,
9736 : SpeedRatio,
9737 : 0.0,
9738 : 0.0,
9739 : 1.0);
9740 : }
9741 :
9742 : // HPWH condenser water temperature difference
9743 43656 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9744 :
9745 : // move the full load outlet temperature rate to the water heater structure variables
9746 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been
9747 : // called)
9748 43656 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9749 : // this CALL does not update node temps, must use WaterThermalTank variables
9750 : // select tank type
9751 43656 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9752 40175 : this->CalcWaterThermalTankMixed(state);
9753 40175 : NewTankTemp = this->TankTemp;
9754 3481 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9755 3481 : this->CalcWaterThermalTankStratified(state);
9756 3481 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9757 : }
9758 : // update inlet temp
9759 43656 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9760 43656 : if (std::abs(state.dataLoopNodes->Node(HPWaterInletNode).Temp - HPWHCondInletNodeLast) < HVAC::SmallTempDiff) {
9761 17261 : break;
9762 : }
9763 26395 : HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9764 : }
9765 :
9766 : } else {
9767 : // Set the PLR to 1 if we're not going to reach setpoint during this DataGlobals::TimeStep.
9768 125784 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9769 : }
9770 : }
9771 :
9772 326828 : if (HeatPump.bIsIHP) {
9773 5070 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9774 2534 : IntegratedHeatPump::ClearCoils(state, HeatPump.DXCoilNum); // clear node info when checking the heating load
9775 : }
9776 : }
9777 :
9778 : // set air-side mass flow rate for final calculation
9779 326828 : if (InletAirMixerNode > 0) {
9780 16038 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate =
9781 16038 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9782 32076 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio *
9783 16038 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule);
9784 16038 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9785 16038 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * state.dataWaterThermalTanks->mixerInletAirSchedule;
9786 : // IF HPWH is off, pass zone node conditions through HPWH air-side
9787 16038 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0) {
9788 11931 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
9789 : }
9790 : } else {
9791 310790 : if (OutdoorAirNode == 0) {
9792 183482 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate =
9793 183482 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9794 : } else {
9795 127308 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9796 127308 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9797 : }
9798 : }
9799 326828 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0) {
9800 172911 : this->SourceInletTemp = this->SourceOutletTemp;
9801 : }
9802 :
9803 : // set water-side mass flow rate for final calculation
9804 326828 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9805 :
9806 326828 : if (MaxSpeedNum > 0) {
9807 :
9808 : // it is important to use mdotAir to reset the notes, otherwise, could fail to converge
9809 39684 : if (InletAirMixerNode > 0) {
9810 4250 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9811 4250 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9812 : } else {
9813 35434 : if (OutdoorAirNode == 0) {
9814 8454 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9815 8454 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9816 : } else {
9817 26980 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9818 26980 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9819 : }
9820 : }
9821 :
9822 : // set the max mass flow rate for outdoor fans
9823 39684 : state.dataLoopNodes->Node(HeatPump.FanOutletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9824 :
9825 39684 : if (HeatPump.bIsIHP) {
9826 : // pass node information using resulting PLR
9827 5070 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9828 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9829 5070 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9830 :
9831 5070 : IntegratedHeatPump::SimIHP(state,
9832 : HeatPump.DXCoilName,
9833 5070 : HeatPump.DXCoilNum,
9834 : HVAC::FanOp::Cycling,
9835 : compressorOp,
9836 5070 : state.dataWaterThermalTanks->hpPartLoadRatio,
9837 : SpeedNum,
9838 : SpeedRatio,
9839 : 0.0,
9840 : 0.0,
9841 : true,
9842 : false,
9843 5070 : 1.0);
9844 5070 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9845 :
9846 5070 : IntegratedHeatPump::SimIHP(state,
9847 : HeatPump.DXCoilName,
9848 5070 : HeatPump.DXCoilNum,
9849 : HVAC::FanOp::Cycling,
9850 : compressorOp,
9851 5070 : state.dataWaterThermalTanks->hpPartLoadRatio,
9852 : SpeedNum,
9853 : SpeedRatio,
9854 : 0.0,
9855 : 0.0,
9856 : true,
9857 : false,
9858 10140 : 1.0);
9859 : } else {
9860 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9861 0 : IntegratedHeatPump::SimIHP(state,
9862 : HeatPump.DXCoilName,
9863 0 : HeatPump.DXCoilNum,
9864 : HVAC::FanOp::Cycling,
9865 : compressorOp,
9866 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9867 : SpeedNum,
9868 : SpeedRatio,
9869 : 0.0,
9870 : 0.0,
9871 : true,
9872 : false,
9873 0 : 1.0);
9874 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9875 :
9876 0 : IntegratedHeatPump::SimIHP(state,
9877 : HeatPump.DXCoilName,
9878 0 : HeatPump.DXCoilNum,
9879 : HVAC::FanOp::Cycling,
9880 : compressorOp,
9881 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9882 : SpeedNum,
9883 : SpeedRatio,
9884 : 0.0,
9885 : 0.0,
9886 : true,
9887 : false,
9888 0 : 1.0);
9889 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9890 : }
9891 : } else {
9892 : // pass node information using resulting PLR
9893 34614 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9894 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9895 26114 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9896 :
9897 26114 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9898 : HeatPump.DXCoilName,
9899 26114 : HeatPump.DXCoilNum,
9900 : HVAC::FanOp::Cycling,
9901 : compressorOp,
9902 26114 : state.dataWaterThermalTanks->hpPartLoadRatio,
9903 : SpeedNum,
9904 : SpeedRatio,
9905 : 0.0,
9906 : 0.0,
9907 : 1.0);
9908 :
9909 26114 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9910 :
9911 26114 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9912 : HeatPump.DXCoilName,
9913 26114 : HeatPump.DXCoilNum,
9914 : HVAC::FanOp::Cycling,
9915 : compressorOp,
9916 26114 : state.dataWaterThermalTanks->hpPartLoadRatio,
9917 : SpeedNum,
9918 : SpeedRatio,
9919 : 0.0,
9920 : 0.0,
9921 : 1.0);
9922 : } else {
9923 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9924 8500 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9925 : HeatPump.DXCoilName,
9926 8500 : HeatPump.DXCoilNum,
9927 : HVAC::FanOp::Cycling,
9928 : compressorOp,
9929 8500 : state.dataWaterThermalTanks->hpPartLoadRatio,
9930 : SpeedNum,
9931 : SpeedRatio,
9932 : 0.0,
9933 : 0.0,
9934 : 1.0);
9935 :
9936 8500 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9937 :
9938 8500 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9939 : HeatPump.DXCoilName,
9940 8500 : HeatPump.DXCoilNum,
9941 : HVAC::FanOp::Cycling,
9942 : compressorOp,
9943 8500 : state.dataWaterThermalTanks->hpPartLoadRatio,
9944 : SpeedNum,
9945 : SpeedRatio,
9946 : 0.0,
9947 : 0.0,
9948 : 1.0);
9949 :
9950 8500 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9951 : }
9952 : }
9953 : } else { // single speed
9954 :
9955 : // pass node information using resulting PLR
9956 287144 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9957 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9958 67992 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9959 :
9960 135984 : DXCoils::SimDXCoil(state,
9961 : HeatPump.DXCoilName,
9962 : compressorOp,
9963 : FirstHVACIteration,
9964 67992 : HeatPump.DXCoilNum,
9965 : HVAC::FanOp::Cycling,
9966 67992 : state.dataWaterThermalTanks->hpPartLoadRatio);
9967 :
9968 67992 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9969 :
9970 135984 : DXCoils::SimDXCoil(state,
9971 : HeatPump.DXCoilName,
9972 : compressorOp,
9973 : FirstHVACIteration,
9974 67992 : HeatPump.DXCoilNum,
9975 : HVAC::FanOp::Cycling,
9976 67992 : state.dataWaterThermalTanks->hpPartLoadRatio);
9977 : } else {
9978 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9979 438304 : DXCoils::SimDXCoil(state,
9980 : HeatPump.DXCoilName,
9981 : compressorOp,
9982 : FirstHVACIteration,
9983 219152 : HeatPump.DXCoilNum,
9984 : HVAC::FanOp::Cycling,
9985 219152 : state.dataWaterThermalTanks->hpPartLoadRatio);
9986 :
9987 219152 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9988 :
9989 438304 : DXCoils::SimDXCoil(state,
9990 : HeatPump.DXCoilName,
9991 : compressorOp,
9992 : FirstHVACIteration,
9993 219152 : HeatPump.DXCoilNum,
9994 : HVAC::FanOp::Cycling,
9995 219152 : state.dataWaterThermalTanks->hpPartLoadRatio);
9996 :
9997 219152 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9998 : }
9999 : }
10000 :
10001 : // Call the tank one more time with the final PLR
10002 326828 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10003 126938 : this->CalcWaterThermalTankMixed(state);
10004 199890 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10005 199890 : this->CalcWaterThermalTankStratified(state);
10006 : } else {
10007 0 : assert(0);
10008 : }
10009 :
10010 : // set HPWH outlet node equal to the outlet air splitter node conditions if outlet air splitter node exists
10011 326828 : if (OutletAirSplitterNode > 0) {
10012 16038 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
10013 16038 : state.dataLoopNodes->Node(ExhaustAirNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
10014 : }
10015 :
10016 : // Check schedule to divert air-side cooling to outdoors.
10017 326828 : if (HeatPump.outletAirSplitterSched != nullptr) {
10018 16038 : Real64 OutletAirSplitterSch = HeatPump.outletAirSplitterSched->getCurrentVal();
10019 16038 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate =
10020 16038 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * (1.0 - OutletAirSplitterSch);
10021 16038 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate =
10022 16038 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * OutletAirSplitterSch;
10023 : }
10024 :
10025 326828 : HeatPump.HeatingPLR = state.dataWaterThermalTanks->hpPartLoadRatio;
10026 326828 : HeatPump.OnCycParaFuelRate = HeatPump.OnCycParaLoad * state.dataWaterThermalTanks->hpPartLoadRatio;
10027 326828 : HeatPump.OnCycParaFuelEnergy = HeatPump.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
10028 326828 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
10029 326828 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
10030 326828 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10031 126938 : HeatPump.ControlTempAvg = this->TankTempAvg;
10032 126938 : HeatPump.ControlTempFinal = this->TankTemp;
10033 199890 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10034 199890 : HeatPump.ControlTempAvg = this->FindStratifiedTankSensedTemp(state, true);
10035 199890 : HeatPump.ControlTempFinal = this->FindStratifiedTankSensedTemp(state);
10036 : } else {
10037 0 : assert(0);
10038 : }
10039 :
10040 326828 : switch (HeatPump.InletAirConfiguration) {
10041 :
10042 : // no sensible capacity to zone for outdoor and scheduled HPWH
10043 143224 : case WTTAmbientTemp::OutsideAir:
10044 : case WTTAmbientTemp::Schedule: {
10045 143224 : HeatPump.HPWaterHeaterSensibleCapacity = 0.0;
10046 143224 : HeatPump.HPWaterHeaterLatentCapacity = 0.0;
10047 :
10048 : // calculate sensible capacity to zone for inlet air configuration equals Zone Only or Zone And Outdoor Air configurations
10049 143224 : break;
10050 : }
10051 183604 : default:
10052 :
10053 183604 : Real64 CpAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(HPAirInletNode).HumRat);
10054 :
10055 : // add parasitics to zone heat balance if parasitic heat load is to zone otherwise neglect parasitics
10056 183604 : if (HeatPump.ParasiticTempIndicator == WTTAmbientTemp::TempZone) {
10057 125832 : HeatPump.HPWaterHeaterSensibleCapacity =
10058 125832 : (state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
10059 125832 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp)) +
10060 125832 : HeatPump.OnCycParaFuelRate + HeatPump.OffCycParaFuelRate;
10061 : } else {
10062 57772 : HeatPump.HPWaterHeaterSensibleCapacity =
10063 57772 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
10064 57772 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp);
10065 : }
10066 :
10067 183604 : HeatPump.HPWaterHeaterLatentCapacity = state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate *
10068 183604 : (state.dataLoopNodes->Node(HPAirOutletNode).HumRat - state.dataLoopNodes->Node(HPAirInletNode).HumRat);
10069 183604 : break;
10070 : }
10071 : }
10072 :
10073 953359 : void WaterThermalTankData::CalcWaterThermalTank(EnergyPlusData &state)
10074 : {
10075 953359 : switch (this->WaterThermalTankType) {
10076 345378 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
10077 345378 : this->CalcWaterThermalTankMixed(state);
10078 345378 : break;
10079 607981 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
10080 607981 : this->CalcWaterThermalTankStratified(state);
10081 607981 : break;
10082 0 : default:
10083 0 : assert(false);
10084 : }
10085 953359 : }
10086 :
10087 537077 : Real64 WaterThermalTankData::GetHPWHSensedTankTemp(EnergyPlusData &state)
10088 : {
10089 537077 : switch (this->WaterThermalTankType) {
10090 197018 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
10091 197018 : return this->TankTemp;
10092 : break;
10093 340059 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
10094 340059 : return this->FindStratifiedTankSensedTemp(state);
10095 : break;
10096 0 : default:
10097 0 : assert(false);
10098 : return 0.0; // silence compiler
10099 : }
10100 : }
10101 :
10102 133725 : void WaterThermalTankData::ConvergeSingleSpeedHPWHCoilAndTank(EnergyPlusData &state, Real64 const partLoadRatio)
10103 : {
10104 133725 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
10105 133725 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
10106 :
10107 133725 : Real64 PrevTankTemp = this->SourceOutletTemp;
10108 457710 : for (int i = 1; i <= 10; ++i) {
10109 :
10110 457710 : DXCoils::CalcHPWHDXCoil(state, HPWH.DXCoilNum, partLoadRatio);
10111 457710 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWH.CondWaterOutletNode).Temp;
10112 :
10113 457710 : this->CalcWaterThermalTank(state);
10114 457710 : state.dataLoopNodes->Node(Coil.WaterInNode).Temp = this->SourceOutletTemp;
10115 :
10116 457710 : if (std::abs(this->SourceOutletTemp - PrevTankTemp) < HVAC::SmallTempDiff) {
10117 133725 : break;
10118 : }
10119 :
10120 323985 : PrevTankTemp = this->SourceOutletTemp;
10121 : }
10122 133725 : }
10123 :
10124 496548 : void WaterThermalTankData::SetVSHPWHFlowRates(EnergyPlusData &state,
10125 : HeatPumpWaterHeaterData &HPWH, // heat pump coil
10126 : int const SpeedNum, // upper speed number
10127 : Real64 const SpeedRatio, // interpolation ration between upper and lower speed
10128 : Real64 const WaterDens, // tank water density
10129 : Real64 &MdotWater, // water flow rate
10130 : bool const FirstHVACIteration)
10131 : {
10132 : // FUNCTION INFORMATION:
10133 : // AUTHOR B.Shen, ORNL, 12/2014
10134 : // DATE WRITTEN May 2005
10135 : // MODIFIED
10136 : // RE-ENGINEERED
10137 :
10138 : // PURPOSE OF THIS FUNCTION:
10139 : // set water and air flow rates driven by the variable-speed HPWH coil
10140 : // calculate resultant HPWH coil output
10141 :
10142 496548 : int SpeedLow = SpeedNum - 1;
10143 496548 : if (SpeedLow < 1) {
10144 56165 : SpeedLow = 1;
10145 : }
10146 :
10147 496548 : int HPWaterInletNode = HPWH.CondWaterInletNode;
10148 496548 : int DXCoilAirInletNode = HPWH.DXCoilAirInletNode;
10149 496548 : if (HPWH.bIsIHP) {
10150 19412 : HPWH.OperatingWaterFlowRate = IntegratedHeatPump::GetWaterVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio);
10151 19412 : state.dataWaterThermalTanks->mdotAir = IntegratedHeatPump::GetAirMassFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10152 19412 : HPWH.OperatingAirFlowRate = IntegratedHeatPump::GetAirVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10153 19412 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10154 19412 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10155 19412 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10156 : } else {
10157 477136 : HPWH.OperatingWaterFlowRate = HPWH.HPWHWaterVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHWaterVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10158 477136 : HPWH.OperatingAirFlowRate = HPWH.HPWHAirVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10159 477136 : state.dataWaterThermalTanks->mdotAir =
10160 477136 : HPWH.HPWHAirMassFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirMassFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10161 : }
10162 :
10163 496548 : MdotWater = HPWH.OperatingWaterFlowRate * WaterDens;
10164 496548 : this->SourceMassFlowRate = MdotWater;
10165 :
10166 496548 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10167 496548 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater;
10168 496548 : this->SourceMassFlowRate = MdotWater;
10169 :
10170 496548 : if (HPWH.InletAirMixerNode > 0) {
10171 18681 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10172 18681 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10173 : } else {
10174 477867 : if (HPWH.OutsideAirNode == 0) {
10175 26652 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10176 26652 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10177 : } else {
10178 451215 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10179 451215 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10180 : }
10181 : }
10182 :
10183 : // put fan component first, regardless placement, to calculate fan power
10184 496548 : int FanInNode = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
10185 :
10186 496548 : state.dataLoopNodes->Node(FanInNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10187 496548 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10188 496548 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10189 496548 : if (HPWH.fanType != HVAC::FanType::SystemModel) {
10190 84846 : state.dataFans->fans(HPWH.FanNum)->massFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10191 : } // system fan will use the inlet node max avail.
10192 :
10193 496548 : state.dataFans->fans(HPWH.FanNum)->simulate(state, FirstHVACIteration, _, _);
10194 496548 : }
10195 :
10196 76295 : Real64 WaterThermalTankData::PLRResidualIterSpeed(EnergyPlusData &state,
10197 : Real64 const SpeedRatio, // speed ratio between two speed levels
10198 : int const HPNum,
10199 : int const SpeedNum,
10200 : int const HPWaterInletNode,
10201 : int const HPWaterOutletNode,
10202 : Real64 const RhoWater,
10203 : Real64 const desTankTemp,
10204 : TankOperatingMode const mode,
10205 : bool const FirstHVACIteration)
10206 : {
10207 : // FUNCTION INFORMATION:
10208 : // AUTHOR B.Shen, ORNL, 12/2014
10209 : // MODIFIED
10210 : // RE-ENGINEERED
10211 :
10212 : // PURPOSE OF THIS FUNCTION:
10213 : // Calculates residual function (desired tank temp - actual tank temp), when iterating speed ration between two speed levels
10214 : // HP water heater output depends on the speed ratio which is being varied to zero the residual.
10215 :
10216 : // METHODOLOGY EMPLOYED:
10217 : // Calls residuals to get tank temperature at the given speed ratio between a lower and an upper speed levels
10218 : // and calculates the residual as defined respectively for DataPlant::PlantEquipmentType::WtrHeaterMixed or
10219 : // DataPlant::PlantEquipmentType::WtrHeaterStratified
10220 :
10221 76295 : this->Mode = mode;
10222 76295 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
10223 76295 : Real64 MdotWater = 0.0;
10224 :
10225 76295 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
10226 :
10227 76295 : this->SetVSHPWHFlowRates(state, HPWH, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
10228 :
10229 76295 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
10230 96 : IntegratedHeatPump::SimIHP(state,
10231 96 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10232 96 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10233 : HVAC::FanOp::Cycling,
10234 : HVAC::CompressorOp::On,
10235 96 : state.dataWaterThermalTanks->hpPartLoadRatio,
10236 : SpeedNum,
10237 : SpeedRatio,
10238 : 0.0,
10239 : 0.0,
10240 : true,
10241 : false,
10242 192 : 1.0);
10243 : } else {
10244 76199 : VariableSpeedCoils::SimVariableSpeedCoils(state,
10245 76199 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10246 76199 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10247 : HVAC::FanOp::Cycling,
10248 : HVAC::CompressorOp::On,
10249 76199 : state.dataWaterThermalTanks->hpPartLoadRatio,
10250 : SpeedNum,
10251 : SpeedRatio,
10252 : 0.0,
10253 : 0.0,
10254 : 1.0);
10255 : }
10256 :
10257 : Real64 CondenserDeltaT;
10258 76295 : CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
10259 :
10260 : // move the full load outlet temperature rate to the water heater structure variables
10261 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been called)
10262 76295 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
10263 :
10264 : // this CALL does not update node temps, must use WaterThermalTank variables
10265 : // select tank type
10266 76295 : Real64 NewTankTemp = 0.0;
10267 76295 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10268 25143 : this->CalcWaterThermalTankMixed(state);
10269 25143 : NewTankTemp = this->TankTemp;
10270 51152 : } else if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10271 51152 : this->CalcWaterThermalTankStratified(state);
10272 51152 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
10273 : }
10274 :
10275 76295 : return desTankTemp - NewTankTemp;
10276 : }
10277 :
10278 55943 : Real64 WaterThermalTankData::PLRResidualHPWH(
10279 : EnergyPlusData &state, Real64 const HPPartLoadRatio, Real64 const desTankTemp, TankOperatingMode const mode, Real64 const mDotWater)
10280 : {
10281 : // FUNCTION INFORMATION:
10282 : // AUTHOR B.Griffith, Richard Raustad
10283 : // DATE WRITTEN Jan 2012
10284 : // MODIFIED
10285 : // RE-ENGINEERED Noel Merket, Oct 2015
10286 :
10287 : // PURPOSE OF THIS FUNCTION:
10288 : // Calculates residual function (desired tank temp - actual tank temp)
10289 : // HP water heater output depends on the part load ratio which is being varied to zero the residual.
10290 :
10291 : // METHODOLOGY EMPLOYED:
10292 : // Calls with CalcWaterThermalTankMixed or CalcWaterThermalTankStratified to get tank temperature at the given part load ratio (source water
10293 : // mass flow rate) and calculates the residual as defined above
10294 :
10295 55943 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
10296 55943 : bool const isVariableSpeed = (HeatPump.NumofSpeed > 0);
10297 55943 : this->Mode = mode;
10298 : // Apply the PLR
10299 55943 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10300 : // For a mixed tank, the PLR is applied to the source mass flow rate.
10301 21133 : this->SourceMassFlowRate = mDotWater * HPPartLoadRatio;
10302 21133 : this->CalcWaterThermalTankMixed(state);
10303 : } else {
10304 34810 : assert(this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified);
10305 : // For a stratified tank, the PLR is applied to the Coil.TotalHeatingEnergyRate
10306 : // whether that's a VarSpeedCoil or DXCoils::DXCoil.
10307 : // Here we create a pointer to the TotalHeatingEnergyRate for the appropriate coil type.
10308 : Real64 *CoilTotalHeatingEnergyRatePtr;
10309 34810 : if (isVariableSpeed) {
10310 621 : if (HeatPump.bIsIHP) {
10311 0 : CoilTotalHeatingEnergyRatePtr = &state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TotalWaterHeatingRate;
10312 : } else {
10313 621 : CoilTotalHeatingEnergyRatePtr = &state.dataVariableSpeedCoils->VarSpeedCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10314 : }
10315 : } else {
10316 34189 : CoilTotalHeatingEnergyRatePtr = &state.dataDXCoils->DXCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10317 : }
10318 : // Copy the value of the total heating energy rate
10319 34810 : Real64 const CoilTotalHeatingEnergyRateBackup = *CoilTotalHeatingEnergyRatePtr;
10320 : // Apply the PLR
10321 34810 : *CoilTotalHeatingEnergyRatePtr *= HPPartLoadRatio;
10322 : // Tank Calculation
10323 34810 : this->CalcWaterThermalTankStratified(state);
10324 : // Restore the original value
10325 34810 : *CoilTotalHeatingEnergyRatePtr = CoilTotalHeatingEnergyRateBackup;
10326 : }
10327 55943 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
10328 55943 : return desTankTemp - NewTankTemp;
10329 : }
10330 :
10331 1362856 : bool WaterThermalTankData::SourceHeatNeed([[maybe_unused]] EnergyPlusData &state,
10332 : Real64 const OutletTemp,
10333 : Real64 const DeadBandTemp,
10334 : Real64 const SetPointTemp_loc)
10335 : {
10336 : // FUNCTION INFORMATION:
10337 : // AUTHOR Yueyue Zhou
10338 : // DATE WRITTEN May 2019
10339 : // MODIFIED na
10340 : // RE-ENGINEERED na
10341 :
10342 : // PURPOSE OF THIS FUNCTION:
10343 : // Determine by tank type, tank temperature and control mode if source side flow is needed
10344 :
10345 : // return value initialization
10346 1362856 : bool NeedsHeatOrCool = false;
10347 :
10348 1362856 : if (!this->IsChilledWaterTank) {
10349 952840 : if (this->SourceSideControlMode == SourceSideControl::IndirectHeatPrimarySetpoint) {
10350 952840 : if (OutletTemp < DeadBandTemp) {
10351 143302 : NeedsHeatOrCool = true;
10352 809538 : } else if ((OutletTemp >= DeadBandTemp) && (OutletTemp < SetPointTemp_loc)) {
10353 : // inside the deadband, use saved mode from water heater calcs
10354 378433 : if (this->SavedMode == TankOperatingMode::Heating) {
10355 88317 : NeedsHeatOrCool = true;
10356 290116 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10357 290116 : NeedsHeatOrCool = false;
10358 : }
10359 :
10360 431105 : } else if (OutletTemp >= SetPointTemp_loc) {
10361 431105 : NeedsHeatOrCool = false;
10362 : }
10363 0 : } else if (this->SourceSideControlMode == SourceSideControl::IndirectHeatAltSetpoint) {
10364 : // get alternate setpoint
10365 0 : Real64 const AltSetpointTemp = this->sourceSideAltSetpointSched->getCurrentVal();
10366 0 : Real64 const AltDeadBandTemp = AltSetpointTemp - this->DeadBandDeltaTemp;
10367 0 : if (OutletTemp < AltDeadBandTemp) {
10368 0 : NeedsHeatOrCool = true;
10369 0 : } else if ((OutletTemp >= AltDeadBandTemp) && (OutletTemp < AltSetpointTemp)) {
10370 : // inside the deadband, use saved mode from water heater calcs
10371 0 : if (this->SavedMode == TankOperatingMode::Heating) {
10372 0 : NeedsHeatOrCool = true;
10373 0 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10374 0 : NeedsHeatOrCool = false;
10375 : }
10376 :
10377 0 : } else if (OutletTemp >= AltSetpointTemp) {
10378 0 : NeedsHeatOrCool = false;
10379 : }
10380 0 : } else if (this->SourceSideControlMode == SourceSideControl::StorageTank) {
10381 0 : if (OutletTemp < this->TankTempLimit) {
10382 0 : NeedsHeatOrCool = true;
10383 : } else {
10384 0 : NeedsHeatOrCool = false;
10385 : }
10386 : }
10387 : } else { // is a chilled water tank so flip logic
10388 410016 : if (OutletTemp > DeadBandTemp) {
10389 10312 : NeedsHeatOrCool = true;
10390 399704 : } else if ((OutletTemp <= DeadBandTemp) && (OutletTemp > SetPointTemp_loc)) {
10391 : // inside the deadband, use saved mode from water thermal tank calcs (modes only for mixed)
10392 290876 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) {
10393 263303 : if (this->SavedMode == TankOperatingMode::Cooling) {
10394 49160 : NeedsHeatOrCool = true;
10395 214143 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10396 214143 : NeedsHeatOrCool = false;
10397 : }
10398 27573 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
10399 27573 : NeedsHeatOrCool = true;
10400 : }
10401 :
10402 108828 : } else if (OutletTemp <= SetPointTemp_loc) {
10403 108828 : NeedsHeatOrCool = false;
10404 : }
10405 : }
10406 1362856 : return NeedsHeatOrCool;
10407 : }
10408 :
10409 7735682 : Real64 WaterThermalTankData::PlantMassFlowRatesFunc(EnergyPlusData &state,
10410 : int const InNodeNum,
10411 : bool const FirstHVACIteration,
10412 : WaterHeaterSide const WaterThermalTankSide,
10413 : const DataPlant::LoopSideLocation PlantLoopSide,
10414 : [[maybe_unused]] bool const PlumbedInSeries,
10415 : DataBranchAirLoopPlant::ControlType const BranchControlType,
10416 : Real64 const OutletTemp,
10417 : Real64 const DeadBandTemp,
10418 : Real64 const SetPointTemp_loc)
10419 : {
10420 :
10421 : // FUNCTION INFORMATION:
10422 : // AUTHOR Brent Griffith
10423 : // DATE WRITTEN October 2007
10424 : // MODIFIED na
10425 : // RE-ENGINEERED na
10426 :
10427 : // PURPOSE OF THIS FUNCTION:
10428 : // collect routines for setting flow rates for Water heaters
10429 : // with plant connections.
10430 :
10431 : // determine current mode. there are three possible
10432 : // 1. passing thru what was given to inlet node
10433 : // 2. potentially making a flow request
10434 : // 3. throttling flow in response to Plant's restrictions (MassFlowRateMaxAvail)
10435 : // init default mode changed to Unassigned
10436 7735682 : FlowMode CurrentMode = FlowMode::Invalid; // default
10437 :
10438 7735682 : if (PlantLoopSide == DataPlant::LoopSideLocation::Invalid) {
10439 438002 : CurrentMode = FlowMode::PassingFlowThru;
10440 7297680 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Supply) {
10441 : // If FlowLock is False (0), the tank sets the plant loop mdot
10442 : // If FlowLock is True (1), the new resolved plant loop mdot is used
10443 6746468 : if (this->UseCurrentFlowLock == DataPlant::FlowLock::Unlocked) {
10444 2819155 : CurrentMode = FlowMode::PassingFlowThru;
10445 2819155 : if ((this->UseSideLoadRequested > 0.0) && (WaterThermalTankSide == WaterHeaterSide::Use)) {
10446 2143609 : CurrentMode = FlowMode::MaybeRequestingFlow;
10447 : }
10448 : } else {
10449 3927313 : CurrentMode = FlowMode::PassingFlowThru;
10450 : }
10451 6746468 : if (WaterThermalTankSide == WaterHeaterSide::Source) {
10452 811644 : CurrentMode = FlowMode::MaybeRequestingFlow;
10453 : }
10454 551212 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Demand) {
10455 :
10456 : // 2. Might be Requesting Flow.
10457 551212 : if (FirstHVACIteration) {
10458 275364 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10459 0 : CurrentMode = FlowMode::PassingFlowThru;
10460 : } else {
10461 275364 : CurrentMode = FlowMode::MaybeRequestingFlow;
10462 : }
10463 : } else {
10464 275848 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10465 0 : CurrentMode = FlowMode::PassingFlowThru;
10466 : } else {
10467 275848 : CurrentMode = FlowMode::ThrottlingFlow;
10468 : }
10469 : }
10470 : }
10471 :
10472 : // evaluate Availability schedule,
10473 7735682 : bool ScheduledAvail = true;
10474 7735682 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10475 5934824 : if (this->useSideAvailSched->getCurrentVal() == 0.0) {
10476 0 : ScheduledAvail = false;
10477 : }
10478 1800858 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10479 1800858 : if (this->sourceSideAvailSched->getCurrentVal() == 0.0) {
10480 59744 : ScheduledAvail = false;
10481 : }
10482 : }
10483 :
10484 : // now act based on current mode
10485 7735682 : Real64 FlowResult = 0.0;
10486 7735682 : switch (CurrentMode) {
10487 :
10488 4229217 : case FlowMode::PassingFlowThru: {
10489 4229217 : if (!ScheduledAvail) {
10490 0 : FlowResult = 0.0;
10491 : } else {
10492 4229217 : FlowResult = state.dataLoopNodes->Node(InNodeNum).MassFlowRate;
10493 : }
10494 :
10495 4229217 : break;
10496 : }
10497 275848 : case FlowMode::ThrottlingFlow: {
10498 : // first determine what mass flow would be if it is to requested
10499 275848 : Real64 MassFlowRequest = 0.0;
10500 275848 : if (!ScheduledAvail) {
10501 29872 : MassFlowRequest = 0.0;
10502 : } else {
10503 245976 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10504 0 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10505 245976 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10506 245976 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10507 : } else {
10508 0 : assert(false);
10509 : }
10510 : }
10511 :
10512 : // next determine if tank temperature is such that source side flow might be requested
10513 275848 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10514 :
10515 275848 : if (MassFlowRequest > 0.0) {
10516 245976 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10517 0 : FlowResult = MassFlowRequest;
10518 245976 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10519 245976 : if (NeedsHeatOrCool) {
10520 61996 : FlowResult = MassFlowRequest;
10521 : } else {
10522 183980 : FlowResult = 0.0;
10523 : }
10524 : } else {
10525 0 : assert(false);
10526 : }
10527 : } else {
10528 29872 : FlowResult = 0.0;
10529 : }
10530 :
10531 : // now throttle against MassFlowRateMaxAvail, MassFlowRateMinAvail, MassFlowRateMax, and MassFlowRateMin
10532 : // see notes about reverse dd compliance (specifically 5ZoneWaterSystems file)
10533 275848 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMinAvail, FlowResult); // okay for compliance (reverse dd)
10534 275848 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMin, FlowResult); // okay for compliance (reverse dd)
10535 275848 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMaxAvail, FlowResult);
10536 : //=> following might take out of reverse dd compliance
10537 275848 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMax, FlowResult);
10538 :
10539 275848 : break;
10540 : }
10541 3230617 : case FlowMode::MaybeRequestingFlow: {
10542 :
10543 : // first determine what mass flow would be if it is to requested
10544 3230617 : Real64 MassFlowRequest = 0.0;
10545 3230617 : if (!ScheduledAvail) {
10546 29872 : MassFlowRequest = 0.0;
10547 : } else {
10548 3200745 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10549 2143609 : if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested > 0.0)) {
10550 27591 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10551 2116018 : } else if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested == 0.0)) {
10552 0 : MassFlowRequest = 0.0;
10553 : } else {
10554 2116018 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10555 : }
10556 :
10557 1057136 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10558 1057136 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10559 : }
10560 : }
10561 :
10562 3230617 : if (WaterThermalTankSide == WaterHeaterSide::Source) { // temperature dependent controls for indirect heating/cooling
10563 1087008 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10564 1087008 : if (MassFlowRequest > 0.0) {
10565 1057115 : if (NeedsHeatOrCool) {
10566 237847 : FlowResult = MassFlowRequest;
10567 : } else {
10568 819268 : FlowResult = 0.0;
10569 : }
10570 : } else {
10571 29893 : FlowResult = 0.0;
10572 : }
10573 : } else { // end source side, begin use side
10574 2143609 : if (MassFlowRequest > 0.0) {
10575 2143609 : FlowResult = MassFlowRequest;
10576 : } else {
10577 0 : FlowResult = 0.0;
10578 : }
10579 : }
10580 3230617 : break;
10581 : }
10582 0 : default:
10583 0 : break;
10584 : }
10585 :
10586 7735682 : if (FlowResult < HVAC::VerySmallMassFlow) {
10587 2410436 : FlowResult = 0.0; // Catch underflow problems
10588 : }
10589 :
10590 7735682 : return FlowResult;
10591 : }
10592 :
10593 992 : void WaterThermalTankData::MinePlantStructForInfo(EnergyPlusData &state)
10594 : {
10595 :
10596 : // SUBROUTINE INFORMATION:
10597 : // AUTHOR Brent Griffith
10598 : // DATE WRITTEN October 2007
10599 : // MODIFIED na
10600 : // RE-ENGINEERED na
10601 :
10602 : // PURPOSE OF THIS SUBROUTINE:
10603 : // get information from plant loop data structure
10604 : // check what we can learn from plant structure against user inputs
10605 :
10606 992 : bool ErrorsFound = false;
10607 :
10608 992 : if (allocated(state.dataPlnt->PlantLoop) && this->UseSidePlantLoc.loopNum > 0) {
10609 :
10610 : // check plant structure for useful data.
10611 :
10612 919 : int PlantLoopNum = this->UseSidePlantLoc.loopNum;
10613 919 : DataPlant::LoopSideLocation LoopSideNum = this->UseSidePlantLoc.loopSideNum;
10614 :
10615 919 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
10616 0 : ShowSevereError(state,
10617 0 : format("Water heater = {} for autosizing Use side flow rate, did not find Sizing:Plant object {}",
10618 0 : this->Name,
10619 0 : state.dataPlnt->PlantLoop(PlantLoopNum).Name));
10620 0 : ErrorsFound = true;
10621 : }
10622 : // Is this wh Use side plumbed in series (default) or are there other branches in parallel?
10623 919 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.Exists) {
10624 919 : if (any_eq(state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.NodeNumOut,
10625 919 : this->UseInletNode)) { // this wh is on the splitter
10626 884 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.TotalOutletNodes > 1) {
10627 808 : this->UseSideSeries = false;
10628 : }
10629 : }
10630 : }
10631 : }
10632 :
10633 992 : if (allocated(state.dataPlnt->PlantLoop) && this->SrcSidePlantLoc.loopNum > 0) {
10634 : // was user's input correct for plant loop name?
10635 231 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0) && (this->DesuperheaterNum == 0) &&
10636 0 : (this->HeatPumpNum == 0)) {
10637 0 : ShowSevereError(state,
10638 0 : format("Water heater = {}for autosizing Source side flow rate, did not find Sizing:Plant object {}",
10639 0 : this->Name,
10640 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).Name));
10641 0 : ErrorsFound = true;
10642 : }
10643 : // Is this wh Source side plumbed in series (default) or are there other branches in parallel?
10644 231 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.Exists) {
10645 231 : if (any_eq(state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.NodeNumOut,
10646 231 : this->SourceInletNode)) { // this wh is on the splitter
10647 231 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.TotalOutletNodes >
10648 : 1) {
10649 77 : this->SourceSideSeries = false;
10650 : }
10651 : }
10652 : }
10653 : }
10654 :
10655 992 : if (ErrorsFound) {
10656 0 : ShowFatalError(state, "Preceding water heater input errors cause program termination");
10657 : }
10658 992 : }
10659 :
10660 786 : void WaterThermalTankData::SizeSupplySidePlantConnections(EnergyPlusData &state, const int loopNum)
10661 : {
10662 :
10663 : // SUBROUTINE INFORMATION:
10664 : // AUTHOR Brent Griffith
10665 : // DATE WRITTEN October 2007
10666 : // MODIFIED na
10667 : // RE-ENGINEERED na
10668 :
10669 : // PURPOSE OF THIS SUBROUTINE:
10670 : // This subroutine is for sizing water heater plant connection flow rates
10671 : // on the supply that have not been specified in the input.
10672 :
10673 : // METHODOLOGY EMPLOYED:
10674 : // This routine is called later in the simulation than the sizing routine for the demand side
10675 : // because the simulation needs to be further along before the needed data are available.
10676 : // For water heaters sides on Supply LoopSide, obtains hot water flow rate from the plant sizing array
10677 : // (adapted approach from boiler sizing routines)
10678 :
10679 : static constexpr std::string_view RoutineName("SizeSupplySidePlantConnections");
10680 :
10681 786 : auto &PlantSizData = state.dataSize->PlantSizData;
10682 :
10683 786 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
10684 786 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
10685 :
10686 786 : if ((this->UseInletNode > 0) && (loopNum == this->UseSidePlantLoc.loopNum)) {
10687 681 : if (this->UseDesignVolFlowRateWasAutoSized) {
10688 651 : int PltSizNum = this->UseSidePlantSizNum;
10689 651 : if (PltSizNum > 0) { // we have a Plant Sizing Object
10690 651 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10691 651 : if (PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
10692 524 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10693 143 : this->UseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10694 : } else {
10695 381 : tmpUseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10696 : }
10697 : } else {
10698 127 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10699 0 : this->UseDesignVolFlowRate = 0.0;
10700 : } else {
10701 127 : tmpUseDesignVolFlowRate = 0.0;
10702 : }
10703 : }
10704 651 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10705 127 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10706 : }
10707 651 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10708 8 : BaseSizer::reportSizerOutput(
10709 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10710 : }
10711 651 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10712 143 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10713 : } else {
10714 508 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
10715 : }
10716 :
10717 : Real64 rho =
10718 651 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10719 651 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10720 143 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10721 : } else {
10722 508 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
10723 : }
10724 : }
10725 : } else {
10726 : // do nothing
10727 : } // plant sizing object
10728 : } else {
10729 30 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10730 : Real64 rho;
10731 30 : if (this->UseSidePlantLoc.loopNum > 0) {
10732 30 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10733 : } else {
10734 0 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
10735 : }
10736 :
10737 30 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10738 :
10739 : } // autosizing needed.
10740 : } // connected to plant
10741 :
10742 786 : if ((this->SourceInletNode > 0) && (loopNum == this->SrcSidePlantLoc.loopNum)) {
10743 105 : if (this->SourceDesignVolFlowRateWasAutoSized) {
10744 55 : int PltSizNum = this->SourceSidePlantSizNum;
10745 55 : if (PltSizNum > 0) {
10746 55 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10747 25 : if (PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
10748 18 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10749 5 : this->SourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10750 : } else {
10751 13 : tmpSourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10752 : }
10753 : } else {
10754 7 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10755 0 : this->SourceDesignVolFlowRate = 0.0;
10756 : } else {
10757 7 : tmpSourceDesignVolFlowRate = 0.0;
10758 : }
10759 : }
10760 25 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10761 5 : BaseSizer::reportSizerOutput(
10762 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10763 : }
10764 25 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10765 0 : BaseSizer::reportSizerOutput(
10766 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10767 : }
10768 25 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10769 5 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10770 : } else {
10771 20 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
10772 : }
10773 : Real64 rho =
10774 25 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10775 25 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10776 5 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10777 : } else {
10778 20 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
10779 : }
10780 : } // plant loop allocation
10781 : } else {
10782 : // do nothing
10783 : } // plant sizing object
10784 : } else {
10785 50 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10786 45 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10787 : Real64 rho;
10788 45 : if (this->SrcSidePlantLoc.loopNum > 0) {
10789 45 : rho = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10790 : } else {
10791 0 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
10792 : }
10793 45 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10794 : }
10795 : } // autosizing needed.
10796 : } // connected to plant
10797 786 : }
10798 :
10799 786 : void WaterThermalTankData::SizeTankForDemandSide(EnergyPlusData &state)
10800 : {
10801 :
10802 : // SUBROUTINE INFORMATION:
10803 : // AUTHOR Brent Griffith
10804 : // DATE WRITTEN February 2008
10805 : // MODIFIED na
10806 : // RE-ENGINEERED na
10807 :
10808 : // PURPOSE OF THIS SUBROUTINE:
10809 : // This subroutine is for sizing water heater tank volume and heater
10810 : // as best we can at this point in simulation. (prior to demand side
10811 : // sizing that needs volume).
10812 :
10813 : // METHODOLOGY EMPLOYED:
10814 : // depending on the sizing design mode...
10815 :
10816 : // REFERENCES:
10817 : // BA benchmark report for residential design mode
10818 :
10819 : // SUBROUTINE PARAMETER DEFINITIONS:
10820 : static constexpr std::string_view RoutineName("SizeTankForDemandSide");
10821 786 : Real64 constexpr GalTocubicMeters(0.0037854);
10822 786 : Real64 constexpr kBtuPerHrToWatts(293.1);
10823 :
10824 786 : Real64 Tstart = 14.44;
10825 786 : Real64 Tfinish = 57.22;
10826 :
10827 786 : Real64 tmpTankVolume = this->Volume;
10828 786 : Real64 tmpMaxCapacity = this->MaxCapacity;
10829 :
10830 786 : switch (this->Sizing.DesignMode) {
10831 :
10832 771 : case SizingMode::Invalid:
10833 : case SizingMode::PeakDraw: {
10834 :
10835 771 : break;
10836 : }
10837 15 : case SizingMode::ResidentialMin: {
10838 :
10839 : // assume can propagate rules for gas to other fuels.
10840 15 : bool FuelTypeIsLikeGas = false;
10841 15 : switch (this->FuelType) {
10842 0 : case Constant::eFuel::NaturalGas:
10843 : case Constant::eFuel::Diesel:
10844 : case Constant::eFuel::Gasoline:
10845 : case Constant::eFuel::Coal:
10846 : case Constant::eFuel::FuelOilNo1:
10847 : case Constant::eFuel::FuelOilNo2:
10848 : case Constant::eFuel::Propane:
10849 : case Constant::eFuel::OtherFuel1:
10850 : case Constant::eFuel::OtherFuel2:
10851 : case Constant::eFuel::DistrictHeatingWater:
10852 : case Constant::eFuel::DistrictHeatingSteam:
10853 0 : FuelTypeIsLikeGas = true;
10854 0 : break;
10855 15 : default: // FuelTypeIsLikeGas stays false
10856 15 : break;
10857 : }
10858 :
10859 15 : if (this->Sizing.NumberOfBedrooms == 1) {
10860 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10861 0 : if (this->VolumeWasAutoSized) {
10862 0 : tmpTankVolume = 20.0 * GalTocubicMeters;
10863 : }
10864 0 : if (this->MaxCapacityWasAutoSized) {
10865 0 : tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
10866 : }
10867 0 : } else if (FuelTypeIsLikeGas) {
10868 0 : if (this->VolumeWasAutoSized) {
10869 0 : tmpTankVolume = 20.0 * GalTocubicMeters;
10870 : }
10871 0 : if (this->MaxCapacityWasAutoSized) {
10872 0 : tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
10873 : }
10874 : }
10875 :
10876 15 : } else if (this->Sizing.NumberOfBedrooms == 2) {
10877 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10878 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10879 0 : if (this->VolumeWasAutoSized) {
10880 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
10881 : }
10882 0 : if (this->MaxCapacityWasAutoSized) {
10883 0 : tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
10884 : }
10885 0 : } else if (FuelTypeIsLikeGas) {
10886 0 : if (this->VolumeWasAutoSized) {
10887 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
10888 : }
10889 0 : if (this->MaxCapacityWasAutoSized) {
10890 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10891 : }
10892 : }
10893 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10894 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10895 0 : if (this->VolumeWasAutoSized) {
10896 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
10897 : }
10898 0 : if (this->MaxCapacityWasAutoSized) {
10899 0 : tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10900 : }
10901 0 : } else if (FuelTypeIsLikeGas) {
10902 0 : if (this->VolumeWasAutoSized) {
10903 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
10904 : }
10905 0 : if (this->MaxCapacityWasAutoSized) {
10906 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10907 : }
10908 : }
10909 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10910 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10911 0 : if (this->VolumeWasAutoSized) {
10912 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
10913 : }
10914 0 : if (this->MaxCapacityWasAutoSized) {
10915 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10916 : }
10917 0 : } else if (FuelTypeIsLikeGas) {
10918 0 : if (this->VolumeWasAutoSized) {
10919 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
10920 : }
10921 0 : if (this->MaxCapacityWasAutoSized) {
10922 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10923 : }
10924 : }
10925 : }
10926 15 : } else if (this->Sizing.NumberOfBedrooms == 3) {
10927 15 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10928 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10929 0 : if (this->VolumeWasAutoSized) {
10930 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
10931 : }
10932 0 : if (this->MaxCapacityWasAutoSized) {
10933 0 : tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10934 : }
10935 0 : } else if (FuelTypeIsLikeGas) {
10936 0 : if (this->VolumeWasAutoSized) {
10937 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
10938 : }
10939 0 : if (this->MaxCapacityWasAutoSized) {
10940 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10941 : }
10942 : }
10943 15 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10944 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10945 0 : if (this->VolumeWasAutoSized) {
10946 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
10947 : }
10948 0 : if (this->MaxCapacityWasAutoSized) {
10949 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10950 : }
10951 0 : } else if (FuelTypeIsLikeGas) {
10952 0 : if (this->VolumeWasAutoSized) {
10953 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
10954 : }
10955 0 : if (this->MaxCapacityWasAutoSized) {
10956 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10957 : }
10958 : }
10959 15 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10960 15 : if (this->FuelType == Constant::eFuel::Electricity) {
10961 15 : if (this->VolumeWasAutoSized) {
10962 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
10963 : }
10964 15 : if (this->MaxCapacityWasAutoSized) {
10965 15 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10966 : }
10967 0 : } else if (FuelTypeIsLikeGas) {
10968 0 : if (this->VolumeWasAutoSized) {
10969 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
10970 : }
10971 0 : if (this->MaxCapacityWasAutoSized) {
10972 0 : tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10973 : }
10974 : }
10975 : }
10976 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
10977 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10978 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10979 0 : if (this->VolumeWasAutoSized) {
10980 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
10981 : }
10982 0 : if (this->MaxCapacityWasAutoSized) {
10983 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10984 : }
10985 0 : } else if (FuelTypeIsLikeGas) {
10986 0 : if (this->VolumeWasAutoSized) {
10987 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
10988 : }
10989 0 : if (this->MaxCapacityWasAutoSized) {
10990 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10991 : }
10992 : }
10993 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10994 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10995 0 : if (this->VolumeWasAutoSized) {
10996 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
10997 : }
10998 0 : if (this->MaxCapacityWasAutoSized) {
10999 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11000 : }
11001 0 : } else if (FuelTypeIsLikeGas) {
11002 0 : if (this->VolumeWasAutoSized) {
11003 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11004 : }
11005 0 : if (this->MaxCapacityWasAutoSized) {
11006 0 : tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11007 : }
11008 : }
11009 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11010 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11011 0 : if (this->VolumeWasAutoSized) {
11012 0 : tmpTankVolume = 66.0 * GalTocubicMeters;
11013 : }
11014 0 : if (this->MaxCapacityWasAutoSized) {
11015 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11016 : }
11017 0 : } else if (FuelTypeIsLikeGas) {
11018 0 : if (this->VolumeWasAutoSized) {
11019 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11020 : }
11021 0 : if (this->MaxCapacityWasAutoSized) {
11022 0 : tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11023 : }
11024 : }
11025 : }
11026 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
11027 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11028 0 : if (this->VolumeWasAutoSized) {
11029 0 : tmpTankVolume = 66.0 * GalTocubicMeters;
11030 : }
11031 0 : if (this->MaxCapacityWasAutoSized) {
11032 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11033 : }
11034 0 : } else if (FuelTypeIsLikeGas) {
11035 0 : if (this->VolumeWasAutoSized) {
11036 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11037 : }
11038 0 : if (this->MaxCapacityWasAutoSized) {
11039 0 : tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
11040 : }
11041 : }
11042 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
11043 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11044 0 : if (this->VolumeWasAutoSized) {
11045 0 : tmpTankVolume = 66.0 * GalTocubicMeters;
11046 : }
11047 0 : if (this->MaxCapacityWasAutoSized) {
11048 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11049 : }
11050 0 : } else if (FuelTypeIsLikeGas) {
11051 0 : if (this->VolumeWasAutoSized) {
11052 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11053 : }
11054 0 : if (this->MaxCapacityWasAutoSized) {
11055 0 : tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
11056 : }
11057 : }
11058 : }
11059 :
11060 15 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11061 0 : this->Volume = tmpTankVolume;
11062 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11063 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11064 : }
11065 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11066 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11067 : }
11068 : }
11069 15 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11070 3 : this->MaxCapacity = tmpMaxCapacity;
11071 3 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11072 3 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11073 : }
11074 3 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11075 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11076 : }
11077 : }
11078 15 : break;
11079 : }
11080 0 : case SizingMode::PerPerson: {
11081 : // how to get number of people?
11082 :
11083 : // MJW TODO: this won't compile now: Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
11084 0 : Real64 SumPeopleAllZones = 0.0;
11085 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
11086 0 : SumPeopleAllZones += thisZone.TotOccupants;
11087 0 : }
11088 0 : if (this->VolumeWasAutoSized) {
11089 0 : tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
11090 : }
11091 :
11092 0 : if (this->MaxCapacityWasAutoSized) {
11093 : Real64 rho;
11094 : Real64 Cp;
11095 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11096 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11097 0 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11098 : } else {
11099 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11100 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11101 : }
11102 :
11103 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) * rho *
11104 : Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11105 : }
11106 :
11107 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11108 0 : this->Volume = tmpTankVolume;
11109 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11110 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11111 : }
11112 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11113 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11114 : }
11115 : }
11116 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11117 0 : this->MaxCapacity = tmpMaxCapacity;
11118 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11119 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11120 : }
11121 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11122 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11123 : }
11124 : }
11125 0 : break;
11126 : }
11127 0 : case SizingMode::PerFloorArea: {
11128 :
11129 : // MJW TODO: this won't compile now: Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
11130 0 : Real64 SumFloorAreaAllZones = 0.0;
11131 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
11132 0 : SumFloorAreaAllZones += thisZone.FloorArea;
11133 0 : }
11134 0 : if (this->VolumeWasAutoSized) {
11135 0 : tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
11136 : }
11137 0 : if (this->MaxCapacityWasAutoSized) {
11138 : Real64 rho;
11139 : Real64 Cp;
11140 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11141 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11142 0 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11143 : } else {
11144 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11145 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11146 : }
11147 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) * rho *
11148 : Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11149 : }
11150 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11151 0 : this->Volume = tmpTankVolume;
11152 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11153 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11154 : }
11155 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11156 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11157 : }
11158 : }
11159 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11160 0 : this->MaxCapacity = tmpMaxCapacity;
11161 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11162 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11163 : }
11164 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11165 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11166 : }
11167 : }
11168 0 : break;
11169 : }
11170 0 : case SizingMode::PerUnit: {
11171 :
11172 0 : if (this->VolumeWasAutoSized) {
11173 0 : tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
11174 : }
11175 :
11176 0 : if (this->MaxCapacityWasAutoSized) {
11177 : Real64 rho;
11178 : Real64 Cp;
11179 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11180 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11181 0 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11182 : } else {
11183 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11184 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11185 : }
11186 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) *
11187 : rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11188 : }
11189 :
11190 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11191 0 : this->Volume = tmpTankVolume;
11192 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11193 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11194 : }
11195 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11196 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11197 : }
11198 : }
11199 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11200 0 : this->MaxCapacity = tmpMaxCapacity;
11201 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11202 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11203 : }
11204 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11205 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11206 : }
11207 : }
11208 0 : break;
11209 : }
11210 0 : case SizingMode::PerSolarColArea: {
11211 0 : break;
11212 : }
11213 0 : default:
11214 0 : break;
11215 : }
11216 :
11217 786 : if (this->MaxCapacityWasAutoSized) {
11218 15 : this->setBackupElementCapacity(state);
11219 : }
11220 :
11221 : // if stratified, might set height.
11222 786 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
11223 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
11224 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
11225 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / Constant::Pi, 0.3333333333333333);
11226 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11227 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
11228 : }
11229 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11230 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
11231 : }
11232 : // check if Constant::AutoCalculate() Use outlet and source inlet are still set to autosize by earlier
11233 0 : if (this->UseOutletHeightWasAutoSized) {
11234 0 : this->UseOutletHeight = this->Height;
11235 : }
11236 0 : if (this->SourceInletHeightWasAutoSized) {
11237 0 : this->SourceInletHeight = this->Height;
11238 : }
11239 : }
11240 : }
11241 786 : }
11242 :
11243 786 : void WaterThermalTankData::SizeTankForSupplySide(EnergyPlusData &state)
11244 : {
11245 :
11246 : // SUBROUTINE INFORMATION:
11247 : // AUTHOR Brent Griffith
11248 : // DATE WRITTEN February 2008
11249 : // MODIFIED na
11250 : // RE-ENGINEERED na
11251 :
11252 : // PURPOSE OF THIS SUBROUTINE:
11253 : // This subroutine is for sizing water heater tank volume and heater
11254 : // at a later point in the simulation when more of the plant is ready.
11255 :
11256 : // METHODOLOGY EMPLOYED:
11257 : // depending on the sizing design mode...
11258 :
11259 : // REFERENCES:
11260 : // BA benchmark report for residential design mode
11261 :
11262 : static constexpr std::string_view RoutineName("SizeTankForSupplySide");
11263 :
11264 786 : Real64 tmpTankVolume = this->Volume;
11265 786 : Real64 tmpMaxCapacity = this->MaxCapacity;
11266 :
11267 786 : if (this->Sizing.DesignMode == SizingMode::PeakDraw) {
11268 30 : if (this->VolumeWasAutoSized) {
11269 20 : tmpTankVolume = this->Sizing.TankDrawTime * this->UseDesignVolFlowRate * Constant::rSecsInHour; // hours | m3/s | (3600 s/1 hour)
11270 : }
11271 30 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11272 4 : this->Volume = tmpTankVolume;
11273 4 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11274 4 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11275 : }
11276 4 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11277 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11278 : }
11279 : }
11280 30 : if (this->MaxCapacityWasAutoSized) {
11281 0 : if (this->Sizing.RecoveryTime > 0.0) {
11282 0 : Real64 rho = 0.0;
11283 0 : Real64 Cp = 0.0;
11284 0 : constexpr Real64 Tstart = 14.44;
11285 0 : constexpr Real64 Tfinish = 57.22;
11286 :
11287 0 : if (this->SrcSidePlantLoc.loopNum > 0) {
11288 0 : rho = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11289 0 : Cp = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum)
11290 0 : .glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11291 : } else {
11292 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11293 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11294 : }
11295 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11296 0 : (this->Sizing.RecoveryTime * Constant::rSecsInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11297 : } else {
11298 0 : ShowFatalError(
11299 0 : state, format("{}: Tank=\"{}\", requested sizing for max capacity but entered Recovery Time is zero.", RoutineName, this->Name));
11300 : }
11301 : }
11302 :
11303 30 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11304 0 : this->MaxCapacity = tmpMaxCapacity;
11305 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11306 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11307 : }
11308 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11309 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11310 : }
11311 : }
11312 756 : } else if (this->Sizing.DesignMode == SizingMode::PerSolarColArea) {
11313 :
11314 0 : this->Sizing.TotalSolarCollectorArea = 0.0;
11315 :
11316 0 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11317 0 : auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
11318 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
11319 : }
11320 :
11321 0 : for (int CollectorNum = 1; CollectorNum <= state.dataPhotovoltaicThermalCollector->NumPVT; ++CollectorNum) {
11322 0 : auto const &collector = state.dataPhotovoltaicThermalCollector->PVT(CollectorNum);
11323 0 : this->Sizing.TotalSolarCollectorArea += collector.AreaCol;
11324 : }
11325 :
11326 0 : if (this->VolumeWasAutoSized) {
11327 0 : if (this->Sizing.TotalSolarCollectorArea > 0) {
11328 0 : tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11329 : } else {
11330 0 : ShowFatalError(state,
11331 0 : format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
11332 : "area of Collectors is zero.",
11333 : RoutineName,
11334 0 : this->Name));
11335 : }
11336 : }
11337 0 : if (this->MaxCapacityWasAutoSized) {
11338 0 : tmpMaxCapacity = 0.0;
11339 : }
11340 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11341 0 : this->Volume = tmpTankVolume;
11342 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11343 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11344 : }
11345 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11346 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11347 : }
11348 : }
11349 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11350 0 : this->MaxCapacity = tmpMaxCapacity;
11351 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11352 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11353 : }
11354 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11355 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11356 : }
11357 : }
11358 : }
11359 :
11360 786 : if (this->MaxCapacityWasAutoSized) {
11361 15 : this->setBackupElementCapacity(state);
11362 : }
11363 :
11364 786 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
11365 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
11366 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
11367 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / Constant::Pi, 0.3333333333333333);
11368 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11369 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
11370 : }
11371 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11372 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
11373 : }
11374 : }
11375 : }
11376 786 : }
11377 :
11378 786 : void WaterThermalTankData::SizeDemandSidePlantConnections(EnergyPlusData &state)
11379 : {
11380 :
11381 : // SUBROUTINE INFORMATION:
11382 : // AUTHOR Brent Griffith
11383 : // DATE WRITTEN October 2007
11384 : // MODIFIED na
11385 : // RE-ENGINEERED na
11386 :
11387 : // PURPOSE OF THIS SUBROUTINE:
11388 : // This subroutine is for sizing water heater plant connection flow rates
11389 : // on the demand side that have not been specified in the input.
11390 :
11391 : // METHODOLOGY EMPLOYED:
11392 : // For water heater sides on the Demand side, hot water flow rates are modeled entirely from user input data
11393 : // because the plant loop is not yet set up nor is plant sizing info populated.
11394 : // sizing is done by calculating an initial
11395 : // recovery rate that if continued would reheat tank in user specified amount of time.
11396 : // initial and final tank temperatures are 14.44 and reheat to 57.22 (values from CalcStandardRatings routine)
11397 :
11398 : static constexpr std::string_view RoutineName("SizeDemandSidePlantConnections");
11399 :
11400 786 : auto &PlantSizData = state.dataSize->PlantSizData;
11401 :
11402 786 : Real64 tankRecoverhours = this->SizingRecoveryTime;
11403 786 : bool ErrorsFound = false;
11404 786 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
11405 786 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
11406 :
11407 : Real64 Tstart;
11408 : Real64 Tfinish;
11409 786 : if (!this->IsChilledWaterTank) {
11410 736 : Tstart = 14.44;
11411 736 : Tfinish = 57.22;
11412 : } else {
11413 50 : Tstart = 14.44;
11414 50 : Tfinish = 9.0;
11415 : }
11416 :
11417 : // determine tank volume to use for sizing.
11418 786 : Real64 TankVolume = this->Volume;
11419 786 : if (this->VolumeWasAutoSized) {
11420 20 : TankVolume = this->Sizing.NominalVolForSizingDemandSideFlow;
11421 : }
11422 :
11423 786 : if (this->UseInletNode > 0) {
11424 786 : if (this->UseDesignVolFlowRateWasAutoSized) {
11425 726 : int PltSizNum = this->UseSidePlantSizNum;
11426 726 : if (PltSizNum > 0) { // we have a Plant Sizing Object
11427 726 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11428 : // probably shouldn't come here as Use side is unlikley to be on demand side (?)
11429 : // but going to treat component with symetry so if connections are reversed it'll still work
11430 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11431 : // in user specified hours.
11432 : // using the plant inlet design temp for sizing.
11433 0 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11434 0 : Real64 eff = this->UseEffectiveness;
11435 0 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11436 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11437 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11438 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11439 : } else {
11440 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11441 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11442 : }
11443 0 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11444 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11445 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11446 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11447 : } else {
11448 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11449 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11450 : }
11451 : } else {
11452 0 : if (!this->IsChilledWaterTank) {
11453 : // plant sizing object design temperature is set too low throw warning.
11454 0 : ShowSevereError(state,
11455 : "Autosizing of Use side water heater design flow rate requires Sizing:Plant object to have an exit "
11456 : "temperature >= 58C");
11457 0 : ShowContinueError(state, format("Occurs for water heater object={}", this->Name));
11458 : } else {
11459 : // plant sizing object design temperature is set too hi throw warning.
11460 0 : ShowSevereError(state,
11461 : "Autosizing of Use side chilled water tank design flow rate requires Sizing:Plant object to have an "
11462 : "exit temperature <= 8C");
11463 0 : ShowContinueError(state, format("Occurs for chilled water storage tank object={}", this->Name));
11464 : }
11465 0 : ErrorsFound = true;
11466 : }
11467 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11468 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11469 : }
11470 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11471 0 : BaseSizer::reportSizerOutput(
11472 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11473 : }
11474 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11475 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11476 : } else {
11477 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
11478 : }
11479 : Real64 rho =
11480 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11481 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11482 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11483 : } else {
11484 0 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
11485 : }
11486 : } // Demand side
11487 : } else {
11488 : // do nothing
11489 : } // plant sizing object
11490 :
11491 : } else {
11492 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11493 60 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11494 : Real64 rho;
11495 60 : if (this->UseSidePlantLoc.loopNum > 0) {
11496 60 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11497 : } else {
11498 0 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
11499 : }
11500 60 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11501 : } // autosizing needed.
11502 : } // connected to plant
11503 :
11504 786 : if (this->SourceInletNode > 0) {
11505 260 : if (this->SourceDesignVolFlowRateWasAutoSized) {
11506 125 : int PltSizNum = this->SourceSidePlantSizNum;
11507 125 : if (PltSizNum > 0) {
11508 110 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11509 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11510 : // in user specified hours.
11511 : // using the plant inlet design temp for sizing.
11512 60 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11513 60 : Real64 eff = this->SourceEffectiveness;
11514 60 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11515 :
11516 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11517 6 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11518 6 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11519 : } else {
11520 24 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11521 24 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11522 : }
11523 30 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11524 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11525 6 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11526 6 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11527 : } else {
11528 24 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11529 24 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11530 : }
11531 : } else {
11532 0 : if (!this->IsChilledWaterTank) {
11533 : // plant sizing object design temperature is set too low throw warning.
11534 0 : ShowSevereError(state,
11535 : "Autosizing of Source side water heater design flow rate requires Sizing:Plant object to have an "
11536 : "exit temperature >= 58C");
11537 0 : ShowContinueError(state, format("Occurs for WaterHeater:Mixed object={}", this->Name));
11538 : } else {
11539 : // plant sizing object design temperature is set too hi throw warning.
11540 0 : ShowSevereError(state,
11541 : "Autosizing of Source side chilled water tank design flow rate requires Sizing:Plant object to have "
11542 : "an exit temperature <= 8C");
11543 0 : ShowContinueError(state, format("Occurs for chilled water storage tank object={}", this->Name));
11544 : }
11545 0 : ErrorsFound = true;
11546 : }
11547 60 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11548 12 : BaseSizer::reportSizerOutput(
11549 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11550 : }
11551 60 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11552 0 : BaseSizer::reportSizerOutput(
11553 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11554 : }
11555 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11556 12 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11557 : } else {
11558 48 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
11559 : }
11560 : Real64 rho =
11561 60 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11562 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11563 12 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11564 : } else {
11565 48 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
11566 : }
11567 : } // demand side
11568 : } else {
11569 : // do nothing
11570 : } // plant sizing object
11571 :
11572 : } else {
11573 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11574 135 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11575 : Real64 rho;
11576 135 : if (this->SrcSidePlantLoc.loopNum > 0) {
11577 100 : rho = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11578 : } else {
11579 35 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
11580 : }
11581 135 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11582 : } // autosizing needed.
11583 : } // connected to plant
11584 :
11585 786 : if (ErrorsFound) {
11586 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
11587 : }
11588 786 : }
11589 :
11590 49 : void WaterThermalTankData::SizeStandAloneWaterHeater(EnergyPlusData &state)
11591 : {
11592 :
11593 : // SUBROUTINE INFORMATION:
11594 : // AUTHOR B. Griffith
11595 : // DATE WRITTEN October 2013
11596 : // MODIFIED na
11597 : // RE-ENGINEERED na
11598 :
11599 : // PURPOSE OF THIS SUBROUTINE:
11600 : // allow autosizing of tank volume and heat capacity for stand alone tanks
11601 :
11602 : // METHODOLOGY EMPLOYED:
11603 : // same as for plant connected water heaters, only draws are scheduled.
11604 :
11605 : // SUBROUTINE PARAMETER DEFINITIONS:
11606 49 : Real64 constexpr GalTocubicMeters(0.0037854);
11607 49 : Real64 constexpr kBtuPerHrToWatts(293.1);
11608 : static constexpr std::string_view routineName = "SizeStandAloneWaterHeater";
11609 :
11610 49 : Real64 Tstart = 14.44;
11611 49 : Real64 Tfinish = 57.22;
11612 49 : Real64 tmpTankVolume = this->Volume;
11613 49 : Real64 tmpMaxCapacity = this->MaxCapacity;
11614 :
11615 49 : if (this->VolumeWasAutoSized || this->MaxCapacityWasAutoSized) {
11616 :
11617 0 : switch (this->Sizing.DesignMode) {
11618 :
11619 0 : case SizingMode::PeakDraw: {
11620 : // get draw rate from maximum in schedule
11621 :
11622 0 : Real64 rho = this->water->getDensity(state, Constant::InitConvTemp, routineName);
11623 0 : Real64 DrawDesignVolFlowRate = this->flowRateSched->getCurrentVal() * this->MassFlowRateMax / rho;
11624 :
11625 0 : if (this->VolumeWasAutoSized) {
11626 0 : tmpTankVolume = this->Sizing.TankDrawTime * DrawDesignVolFlowRate * Constant::rSecsInHour; // hours | m3/s | (3600 s/1 hour)
11627 0 : this->Volume = tmpTankVolume;
11628 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11629 : }
11630 0 : if (this->MaxCapacityWasAutoSized) {
11631 0 : if (this->Sizing.RecoveryTime > 0.0) {
11632 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11633 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11634 :
11635 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11636 0 : (this->Sizing.RecoveryTime * Constant::rSecsInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11637 : } else {
11638 0 : ShowFatalError(
11639 : state,
11640 0 : format("{}: Tank=\"{}\", requested sizing for max capacity but entered Recovery Time is zero.", routineName, this->Name));
11641 : }
11642 0 : this->MaxCapacity = tmpMaxCapacity;
11643 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11644 : }
11645 :
11646 0 : break;
11647 : }
11648 0 : case SizingMode::ResidentialMin: {
11649 : // assume can propagate rules for gas to other fuels.
11650 0 : bool FuelTypeIsLikeGas = false;
11651 0 : switch (this->FuelType) {
11652 0 : case Constant::eFuel::NaturalGas:
11653 : case Constant::eFuel::Diesel:
11654 : case Constant::eFuel::Gasoline:
11655 : case Constant::eFuel::Coal:
11656 : case Constant::eFuel::FuelOilNo1:
11657 : case Constant::eFuel::FuelOilNo2:
11658 : case Constant::eFuel::Propane:
11659 : case Constant::eFuel::OtherFuel1:
11660 : case Constant::eFuel::OtherFuel2:
11661 : case Constant::eFuel::DistrictHeatingWater:
11662 : case Constant::eFuel::DistrictHeatingSteam:
11663 0 : FuelTypeIsLikeGas = true;
11664 0 : break;
11665 0 : default: // FuelTypeIsLikeGas stays false
11666 0 : break;
11667 : }
11668 :
11669 0 : if (this->Sizing.NumberOfBedrooms == 1) {
11670 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11671 0 : if (this->VolumeWasAutoSized) {
11672 0 : tmpTankVolume = 20.0 * GalTocubicMeters;
11673 : }
11674 0 : if (this->MaxCapacityWasAutoSized) {
11675 0 : tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
11676 : }
11677 0 : } else if (FuelTypeIsLikeGas) {
11678 0 : if (this->VolumeWasAutoSized) {
11679 0 : tmpTankVolume = 20.0 * GalTocubicMeters;
11680 : }
11681 0 : if (this->MaxCapacityWasAutoSized) {
11682 0 : tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
11683 : }
11684 : }
11685 :
11686 0 : } else if (this->Sizing.NumberOfBedrooms == 2) {
11687 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11688 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11689 0 : if (this->VolumeWasAutoSized) {
11690 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
11691 : }
11692 0 : if (this->MaxCapacityWasAutoSized) {
11693 0 : tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
11694 : }
11695 0 : } else if (FuelTypeIsLikeGas) {
11696 0 : if (this->VolumeWasAutoSized) {
11697 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
11698 : }
11699 0 : if (this->MaxCapacityWasAutoSized) {
11700 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11701 : }
11702 : }
11703 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11704 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11705 0 : if (this->VolumeWasAutoSized) {
11706 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11707 : }
11708 0 : if (this->MaxCapacityWasAutoSized) {
11709 0 : tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11710 : }
11711 0 : } else if (FuelTypeIsLikeGas) {
11712 0 : if (this->VolumeWasAutoSized) {
11713 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
11714 : }
11715 0 : if (this->MaxCapacityWasAutoSized) {
11716 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11717 : }
11718 : }
11719 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11720 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11721 0 : if (this->VolumeWasAutoSized) {
11722 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11723 : }
11724 0 : if (this->MaxCapacityWasAutoSized) {
11725 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11726 : }
11727 0 : } else if (FuelTypeIsLikeGas) {
11728 0 : if (this->VolumeWasAutoSized) {
11729 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11730 : }
11731 0 : if (this->MaxCapacityWasAutoSized) {
11732 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11733 : }
11734 : }
11735 : }
11736 0 : } else if (this->Sizing.NumberOfBedrooms == 3) {
11737 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11738 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11739 0 : if (this->VolumeWasAutoSized) {
11740 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11741 : }
11742 0 : if (this->MaxCapacityWasAutoSized) {
11743 0 : tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11744 : }
11745 0 : } else if (FuelTypeIsLikeGas) {
11746 0 : if (this->VolumeWasAutoSized) {
11747 0 : tmpTankVolume = 30.0 * GalTocubicMeters;
11748 : }
11749 0 : if (this->MaxCapacityWasAutoSized) {
11750 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11751 : }
11752 : }
11753 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11754 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11755 0 : if (this->VolumeWasAutoSized) {
11756 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11757 : }
11758 0 : if (this->MaxCapacityWasAutoSized) {
11759 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11760 : }
11761 0 : } else if (FuelTypeIsLikeGas) {
11762 0 : if (this->VolumeWasAutoSized) {
11763 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11764 : }
11765 0 : if (this->MaxCapacityWasAutoSized) {
11766 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11767 : }
11768 : }
11769 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11770 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11771 0 : if (this->VolumeWasAutoSized) {
11772 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11773 : }
11774 0 : if (this->MaxCapacityWasAutoSized) {
11775 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11776 : }
11777 0 : } else if (FuelTypeIsLikeGas) {
11778 0 : if (this->VolumeWasAutoSized) {
11779 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11780 : }
11781 0 : if (this->MaxCapacityWasAutoSized) {
11782 0 : tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11783 : }
11784 : }
11785 : }
11786 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
11787 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11788 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11789 0 : if (this->VolumeWasAutoSized) {
11790 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11791 : }
11792 0 : if (this->MaxCapacityWasAutoSized) {
11793 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11794 : }
11795 0 : } else if (FuelTypeIsLikeGas) {
11796 0 : if (this->VolumeWasAutoSized) {
11797 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11798 : }
11799 0 : if (this->MaxCapacityWasAutoSized) {
11800 0 : tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11801 : }
11802 : }
11803 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11804 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11805 0 : if (this->VolumeWasAutoSized) {
11806 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11807 : }
11808 0 : if (this->MaxCapacityWasAutoSized) {
11809 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11810 : }
11811 0 : } else if (FuelTypeIsLikeGas) {
11812 0 : if (this->VolumeWasAutoSized) {
11813 0 : tmpTankVolume = 40.0 * GalTocubicMeters;
11814 : }
11815 0 : if (this->MaxCapacityWasAutoSized) {
11816 0 : tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11817 : }
11818 : }
11819 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11820 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11821 0 : if (this->VolumeWasAutoSized) {
11822 0 : tmpTankVolume = 66.0 * GalTocubicMeters;
11823 : }
11824 0 : if (this->MaxCapacityWasAutoSized) {
11825 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11826 : }
11827 0 : } else if (FuelTypeIsLikeGas) {
11828 0 : if (this->VolumeWasAutoSized) {
11829 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11830 : }
11831 0 : if (this->MaxCapacityWasAutoSized) {
11832 0 : tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11833 : }
11834 : }
11835 : }
11836 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
11837 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11838 0 : if (this->VolumeWasAutoSized) {
11839 0 : tmpTankVolume = 66.0 * GalTocubicMeters;
11840 : }
11841 0 : if (this->MaxCapacityWasAutoSized) {
11842 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11843 : }
11844 0 : } else if (FuelTypeIsLikeGas) {
11845 0 : if (this->VolumeWasAutoSized) {
11846 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11847 : }
11848 0 : if (this->MaxCapacityWasAutoSized) {
11849 0 : tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
11850 : }
11851 : }
11852 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
11853 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11854 0 : if (this->VolumeWasAutoSized) {
11855 0 : tmpTankVolume = 66.0 * GalTocubicMeters;
11856 : }
11857 0 : if (this->MaxCapacityWasAutoSized) {
11858 0 : tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11859 : }
11860 0 : } else if (FuelTypeIsLikeGas) {
11861 0 : if (this->VolumeWasAutoSized) {
11862 0 : tmpTankVolume = 50.0 * GalTocubicMeters;
11863 : }
11864 0 : if (this->MaxCapacityWasAutoSized) {
11865 0 : tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
11866 : }
11867 : }
11868 : }
11869 0 : if (this->VolumeWasAutoSized) {
11870 0 : this->Volume = tmpTankVolume;
11871 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11872 : }
11873 0 : if (this->MaxCapacityWasAutoSized) {
11874 0 : this->MaxCapacity = tmpMaxCapacity;
11875 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11876 : }
11877 :
11878 0 : break;
11879 : }
11880 0 : case SizingMode::PerPerson: {
11881 : // how to get number of people?
11882 :
11883 : // MJW TODO: this won't compile now: Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
11884 0 : Real64 SumPeopleAllZones = 0.0;
11885 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
11886 0 : SumPeopleAllZones += thisZone.TotOccupants;
11887 0 : }
11888 0 : if (this->VolumeWasAutoSized) {
11889 0 : tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
11890 : }
11891 0 : if (this->MaxCapacityWasAutoSized) {
11892 0 : Real64 rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11893 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11894 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) *
11895 : rho * Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11896 : }
11897 :
11898 0 : if (this->VolumeWasAutoSized) {
11899 0 : this->Volume = tmpTankVolume;
11900 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11901 : }
11902 0 : if (this->MaxCapacityWasAutoSized) {
11903 0 : this->MaxCapacity = tmpMaxCapacity;
11904 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11905 : }
11906 :
11907 0 : break;
11908 : }
11909 0 : case SizingMode::PerFloorArea: {
11910 :
11911 : // MJW TODO: this won't compile now: Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
11912 0 : Real64 SumFloorAreaAllZones = 0.0;
11913 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
11914 0 : SumFloorAreaAllZones += thisZone.FloorArea;
11915 0 : }
11916 0 : if (this->VolumeWasAutoSized) {
11917 0 : tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
11918 : }
11919 :
11920 0 : if (this->MaxCapacityWasAutoSized) {
11921 0 : Real64 rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11922 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11923 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) *
11924 : rho * Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11925 : }
11926 0 : if (this->VolumeWasAutoSized) {
11927 0 : this->Volume = tmpTankVolume;
11928 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11929 : }
11930 0 : if (this->MaxCapacityWasAutoSized) {
11931 0 : this->MaxCapacity = tmpMaxCapacity;
11932 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11933 : }
11934 0 : break;
11935 : }
11936 0 : case SizingMode::PerUnit: {
11937 :
11938 0 : if (this->VolumeWasAutoSized) {
11939 0 : tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
11940 : }
11941 :
11942 0 : if (this->MaxCapacityWasAutoSized) {
11943 0 : Real64 rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11944 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11945 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) *
11946 0 : (1.0 / Constant::rSecsInHour) * rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11947 : }
11948 :
11949 0 : if (this->VolumeWasAutoSized) {
11950 0 : this->Volume = tmpTankVolume;
11951 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11952 : }
11953 0 : if (this->MaxCapacityWasAutoSized) {
11954 0 : this->MaxCapacity = tmpMaxCapacity;
11955 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11956 : }
11957 0 : break;
11958 : }
11959 0 : case SizingMode::PerSolarColArea: {
11960 :
11961 0 : this->Sizing.TotalSolarCollectorArea = 0.0;
11962 :
11963 0 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11964 0 : auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
11965 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
11966 : }
11967 :
11968 0 : for (int CollectorNum = 1; CollectorNum <= state.dataPhotovoltaicThermalCollector->NumPVT; ++CollectorNum) {
11969 0 : auto const &collector = state.dataPhotovoltaicThermalCollector->PVT(CollectorNum);
11970 0 : this->Sizing.TotalSolarCollectorArea += collector.AreaCol;
11971 : }
11972 :
11973 0 : if (this->VolumeWasAutoSized) {
11974 0 : if (this->Sizing.TotalSolarCollectorArea > 0) {
11975 0 : tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11976 : } else {
11977 0 : ShowFatalError(state,
11978 0 : format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
11979 : "area of Collectors is zero.",
11980 : routineName,
11981 0 : this->Name));
11982 : }
11983 : }
11984 0 : if (this->MaxCapacityWasAutoSized) {
11985 0 : tmpMaxCapacity = 0.0;
11986 : }
11987 :
11988 0 : if (this->VolumeWasAutoSized) {
11989 0 : this->Volume = tmpTankVolume;
11990 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11991 : }
11992 0 : if (this->MaxCapacityWasAutoSized) {
11993 0 : this->MaxCapacity = tmpMaxCapacity;
11994 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11995 : }
11996 0 : break;
11997 : }
11998 0 : default:
11999 0 : if (this->MaxCapacityWasAutoSized) {
12000 0 : this->setBackupElementCapacity(state);
12001 : }
12002 0 : break;
12003 : }
12004 : }
12005 49 : }
12006 :
12007 6336629 : void WaterThermalTankData::UpdateWaterThermalTank(EnergyPlusData &state)
12008 : {
12009 :
12010 : // SUBROUTINE INFORMATION:
12011 : // AUTHOR Brandon Anderson
12012 : // DATE WRITTEN May 2000
12013 : // MODIFIED na
12014 : // Nov 2011, BAN; removed the use and source heat rate re-calculation for stratified tank
12015 : // for energy conservation verification.
12016 : // RE-ENGINEERED Feb 2004, PGE
12017 :
12018 : // PURPOSE OF THIS SUBROUTINE:
12019 : // Updates the node variables with local variables.
12020 :
12021 6336629 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
12022 5935311 : state.dataLoopNodes->Node(UseOutletNode) = state.dataLoopNodes->Node(this->UseInletNode); // this could wipe out setpoints on outlet node
12023 :
12024 5935311 : state.dataLoopNodes->Node(this->UseOutletNode).Temp = this->UseOutletTemp;
12025 : }
12026 :
12027 6336629 : if (this->SourceInletNode > 0 && this->SourceOutletNode > 0) {
12028 1800956 : state.dataLoopNodes->Node(this->SourceOutletNode) = state.dataLoopNodes->Node(this->SourceInletNode);
12029 :
12030 1800956 : state.dataLoopNodes->Node(this->SourceOutletNode).Temp = this->SourceOutletTemp;
12031 : }
12032 6336629 : }
12033 :
12034 6336629 : void WaterThermalTankData::ReportWaterThermalTank(EnergyPlusData &state)
12035 : {
12036 :
12037 : // SUBROUTINE INFORMATION:
12038 : // AUTHOR Brandon Anderson
12039 : // DATE WRITTEN May 2000
12040 : // MODIFIED na
12041 : // RE-ENGINEERED Feb 2004, PGE
12042 :
12043 6336629 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
12044 :
12045 6336629 : this->UnmetEnergy = this->UnmetRate * SecInTimeStep;
12046 6336629 : this->LossEnergy = this->LossRate * SecInTimeStep;
12047 6336629 : this->FlueLossEnergy = this->FlueLossRate * SecInTimeStep;
12048 6336629 : this->UseEnergy = this->UseRate * SecInTimeStep;
12049 6336629 : this->TotalDemandEnergy = this->TotalDemandRate * SecInTimeStep;
12050 6336629 : this->SourceEnergy = this->SourceRate * SecInTimeStep;
12051 6336629 : this->HeaterEnergy = this->HeaterRate * SecInTimeStep;
12052 6336629 : this->HeaterEnergy1 = this->HeaterRate1 * SecInTimeStep;
12053 6336629 : this->HeaterEnergy2 = this->HeaterRate2 * SecInTimeStep;
12054 6336629 : this->FuelEnergy = this->FuelRate * SecInTimeStep;
12055 6336629 : this->VentEnergy = this->VentRate * SecInTimeStep;
12056 6336629 : this->OffCycParaFuelEnergy = this->OffCycParaFuelRate * SecInTimeStep;
12057 6336629 : this->OffCycParaEnergyToTank = this->OffCycParaRateToTank * SecInTimeStep;
12058 6336629 : this->OnCycParaFuelEnergy = this->OnCycParaFuelRate * SecInTimeStep;
12059 6336629 : this->OnCycParaEnergyToTank = this->OnCycParaRateToTank * SecInTimeStep;
12060 6336629 : this->NetHeatTransferEnergy = this->NetHeatTransferRate * SecInTimeStep;
12061 6336629 : this->VolumeConsumed = this->VolFlowRate * SecInTimeStep;
12062 6336629 : }
12063 :
12064 349 : void WaterThermalTankData::CalcStandardRatings(EnergyPlusData &state)
12065 : {
12066 :
12067 : // SUBROUTINE INFORMATION:
12068 : // AUTHOR Peter Graham Ellis
12069 : // DATE WRITTEN January 2005
12070 : // MODIFIED R. Raustad, July 2005 - added HPWH to ratings procedure
12071 : // RE-ENGINEERED na
12072 :
12073 : // PURPOSE OF THIS SUBROUTINE:
12074 : // Calculates the water heater standard ratings, such as Energy Factor and Recovery Efficiency. Results are written
12075 : // to the EIO file. Standard ratings are not calculated for storage-only tanks, i.e., MaxCapacity = 0, nor for Integrated Heat Pumps
12076 :
12077 : // METHODOLOGY EMPLOYED:
12078 : // Water heater inputs are set to the specified test conditions. For HPWHs, the heating capacity and COP are assumed
12079 : // to be the primary element in the water heater and are used during the rating procedure. CalcWaterThermalTankMixed
12080 : // is iteratively called in a self-contained, 24 hour simulation of the standard test procedure.
12081 :
12082 : // REFERENCES:
12083 : // Title 10, Code of Federal Regulations, Part 430- Energy Conservation Program for Consumer Products, Appendix E to
12084 : // Subpart B- Uniform Test Procedure for Measuring the Energy Consumption of Water Heaters, January 1, 2004.
12085 :
12086 349 : if (this->AlreadyRated) { // bail we already did this one
12087 154 : return;
12088 : }
12089 :
12090 : bool FirstTimeFlag; // used during HPWH rating procedure
12091 195 : bool bIsVSCoil = false;
12092 : Real64 RecoveryEfficiency;
12093 : Real64 EnergyFactor;
12094 195 : Real64 RatedDXCoilTotalCapacity = 0.0;
12095 195 : if (this->MaxCapacity > 0.0 || this->HeatPumpNum > 0) {
12096 : // Set test conditions
12097 190 : this->AmbientTemp = 19.7222; // 67.5 F
12098 190 : this->UseInletTemp = 14.4444; // 58 F
12099 190 : this->SetPointTemp = 57.2222; // 135 F
12100 190 : this->SetPointTemp2 = 57.2222; // 135 F
12101 190 : this->TankTemp = 57.2222; // Initialize tank temperature
12102 190 : if (this->Nodes > 0) {
12103 144 : for (auto &e : this->Node) {
12104 128 : e.Temp = 57.2222;
12105 : }
12106 : }
12107 :
12108 190 : Real64 TotalDrawMass = 0.243402 * Psychrometrics::RhoH2O(Constant::InitConvTemp); // 64.3 gal * rho
12109 190 : Real64 DrawMass = TotalDrawMass / 6.0; // 6 equal draws
12110 190 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
12111 190 : Real64 DrawMassFlowRate = DrawMass / SecInTimeStep;
12112 190 : Real64 FuelEnergy_loc = 0.0;
12113 190 : FirstTimeFlag = true;
12114 :
12115 190 : int TimeStepPerHour = int(1.0 / state.dataHVACGlobal->TimeStepSys);
12116 : // Simulate 24 hour test
12117 23566 : for (int Step = 1; Step <= TimeStepPerHour * 24; ++Step) {
12118 :
12119 23376 : if (Step == 1 || Step == (1 + TimeStepPerHour) || Step == (1 + TimeStepPerHour * 2) || Step == (1 + TimeStepPerHour * 3) ||
12120 22616 : Step == (1 + TimeStepPerHour * 4) || Step == (1 + TimeStepPerHour * 5)) { // Hour 1 | Hour 2 | Hour 3 | Hour 4 | Hour 5 | Hour 6
12121 :
12122 1140 : this->UseMassFlowRate = DrawMassFlowRate;
12123 : } else {
12124 22236 : this->UseMassFlowRate = 0.0;
12125 : }
12126 :
12127 23376 : this->SavedTankTemp = this->TankTemp;
12128 23376 : this->SavedMode = this->Mode;
12129 23376 : if (this->Nodes > 0) {
12130 33552 : for (auto &e : this->Node) {
12131 30336 : e.SavedTemp = e.Temp;
12132 : }
12133 3216 : this->SavedHeaterOn1 = this->HeaterOn1;
12134 3216 : this->SavedHeaterOn2 = this->HeaterOn2;
12135 : }
12136 :
12137 23376 : if (this->HeatPumpNum == 0) {
12138 :
12139 19584 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
12140 18960 : this->CalcWaterThermalTankMixed(state);
12141 :
12142 624 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12143 624 : this->CalcWaterThermalTankStratified(state);
12144 : }
12145 :
12146 : } else {
12147 :
12148 3792 : int HPNum = this->HeatPumpNum; // Convenience variable
12149 3792 : Real64 AmbientHumRat = 0.00717; // Humidity ratio at 67.5 F / 50% RH
12150 :
12151 : // set the heat pump air- and water-side mass flow rate
12152 3792 : Real64 MdotWater = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate * Psychrometrics::RhoH2O(this->TankTemp);
12153 3792 : Real64 mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
12154 :
12155 : // ?? why is HPWH condenser inlet node temp reset inside the for loop? shouldn't it chnage with the tank temp throughout these
12156 : // iterations?
12157 3792 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
12158 : // set the condenser inlet node mass flow rate and temperature
12159 2064 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).MassFlowRate = MdotWater;
12160 2064 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12161 : }
12162 :
12163 : // initialize temperatures for HPWH DX Coil heating capacity and COP curves
12164 3792 : state.dataHVACGlobal->HPWHInletDBTemp = this->AmbientTemp;
12165 7584 : state.dataHVACGlobal->HPWHInletWBTemp =
12166 3792 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, AmbientHumRat, state.dataEnvrn->OutBaroPress);
12167 :
12168 : // set up full air flow on DX coil inlet node
12169 3792 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode > 0) {
12170 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRate = mdotAir;
12171 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRateMaxAvail = mdotAir;
12172 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Temp = this->AmbientTemp;
12173 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).HumRat = AmbientHumRat;
12174 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Enthalpy =
12175 288 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
12176 : } else {
12177 3504 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode == 0) {
12178 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRate = mdotAir;
12179 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRateMaxAvail =
12180 : mdotAir;
12181 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Temp = this->AmbientTemp;
12182 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).HumRat = AmbientHumRat;
12183 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Enthalpy =
12184 1056 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
12185 : } else {
12186 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRate = mdotAir;
12187 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRateMaxAvail = mdotAir;
12188 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Temp = this->AmbientTemp;
12189 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).HumRat = AmbientHumRat;
12190 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Enthalpy =
12191 2448 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
12192 : }
12193 : }
12194 :
12195 3792 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = this->AmbientTemp;
12196 :
12197 3792 : if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
12198 6960 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") ||
12199 3168 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP)) {
12200 768 : bIsVSCoil = true;
12201 768 : std::string VSCoilName = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName;
12202 768 : int VSCoilNum = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
12203 768 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
12204 144 : VSCoilNum =
12205 144 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
12206 : VSCoilName =
12207 144 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilName;
12208 : }
12209 :
12210 768 : Real64 RhoWater = Psychrometrics::RhoH2O(this->TankTemp);
12211 768 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
12212 768 : this->SetVSHPWHFlowRates(
12213 : state,
12214 : HPWH,
12215 768 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12216 : 1.0,
12217 : RhoWater,
12218 : MdotWater,
12219 : true);
12220 : // simulate the HPWH coil/fan to find heating capacity
12221 768 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).fanPlace == HVAC::FanPlace::BlowThru) {
12222 : // simulate fan and DX coil twice
12223 576 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12224 :
12225 576 : VariableSpeedCoils::SimVariableSpeedCoils(
12226 : state,
12227 : VSCoilName,
12228 : VSCoilNum,
12229 : HVAC::FanOp::Cycling,
12230 : HVAC::CompressorOp::On,
12231 : 1.0,
12232 576 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12233 : 1.0,
12234 : 0.0,
12235 : 0.0,
12236 : 1.0);
12237 :
12238 576 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12239 :
12240 576 : VariableSpeedCoils::SimVariableSpeedCoils(
12241 : state,
12242 : VSCoilName,
12243 : VSCoilNum,
12244 : HVAC::FanOp::Cycling,
12245 : HVAC::CompressorOp::On,
12246 : 1.0,
12247 576 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12248 : 1.0,
12249 : 0.0,
12250 : 0.0,
12251 : 1.0);
12252 : } else {
12253 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
12254 192 : VariableSpeedCoils::SimVariableSpeedCoils(
12255 : state,
12256 : VSCoilName,
12257 : VSCoilNum,
12258 : HVAC::FanOp::Cycling,
12259 : HVAC::CompressorOp::On,
12260 : 1.0,
12261 192 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12262 : 1.0,
12263 : 0.0,
12264 : 0.0,
12265 : 1.0);
12266 :
12267 192 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12268 :
12269 192 : VariableSpeedCoils::SimVariableSpeedCoils(
12270 : state,
12271 : VSCoilName,
12272 : VSCoilNum,
12273 : HVAC::FanOp::Cycling,
12274 : HVAC::CompressorOp::On,
12275 : 1.0,
12276 192 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12277 : 1.0,
12278 : 0.0,
12279 : 0.0,
12280 : 1.0);
12281 :
12282 192 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12283 : }
12284 :
12285 768 : this->MaxCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
12286 768 : this->MinCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
12287 768 : this->Efficiency = state.dataVariableSpeedCoils->VSHPWHHeatingCOP;
12288 768 : } else {
12289 3024 : bIsVSCoil = false;
12290 : // simulate the HPWH coil/fan to find heating capacity
12291 3024 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).fanPlace == HVAC::FanPlace::BlowThru) {
12292 720 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
12293 : // reset water inlet temp. If already sized, no harm.
12294 7 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12295 :
12296 21 : DXCoils::SimDXCoil(state,
12297 7 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12298 : HVAC::CompressorOp::On,
12299 : true,
12300 7 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12301 : HVAC::FanOp::Cycling,
12302 14 : 1.0);
12303 7 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12304 : }
12305 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
12306 : // PLR=1 here.
12307 720 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12308 :
12309 2160 : DXCoils::SimDXCoil(state,
12310 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12311 : HVAC::CompressorOp::On,
12312 : true,
12313 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12314 : HVAC::FanOp::Cycling,
12315 1440 : 1.0);
12316 :
12317 720 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12318 :
12319 2160 : DXCoils::SimDXCoil(state,
12320 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12321 : HVAC::CompressorOp::On,
12322 : true,
12323 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12324 : HVAC::FanOp::Cycling,
12325 1440 : 1.0);
12326 : } else {
12327 2304 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
12328 : // reset water inlet temp. If already sized, no harm.
12329 27 : DXCoils::SimDXCoil(state,
12330 9 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12331 : HVAC::CompressorOp::On,
12332 : true,
12333 9 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12334 : HVAC::FanOp::Cycling,
12335 18 : 1.0);
12336 9 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12337 : }
12338 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
12339 : // PLR=1 here.
12340 6912 : DXCoils::SimDXCoil(state,
12341 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12342 : HVAC::CompressorOp::On,
12343 : true,
12344 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12345 : HVAC::FanOp::Cycling,
12346 4608 : 1.0);
12347 :
12348 2304 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12349 :
12350 6912 : DXCoils::SimDXCoil(state,
12351 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12352 : HVAC::CompressorOp::On,
12353 : true,
12354 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12355 : HVAC::FanOp::Cycling,
12356 4608 : 1.0);
12357 :
12358 2304 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12359 : }
12360 :
12361 3024 : this->MaxCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12362 3024 : this->MinCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12363 3024 : this->Efficiency = state.dataDXCoils->HPWHHeatingCOP;
12364 : }
12365 :
12366 3792 : if (FirstTimeFlag) {
12367 23 : RatedDXCoilTotalCapacity = state.dataHVACGlobal->DXCoilTotalCapacity;
12368 23 : FirstTimeFlag = false;
12369 : }
12370 :
12371 : // Switch the HPWH info with the tank info and call CalcWaterThermalTankMixed to get Standard Rating
12372 : // (backup element is assumed to be disabled during the rating procedure)
12373 3792 : this->SourceMassFlowRate = 0.0;
12374 3792 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OnCycParaLoad;
12375 3792 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OffCycParaLoad;
12376 3792 : this->OffCycParaFracToTank = 0.0;
12377 3792 : this->OnCycParaFracToTank = 0.0;
12378 3792 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilPLFFPLR;
12379 :
12380 3792 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
12381 1200 : if (this->Efficiency > 0.0) {
12382 1056 : this->CalcWaterThermalTankMixed(state);
12383 : }
12384 :
12385 2592 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12386 2592 : if (this->Efficiency > 0.0) {
12387 2592 : this->CalcWaterThermalTankStratified(state);
12388 : }
12389 : }
12390 :
12391 : // reset the water heater data to original values
12392 3792 : this->MaxCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12393 3792 : this->MinCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12394 3792 : this->Efficiency = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementEfficiency;
12395 3792 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaLoad;
12396 3792 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaLoad;
12397 3792 : this->OnCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaFracToTank;
12398 3792 : this->OffCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaFracToTank;
12399 3792 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHPLFCurve;
12400 : }
12401 :
12402 23376 : FuelEnergy_loc += (this->FuelRate + this->OffCycParaFuelRate + this->OnCycParaFuelRate) * SecInTimeStep;
12403 :
12404 : } // Step
12405 :
12406 190 : if (this->FirstRecoveryDone && this->FirstRecoveryFuel > 0.0) {
12407 : // Calculate Recovery Efficiency based on energy used to recover from the first draw
12408 : // FirstRecoveryFuel is recorded inside the CalcWaterThermalTank subroutine
12409 172 : RecoveryEfficiency = DrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / this->FirstRecoveryFuel;
12410 :
12411 : // Calculate Energy Factor based on total energy (including parasitics) used over entire test
12412 172 : EnergyFactor = TotalDrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / FuelEnergy_loc;
12413 :
12414 : } else {
12415 18 : RecoveryEfficiency = 0.0;
12416 18 : EnergyFactor = 0.0;
12417 : // If this a regular tank, or an HPWH that's not an Integrated one
12418 18 : if ((this->HeatPumpNum == 0) || !state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).bIsIHP) {
12419 34 : ShowWarningError(
12420 : state,
12421 34 : format("Water heater = {}: Recovery Efficiency and Energy Factor could not be calculated during the test for standard ratings",
12422 17 : this->Name));
12423 51 : ShowContinueError(state, "Setpoint was never recovered and/or heater never turned on");
12424 : }
12425 : }
12426 :
12427 190 : } else {
12428 :
12429 : // Storage-only tank
12430 5 : RecoveryEfficiency = 0.0;
12431 5 : EnergyFactor = 0.0;
12432 :
12433 : } // WaterThermalTank(WaterThermalTankNum)%MaxCapacity > 0.0
12434 :
12435 : // create predefined report
12436 : // Store values for the input verification and summary report
12437 195 : std::string equipName;
12438 195 : if (this->HeatPumpNum == 0) {
12439 172 : equipName = this->Name;
12440 172 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHType, equipName, this->Type);
12441 172 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12442 172 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, this->MaxCapacity);
12443 172 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12444 172 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12445 172 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12446 : } else {
12447 23 : equipName = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name;
12448 46 : OutputReportPredefined::PreDefTableEntry(
12449 46 : state, state.dataOutRptPredefined->pdchSWHType, equipName, state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type);
12450 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12451 23 : if (bIsVSCoil) {
12452 21 : OutputReportPredefined::PreDefTableEntry(
12453 14 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataVariableSpeedCoils->VSHPWHHeatingCapacity);
12454 : } else {
12455 48 : OutputReportPredefined::PreDefTableEntry(
12456 32 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataDXCoils->HPWHHeatingCapacity);
12457 : }
12458 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12459 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12460 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12461 : }
12462 :
12463 : // Write test results
12464 195 : if (this->HeatPumpNum == 0) {
12465 : Real64 MaxCapacity_loc;
12466 172 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12467 5 : if (this->StratifiedControlMode == PriorityControlMode::MasterSlave) {
12468 5 : MaxCapacity_loc = max(this->MaxCapacity, this->MaxCapacity2);
12469 : } else { // PrioritySimultaneous
12470 0 : MaxCapacity_loc = this->MaxCapacity + this->MaxCapacity2;
12471 : }
12472 : } else { // WaterHeaterMixed
12473 167 : MaxCapacity_loc = this->MaxCapacity;
12474 : }
12475 :
12476 : static constexpr std::string_view Format_720("Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T}\n");
12477 172 : print(state.files.eio, Format_720, this->Type, this->Name, this->Volume, MaxCapacity_loc, RecoveryEfficiency, EnergyFactor);
12478 : } else {
12479 : static constexpr std::string_view Format_721("Heat Pump Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T},{:.0T}\n");
12480 23 : print(state.files.eio,
12481 : Format_721,
12482 23 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type,
12483 23 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
12484 23 : this->Volume,
12485 23 : state.dataDXCoils->HPWHHeatingCapacity,
12486 : RecoveryEfficiency,
12487 : EnergyFactor,
12488 : RatedDXCoilTotalCapacity);
12489 : }
12490 :
12491 195 : this->AlreadyRated = true;
12492 195 : }
12493 :
12494 10 : void WaterThermalTankData::ReportCWTankInits(EnergyPlusData &state)
12495 : {
12496 :
12497 : // SUBROUTINE INFORMATION:
12498 : // AUTHOR B. Griffith
12499 : // DATE WRITTEN March 2009
12500 : // MODIFIED na
12501 : // RE-ENGINEERED na
12502 :
12503 : // PURPOSE OF THIS SUBROUTINE:
12504 : // send chilled water tank info to EIO
12505 :
12506 10 : if (this->myOneTimeInitFlag) {
12507 0 : this->setupOutputVars(state);
12508 0 : this->myOneTimeInitFlag = false;
12509 : }
12510 :
12511 10 : if (this->AlreadyReported) { // bail we already did this one
12512 4 : return;
12513 : }
12514 :
12515 : static constexpr std::string_view Format_728("Chilled Water Tank Information,{},{},{:.4T},{:.4T},{:.4T}\n");
12516 6 : print(state.files.eio, Format_728, this->Type, this->Name, this->Volume, this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
12517 :
12518 6 : this->AlreadyReported = true;
12519 : }
12520 :
12521 801434 : Real64 WaterThermalTankData::FindStratifiedTankSensedTemp(EnergyPlusData &state, bool UseAverage)
12522 : {
12523 :
12524 : // FUNCTION INFORMATION:
12525 : // AUTHOR B. Griffith
12526 : // DATE WRITTEN March 2012
12527 : // MODIFIED na
12528 : // RE-ENGINEERED Noel Merket, April 2015
12529 :
12530 : // PURPOSE OF THIS FUNCTION:
12531 : // find tank temperature depending on how sensed
12532 :
12533 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
12534 801434 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
12535 : Real64 ControlSensor1Temp;
12536 : Real64 ControlSensor2Temp;
12537 :
12538 801434 : if (UseAverage) {
12539 199890 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).TempAvg;
12540 199890 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).TempAvg;
12541 : } else {
12542 601544 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).Temp;
12543 601544 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).Temp;
12544 : }
12545 :
12546 801434 : Real64 SensedTemp = ControlSensor1Temp * HPWH.ControlSensor1Weight + ControlSensor2Temp * HPWH.ControlSensor2Weight;
12547 :
12548 801434 : return SensedTemp;
12549 : }
12550 :
12551 6551906 : Real64 WaterThermalTankData::getDeadBandTemp()
12552 : {
12553 6551906 : if (this->IsChilledWaterTank) {
12554 389660 : return (this->SetPointTemp + this->DeadBandDeltaTemp);
12555 : } else {
12556 6162246 : return (this->SetPointTemp - this->DeadBandDeltaTemp);
12557 : }
12558 : }
12559 5928161 : void WaterThermalTankData::oneTimeInit(EnergyPlusData &state)
12560 : {
12561 5928161 : if (this->myOneTimeInitFlag) {
12562 178 : this->setupOutputVars(state);
12563 178 : this->myOneTimeInitFlag = false;
12564 : }
12565 5928161 : }
12566 :
12567 30 : void WaterThermalTankData::setBackupElementCapacity(EnergyPlusData &state)
12568 : {
12569 : // Fix for #9001: The BackupElementCapacity was not being reset from the autosize value (-99999) which resulted in
12570 : // negative electric consumption. Using a test for any negative numbers here instead of just -99999 for safety.
12571 : // Only reset the backup element capacity if a problem has been occured.
12572 30 : if (this->HeatPumpNum > 0) {
12573 20 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
12574 20 : return;
12575 : }
12576 0 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity < 0.0) {
12577 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity = this->MaxCapacity;
12578 : }
12579 10 : } else if (this->DesuperheaterNum > 0) {
12580 0 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity < 0.0) {
12581 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity = this->MaxCapacity;
12582 : }
12583 : }
12584 : }
12585 :
12586 16 : bool GetHeatPumpWaterHeaterNodeNumber(EnergyPlusData &state, int const NodeNumber)
12587 : {
12588 : // PURPOSE OF THIS FUNCTION:
12589 : // Check if a node is used by a heat pump water heater
12590 : // and can be excluded from an airflow network.
12591 :
12592 : // Return value
12593 : bool HeatPumpWaterHeaterNodeException;
12594 :
12595 : int HeatPumpWaterHeaterIndex;
12596 :
12597 16 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
12598 0 : GetWaterThermalTankInput(state);
12599 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
12600 : }
12601 :
12602 16 : HeatPumpWaterHeaterNodeException = false;
12603 :
12604 26 : for (HeatPumpWaterHeaterIndex = 1; HeatPumpWaterHeaterIndex <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HeatPumpWaterHeaterIndex) {
12605 :
12606 : // Get heat pump water heater data
12607 16 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpWaterHeaterIndex);
12608 :
12609 : // "Zone and outdoor air" configuration is expected break the conservation of mass
12610 16 : if (HPWH.InletAirConfiguration != WTTAmbientTemp::ZoneAndOA) {
12611 :
12612 : // Air outlet node
12613 16 : if (NodeNumber == HPWH.HeatPumpAirOutletNode) {
12614 2 : HeatPumpWaterHeaterNodeException = true;
12615 2 : break;
12616 : }
12617 :
12618 : // Air inlet node
12619 14 : if (NodeNumber == HPWH.HeatPumpAirInletNode) {
12620 2 : HeatPumpWaterHeaterNodeException = true;
12621 2 : break;
12622 : }
12623 :
12624 : // Get fan inlet node index
12625 12 : int FanInletNodeIndex = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
12626 :
12627 : // Fan inlet node
12628 12 : if (NodeNumber == FanInletNodeIndex) {
12629 2 : HeatPumpWaterHeaterNodeException = true;
12630 2 : break;
12631 : }
12632 :
12633 : // Fan outlet node
12634 10 : if (NodeNumber == HPWH.FanOutletNode) {
12635 0 : HeatPumpWaterHeaterNodeException = true;
12636 0 : break;
12637 : }
12638 :
12639 : // Outside air node
12640 10 : if (NodeNumber == HPWH.OutsideAirNode) {
12641 0 : HeatPumpWaterHeaterNodeException = true;
12642 0 : break;
12643 : }
12644 :
12645 : // Exhaust air node
12646 10 : if (NodeNumber == HPWH.ExhaustAirNode) {
12647 0 : HeatPumpWaterHeaterNodeException = true;
12648 0 : break;
12649 : }
12650 : }
12651 : }
12652 :
12653 16 : return HeatPumpWaterHeaterNodeException;
12654 : }
12655 :
12656 0 : int getHeatPumpWaterHeaterIndex(EnergyPlusData &state, std::string_view CompName)
12657 : {
12658 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
12659 0 : GetWaterThermalTankInput(state);
12660 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
12661 : }
12662 :
12663 0 : for (int HPNum = 1; HPNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPNum) {
12664 0 : if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name, CompName)) {
12665 0 : return HPNum;
12666 : }
12667 : }
12668 :
12669 0 : return 0;
12670 : }
12671 :
12672 : } // namespace EnergyPlus::WaterThermalTanks
|