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 0 : PlantComponent *WaterThermalTankData::factory(EnergyPlusData &state, std::string const &objectName)
136 : {
137 : // Process the input data
138 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
139 0 : GetWaterThermalTankInput(state);
140 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
141 : }
142 :
143 : // Now look for this object in the list
144 0 : for (auto &tank : state.dataWaterThermalTanks->WaterThermalTank) {
145 0 : if (tank.Name == objectName) {
146 0 : 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 1 : void WaterThermalTankData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
156 : {
157 1 : this->initialize(state, true);
158 1 : this->MinePlantStructForInfo(state);
159 1 : if (calledFromLocation.loopNum > 0) {
160 1 : if ((this->SrcSidePlantLoc.loopNum == calledFromLocation.loopNum) || (this->UseSidePlantLoc.loopNum == calledFromLocation.loopNum)) {
161 1 : this->SizeTankForDemandSide(state);
162 1 : this->SizeDemandSidePlantConnections(state);
163 1 : this->SizeSupplySidePlantConnections(state, calledFromLocation.loopNum);
164 1 : this->SizeTankForSupplySide(state);
165 : } else {
166 0 : 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 1 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
176 1 : if (!this->IsChilledWaterTank) {
177 1 : this->CalcStandardRatings(state);
178 : } else {
179 0 : this->ReportCWTankInits(state);
180 : }
181 : }
182 : }
183 :
184 0 : void WaterThermalTankData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
185 : [[maybe_unused]] const PlantLocation &calledFromLocation,
186 : Real64 &MaxLoad,
187 : Real64 &MinLoad,
188 : Real64 &OptLoad)
189 : {
190 0 : MinLoad = 0.0;
191 0 : MaxLoad = this->MaxCapacity;
192 0 : OptLoad = this->MaxCapacity;
193 0 : }
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 0 : int getHPTankIDX(EnergyPlusData &state, std::string_view CompName, int &CompIndex)
235 : {
236 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
237 0 : GetWaterThermalTankInput(state);
238 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
239 : }
240 :
241 : int CompNum;
242 :
243 0 : 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 0 : CompNum = CompIndex;
251 0 : 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 0 : if (state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName) {
259 0 : 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 0 : state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName = false;
267 : }
268 : }
269 :
270 0 : return CompNum;
271 : }
272 :
273 0 : 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 0 : this->callerLoopNum = calledFromLocation.loopNum;
284 :
285 0 : this->oneTimeInit(state);
286 :
287 0 : if (this->MyOneTimeFlagWH) {
288 0 : this->MyOneTimeFlagWH = false;
289 : } else {
290 0 : if (this->MyTwoTimeFlagWH) {
291 0 : this->MinePlantStructForInfo(state); // call it again to get control types filled out
292 0 : this->MyTwoTimeFlagWH = false;
293 : }
294 : }
295 0 : this->UseSideLoadRequested = std::abs(CurLoad);
296 0 : if (this->UseSidePlantLoc.loopNum > 0 && this->UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
297 0 : !state.dataGlobal->KickOffSimulation) {
298 0 : this->UseCurrentFlowLock = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).LoopSide(this->UseSidePlantLoc.loopSideNum).FlowLock;
299 : } else {
300 0 : this->UseCurrentFlowLock = DataPlant::FlowLock::Locked;
301 : }
302 0 : this->initialize(state, FirstHVACIteration);
303 : // Plant connected water heaters may have a desuperheater heating coil attached
304 0 : if (this->DesuperheaterNum == 0) {
305 0 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) ||
306 0 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed)) {
307 0 : this->CalcWaterThermalTankMixed(state);
308 0 : } else if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
309 0 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
310 0 : this->CalcWaterThermalTankStratified(state);
311 : }
312 0 : } else if (this->DesuperheaterNum > 0) {
313 0 : this->CalcDesuperheaterWaterHeater(state, FirstHVACIteration);
314 : }
315 0 : this->UpdateWaterThermalTank(state);
316 0 : this->ReportWaterThermalTank(state);
317 : // reset the caller loop num to mimic what was happening in PlantLoopEquip
318 0 : this->callerLoopNum = 0;
319 0 : }
320 :
321 0 : PlantComponent *HeatPumpWaterHeaterData::factory(EnergyPlusData &state, std::string const &objectName)
322 : {
323 : // Process the input data
324 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
325 0 : GetWaterThermalTankInput(state);
326 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
327 : }
328 :
329 : // Now look for this object in the list
330 0 : for (auto &HPWH : state.dataWaterThermalTanks->HPWaterHeater) {
331 0 : if (HPWH.Name == objectName) {
332 0 : 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 0 : void HeatPumpWaterHeaterData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
342 : {
343 0 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
344 0 : Tank.onInitLoopEquip(state, calledFromLocation);
345 0 : }
346 :
347 0 : void HeatPumpWaterHeaterData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
348 : [[maybe_unused]] const PlantLocation &calledFromLocation,
349 : Real64 &MaxLoad,
350 : Real64 &MinLoad,
351 : Real64 &OptLoad)
352 : {
353 0 : MinLoad = 0.0;
354 0 : MaxLoad = this->Capacity;
355 0 : OptLoad = this->Capacity;
356 0 : }
357 :
358 1 : 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 1 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
368 :
369 : // set caller loop num to mimic what plantloopequip was doing
370 1 : Tank.callerLoopNum = calledFromLocation.loopNum;
371 :
372 1 : if (this->myOneTimeInitFlag) {
373 1 : if (Tank.myOneTimeInitFlag) {
374 1 : Tank.setupOutputVars(state);
375 1 : Tank.myOneTimeInitFlag = false;
376 : }
377 1 : this->myOneTimeInitFlag = false;
378 : }
379 :
380 1 : if (this->MyOneTimeFlagHP) {
381 1 : this->MyOneTimeFlagHP = false;
382 : } else {
383 0 : if (this->MyTwoTimeFlagHP) {
384 0 : Tank.MinePlantStructForInfo(state); // call it again to get control types filled out
385 0 : this->MyTwoTimeFlagHP = false;
386 : }
387 : }
388 1 : Tank.UseSideLoadRequested = std::abs(CurLoad);
389 1 : if (Tank.UseSidePlantLoc.loopNum > 0 && Tank.UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
390 0 : !state.dataGlobal->KickOffSimulation) {
391 0 : Tank.UseCurrentFlowLock = state.dataPlnt->PlantLoop(Tank.UseSidePlantLoc.loopNum).LoopSide(Tank.UseSidePlantLoc.loopSideNum).FlowLock;
392 : } else {
393 1 : Tank.UseCurrentFlowLock = DataPlant::FlowLock::Locked;
394 : }
395 :
396 1 : Tank.initialize(state, FirstHVACIteration);
397 :
398 1 : int InletNodeSav = this->HeatPumpAirInletNode;
399 1 : int OutletNodeSav = this->HeatPumpAirOutletNode;
400 1 : int DXINletNodeSav = this->DXCoilAirInletNode;
401 1 : int IHPFanIndexSav = this->FanNum;
402 1 : std::string IHPFanNameSave = this->FanName;
403 1 : HVAC::FanPlace IHPFanplaceSav = this->fanPlace;
404 :
405 1 : if (this->bIsIHP) // pass the tank indexes to the IHP object
406 : {
407 0 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankType = this->HPWHType;
408 0 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankName = this->Name;
409 0 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankID = this->WaterHeaterTankNum;
410 0 : IntegratedHeatPump::IHPOperationMode IHPMode = IntegratedHeatPump::GetCurWorkMode(state, this->DXCoilNum);
411 :
412 0 : if ((IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) ||
413 0 : (IntegratedHeatPump::IHPOperationMode::SpaceClgDedicatedWaterHtg == IHPMode) ||
414 0 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) ||
415 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOn == IHPMode)) { // default is to specify the air nodes for SCWH mode
416 0 : bool bDWHCoilReading = false;
417 0 : this->HeatPumpAirInletNode =
418 0 : VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state,
419 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
420 0 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
421 : bDWHCoilReading);
422 0 : this->HeatPumpAirOutletNode =
423 0 : VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state,
424 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
425 0 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
426 : bDWHCoilReading);
427 0 : this->DXCoilAirInletNode = this->HeatPumpAirInletNode;
428 0 : } else // default is to input outdoor fan to the HPWH
429 : {
430 0 : this->FanNum = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanID;
431 0 : this->FanName = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanName;
432 0 : this->fanPlace = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).fanPlace;
433 : }
434 : }
435 :
436 1 : Tank.CalcHeatPumpWaterHeater(state, FirstHVACIteration);
437 1 : Tank.UpdateWaterThermalTank(state);
438 1 : Tank.ReportWaterThermalTank(state);
439 :
440 1 : this->HeatPumpAirInletNode = InletNodeSav;
441 1 : this->HeatPumpAirOutletNode = OutletNodeSav;
442 1 : this->DXCoilAirInletNode = DXINletNodeSav;
443 1 : this->FanNum = IHPFanIndexSav;
444 1 : this->FanName = IHPFanNameSave;
445 1 : this->fanPlace = IHPFanplaceSav;
446 : // reset caller loop num to 0 to mimic what plantloopequip was doing
447 1 : Tank.callerLoopNum = 0;
448 1 : }
449 0 : void HeatPumpWaterHeaterData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
450 : {
451 0 : }
452 :
453 0 : 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 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
474 0 : GetWaterThermalTankInput(state);
475 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
476 : }
477 :
478 0 : 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 0 : if (Tank.StandAlone) {
482 0 : bool localRunFlag = true;
483 0 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
484 0 : 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 0 : } 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 0 : auto &HPWaterHtr = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum);
494 :
495 0 : if (HPWaterHtr.StandAlone &&
496 0 : (HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::Schedule)) {
497 0 : bool LocalRunFlag = true;
498 0 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
499 0 : 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 0 : } else if (Tank.DesuperheaterNum > 0) {
505 0 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).StandAlone) {
506 0 : bool localRunFlag = true;
507 0 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
508 0 : Tank.simulate(state, A, FirstHVACIteration, MyLoad, localRunFlag);
509 : }
510 : }
511 0 : }
512 :
513 1 : 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 1 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
534 1 : GetWaterThermalTankInput(state);
535 1 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
536 : }
537 :
538 : // Find the correct Heat Pump Water Heater
539 : int HeatPumpNum;
540 1 : if (CompIndex == 0) {
541 0 : HeatPumpNum = Util::FindItemInList(CompName, state.dataWaterThermalTanks->HPWaterHeater);
542 0 : if (HeatPumpNum == 0) {
543 0 : ShowFatalError(state, format("SimHeatPumpWaterHeater: Unit not found={}", CompName));
544 : }
545 0 : CompIndex = HeatPumpNum;
546 : } else {
547 1 : HeatPumpNum = CompIndex;
548 1 : 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 1 : if (state.dataGlobal->DoingSizing) return;
563 :
564 : // For HPWHs, StandAlone means not connected to a plant loop (use nodes are not used, source nodes are connected to a HPWH)
565 1 : if (state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).StandAlone) {
566 1 : bool LocalRunFlag = true;
567 : Real64 MyLoad;
568 :
569 1 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
570 1 : state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).simulate(state, A, FirstHVACIteration, MyLoad, LocalRunFlag);
571 :
572 1 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
573 1 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
574 : } else {
575 : // HPWH is plant connected and will get simulated when called from plant SimWaterThermalTank, but need to update loads met here
576 0 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
577 0 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
578 : }
579 : }
580 :
581 249958 : void CalcWaterThermalTankZoneGains(EnergyPlusData &state)
582 : {
583 :
584 : // SUBROUTINE INFORMATION:
585 : // AUTHOR Peter Graham Ellis
586 : // DATE WRITTEN March 2005
587 : // MODIFIED B. Griffith November 2011, new internal gains structure
588 : // RE-ENGINEERED na
589 :
590 : // PURPOSE OF THIS SUBROUTINE:
591 : // Calculates the zone internal gains due to water heater skin losses during sizing.
592 : // initializes gains to zone at begin environment.
593 :
594 : // METHODOLOGY EMPLOYED:
595 : // Sums the tank losses from all of the water heaters in the zone to add as a gain to the zone.
596 : // Now used to determine tank losses during sizing. Internal gains are summed in a centralized way now
597 :
598 249958 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) {
599 :
600 249958 : if (!state.dataGlobal->DoingSizing) {
601 184216 : return;
602 : } else {
603 65742 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
604 40 : GetWaterThermalTankInput(state);
605 40 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
606 : }
607 65742 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) return;
608 : }
609 : }
610 :
611 0 : if (state.dataGlobal->BeginEnvrnFlag && state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag) {
612 0 : for (auto &e : state.dataWaterThermalTanks->WaterThermalTank) {
613 0 : e.AmbientZoneGain = 0.0;
614 0 : e.FuelEnergy = 0.0;
615 0 : e.OffCycParaFuelEnergy = 0.0;
616 0 : e.OnCycParaFuelEnergy = 0.0;
617 : }
618 0 : state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = false;
619 : }
620 :
621 0 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = true;
622 :
623 0 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
624 0 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
625 0 : if (Tank.AmbientTempZone == 0) continue;
626 0 : auto const &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(Tank.AmbientTempZone);
627 0 : if (state.dataGlobal->DoingSizing) {
628 : // Initialize tank temperature to setpoint
629 : // (use HPWH or Desuperheater heating coil set point if applicable)
630 0 : Sched::Schedule *sched = nullptr;
631 0 : if (Tank.HeatPumpNum > 0) {
632 0 : sched = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum).setptTempSched;
633 0 : } else if (Tank.DesuperheaterNum > 0) {
634 0 : sched = state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).setptTempSched;
635 : } else {
636 0 : sched = Tank.setptTempSched;
637 : }
638 :
639 0 : Real64 TankTemp = (sched != nullptr) ? sched->getCurrentVal() : 20.0;
640 :
641 0 : Real64 QLossToZone = 0.0;
642 0 : switch (Tank.WaterThermalTankType) {
643 0 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
644 0 : QLossToZone = max(Tank.OnCycLossCoeff * Tank.OnCycLossFracToZone, Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone) *
645 0 : (TankTemp - thisZoneHB.MAT);
646 0 : break;
647 : }
648 0 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
649 0 : QLossToZone = max(Tank.Node(1).OnCycLossCoeff * Tank.SkinLossFracToZone, Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone) *
650 0 : (TankTemp - thisZoneHB.MAT);
651 0 : break;
652 : }
653 0 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
654 0 : QLossToZone = Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone * (TankTemp - thisZoneHB.MAT);
655 0 : break;
656 : }
657 0 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
658 0 : QLossToZone = Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone * (TankTemp - thisZoneHB.MAT);
659 0 : break;
660 : }
661 0 : default:
662 0 : break;
663 : }
664 0 : Tank.AmbientZoneGain = QLossToZone;
665 : }
666 : }
667 : }
668 :
669 5 : bool getDesuperHtrInput(EnergyPlusData &state)
670 : {
671 5 : bool ErrorsFound = false;
672 : static constexpr std::string_view routineName = "getDesuperHtrInput";
673 : // Make local copies of IPShortCut because other getinputs might overwrite the ones in state <-- need to fix this idiom
674 5 : std::string cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
675 5 : Array1D<std::string> cAlphaArgs = state.dataIPShortCut->cAlphaArgs;
676 5 : Array1D<Real64> rNumericArgs = state.dataIPShortCut->rNumericArgs;
677 5 : Array1D<bool> lNumericFieldBlanks = state.dataIPShortCut->lNumericFieldBlanks;
678 5 : Array1D<bool> lAlphaFieldBlanks = state.dataIPShortCut->lAlphaFieldBlanks;
679 5 : Array1D<std::string> cAlphaFieldNames = state.dataIPShortCut->cAlphaFieldNames;
680 5 : Array1D<std::string> cNumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
681 :
682 5 : cCurrentModuleObject = cCoilDesuperheater;
683 11 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
684 : int NumAlphas;
685 : int NumNums;
686 : int IOStat;
687 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
688 : cCurrentModuleObject,
689 : DesuperheaterNum,
690 : cAlphaArgs,
691 : NumAlphas,
692 : rNumericArgs,
693 : NumNums,
694 : IOStat,
695 : lNumericFieldBlanks,
696 : lAlphaFieldBlanks,
697 : cAlphaFieldNames,
698 : cNumericFieldNames);
699 :
700 6 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
701 :
702 6 : Util::IsNameEmpty(state, cAlphaArgs(1), cCurrentModuleObject, ErrorsFound);
703 :
704 : // ErrorsFound will be set to True if problem was found, left untouched otherwise
705 6 : GlobalNames::VerifyUniqueCoilName(state, cCurrentModuleObject, cAlphaArgs(1), ErrorsFound, cCurrentModuleObject + " Name");
706 :
707 6 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
708 :
709 6 : DesupHtr.Name = cAlphaArgs(1);
710 6 : DesupHtr.Type = cCurrentModuleObject;
711 :
712 : // convert availability schedule name to pointer
713 6 : if (lAlphaFieldBlanks(2)) {
714 0 : DesupHtr.availSched = Sched::GetScheduleAlwaysOn(state);
715 6 : } else if ((DesupHtr.availSched = Sched::GetSchedule(state, cAlphaArgs(2))) == nullptr) {
716 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
717 0 : ErrorsFound = true;
718 : }
719 :
720 : // convert schedule name to pointer
721 6 : if (lAlphaFieldBlanks(3)) {
722 6 : } else if ((DesupHtr.setptTempSched = Sched::GetSchedule(state, cAlphaArgs(3))) == nullptr) {
723 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(3), cAlphaArgs(3));
724 0 : ErrorsFound = true;
725 : }
726 :
727 6 : DesupHtr.DeadBandTempDiff = rNumericArgs(1);
728 6 : if (DesupHtr.DeadBandTempDiff <= 0.0 || DesupHtr.DeadBandTempDiff > 20.0) {
729 0 : ShowSevereError(state,
730 0 : format("{} = {}: {} must be > 0 and <= 20. {} = {:.1T}",
731 : cCurrentModuleObject,
732 0 : DesupHtr.Name,
733 : cNumericFieldNames(1),
734 : cNumericFieldNames(1),
735 : rNumericArgs(1)));
736 0 : ErrorsFound = true;
737 : }
738 :
739 : // Error limits on heat reclaim efficiency applied after source type identified
740 :
741 6 : DesupHtr.RatedInletWaterTemp = rNumericArgs(3);
742 6 : DesupHtr.RatedOutdoorAirTemp = rNumericArgs(4);
743 6 : DesupHtr.MaxInletWaterTemp = rNumericArgs(5);
744 :
745 6 : if (!lAlphaFieldBlanks(4)) {
746 0 : DesupHtr.HEffFTemp = Curve::GetCurveIndex(state, cAlphaArgs(4));
747 0 : if (DesupHtr.HEffFTemp == 0) {
748 0 : ShowSevereError(state,
749 0 : format("{} = {}: {} not found = {}", cCurrentModuleObject, DesupHtr.Name, cAlphaFieldNames(4), cAlphaArgs(4)));
750 0 : ErrorsFound = true;
751 : } else {
752 0 : ErrorsFound |= Curve::CheckCurveDims(state,
753 : DesupHtr.HEffFTemp, // Curve index
754 : {2}, // Valid dimensions
755 : routineName, // Routine name
756 : cCurrentModuleObject, // Object Type
757 : DesupHtr.Name, // Object Name
758 0 : cAlphaFieldNames(4)); // Field Name
759 0 : if (!ErrorsFound) {
760 0 : if (DesupHtr.HEffFTemp > 0) {
761 0 : Real64 HEffFTemp = min(
762 0 : 1.0, max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, DesupHtr.RatedInletWaterTemp, DesupHtr.RatedOutdoorAirTemp)));
763 0 : if (std::abs(HEffFTemp - 1.0) > 0.05) {
764 0 : ShowWarningError(state, format("{}, \"{}\":", cCurrentModuleObject, DesupHtr.Name));
765 0 : ShowContinueError(state, format("The {} should be normalized ", cAlphaFieldNames(4)));
766 0 : ShowContinueError(state, format(" to 1.0 at the rating point. Curve output at the rating point = {:.3T}", HEffFTemp));
767 0 : ShowContinueError(state, " The simulation continues using the user-specified curve.");
768 : }
769 : }
770 : }
771 : }
772 : }
773 :
774 6 : DesupHtr.WaterInletNode = NodeInputManager::GetOnlySingleNode(state,
775 6 : cAlphaArgs(5),
776 : ErrorsFound,
777 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
778 6 : cAlphaArgs(1),
779 : DataLoopNode::NodeFluidType::Water,
780 : DataLoopNode::ConnectionType::Inlet,
781 : NodeInputManager::CompFluidStream::Primary,
782 : DataLoopNode::ObjectIsParent);
783 :
784 6 : DesupHtr.WaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
785 6 : cAlphaArgs(6),
786 : ErrorsFound,
787 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
788 6 : cAlphaArgs(1),
789 : DataLoopNode::NodeFluidType::Water,
790 : DataLoopNode::ConnectionType::Outlet,
791 : NodeInputManager::CompFluidStream::Primary,
792 : DataLoopNode::ObjectIsParent);
793 :
794 6 : DesupHtr.InletNodeName1 = cAlphaArgs(5);
795 6 : DesupHtr.OutletNodeName1 = cAlphaArgs(6);
796 :
797 6 : DesupHtr.TankType = cAlphaArgs(7);
798 :
799 6 : if (!Util::SameString(DesupHtr.TankType, cMixedWHModuleObj) && !Util::SameString(DesupHtr.TankType, cStratifiedWHModuleObj)) {
800 :
801 0 : ShowSevereError(state, format("{} = {}:", cCurrentModuleObject, state.dataWaterThermalTanks->HPWaterHeater(DesuperheaterNum).Name));
802 0 : ShowContinueError(state, format("Desuperheater can only be used with {} or {}.", cMixedWHModuleObj, cStratifiedWHModuleObj));
803 0 : ErrorsFound = true;
804 : }
805 :
806 6 : DesupHtr.TankName = cAlphaArgs(8);
807 :
808 : // Set up comp set for water side nodes (reverse inlet/outlet for water heater)
809 6 : BranchNodeConnections::SetUpCompSets(state, DesupHtr.Type, DesupHtr.Name, DesupHtr.TankType, DesupHtr.TankName, cAlphaArgs(6), cAlphaArgs(5));
810 :
811 6 : std::string const heatSourceObjType = cAlphaArgs(9);
812 :
813 12 : if ((Util::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
814 12 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
815 12 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
816 0 : if (lNumericFieldBlanks(2)) {
817 0 : DesupHtr.HeatReclaimRecoveryEff = 0.8;
818 : } else {
819 0 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
820 0 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.9) {
821 0 : ShowSevereError(state,
822 0 : format("{} = {}: {} must be > 0.0 and <= 0.9, Efficiency = {:.3T}",
823 : cCurrentModuleObject,
824 0 : DesupHtr.Name,
825 : cNumericFieldNames(2),
826 0 : DesupHtr.HeatReclaimRecoveryEff));
827 0 : ErrorsFound = true;
828 : }
829 : } // Blank Num(2)
830 : } else { // max is 0.3 for all other sources
831 6 : if (lNumericFieldBlanks(2)) {
832 0 : DesupHtr.HeatReclaimRecoveryEff = 0.25;
833 : } else {
834 6 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
835 6 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.3) {
836 0 : ShowSevereError(state,
837 0 : format("{} = {}: {} must be > 0.0 and <= 0.3, {} = {:.3T}",
838 : cCurrentModuleObject,
839 0 : DesupHtr.Name,
840 : cNumericFieldNames(2),
841 : cNumericFieldNames(2),
842 0 : DesupHtr.HeatReclaimRecoveryEff));
843 0 : ErrorsFound = true;
844 : }
845 : } // Blank Num(2)
846 : } // setting limits on heat recovery efficiency
847 :
848 : // Find the Refrigeration equipment index associated with the desuperheater heating coil.
849 6 : bool errFlag = false;
850 6 : DesupHtr.HeatingSourceType = heatSourceObjType;
851 6 : DesupHtr.HeatingSourceName = cAlphaArgs(10);
852 6 : if (Util::SameString(heatSourceObjType, "Refrigeration:CompressorRack")) {
853 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CompressorRackRefrigeratedCase;
854 0 : for (int RackNum = 1; RackNum <= state.dataRefrigCase->NumRefrigeratedRacks; ++RackNum) {
855 0 : if (!Util::SameString(state.dataHeatBal->HeatReclaimRefrigeratedRack(RackNum).Name, cAlphaArgs(10))) continue;
856 0 : DesupHtr.ReclaimHeatingSourceIndexNum = RackNum;
857 0 : if (allocated(state.dataHeatBal->HeatReclaimRefrigeratedRack)) {
858 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
859 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(DesupHtr.ReclaimHeatingSourceIndexNum);
860 0 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
861 0 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
862 0 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
863 0 : num = 0.0;
864 : }
865 0 : DesupHtr.ValidSourceType = true;
866 0 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
867 0 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
868 0 : ShowSevereError(
869 : state,
870 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
871 : cCurrentModuleObject,
872 0 : DesupHtr.Name,
873 0 : DesupHtr.HeatingSourceName));
874 0 : ErrorsFound = true;
875 : }
876 : }
877 0 : break;
878 : }
879 12 : } else if ((Util::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
880 12 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
881 12 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
882 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CondenserRefrigeration;
883 0 : for (int CondNum = 1; CondNum <= state.dataRefrigCase->NumRefrigCondensers; ++CondNum) {
884 0 : if (!Util::SameString(state.dataHeatBal->HeatReclaimRefrigCondenser(CondNum).Name, cAlphaArgs(10))) continue;
885 0 : DesupHtr.ReclaimHeatingSourceIndexNum = CondNum;
886 0 : if (allocated(state.dataHeatBal->HeatReclaimRefrigCondenser)) {
887 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
888 0 : state.dataHeatBal->HeatReclaimRefrigCondenser(DesupHtr.ReclaimHeatingSourceIndexNum);
889 0 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
890 0 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
891 0 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
892 0 : num = 0.0;
893 : }
894 0 : DesupHtr.ValidSourceType = true;
895 0 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
896 0 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.9) {
897 0 : ShowSevereError(
898 : state,
899 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.9",
900 : cCurrentModuleObject,
901 0 : DesupHtr.Name,
902 0 : DesupHtr.HeatingSourceName));
903 0 : ErrorsFound = true;
904 : }
905 : }
906 0 : break;
907 : }
908 9 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed") ||
909 6 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoSpeed") ||
910 12 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:MultiSpeed") ||
911 8 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
912 :
913 4 : if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed")) {
914 3 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXCooling;
915 1 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
916 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiMode;
917 : } else {
918 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiSpeed;
919 : }
920 4 : DXCoils::GetDXCoilIndex(state, DesupHtr.HeatingSourceName, DesupHtr.ReclaimHeatingSourceIndexNum, errFlag, cCurrentModuleObject);
921 4 : if (allocated(state.dataHeatBal->HeatReclaimDXCoil)) {
922 4 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimDXCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
923 4 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
924 3 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
925 7 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
926 4 : num = 0.0;
927 : }
928 4 : DesupHtr.ValidSourceType = true;
929 4 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
930 4 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
931 0 : ShowSevereError(state,
932 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
933 : cCurrentModuleObject,
934 0 : DesupHtr.Name,
935 0 : DesupHtr.HeatingSourceName));
936 0 : ErrorsFound = true;
937 : }
938 : }
939 4 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:VariableSpeed") ||
940 4 : Util::SameString(heatSourceObjType, "Coil:Cooling:WaterToAirHeatPump:VariableSpeedEquationFit")) {
941 :
942 1 : if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:VariableSpeed")) {
943 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXVariableCooling;
944 : } else {
945 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::AirWaterHeatPumpVSEQ;
946 : }
947 1 : DesupHtr.ReclaimHeatingSourceIndexNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(state, heatSourceObjType, cAlphaArgs(10), errFlag);
948 1 : if (allocated(state.dataHeatBal->HeatReclaimVS_Coil)) {
949 1 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimVS_Coil(DesupHtr.ReclaimHeatingSourceIndexNum);
950 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
951 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
952 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
953 1 : num = 0.0;
954 : }
955 1 : DesupHtr.ValidSourceType = true;
956 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
957 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
958 0 : ShowSevereError(state,
959 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
960 : cCurrentModuleObject,
961 0 : DesupHtr.Name,
962 0 : DesupHtr.HeatingSourceName));
963 0 : ErrorsFound = true;
964 : }
965 : }
966 1 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:WaterToAirHeatPump:EquationFit")) {
967 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::AirWaterHeatPumpEQ;
968 1 : DesupHtr.ReclaimHeatingSourceIndexNum = WaterToAirHeatPumpSimple::GetCoilIndex(state, heatSourceObjType, cAlphaArgs(10), errFlag);
969 1 : if (allocated(state.dataHeatBal->HeatReclaimSimple_WAHPCoil)) {
970 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
971 1 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
972 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
973 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
974 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
975 1 : num = 0.0;
976 : }
977 1 : DesupHtr.ValidSourceType = true;
978 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
979 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
980 0 : ShowSevereError(state,
981 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
982 : cCurrentModuleObject,
983 0 : DesupHtr.Name,
984 0 : DesupHtr.HeatingSourceName));
985 0 : ErrorsFound = true;
986 : }
987 : }
988 0 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX")) {
989 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CoilCoolingDX;
990 0 : DesupHtr.ReclaimHeatingSourceIndexNum = CoilCoolingDX::factory(state, cAlphaArgs(10));
991 0 : if (DesupHtr.ReclaimHeatingSourceIndexNum < 0) {
992 0 : ShowSevereError(
993 : state,
994 0 : format("{}={}, could not find desuperheater coil {}={}", cCurrentModuleObject, DesupHtr.Name, cAlphaArgs(9), cAlphaArgs(10)));
995 0 : ErrorsFound = true;
996 : } else {
997 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
998 0 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat;
999 0 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
1000 0 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
1001 0 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
1002 0 : num = 0.0;
1003 : }
1004 0 : DesupHtr.ValidSourceType = true;
1005 0 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
1006 0 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
1007 0 : ShowSevereError(
1008 : state,
1009 0 : format("{}, \"{}\" sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
1010 : cCurrentModuleObject,
1011 0 : DesupHtr.Name,
1012 0 : DesupHtr.HeatingSourceName));
1013 0 : ErrorsFound = true;
1014 : }
1015 : }
1016 : } else {
1017 0 : ShowSevereError(state, format("{} = {}:", cCurrentModuleObject, DesupHtr.Name));
1018 0 : ShowContinueError(state, " desuperheater can only be used with Coil:Cooling:DX:SingleSpeed, ");
1019 0 : ShowContinueError(state,
1020 : " Coil:Cooling:DX:TwoSpeed, Coil:Cooling:DX:MultiSpeed, Coil:Cooling:DX:TwoStageWithHumidityControlMode, "
1021 : "Coil:Cooling:DX:VariableSpeed, Coil:Cooling:WaterToAirHeatPump:VariableSpeedEquationFit, "
1022 : "Coil:Cooling:WaterToAirHeatPump:EquationFit, Refrigeration:CompressorRack,");
1023 0 : ShowContinueError(state, " Refrigeration:Condenser:AirCooled ,Refrigeration:Condenser:EvaporativeCooled, ");
1024 0 : ShowContinueError(state, " or Refrigeration:Condenser:WaterCooled.");
1025 0 : ShowContinueError(state, format(" Invalid desuperheater heat source object: {} \"{}\"", heatSourceObjType, cAlphaArgs(10)));
1026 0 : ErrorsFound = true;
1027 : }
1028 6 : if (errFlag) {
1029 0 : ShowContinueError(state, format("...occurs in {}={}", cCurrentModuleObject, DesupHtr.Name));
1030 0 : ErrorsFound = true;
1031 : }
1032 :
1033 6 : if (DesupHtr.ReclaimHeatingSourceIndexNum == 0 && DesupHtr.ReclaimHeatingSource != ReclaimHeatObjectType::CoilCoolingDX) {
1034 0 : ShowSevereError(state,
1035 0 : format("{}, \"{}\" desuperheater heat source object not found: {} \"{}\"",
1036 : cCurrentModuleObject,
1037 0 : DesupHtr.Name,
1038 : heatSourceObjType,
1039 : cAlphaArgs(10)));
1040 0 : ErrorsFound = true;
1041 : }
1042 :
1043 6 : DesupHtr.OperatingWaterFlowRate = rNumericArgs(6);
1044 6 : if (DesupHtr.OperatingWaterFlowRate <= 0.0) {
1045 0 : ShowSevereError(state,
1046 0 : format("{} = {}: {} must be greater than 0. {} = {:.6T}",
1047 : cCurrentModuleObject,
1048 0 : DesupHtr.Name,
1049 : cNumericFieldNames(6),
1050 : cNumericFieldNames(6),
1051 : rNumericArgs(6)));
1052 0 : ErrorsFound = true;
1053 : }
1054 :
1055 6 : DesupHtr.PumpElecPower = rNumericArgs(7);
1056 6 : if (DesupHtr.PumpElecPower < 0.0) {
1057 0 : ShowSevereError(state,
1058 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1059 : cCurrentModuleObject,
1060 0 : DesupHtr.Name,
1061 : cNumericFieldNames(7),
1062 : cNumericFieldNames(7),
1063 : rNumericArgs(7)));
1064 0 : ErrorsFound = true;
1065 : }
1066 :
1067 6 : if ((DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate) > 7.9264e6) {
1068 0 : ShowWarningError(state,
1069 0 : format("{} = {}: {} to {} ratio > 7.9264E6. {} to {} = {:.3T}",
1070 : cCurrentModuleObject,
1071 0 : DesupHtr.Name,
1072 : cNumericFieldNames(7),
1073 : cNumericFieldNames(6),
1074 : cNumericFieldNames(7),
1075 : cNumericFieldNames(6),
1076 0 : (DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate)));
1077 0 : ShowContinueError(state, format(" Suggest reducing {} or increasing {}.", cNumericFieldNames(7), cNumericFieldNames(6)));
1078 0 : ShowContinueError(state, " The simulation will continue using the user defined values.");
1079 : }
1080 :
1081 6 : DesupHtr.PumpFracToWater = rNumericArgs(8);
1082 6 : if (DesupHtr.PumpFracToWater < 0.0 || DesupHtr.PumpFracToWater > 1.0) {
1083 0 : ShowSevereError(state,
1084 0 : format("{} = {}: {} must be >= 0 or <= 1. {} = {:.3T}",
1085 : cCurrentModuleObject,
1086 0 : DesupHtr.Name,
1087 : cNumericFieldNames(8),
1088 : cNumericFieldNames(8),
1089 : rNumericArgs(8)));
1090 0 : ErrorsFound = true;
1091 : }
1092 :
1093 6 : DesupHtr.OnCycParaLoad = rNumericArgs(9);
1094 6 : if (DesupHtr.OnCycParaLoad < 0.0) {
1095 0 : ShowSevereError(state,
1096 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1097 : cCurrentModuleObject,
1098 0 : DesupHtr.Name,
1099 : cNumericFieldNames(9),
1100 : cNumericFieldNames(9),
1101 : rNumericArgs(9)));
1102 0 : ErrorsFound = true;
1103 : }
1104 :
1105 6 : DesupHtr.OffCycParaLoad = rNumericArgs(10);
1106 6 : if (DesupHtr.OffCycParaLoad < 0.0) {
1107 0 : ShowSevereError(state,
1108 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1109 : cCurrentModuleObject,
1110 0 : DesupHtr.Name,
1111 : cNumericFieldNames(10),
1112 : cNumericFieldNames(10),
1113 : rNumericArgs(10)));
1114 0 : ErrorsFound = true;
1115 : }
1116 6 : }
1117 :
1118 5 : if (ErrorsFound) {
1119 0 : ShowFatalError(state, format("Errors found in getting {} input. Preceding condition causes termination.", cCurrentModuleObject));
1120 : }
1121 :
1122 5 : return ErrorsFound;
1123 :
1124 5 : } // namespace WaterThermalTanks
1125 :
1126 10 : bool getHPWaterHeaterInput(EnergyPlusData &state)
1127 : {
1128 :
1129 : static constexpr std::string_view routineName = "getHPWaterHeaterInput";
1130 10 : bool ErrorsFound = false;
1131 :
1132 10 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
1133 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
1134 : int nAlphaOffset; // the difference of array location between alpha items between pumped and wrapped condensers
1135 : int nNumericOffset; // the difference of array location between numeric items between pumped and wrapped condensers
1136 : int nNumPossibleNumericArgs; // the number of possible numeric arguments in the idd
1137 : int nNumPossibleAlphaArgs; // the number of possible numeric arguments in the idd
1138 :
1139 : // For looking up in IDF/epJSON, you need the index that corresponds to the actual object type (Pumped or Wrapped)
1140 : int HPWaterHeaterNumOfSpecificType;
1141 :
1142 22 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
1143 :
1144 : // Create reference to current HPWH object in array.
1145 12 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
1146 :
1147 : // Initialize the offsets to zero
1148 12 : nAlphaOffset = 0;
1149 12 : nNumericOffset = 0;
1150 :
1151 : DataLoopNode::ConnectionObjectType objType;
1152 :
1153 12 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
1154 : // Pumped Condenser
1155 7 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
1156 7 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpPumpedCondenser;
1157 7 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped;
1158 7 : nNumPossibleAlphaArgs = 29;
1159 7 : nNumPossibleNumericArgs = 9;
1160 : // Actual index of Pumped type
1161 7 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum;
1162 : } else {
1163 : // Wrapped Condenser
1164 5 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
1165 5 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpWrappedCondenser;
1166 5 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped;
1167 5 : nNumPossibleAlphaArgs = 27;
1168 5 : nNumPossibleNumericArgs = 10;
1169 : // Actual index of Wrapped type
1170 5 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum - NumPumpedCondenser;
1171 : }
1172 :
1173 : int NumAlphas;
1174 : int NumNums;
1175 : int IOStat;
1176 24 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1177 12 : state.dataIPShortCut->cCurrentModuleObject,
1178 : HPWaterHeaterNumOfSpecificType,
1179 12 : state.dataIPShortCut->cAlphaArgs,
1180 : NumAlphas,
1181 12 : state.dataIPShortCut->rNumericArgs,
1182 : NumNums,
1183 : IOStat,
1184 12 : state.dataIPShortCut->lNumericFieldBlanks,
1185 12 : state.dataIPShortCut->lAlphaFieldBlanks,
1186 12 : state.dataIPShortCut->cAlphaFieldNames,
1187 12 : state.dataIPShortCut->cNumericFieldNames);
1188 :
1189 12 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
1190 :
1191 : // Copy those lists into C++ std::maps // Why, no really why? This is really dumb
1192 12 : std::map<int, std::string> hpwhAlpha;
1193 12 : std::map<int, Real64> hpwhNumeric;
1194 12 : std::map<int, bool> hpwhAlphaBlank;
1195 12 : std::map<int, bool> hpwhNumericBlank;
1196 12 : std::map<int, std::string> hpwhAlphaFieldNames;
1197 12 : std::map<int, std::string> hpwhNumericFieldNames;
1198 116 : for (int i = 1; i <= NumNums; ++i) {
1199 104 : hpwhNumeric[i] = state.dataIPShortCut->rNumericArgs(i);
1200 104 : hpwhNumericBlank[i] = state.dataIPShortCut->lNumericFieldBlanks(i);
1201 104 : hpwhNumericFieldNames[i] = state.dataIPShortCut->cNumericFieldNames(i);
1202 : }
1203 26 : for (int i = NumNums + 1; i <= nNumPossibleNumericArgs; ++i) {
1204 14 : hpwhNumericBlank[i] = true;
1205 : }
1206 326 : for (int i = 1; i <= NumAlphas; ++i) {
1207 314 : hpwhAlpha[i] = state.dataIPShortCut->cAlphaArgs(i);
1208 314 : hpwhAlphaBlank[i] = state.dataIPShortCut->lAlphaFieldBlanks(i);
1209 314 : hpwhAlphaFieldNames[i] = state.dataIPShortCut->cAlphaFieldNames(i);
1210 : }
1211 36 : for (int i = NumAlphas + 1; i <= nNumPossibleAlphaArgs; ++i) {
1212 24 : hpwhAlphaBlank[i] = true;
1213 : }
1214 12 : Util::IsNameEmpty(state, hpwhAlpha[1], state.dataIPShortCut->cCurrentModuleObject, ErrorsFound);
1215 :
1216 : // Name and type
1217 12 : HPWH.Name = hpwhAlpha[1];
1218 12 : HPWH.Type = state.dataIPShortCut->cCurrentModuleObject;
1219 :
1220 : // Availability Schedule
1221 : // convert schedule name to pointer
1222 12 : if (hpwhAlphaBlank[2]) {
1223 6 : HPWH.availSched = Sched::GetScheduleAlwaysOn(state);
1224 6 : } else if ((HPWH.availSched = Sched::GetSchedule(state, hpwhAlpha[2])) == nullptr) {
1225 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[2], hpwhAlpha[2]);
1226 0 : ErrorsFound = true;
1227 : }
1228 :
1229 : // Compressor Setpoint Temperature Schedule
1230 : // convert schedule name to pointer
1231 12 : if (hpwhAlphaBlank[3]) {
1232 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[3]);
1233 0 : ErrorsFound = true;
1234 12 : } else if ((HPWH.setptTempSched = Sched::GetSchedule(state, hpwhAlpha[3])) == nullptr) {
1235 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[3], hpwhAlpha[3]);
1236 0 : ErrorsFound = true;
1237 : }
1238 :
1239 : // Dead Band Temperature Difference
1240 12 : HPWH.DeadBandTempDiff = hpwhNumeric[1 + nNumericOffset];
1241 12 : if (HPWH.DeadBandTempDiff <= 0.0 || HPWH.DeadBandTempDiff > 20.0) {
1242 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1243 0 : ShowContinueError(state,
1244 0 : format("{}{}",
1245 0 : hpwhNumericFieldNames[1 + nNumericOffset],
1246 0 : format(" difference must be > 0 and <= 20. Dead band = {:.1T}", hpwhNumeric[1 + nNumericOffset])));
1247 0 : ErrorsFound = true;
1248 : }
1249 :
1250 12 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1251 :
1252 : // Condenser Inlet/Outlet Nodes
1253 7 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1254 7 : hpwhAlpha[4],
1255 : ErrorsFound,
1256 : objType,
1257 7 : HPWH.Name,
1258 : DataLoopNode::NodeFluidType::Water,
1259 : DataLoopNode::ConnectionType::Inlet,
1260 : NodeInputManager::CompFluidStream::Secondary,
1261 : DataLoopNode::ObjectIsParent);
1262 7 : HPWH.InletNodeName1 = hpwhAlpha[4];
1263 7 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1264 7 : hpwhAlpha[5],
1265 : ErrorsFound,
1266 : objType,
1267 7 : HPWH.Name,
1268 : DataLoopNode::NodeFluidType::Water,
1269 : DataLoopNode::ConnectionType::Outlet,
1270 : NodeInputManager::CompFluidStream::Secondary,
1271 : DataLoopNode::ObjectIsParent);
1272 7 : HPWH.OutletNodeName1 = hpwhAlpha[5];
1273 :
1274 : // Condenser Water Flow Rate
1275 7 : HPWH.OperatingWaterFlowRate = hpwhNumeric[2];
1276 7 : if (HPWH.OperatingWaterFlowRate <= 0.0 && hpwhNumeric[2] != Constant::AutoCalculate) {
1277 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1278 0 : ShowContinueError(state,
1279 0 : format("{} must be greater than 0. Condenser water flow rate = {:.6T}", hpwhNumericFieldNames[2], hpwhNumeric[2]));
1280 0 : ErrorsFound = true;
1281 : }
1282 :
1283 5 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1284 :
1285 : // Wrapped Condenser Location
1286 5 : HPWH.WrappedCondenserBottomLocation = hpwhNumeric[2 + nNumericOffset];
1287 5 : HPWH.WrappedCondenserTopLocation = hpwhNumeric[3 + nNumericOffset];
1288 :
1289 5 : if (HPWH.WrappedCondenserBottomLocation < 0.0) {
1290 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1291 0 : ShowContinueError(state,
1292 0 : format("{} must be greater than 0. Condenser bottom location = {:.6T}",
1293 0 : hpwhNumericFieldNames[2],
1294 0 : HPWH.WrappedCondenserBottomLocation));
1295 0 : ErrorsFound = true;
1296 : }
1297 :
1298 5 : if (HPWH.WrappedCondenserBottomLocation >= HPWH.WrappedCondenserTopLocation) {
1299 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1300 0 : ShowContinueError(state,
1301 0 : format("{} ({:.6T}) must be greater than {} ({:.6T}).",
1302 0 : HPWH.WrappedCondenserTopLocation,
1303 0 : hpwhNumericFieldNames[2],
1304 0 : hpwhNumericFieldNames[3],
1305 0 : HPWH.WrappedCondenserBottomLocation));
1306 0 : ErrorsFound = true;
1307 : }
1308 :
1309 : // Reset the offset
1310 5 : nAlphaOffset = -2;
1311 5 : nNumericOffset = 1;
1312 :
1313 : } else {
1314 0 : assert(0);
1315 : }
1316 :
1317 : // Evaporator Air Flow Rate
1318 12 : HPWH.OperatingAirFlowRate = hpwhNumeric[3 + nNumericOffset];
1319 12 : if (HPWH.OperatingAirFlowRate <= 0.0 && hpwhNumeric[3 + nNumericOffset] != Constant::AutoCalculate) {
1320 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1321 0 : ShowContinueError(state,
1322 0 : format("{}{}",
1323 0 : hpwhNumericFieldNames[3 + nNumericOffset],
1324 0 : format(" must be greater than 0. Evaporator air flow rate = {:.6T}", hpwhNumeric[3 + nNumericOffset])));
1325 0 : ErrorsFound = true;
1326 : }
1327 :
1328 : // Inlet Air Configuration
1329 12 : HPWH.InletAirConfiguration = static_cast<WTTAmbientTemp>(getEnumValue(HPWHAmbientTempNamesUC, Util::makeUPPER(hpwhAlpha[6 + nAlphaOffset])));
1330 12 : switch (HPWH.InletAirConfiguration) {
1331 5 : case WTTAmbientTemp::Schedule: {
1332 :
1333 : // Inlet Air Temperature Schedule
1334 5 : if (hpwhAlphaBlank[11 + nAlphaOffset]) {
1335 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[11 + nAlphaOffset]);
1336 0 : ErrorsFound = true;
1337 5 : } else if ((HPWH.ambientTempSched = Sched::GetSchedule(state, hpwhAlpha[11 + nAlphaOffset])) == nullptr) {
1338 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[11 + nAlphaOffset], hpwhAlpha[11 + nAlphaOffset]);
1339 0 : ErrorsFound = true;
1340 : }
1341 :
1342 : // Inlet Air Humidity Schedule
1343 5 : if (hpwhAlphaBlank[12 + nAlphaOffset]) {
1344 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[12 + nAlphaOffset]);
1345 0 : ErrorsFound = true;
1346 5 : } else if ((HPWH.ambientRHSched = Sched::GetSchedule(state, hpwhAlpha[12 + nAlphaOffset])) == nullptr) {
1347 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[12 + nAlphaOffset], hpwhAlpha[12 + nAlphaOffset]);
1348 0 : ErrorsFound = true;
1349 5 : } else if (!HPWH.ambientRHSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
1350 0 : Sched::ShowSevereBadMinMax(
1351 0 : state, eoh, hpwhAlphaFieldNames[12 + nAlphaOffset], hpwhAlpha[12 + nAlphaOffset], Clusive::In, 0.0, Clusive::In, 1.0);
1352 0 : ErrorsFound = true;
1353 : }
1354 5 : } break;
1355 :
1356 4 : case WTTAmbientTemp::ZoneAndOA:
1357 : case WTTAmbientTemp::TempZone: {
1358 :
1359 : // Inlet Air Zone
1360 4 : if (!hpwhAlphaBlank[13 + nAlphaOffset]) {
1361 4 : HPWH.AmbientTempZone = Util::FindItemInList(hpwhAlpha[13 + nAlphaOffset], state.dataHeatBal->Zone);
1362 4 : if (HPWH.AmbientTempZone == 0) {
1363 2 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1364 2 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[13 + nAlphaOffset], hpwhAlpha[13 + nAlphaOffset]));
1365 2 : ErrorsFound = true;
1366 : }
1367 : } else {
1368 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1369 0 : ShowContinueError(state, format("required {} is blank.", hpwhAlphaFieldNames[13 + nAlphaOffset]));
1370 0 : ErrorsFound = true;
1371 : }
1372 4 : break;
1373 : }
1374 3 : default:
1375 : case WTTAmbientTemp::OutsideAir:
1376 3 : break;
1377 : }
1378 :
1379 : // Read air inlet nodes after mixer/splitter nodes have been read in (state.dataIPShortCut->cAlphaArgs 7-10),
1380 : // Node_ConnectionType differs for inlet node if mixer/splitter node exists
1381 :
1382 : // Tank Name
1383 : // We will verify this exists and is the right kind of tank later when the tanks are all loaded.
1384 12 : HPWH.TankName = hpwhAlpha[15 + nAlphaOffset];
1385 12 : HPWH.TankType = hpwhAlpha[14 + nAlphaOffset];
1386 :
1387 : // Use Side Inlet/Outlet
1388 : // Get the water heater tank use side inlet node names for HPWHs connected to a plant loop
1389 : // Save the name of the node for use with set up comp sets
1390 12 : HPWH.InletNodeName2 = hpwhAlpha[16 + nAlphaOffset];
1391 12 : HPWH.OutletNodeName2 = hpwhAlpha[17 + nAlphaOffset];
1392 :
1393 12 : if (!hpwhAlphaBlank[16 + nAlphaOffset] && !hpwhAlphaBlank[17 + nAlphaOffset]) {
1394 7 : HPWH.WHUseInletNode = NodeInputManager::GetOnlySingleNode(state,
1395 7 : HPWH.InletNodeName2,
1396 : ErrorsFound,
1397 : objType,
1398 7 : HPWH.Name,
1399 : DataLoopNode::NodeFluidType::Water,
1400 : DataLoopNode::ConnectionType::Inlet,
1401 : NodeInputManager::CompFluidStream::Primary,
1402 : DataLoopNode::ObjectIsParent);
1403 14 : HPWH.WHUseOutletNode = NodeInputManager::GetOnlySingleNode(state,
1404 7 : HPWH.OutletNodeName2,
1405 : ErrorsFound,
1406 : objType,
1407 7 : HPWH.Name,
1408 : DataLoopNode::NodeFluidType::Water,
1409 : DataLoopNode::ConnectionType::Outlet,
1410 : NodeInputManager::CompFluidStream::Primary,
1411 : DataLoopNode::ObjectIsParent);
1412 : }
1413 :
1414 : // DX Coil
1415 : // get Coil:DX:HeatPumpWaterHeater object
1416 12 : HPWH.DXCoilName = hpwhAlpha[19 + nAlphaOffset];
1417 12 : HPWH.DXCoilType = hpwhAlpha[18 + nAlphaOffset];
1418 :
1419 : // check that the DX Coil exists
1420 12 : bool DXCoilErrFlag = false;
1421 12 : bool bIsVScoil = false;
1422 12 : DXCoils::GetDXCoilIndex(state, HPWH.DXCoilName, HPWH.DXCoilNum, DXCoilErrFlag, state.dataIPShortCut->cCurrentModuleObject, true);
1423 12 : if (DXCoilErrFlag) {
1424 : // This could be a variable speed heat pump water heater
1425 0 : bool bVSCoilErrFlag = false;
1426 :
1427 0 : bool checkIHPFirst = IntegratedHeatPump::IHPInModel(state);
1428 0 : if (checkIHPFirst) {
1429 0 : HPWH.DXCoilNum =
1430 0 : IntegratedHeatPump::GetCoilIndexIHP(state, "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE", HPWH.DXCoilName, bVSCoilErrFlag);
1431 :
1432 0 : if (!bVSCoilErrFlag) {
1433 0 : HPWH.bIsIHP = true;
1434 : }
1435 : }
1436 :
1437 0 : if (bVSCoilErrFlag || !checkIHPFirst) {
1438 0 : bVSCoilErrFlag = false;
1439 0 : HPWH.DXCoilNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(
1440 0 : state, "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed", HPWH.DXCoilName, bVSCoilErrFlag);
1441 :
1442 0 : if (bVSCoilErrFlag) {
1443 0 : ShowContinueError(state, format("...occurs in {} ={}", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1444 0 : ShowContinueError(state, format("...could not find either DXCoils::DXCoil or Variable Speed Coil {}", HPWH.DXCoilName));
1445 0 : ErrorsFound = true;
1446 : }
1447 : }
1448 :
1449 0 : bIsVScoil = true;
1450 0 : HPWH.DXCoilTypeNum = 0;
1451 0 : if (HPWH.bIsIHP) {
1452 0 : HPWH.DXCoilType = "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE";
1453 : } else {
1454 0 : HPWH.DXCoilType = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum).VarSpeedCoilType;
1455 : }
1456 : } else {
1457 : // this is a single speed coil
1458 12 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1459 12 : if (!Util::SameString(HPWH.DXCoilType, Coil.DXCoilType)) {
1460 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1461 0 : ShowContinueError(state, format("specifies the coil {}=\"{}\".", HPWH.DXCoilType, HPWH.DXCoilName));
1462 0 : ShowContinueError(state, format("However, {} is a coil of type {}.", HPWH.DXCoilName, Coil.DXCoilType));
1463 0 : ErrorsFound = true;
1464 : }
1465 12 : HPWH.DXCoilTypeNum = Coil.DXCoilType_Num;
1466 : }
1467 :
1468 : // Make sure that the coil and tank are compatible.
1469 12 : if (bIsVScoil) {
1470 0 : if (HPWH.HPWHType != DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1471 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1472 0 : ShowContinueError(state,
1473 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed can only be used with a pumped condenser heat pump "
1474 : "water heater.");
1475 0 : ErrorsFound = true;
1476 : }
1477 : } else {
1478 12 : if (!((HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterPumped &&
1479 7 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) ||
1480 5 : (HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterWrapped &&
1481 5 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped))) {
1482 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1483 0 : std::string ExpectedCoilType;
1484 0 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1485 0 : ExpectedCoilType = HVAC::cAllCoilTypes(HVAC::CoilDX_HeatPumpWaterHeaterPumped);
1486 0 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1487 0 : ExpectedCoilType = HVAC::cAllCoilTypes(HVAC::CoilDX_HeatPumpWaterHeaterWrapped);
1488 : } else {
1489 0 : assert(0);
1490 : }
1491 0 : ShowContinueError(state, format("can only be used with {}", ExpectedCoilType));
1492 0 : ErrorsFound = true;
1493 0 : }
1494 : }
1495 :
1496 : // Dummy condenser Inlet/Outlet Nodes for wrapped tanks
1497 12 : if (HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterWrapped) {
1498 5 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1499 :
1500 5 : HPWH.InletNodeName1 = "DUMMY CONDENSER INLET " + Coil.Name;
1501 5 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1502 5 : HPWH.InletNodeName1,
1503 : ErrorsFound,
1504 : objType,
1505 5 : HPWH.Name,
1506 : DataLoopNode::NodeFluidType::Water,
1507 : DataLoopNode::ConnectionType::Inlet,
1508 : NodeInputManager::CompFluidStream::Secondary,
1509 : DataLoopNode::ObjectIsParent);
1510 5 : HPWH.OutletNodeName1 = "DUMMY CONDENSER OUTLET " + Coil.Name;
1511 10 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1512 5 : HPWH.OutletNodeName1,
1513 : ErrorsFound,
1514 : objType,
1515 5 : HPWH.Name,
1516 : DataLoopNode::NodeFluidType::Water,
1517 : DataLoopNode::ConnectionType::Outlet,
1518 : NodeInputManager::CompFluidStream::Secondary,
1519 : DataLoopNode::ObjectIsParent);
1520 : }
1521 :
1522 : // Minimum Inlet Air Temperature for Compressor Operation
1523 12 : HPWH.MinAirTempForHPOperation = hpwhNumeric[4 + nNumericOffset];
1524 :
1525 : // Maximum Inlet Air Temperature for Compressor Operation
1526 12 : HPWH.MaxAirTempForHPOperation = hpwhNumeric[5 + nNumericOffset];
1527 12 : if (HPWH.MaxAirTempForHPOperation <= HPWH.MinAirTempForHPOperation) {
1528 0 : ShowWarningError(state,
1529 0 : format("{}=\"{}\": maximum inlet air temperature for heat pump compressor operation",
1530 0 : state.dataIPShortCut->cCurrentModuleObject,
1531 0 : HPWH.Name));
1532 0 : ShowContinueError(state, "must be greater than the minimum inlet air temperature for heat pump compressor operation.");
1533 0 : ShowContinueError(state, format("...Minimum inlet air temperature = {:.1T}", HPWH.MinAirTempForHPOperation));
1534 0 : ShowContinueError(state, format("...Maximum inlet air temperature = {:.1T}", HPWH.MaxAirTempForHPOperation));
1535 : }
1536 :
1537 : // Compressor Location
1538 12 : HPWH.CrankcaseTempIndicator =
1539 12 : static_cast<CrankcaseHeaterControlTemp>(getEnumValue(CrankcaseHeaterControlTempNamesUC, Util::makeUPPER(hpwhAlpha[20 + nAlphaOffset])));
1540 :
1541 12 : switch (HPWH.CrankcaseTempIndicator) {
1542 3 : case CrankcaseHeaterControlTemp::Schedule: {
1543 3 : if (hpwhAlphaBlank[21 + nAlphaOffset]) {
1544 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[21 + nAlphaOffset]);
1545 0 : ErrorsFound = true;
1546 3 : } else if ((HPWH.crankcaseTempSched = Sched::GetSchedule(state, hpwhAlpha[21 + nAlphaOffset])) == nullptr) {
1547 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[21 + nAlphaOffset], hpwhAlpha[21 + nAlphaOffset]);
1548 0 : ErrorsFound = true;
1549 : }
1550 3 : } break;
1551 :
1552 4 : case CrankcaseHeaterControlTemp::Zone: {
1553 4 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1554 0 : ShowSevereError(state,
1555 0 : format("{}=\"{}\": Inlet Air Configuration must be Zone Air Only or Zone And",
1556 0 : state.dataIPShortCut->cCurrentModuleObject,
1557 0 : HPWH.Name));
1558 0 : ShowContinueError(state, " Outdoor Air when compressor location equals ZONE.");
1559 0 : ErrorsFound = true;
1560 : }
1561 :
1562 4 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1563 0 : ShowWarningError(state,
1564 0 : format("{}=\"{}\" {} was provided but will not be used based on compressor location input=\"{}\".",
1565 0 : state.dataIPShortCut->cCurrentModuleObject,
1566 0 : HPWH.Name,
1567 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1568 0 : hpwhAlpha[20 + nAlphaOffset]));
1569 : }
1570 4 : break;
1571 : }
1572 5 : case CrankcaseHeaterControlTemp::Outdoors: {
1573 5 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1574 0 : ShowWarningError(state,
1575 0 : format("{}=\"{}\" {} was provided but will not be used based on {}=\"{}\".",
1576 0 : state.dataIPShortCut->cCurrentModuleObject,
1577 0 : HPWH.Name,
1578 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1579 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1580 0 : hpwhAlpha[20 + nAlphaOffset]));
1581 : }
1582 5 : break;
1583 : }
1584 0 : default:
1585 0 : break;
1586 : }
1587 :
1588 : // Fan Name
1589 12 : HPWH.FanName = hpwhAlpha[23 + nAlphaOffset];
1590 :
1591 12 : Real64 FanVolFlow = 0.0;
1592 12 : bool errFlag(false);
1593 :
1594 12 : HPWH.fanType = static_cast<HVAC::FanType>(getEnumValue(HVAC::fanTypeNamesUC, hpwhAlpha[22 + nAlphaOffset]));
1595 :
1596 12 : if ((HPWH.FanNum = Fans::GetFanIndex(state, HPWH.FanName)) == 0) {
1597 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[23 + nAlphaOffset], HPWH.FanName);
1598 0 : ErrorsFound = true;
1599 : } else {
1600 12 : assert(HPWH.fanType == state.dataFans->fans(HPWH.FanNum)->type);
1601 12 : FanVolFlow = state.dataFans->fans(HPWH.FanNum)->maxAirFlowRate;
1602 : }
1603 : // issue #5630, set fan info in coils.
1604 12 : if (bIsVScoil) {
1605 0 : VariableSpeedCoils::setVarSpeedHPWHFanType(state, HPWH.DXCoilNum, HPWH.fanType);
1606 0 : VariableSpeedCoils::setVarSpeedHPWHFanIndex(state, HPWH.DXCoilNum, HPWH.FanNum);
1607 : } else {
1608 : // LOL
1609 12 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanName);
1610 12 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanNum);
1611 12 : DXCoils::SetDXCoolingCoilData(
1612 12 : state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.fanType);
1613 : }
1614 :
1615 12 : if (errFlag) {
1616 0 : ErrorsFound = true;
1617 12 : } else if (HPWH.fanType != HVAC::FanType::OnOff && HPWH.fanType != HVAC::FanType::SystemModel) {
1618 0 : ShowSevereError(state, format("{}=\"{}\": illegal fan type specified.", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1619 0 : ShowContinueError(
1620 : state,
1621 0 : format(" The fan object ({}) type must be Fan:SystemModel or Fan:OnOff when used with a heat pump water heater.", HPWH.FanName));
1622 0 : ErrorsFound = true;
1623 12 : } else if (HPWH.fanType != HVAC::FanType::OnOff && HPWH.fanType != HVAC::FanType::SystemModel) {
1624 0 : ShowSevereError(state, format("{}=\"{}\": illegal fan type specified.", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1625 0 : ShowContinueError(state, format(" The {} must specify that the fan object", state.dataIPShortCut->cCurrentModuleObject));
1626 0 : ShowContinueError(state,
1627 : " is of type FanSystemModel or Fan:OnOff in addition to the fan actually being of that type and defined elsewhere.");
1628 : }
1629 :
1630 12 : if (FanVolFlow != DataSizing::AutoSize && !errFlag) {
1631 7 : if (FanVolFlow < HPWH.OperatingAirFlowRate) {
1632 0 : ShowSevereError(state,
1633 0 : format("{} - air flow rate = {:.7T} in fan object {} is less than the HPWHs evaporator air flow rate.",
1634 0 : state.dataIPShortCut->cCurrentModuleObject,
1635 : FanVolFlow,
1636 0 : HPWH.FanName));
1637 0 : ShowContinueError(state, " The fan flow rate must be >= to the HPWHs evaporator volumetric air flow rate.");
1638 0 : ShowContinueError(state, format(" Occurs in unit = {}", HPWH.Name));
1639 0 : ErrorsFound = true;
1640 : }
1641 : }
1642 :
1643 : // Fan Placement
1644 12 : HPWH.fanPlace = static_cast<HVAC::FanPlace>(getEnumValue(HVAC::fanPlaceNamesUC, hpwhAlpha[24 + nAlphaOffset]));
1645 12 : if (HPWH.fanPlace == HVAC::FanPlace::Invalid) {
1646 0 : ShowSevereInvalidKey(state, eoh, hpwhAlphaFieldNames[24 + nAlphaOffset], hpwhAlpha[24 + nAlphaOffset]);
1647 0 : ErrorsFound = true;
1648 : }
1649 :
1650 12 : if (HPWH.DXCoilNum > 0 && !bIsVScoil) {
1651 : // get HPWH capacity, air inlet node, and PLF curve info from DX coil object
1652 12 : HPWH.Capacity = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2;
1653 12 : HPWH.DXCoilAirInletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
1654 12 : HPWH.DXCoilPLFFPLR = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).PLFFPLR(1);
1655 : // check the range of condenser pump power to be <= 5 gpm/ton
1656 12 : if (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower / state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2 >
1657 : 0.1422) {
1658 0 : ShowWarningError(
1659 : state,
1660 0 : format("{}= {}{}",
1661 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).DXCoilType,
1662 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).Name,
1663 0 : format(": Rated condenser pump power per watt of rated heating capacity has exceeded the recommended maximum of 0.1422 "
1664 : "W/W (41.67 watt/MBH). Condenser pump power per watt = {:.4T}",
1665 0 : (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower /
1666 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2))));
1667 : }
1668 0 : } else if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
1669 :
1670 0 : if (HPWH.bIsIHP) {
1671 0 : HPWH.Capacity =
1672 0 : GetDWHCoilCapacityIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1673 0 : HPWH.DXCoilAirInletNode = IntegratedHeatPump::GetCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1674 0 : HPWH.DXCoilPLFFPLR =
1675 0 : GetIHPDWHCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1676 : } else {
1677 0 : HPWH.Capacity = VariableSpeedCoils::GetCoilCapacityVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1678 0 : HPWH.DXCoilAirInletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1679 0 : HPWH.DXCoilPLFFPLR = VariableSpeedCoils::GetVSCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1680 : }
1681 : // check the range of condenser pump power to be <= 5 gpm/ton, will be checked in the coil object
1682 : }
1683 :
1684 12 : if (HPWH.OperatingWaterFlowRate == Constant::AutoCalculate) {
1685 7 : HPWH.OperatingWaterFlowRate = 0.00000004487 * HPWH.Capacity;
1686 7 : HPWH.WaterFlowRateAutoSized = true;
1687 : }
1688 :
1689 12 : if (HPWH.OperatingAirFlowRate == Constant::AutoCalculate) {
1690 7 : HPWH.OperatingAirFlowRate = 0.00005035 * HPWH.Capacity;
1691 7 : HPWH.AirFlowRateAutoSized = true;
1692 : }
1693 :
1694 : // On Cycle Parasitic Electric Load
1695 12 : HPWH.OnCycParaLoad = hpwhNumeric[6 + nNumericOffset];
1696 12 : if (HPWH.OnCycParaLoad < 0.0) {
1697 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1698 0 : ShowContinueError(state,
1699 0 : format("{} must be >= 0. {}{}",
1700 0 : hpwhNumericFieldNames[6 + nNumericOffset],
1701 0 : hpwhNumericFieldNames[6 + nNumericOffset],
1702 0 : format(" = {:.2T}", hpwhNumeric[6 + nNumericOffset])));
1703 0 : ErrorsFound = true;
1704 : }
1705 :
1706 : // Off Cycle Parasitic Electric Load
1707 12 : HPWH.OffCycParaLoad = hpwhNumeric[7 + nNumericOffset];
1708 12 : if (HPWH.OffCycParaLoad < 0.0) {
1709 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1710 0 : ShowContinueError(state,
1711 0 : format("{} must be >= 0. {}{}",
1712 0 : hpwhNumericFieldNames[7 + nNumericOffset],
1713 0 : hpwhNumericFieldNames[2 + nNumericOffset],
1714 0 : format(" = {:.2T}", hpwhNumeric[7 + nNumericOffset])));
1715 0 : ErrorsFound = true;
1716 : }
1717 :
1718 : // Parasitic Heat Rejection Location
1719 12 : if (Util::SameString(hpwhAlpha[25 + nAlphaOffset], "Zone")) {
1720 2 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::TempZone;
1721 2 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1722 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1723 0 : ShowContinueError(state, format("{} must be ZoneAirOnly or ZoneAndOutdoorAir", hpwhAlphaFieldNames[25 + nAlphaOffset]));
1724 0 : ShowContinueError(state, " when parasitic heat rejection location equals Zone.");
1725 0 : ErrorsFound = true;
1726 : }
1727 10 : } else if (Util::SameString(hpwhAlpha[25 + nAlphaOffset], "Outdoors")) {
1728 10 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::OutsideAir;
1729 : } else {
1730 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1731 0 : ShowContinueError(state, " parasitic heat rejection location must be either Zone or Outdoors.");
1732 0 : ErrorsFound = true;
1733 : }
1734 :
1735 : // Inlet Air Mixer Node
1736 : // get mixer/splitter nodes only when Inlet Air Configuration is ZoneAndOutdoorAir
1737 12 : if (!hpwhAlphaBlank[26 + nAlphaOffset]) {
1738 : // For the inlet air mixer node, NodeConnectionType is outlet from the HPWH inlet air node
1739 0 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1740 0 : HPWH.InletAirMixerNode = NodeInputManager::GetOnlySingleNode(state,
1741 0 : hpwhAlpha[26 + nAlphaOffset],
1742 : ErrorsFound,
1743 : objType,
1744 0 : HPWH.Name + "-INLET AIR MIXER",
1745 : DataLoopNode::NodeFluidType::Air,
1746 : DataLoopNode::ConnectionType::Outlet,
1747 : NodeInputManager::CompFluidStream::Primary,
1748 : DataLoopNode::ObjectIsNotParent);
1749 : } else {
1750 0 : ShowWarningError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1751 0 : ShowContinueError(state,
1752 : "Inlet air mixer node name specified but only required when Inlet Air Configuration is selected as "
1753 : "Zone and OutdoorAir. Node name disregarded and simulation continues.");
1754 : }
1755 12 : } else if (hpwhAlphaBlank[26 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1756 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1757 0 : ShowContinueError(state, "Inlet air mixer node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1758 0 : ErrorsFound = true;
1759 : }
1760 :
1761 : // Outlet Air Splitter Node
1762 12 : if (!hpwhAlphaBlank[27 + nAlphaOffset]) {
1763 : // For the outlet air splitter node, NodeConnectionType is inlet to the HPWH outlet air node
1764 0 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1765 0 : HPWH.OutletAirSplitterNode = NodeInputManager::GetOnlySingleNode(state,
1766 0 : hpwhAlpha[27 + nAlphaOffset],
1767 : ErrorsFound,
1768 : objType,
1769 0 : HPWH.Name + "-OUTLET AIR SPLITTER",
1770 : DataLoopNode::NodeFluidType::Air,
1771 : DataLoopNode::ConnectionType::Inlet,
1772 : NodeInputManager::CompFluidStream::Primary,
1773 : DataLoopNode::ObjectIsNotParent);
1774 : } else {
1775 0 : ShowWarningError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1776 0 : ShowContinueError(state,
1777 : "Outlet air splitter node name specified but only required when Inlet Air Configuration is selected as "
1778 : "ZoneAndOutdoorAir. Node name disregarded and simulation continues.");
1779 : }
1780 12 : } else if (hpwhAlphaBlank[27 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1781 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1782 0 : ShowContinueError(state, "Outlet air splitter node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1783 0 : ErrorsFound = true;
1784 : }
1785 :
1786 : // get node data for HPWH
1787 12 : if (HPWH.InletAirMixerNode != 0) {
1788 : // when mixer/splitter nodes are used the HPWH's inlet/outlet node are set up as DataLoopNode::ObjectIsNotParent
1789 :
1790 0 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1791 0 : hpwhAlpha[7 + nAlphaOffset],
1792 : ErrorsFound,
1793 : objType,
1794 0 : HPWH.Name + "-INLET AIR MIXER",
1795 : DataLoopNode::NodeFluidType::Air,
1796 : DataLoopNode::ConnectionType::Inlet,
1797 : NodeInputManager::CompFluidStream::Primary,
1798 : DataLoopNode::ObjectIsNotParent);
1799 :
1800 0 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1801 0 : hpwhAlpha[8 + nAlphaOffset],
1802 : ErrorsFound,
1803 : objType,
1804 0 : HPWH.Name + "-OUTLET AIR SPLITTER",
1805 : DataLoopNode::NodeFluidType::Air,
1806 : DataLoopNode::ConnectionType::Outlet,
1807 : NodeInputManager::CompFluidStream::Primary,
1808 : DataLoopNode::ObjectIsNotParent);
1809 :
1810 0 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1811 0 : hpwhAlpha[9 + nAlphaOffset],
1812 : ErrorsFound,
1813 : objType,
1814 0 : HPWH.Name,
1815 : DataLoopNode::NodeFluidType::Air,
1816 : DataLoopNode::ConnectionType::OutsideAirReference,
1817 : NodeInputManager::CompFluidStream::Primary,
1818 : DataLoopNode::ObjectIsParent);
1819 0 : if (!hpwhAlpha[9 + nAlphaOffset].empty()) {
1820 : bool Okay;
1821 0 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, HPWH.OutsideAirNode, Okay);
1822 0 : if (!Okay) {
1823 0 : ShowWarningError(state,
1824 0 : format("{}=\"{}\": Adding outdoor air node={}",
1825 0 : state.dataIPShortCut->cCurrentModuleObject,
1826 0 : HPWH.Name,
1827 0 : hpwhAlpha[9 + nAlphaOffset]));
1828 : }
1829 : }
1830 :
1831 0 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1832 0 : hpwhAlpha[10 + nAlphaOffset],
1833 : ErrorsFound,
1834 : objType,
1835 0 : HPWH.Name,
1836 : DataLoopNode::NodeFluidType::Air,
1837 : DataLoopNode::ConnectionType::ReliefAir,
1838 : NodeInputManager::CompFluidStream::Primary,
1839 : DataLoopNode::ObjectIsParent);
1840 :
1841 : } else {
1842 : // when mixer/splitter nodes are NOT used the HPWH's inlet/outlet nodes are set up as DataLoopNode::ObjectIsParent
1843 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1844 : // for scheduled HPWH's the inlet node is not on any branch or parent object, make it an outlet node
1845 : // to avoid node connection errors
1846 5 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1847 5 : hpwhAlpha[7 + nAlphaOffset],
1848 : ErrorsFound,
1849 : objType,
1850 5 : HPWH.Name,
1851 : DataLoopNode::NodeFluidType::Air,
1852 : DataLoopNode::ConnectionType::Outlet,
1853 : NodeInputManager::CompFluidStream::Primary,
1854 : DataLoopNode::ObjectIsParent);
1855 :
1856 5 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1857 5 : hpwhAlpha[8 + nAlphaOffset],
1858 : ErrorsFound,
1859 : objType,
1860 5 : HPWH.Name,
1861 : DataLoopNode::NodeFluidType::Air,
1862 : DataLoopNode::ConnectionType::Outlet,
1863 : NodeInputManager::CompFluidStream::Primary,
1864 : DataLoopNode::ObjectIsParent);
1865 :
1866 : } else { // HPWH is connected to a zone with no mixer/splitter nodes
1867 7 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1868 4 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1869 4 : hpwhAlpha[7 + nAlphaOffset],
1870 : ErrorsFound,
1871 : objType,
1872 4 : HPWH.Name,
1873 : DataLoopNode::NodeFluidType::Air,
1874 : DataLoopNode::ConnectionType::Inlet,
1875 : NodeInputManager::CompFluidStream::Primary,
1876 : DataLoopNode::ObjectIsParent);
1877 :
1878 4 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1879 4 : hpwhAlpha[8 + nAlphaOffset],
1880 : ErrorsFound,
1881 : objType,
1882 4 : HPWH.Name,
1883 : DataLoopNode::NodeFluidType::Air,
1884 : DataLoopNode::ConnectionType::Outlet,
1885 : NodeInputManager::CompFluidStream::Primary,
1886 : DataLoopNode::ObjectIsParent);
1887 : } else { // HPWH is located outdoors
1888 3 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1889 3 : hpwhAlpha[9 + nAlphaOffset],
1890 : ErrorsFound,
1891 : objType,
1892 3 : HPWH.Name,
1893 : DataLoopNode::NodeFluidType::Air,
1894 : DataLoopNode::ConnectionType::OutsideAirReference,
1895 : NodeInputManager::CompFluidStream::Primary,
1896 : DataLoopNode::ObjectIsParent);
1897 3 : if (!hpwhAlphaBlank[9 + nAlphaOffset]) {
1898 : bool Okay;
1899 2 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, HPWH.OutsideAirNode, Okay);
1900 2 : if (!Okay) {
1901 4 : ShowWarningError(state,
1902 4 : format("{}=\"{}\": Adding outdoor air node ={}",
1903 2 : state.dataIPShortCut->cCurrentModuleObject,
1904 2 : HPWH.Name,
1905 4 : hpwhAlpha[9 + nAlphaOffset]));
1906 : }
1907 : }
1908 :
1909 3 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1910 3 : hpwhAlpha[10 + nAlphaOffset],
1911 : ErrorsFound,
1912 : objType,
1913 3 : HPWH.Name,
1914 : DataLoopNode::NodeFluidType::Air,
1915 : DataLoopNode::ConnectionType::ReliefAir,
1916 : NodeInputManager::CompFluidStream::Primary,
1917 : DataLoopNode::ObjectIsParent);
1918 : }
1919 : }
1920 : }
1921 : // check that required node names are present
1922 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule || HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1923 9 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1924 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1925 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1926 0 : ShowContinueError(
1927 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[7 + nAlphaOffset], hpwhAlphaFieldNames[8 + nAlphaOffset]));
1928 0 : ErrorsFound = true;
1929 : }
1930 3 : } else if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
1931 3 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1932 1 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1933 1 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1934 2 : ShowContinueError(
1935 2 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[9 + nAlphaOffset], hpwhAlphaFieldNames[10 + nAlphaOffset]));
1936 1 : ErrorsFound = true;
1937 : }
1938 0 : } else if (HPWH.InletAirMixerNode > 0 && HPWH.OutletAirSplitterNode > 0 && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1939 0 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0 || HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1940 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1941 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1942 0 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1943 0 : ShowContinueError(
1944 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[7 + nAlphaOffset], hpwhAlphaFieldNames[8 + nAlphaOffset]));
1945 : }
1946 0 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1947 0 : ShowContinueError(
1948 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[9 + nAlphaOffset], hpwhAlphaFieldNames[10 + nAlphaOffset]));
1949 : }
1950 0 : ErrorsFound = true;
1951 : }
1952 : }
1953 :
1954 : // check that the HPWH inlet and outlet nodes are in the same zone (ZoneHVAC:EquipmentConnections) when
1955 : // Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
1956 12 : if ((HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) &&
1957 4 : HPWH.AmbientTempZone > 0) {
1958 2 : if (allocated(state.dataZoneEquip->ZoneEquipConfig)) {
1959 1 : bool FoundInletNode = false;
1960 1 : bool FoundOutletNode = false;
1961 1 : int ZoneNum = HPWH.AmbientTempZone;
1962 1 : if (ZoneNum <= state.dataGlobal->NumOfZones) {
1963 4 : for (int SupAirIn = 1; SupAirIn <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumInletNodes; ++SupAirIn) {
1964 3 : if (HPWH.HeatPumpAirOutletNode != state.dataZoneEquip->ZoneEquipConfig(ZoneNum).InletNode(SupAirIn)) continue;
1965 1 : FoundOutletNode = true;
1966 : }
1967 3 : for (int ExhAirOut = 1; ExhAirOut <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumExhaustNodes; ++ExhAirOut) {
1968 2 : if (HPWH.HeatPumpAirInletNode != state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ExhaustNode(ExhAirOut)) continue;
1969 1 : FoundInletNode = true;
1970 : }
1971 1 : if (!FoundInletNode) {
1972 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1973 0 : ShowContinueError(state,
1974 0 : format("The HPWH's air inlet node name = {} was not properly specified ", hpwhAlpha[7 + nAlphaOffset]));
1975 0 : ShowContinueError(
1976 : state,
1977 0 : format("as an exhaust air node for zone = {} in a ZoneHVAC:EquipmentConnections object.", hpwhAlpha[13 + nAlphaOffset]));
1978 0 : ErrorsFound = true;
1979 : }
1980 1 : if (!FoundOutletNode) {
1981 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1982 0 : ShowContinueError(state,
1983 0 : format("The HPWH's air outlet node name = {} was not properly specified ", hpwhAlpha[8 + nAlphaOffset]));
1984 0 : ShowContinueError(
1985 : state,
1986 0 : format("as an inlet air node for zone = {} in a ZoneHVAC:EquipmentConnections object.", hpwhAlpha[13 + nAlphaOffset]));
1987 0 : ErrorsFound = true;
1988 : }
1989 : }
1990 : } else {
1991 1 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1992 2 : ShowContinueError(state,
1993 : "Heat pump water heater air inlet node name and air outlet node name must be listed in a "
1994 : "ZoneHVAC:EquipmentConnections object when Inlet Air Configuration is equal to ZoneAirOnly or "
1995 : "ZoneAndOutdoorAir.");
1996 1 : ErrorsFound = true;
1997 : }
1998 : }
1999 :
2000 : // only get the inlet air mixer schedule if the inlet air configuration is zone and outdoor air
2001 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
2002 0 : if (hpwhAlphaBlank[28 + nAlphaOffset]) {
2003 0 : ShowSevereEmptyField(state, eoh, hpwhAlphaFieldNames[28 + nAlphaOffset]);
2004 0 : ErrorsFound = true;
2005 : // set outlet air splitter schedule index equal to inlet air mixer schedule index
2006 : // (place holder for when zone pressurization/depressurization is allowed and different schedules can be used)
2007 0 : } else if ((HPWH.inletAirMixerSched = HPWH.outletAirSplitterSched = Sched::GetSchedule(state, hpwhAlpha[28 + nAlphaOffset])) == nullptr) {
2008 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[28 + nAlphaOffset], hpwhAlpha[28 + nAlphaOffset]);
2009 0 : ErrorsFound = true;
2010 0 : } else if (!HPWH.inletAirMixerSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
2011 0 : Sched::ShowSevereBadMinMax(
2012 0 : state, eoh, hpwhAlphaFieldNames[28 + nAlphaOffset], hpwhAlpha[28 + nAlphaOffset], Clusive::In, 0.0, Clusive::In, 1.0);
2013 0 : ErrorsFound = true;
2014 : }
2015 : }
2016 :
2017 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2018 12 : if (HPWH.fanPlace == HVAC::FanPlace::DrawThru) {
2019 12 : if (HPWH.OutletAirSplitterNode != 0) {
2020 0 : HPWH.FanOutletNode = HPWH.OutletAirSplitterNode;
2021 12 : } else if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2022 3 : HPWH.FanOutletNode = HPWH.ExhaustAirNode;
2023 : } else {
2024 9 : HPWH.FanOutletNode = HPWH.HeatPumpAirOutletNode;
2025 : }
2026 0 : } else if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2027 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2028 0 : if (bIsVScoil) {
2029 0 : if (HPWH.bIsIHP) {
2030 0 : HPWH.FanOutletNode = IntegratedHeatPump::GetDWHCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2031 : } else {
2032 0 : HPWH.FanOutletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2033 : }
2034 : } else {
2035 0 : HPWH.FanOutletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
2036 : }
2037 : }
2038 :
2039 : // check that fan outlet node is indeed correct
2040 12 : int FanOutletNodeNum = state.dataFans->fans(HPWH.FanNum)->outletNodeNum;
2041 :
2042 12 : if (FanOutletNodeNum != HPWH.FanOutletNode) {
2043 1 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2044 2 : ShowContinueError(state, "Heat pump water heater fan outlet node name does not match next connected component.");
2045 1 : if (FanOutletNodeNum != 0) {
2046 1 : ShowContinueError(state, format("Fan outlet node name = {}", state.dataLoopNodes->NodeID(FanOutletNodeNum)));
2047 : }
2048 1 : if (HPWH.FanOutletNode != 0) {
2049 0 : ShowContinueError(state, format("Expected fan outlet node name = {}", state.dataLoopNodes->NodeID(HPWH.FanOutletNode)));
2050 : }
2051 1 : ErrorsFound = true;
2052 : }
2053 12 : int FanInletNodeNum = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
2054 :
2055 12 : int HPWHFanInletNodeNum(0);
2056 12 : if (HPWH.InletAirMixerNode != 0) {
2057 0 : HPWHFanInletNodeNum = HPWH.InletAirMixerNode;
2058 : } else {
2059 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2060 3 : HPWHFanInletNodeNum = HPWH.OutsideAirNode;
2061 : } else {
2062 9 : HPWHFanInletNodeNum = HPWH.HeatPumpAirInletNode;
2063 : }
2064 : }
2065 12 : if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2066 0 : if (FanInletNodeNum != HPWHFanInletNodeNum) {
2067 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2068 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2069 0 : if (FanOutletNodeNum != 0) {
2070 0 : ShowContinueError(state, format("Fan inlet node name = {}", state.dataLoopNodes->NodeID(FanInletNodeNum)));
2071 : }
2072 0 : if (HPWH.FanOutletNode != 0) {
2073 0 : ShowContinueError(state, format("Expected fan inlet node name = {}", state.dataLoopNodes->NodeID(HPWHFanInletNodeNum)));
2074 : }
2075 0 : ErrorsFound = true;
2076 : }
2077 : }
2078 :
2079 12 : int DXCoilAirOutletNodeNum(0);
2080 12 : if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
2081 0 : if (HPWH.bIsIHP) {
2082 0 : DXCoilAirOutletNodeNum = IntegratedHeatPump::GetDWHCoilOutletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2083 : } else {
2084 0 : DXCoilAirOutletNodeNum = VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2085 : }
2086 :
2087 12 : } else if (HPWH.DXCoilNum > 0) {
2088 12 : DXCoilAirOutletNodeNum = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirOutNode;
2089 : }
2090 12 : if (HPWH.fanPlace == HVAC::FanPlace::DrawThru) {
2091 12 : if (FanInletNodeNum != DXCoilAirOutletNodeNum) {
2092 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2093 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2094 0 : if (FanInletNodeNum != 0) {
2095 0 : ShowContinueError(state, format("Fan inlet node name = {}", state.dataLoopNodes->NodeID(FanInletNodeNum)));
2096 : }
2097 0 : if (DXCoilAirOutletNodeNum != 0) {
2098 0 : ShowContinueError(state, format("Expected fan inlet node name = {}", state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum)));
2099 : }
2100 0 : ErrorsFound = true;
2101 : }
2102 0 : } else if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2103 0 : int HPWHCoilOutletNodeNum(0);
2104 0 : if (HPWH.OutletAirSplitterNode != 0) {
2105 0 : HPWHCoilOutletNodeNum = HPWH.OutletAirSplitterNode;
2106 : } else {
2107 0 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2108 0 : HPWHCoilOutletNodeNum = HPWH.ExhaustAirNode;
2109 : } else {
2110 0 : HPWHCoilOutletNodeNum = HPWH.HeatPumpAirOutletNode;
2111 : }
2112 : }
2113 0 : if (DXCoilAirOutletNodeNum != HPWHCoilOutletNodeNum) {
2114 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2115 0 : ShowContinueError(state, "Heat pump water heater coil outlet node name does not match next connected component.");
2116 0 : if (DXCoilAirOutletNodeNum != 0) {
2117 0 : ShowContinueError(state, format("Coil outlet node name = {}", state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum)));
2118 : }
2119 0 : if (HPWHCoilOutletNodeNum != 0) {
2120 0 : ShowContinueError(state, format("Expected coil outlet node name = {}", state.dataLoopNodes->NodeID(HPWHCoilOutletNodeNum)));
2121 : }
2122 0 : ErrorsFound = true;
2123 : }
2124 : }
2125 :
2126 : // set the max mass flow rate for outdoor fans
2127 12 : if (HPWH.FanOutletNode > 0)
2128 11 : state.dataLoopNodes->Node(HPWH.FanOutletNode).MassFlowRateMax =
2129 22 : HPWH.OperatingAirFlowRate * Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, 20.0, 0.0);
2130 :
2131 12 : if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2132 0 : if (HPWH.InletAirMixerNode > 0) {
2133 0 : HPWH.FanInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2134 0 : HPWH.FanOutletNode_str = "UNDEFINED";
2135 : } else {
2136 0 : if (HPWH.OutsideAirNode == 0) {
2137 0 : HPWH.FanInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2138 0 : HPWH.FanOutletNode_str = "UNDEFINED";
2139 : } else {
2140 0 : HPWH.FanInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2141 0 : HPWH.FanOutletNode_str = "UNDEFINED";
2142 : }
2143 : }
2144 0 : if (HPWH.OutletAirSplitterNode > 0) {
2145 0 : HPWH.CoilInletNode_str = "UNDEFINED";
2146 0 : HPWH.CoilOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2147 : } else {
2148 0 : if (HPWH.OutsideAirNode == 0) {
2149 0 : HPWH.CoilInletNode_str = "UNDEFINED";
2150 0 : HPWH.CoilOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2151 : } else {
2152 0 : HPWH.CoilInletNode_str = "UNDEFINED";
2153 0 : HPWH.CoilOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2154 : }
2155 : }
2156 : } else {
2157 12 : if (HPWH.InletAirMixerNode > 0) {
2158 0 : HPWH.CoilInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2159 0 : HPWH.CoilOutletNode_str = "UNDEFINED";
2160 : } else {
2161 12 : if (HPWH.OutsideAirNode == 0) {
2162 10 : HPWH.CoilInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2163 10 : HPWH.CoilOutletNode_str = "UNDEFINED";
2164 : } else {
2165 2 : HPWH.CoilInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2166 2 : HPWH.CoilOutletNode_str = "UNDEFINED";
2167 : }
2168 : }
2169 12 : if (HPWH.OutletAirSplitterNode > 0) {
2170 0 : HPWH.FanInletNode_str = "UNDEFINED";
2171 0 : HPWH.FanOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2172 : } else {
2173 12 : if (HPWH.OutsideAirNode == 0) {
2174 10 : HPWH.FanInletNode_str = "UNDEFINED";
2175 10 : HPWH.FanOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2176 : } else {
2177 2 : HPWH.FanInletNode_str = "UNDEFINED";
2178 2 : HPWH.FanOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2179 : }
2180 : }
2181 : }
2182 :
2183 : // set up comp set for air side nodes (can be blow thru or draw thru, may or may not have damper nodes)
2184 12 : if (HPWH.bIsIHP) {
2185 0 : BranchNodeConnections::SetUpCompSets(
2186 0 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName + " Outdoor Coil", HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2187 : } else {
2188 12 : BranchNodeConnections::SetUpCompSets(
2189 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2190 : }
2191 :
2192 24 : BranchNodeConnections::SetUpCompSets(
2193 12 : state, HPWH.Type, HPWH.Name, HVAC::fanTypeNames[(int)HPWH.fanType], HPWH.FanName, HPWH.FanInletNode_str, HPWH.FanOutletNode_str);
2194 :
2195 : // Control Logic Flag
2196 19 : std::string CtrlLogicFlag = hpwhAlphaBlank[29 + nAlphaOffset] ? "SIMULTANEOUS" : hpwhAlpha[29 + nAlphaOffset];
2197 12 : if (Util::SameString(CtrlLogicFlag, "SIMULTANEOUS")) {
2198 7 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = true;
2199 5 : } else if (Util::SameString(CtrlLogicFlag, "MUTUALLYEXCLUSIVE")) {
2200 5 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = false;
2201 : } else {
2202 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2203 0 : ShowContinueError(state, format("{} is not a valid value for field Tank Element Control Logic.", CtrlLogicFlag));
2204 0 : ErrorsFound = true;
2205 : }
2206 :
2207 : // Control Sensor 1 Location In Stratified Tank
2208 12 : if (!hpwhNumericBlank[8 + nNumericOffset]) {
2209 5 : HPWH.ControlSensor1Height = hpwhNumeric[8 + nNumericOffset];
2210 : } else {
2211 : // use heater1 location, which we don't know right now
2212 7 : HPWH.ControlSensor1Height = -1.0;
2213 : }
2214 :
2215 : // Control Sensor 1 Weight
2216 12 : HPWH.ControlSensor1Weight = hpwhNumericBlank[9 + nNumericOffset] ? 1.0 : hpwhNumeric[9 + nNumericOffset];
2217 :
2218 : // Control Sensor 2 Location In Stratified Tank
2219 12 : if (!hpwhNumericBlank[10 + nNumericOffset]) {
2220 12 : HPWH.ControlSensor2Height = hpwhNumeric[10 + nNumericOffset];
2221 : } else {
2222 0 : HPWH.ControlSensor2Height = -1.0;
2223 : }
2224 :
2225 : // Control Sensor 2 Weight
2226 12 : HPWH.ControlSensor2Weight = 1.0 - HPWH.ControlSensor1Weight;
2227 12 : }
2228 :
2229 10 : return ErrorsFound;
2230 : }
2231 :
2232 16 : bool getWaterHeaterMixedInputs(EnergyPlusData &state)
2233 : {
2234 16 : bool ErrorsFound = false;
2235 16 : state.dataIPShortCut->cCurrentModuleObject = cMixedWHModuleObj;
2236 : static constexpr std::string_view routineName = "getWaterHeaterMixedInputs";
2237 :
2238 34 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed; ++WaterThermalTankNum) {
2239 : int NumAlphas;
2240 : int NumNums;
2241 : int IOStat;
2242 36 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2243 18 : state.dataIPShortCut->cCurrentModuleObject,
2244 : WaterThermalTankNum,
2245 18 : state.dataIPShortCut->cAlphaArgs,
2246 : NumAlphas,
2247 18 : state.dataIPShortCut->rNumericArgs,
2248 : NumNums,
2249 : IOStat,
2250 18 : state.dataIPShortCut->lNumericFieldBlanks,
2251 18 : state.dataIPShortCut->lAlphaFieldBlanks,
2252 18 : state.dataIPShortCut->cAlphaFieldNames,
2253 18 : state.dataIPShortCut->cNumericFieldNames);
2254 :
2255 18 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
2256 18 : GlobalNames::VerifyUniqueInterObjectName(state,
2257 18 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2258 18 : state.dataIPShortCut->cAlphaArgs(1),
2259 18 : state.dataIPShortCut->cCurrentModuleObject,
2260 18 : state.dataIPShortCut->cAlphaFieldNames(1),
2261 : ErrorsFound);
2262 :
2263 18 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2264 :
2265 18 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2266 18 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2267 18 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterMixed;
2268 :
2269 18 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
2270 0 : ShowSevereError(state, "Fluid properties for WATER not found");
2271 0 : ErrorsFound = true;
2272 : }
2273 :
2274 : // default to always on
2275 18 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2276 18 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2277 :
2278 : // A user field will be added in a later release
2279 18 : Tank.EndUseSubcategoryName = "Water Heater";
2280 :
2281 18 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2282 18 : if (Tank.Volume == DataSizing::AutoSize) {
2283 1 : Tank.VolumeWasAutoSized = true;
2284 : }
2285 18 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
2286 : // Set volume to a really small number to simulate a tankless/instantaneous water heater
2287 0 : Tank.Volume = 0.000001; // = 1 cm3
2288 : }
2289 :
2290 18 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
2291 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2));
2292 0 : ErrorsFound = true;
2293 18 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
2294 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
2295 0 : ErrorsFound = true;
2296 : }
2297 :
2298 18 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
2299 18 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
2300 : } else {
2301 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2302 0 : Tank.DeadBandDeltaTemp = 0.5;
2303 : }
2304 :
2305 18 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2306 18 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
2307 : } else {
2308 : // Default to very large number
2309 : // BG comment why a large number here why not boilng point of water?
2310 0 : Tank.TankTempLimit = 100.0; // 1.0E9
2311 : }
2312 :
2313 18 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
2314 18 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2315 0 : Tank.MaxCapacityWasAutoSized = true;
2316 : }
2317 :
2318 18 : if ((state.dataIPShortCut->rNumericArgs(5) > Tank.MaxCapacity) && (!Tank.MaxCapacityWasAutoSized)) {
2319 0 : ShowSevereError(state,
2320 0 : format("{} = {}: Heater Minimum Capacity cannot be greater than Heater Maximum Capacity",
2321 0 : state.dataIPShortCut->cCurrentModuleObject,
2322 0 : state.dataIPShortCut->cAlphaArgs(1)));
2323 0 : ErrorsFound = true;
2324 : } else {
2325 18 : Tank.MinCapacity = state.dataIPShortCut->rNumericArgs(5);
2326 : }
2327 :
2328 : // Validate Heater Control Type
2329 18 : Tank.ControlType =
2330 18 : static_cast<HeaterControlMode>(getEnumValue(HeaterControlModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
2331 18 : switch (Tank.ControlType) {
2332 18 : case HeaterControlMode::Cycle: {
2333 18 : Tank.MinCapacity = Tank.MaxCapacity;
2334 18 : break;
2335 : }
2336 0 : case HeaterControlMode::Modulate: {
2337 :
2338 : // CASE ('MODULATE WITH OVERHEAT') ! Not yet implemented
2339 :
2340 : // CASE ('MODULATE WITH UNDERHEAT') ! Not yet implemented
2341 :
2342 0 : break;
2343 : }
2344 0 : default: {
2345 0 : ShowSevereError(state,
2346 0 : format("{} = {}: Invalid Control Type entered={}",
2347 0 : state.dataIPShortCut->cCurrentModuleObject,
2348 0 : state.dataIPShortCut->cAlphaArgs(1),
2349 0 : state.dataIPShortCut->cAlphaArgs(3)));
2350 0 : ErrorsFound = true;
2351 0 : break;
2352 : }
2353 : }
2354 :
2355 18 : Tank.VolFlowRateMin = state.dataIPShortCut->rNumericArgs(6);
2356 18 : Tank.VolFlowRateMin = max(0.0, Tank.VolFlowRateMin);
2357 18 : Tank.IgnitionDelay = state.dataIPShortCut->rNumericArgs(7); // Not yet implemented
2358 :
2359 : // Validate Heater Fuel Type
2360 18 : Tank.FuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(4)));
2361 18 : switch (Tank.FuelType) {
2362 0 : case Constant::eFuel::Invalid: {
2363 0 : ShowSevereError(state,
2364 0 : format("{} = {}: Invalid Heater Fuel Type entered={}",
2365 0 : state.dataIPShortCut->cCurrentModuleObject,
2366 0 : state.dataIPShortCut->cAlphaArgs(1),
2367 0 : state.dataIPShortCut->cAlphaArgs(4)));
2368 : // Set to Electric to avoid errors when setting up output variables
2369 0 : Tank.FuelType = Constant::eFuel::Electricity;
2370 0 : ErrorsFound = true;
2371 0 : break;
2372 : }
2373 18 : default:
2374 18 : break;
2375 : }
2376 :
2377 18 : if (state.dataIPShortCut->rNumericArgs(8) > 0.0) {
2378 18 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(8);
2379 18 : if (state.dataIPShortCut->rNumericArgs(8) > 1.0) {
2380 0 : ShowWarningError(state,
2381 0 : fmt::format("{} = {}: {}={} should not typically be greater than 1.",
2382 0 : state.dataIPShortCut->cCurrentModuleObject,
2383 0 : state.dataIPShortCut->cAlphaArgs(1),
2384 0 : state.dataIPShortCut->cNumericFieldNames(8),
2385 0 : state.dataIPShortCut->rNumericArgs(8)));
2386 : }
2387 : } else {
2388 0 : ShowSevereError(state,
2389 0 : format("{} = {}: Heater Thermal Efficiency must be greater than zero",
2390 0 : state.dataIPShortCut->cCurrentModuleObject,
2391 0 : state.dataIPShortCut->cAlphaArgs(1)));
2392 0 : ErrorsFound = true;
2393 : }
2394 :
2395 18 : if (!state.dataIPShortCut->cAlphaArgs(5).empty()) {
2396 0 : Tank.PLFCurve = Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(5));
2397 0 : if (Tank.PLFCurve == 0) {
2398 0 : ShowSevereError(state,
2399 0 : format("{} = {}: Part Load Factor curve not found = {}",
2400 0 : state.dataIPShortCut->cCurrentModuleObject,
2401 0 : state.dataIPShortCut->cAlphaArgs(1),
2402 0 : state.dataIPShortCut->cAlphaArgs(5)));
2403 0 : ErrorsFound = true;
2404 : } else {
2405 : bool IsValid;
2406 0 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::ValidatePLFCurve(state, Tank.PLFCurve, IsValid);
2407 :
2408 0 : if (!IsValid) {
2409 0 : ShowSevereError(
2410 : state,
2411 0 : format("{} = {}: Part Load Factor curve failed to evaluate to greater than zero for all numbers in the domain of 0 to 1",
2412 0 : state.dataIPShortCut->cCurrentModuleObject,
2413 0 : state.dataIPShortCut->cAlphaArgs(1)));
2414 0 : ErrorsFound = true;
2415 : }
2416 :
2417 0 : ErrorsFound |= Curve::CheckCurveDims(state,
2418 : Tank.PLFCurve, // Curve index
2419 : {1}, // Valid dimensions
2420 : routineName, // Routine name
2421 0 : state.dataIPShortCut->cCurrentModuleObject, // Object Type
2422 : Tank.Name, // Object Name
2423 0 : state.dataIPShortCut->cAlphaFieldNames(5)); // Field Name
2424 : }
2425 : }
2426 :
2427 18 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(9);
2428 :
2429 : // Validate Off-Cycle Parasitic Fuel Type
2430 18 : Tank.OffCycParaFuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(6)));
2431 18 : switch (Tank.OffCycParaFuelType) {
2432 1 : case Constant::eFuel::Invalid:
2433 1 : if (state.dataIPShortCut->cAlphaArgs(6).empty()) { // If blank, default to Fuel Type for heater
2434 1 : Tank.OffCycParaFuelType = Tank.FuelType;
2435 : } else { // could have been an unsupported value
2436 0 : ShowSevereError(state,
2437 0 : format("{} = {}: Invalid Off-Cycle Parasitic Fuel Type entered={}",
2438 0 : state.dataIPShortCut->cCurrentModuleObject,
2439 0 : state.dataIPShortCut->cAlphaArgs(1),
2440 0 : state.dataIPShortCut->cAlphaArgs(6)));
2441 : // Set to Electric to avoid errors when setting up output variables
2442 0 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
2443 0 : ErrorsFound = true;
2444 : }
2445 1 : break;
2446 17 : default:
2447 17 : break;
2448 : }
2449 :
2450 18 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(10);
2451 :
2452 18 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(11);
2453 :
2454 : // Validate On-Cycle Parasitic Fuel Type
2455 18 : Tank.OnCycParaFuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(7)));
2456 18 : switch (Tank.OnCycParaFuelType) {
2457 1 : case Constant::eFuel::Invalid:
2458 1 : if (state.dataIPShortCut->cAlphaArgs(7).empty()) { // If blank, default to Fuel Type for heater
2459 1 : Tank.OnCycParaFuelType = Tank.FuelType;
2460 : } else { // could have been an unsupported value
2461 0 : ShowSevereError(state,
2462 0 : format("{} = {}: Invalid On-Cycle Parasitic Fuel Type entered={}",
2463 0 : state.dataIPShortCut->cCurrentModuleObject,
2464 0 : state.dataIPShortCut->cAlphaArgs(1),
2465 0 : state.dataIPShortCut->cAlphaArgs(7)));
2466 : // Set to Electric to avoid errors when setting up output variables
2467 0 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
2468 0 : ErrorsFound = true;
2469 : }
2470 1 : break;
2471 17 : default:
2472 17 : break;
2473 : }
2474 :
2475 18 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(12);
2476 :
2477 18 : Tank.AmbientTempIndicator =
2478 18 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(8))));
2479 18 : switch (Tank.AmbientTempIndicator) {
2480 :
2481 14 : case WTTAmbientTemp::Schedule: {
2482 14 : if (state.dataIPShortCut->lAlphaFieldBlanks(9)) {
2483 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(9));
2484 0 : ErrorsFound = true;
2485 14 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(9))) == nullptr) {
2486 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(9), state.dataIPShortCut->cAlphaArgs(9));
2487 0 : ErrorsFound = true;
2488 : }
2489 14 : } break;
2490 :
2491 4 : case WTTAmbientTemp::TempZone: {
2492 4 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(10), state.dataHeatBal->Zone);
2493 4 : if (Tank.AmbientTempZone == 0) {
2494 4 : ShowSevereError(state,
2495 8 : format("{} = {}: Ambient Temperature Zone not found = {}",
2496 2 : state.dataIPShortCut->cCurrentModuleObject,
2497 2 : state.dataIPShortCut->cAlphaArgs(1),
2498 2 : state.dataIPShortCut->cAlphaArgs(10)));
2499 2 : ErrorsFound = true;
2500 : }
2501 4 : } break;
2502 :
2503 0 : case WTTAmbientTemp::OutsideAir: {
2504 0 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
2505 0 : state.dataIPShortCut->cAlphaArgs(11),
2506 : ErrorsFound,
2507 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2508 0 : state.dataIPShortCut->cAlphaArgs(1),
2509 : DataLoopNode::NodeFluidType::Air,
2510 : DataLoopNode::ConnectionType::OutsideAirReference,
2511 : NodeInputManager::CompFluidStream::Primary,
2512 : DataLoopNode::ObjectIsNotParent);
2513 0 : if (!state.dataIPShortCut->cAlphaArgs(11).empty()) {
2514 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
2515 0 : ShowSevereError(state,
2516 0 : format("{} = {}: Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node",
2517 0 : state.dataIPShortCut->cCurrentModuleObject,
2518 0 : state.dataIPShortCut->cAlphaArgs(1)));
2519 0 : ShowContinueError(state, format("...Referenced Node Name={}", state.dataIPShortCut->cAlphaArgs(11)));
2520 0 : ErrorsFound = true;
2521 : }
2522 : } else {
2523 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
2524 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
2525 0 : ErrorsFound = true;
2526 : }
2527 :
2528 0 : break;
2529 : }
2530 0 : default: {
2531 0 : ShowSevereError(state,
2532 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
2533 0 : state.dataIPShortCut->cCurrentModuleObject,
2534 0 : state.dataIPShortCut->cAlphaArgs(1),
2535 0 : state.dataIPShortCut->cAlphaArgs(8)));
2536 0 : ShowContinueError(state, " Valid entries are SCHEDULE, ZONE, and OUTDOORS.");
2537 0 : ErrorsFound = true;
2538 0 : break;
2539 : }
2540 : }
2541 :
2542 18 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(13);
2543 18 : Tank.OffCycLossFracToZone = state.dataIPShortCut->rNumericArgs(14);
2544 :
2545 18 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(15);
2546 18 : Tank.OnCycLossFracToZone = state.dataIPShortCut->rNumericArgs(16);
2547 18 : Real64 rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
2548 :
2549 18 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(17) * rho;
2550 :
2551 18 : if ((state.dataIPShortCut->cAlphaArgs(14).empty()) && (state.dataIPShortCut->cAlphaArgs(15).empty())) {
2552 12 : if (state.dataIPShortCut->lAlphaFieldBlanks(12)) {
2553 9 : } else if ((Tank.flowRateSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(12))) == nullptr) {
2554 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(12), state.dataIPShortCut->cAlphaArgs(12));
2555 0 : ErrorsFound = true;
2556 : }
2557 : }
2558 :
2559 18 : if (state.dataIPShortCut->lAlphaFieldBlanks(13)) {
2560 0 : } else if ((Tank.useInletTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(13))) == nullptr) {
2561 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(13), state.dataIPShortCut->cAlphaArgs(13));
2562 0 : ErrorsFound = true;
2563 : }
2564 :
2565 18 : if (NumNums > 17) {
2566 18 : if ((state.dataIPShortCut->rNumericArgs(18) > 1) || (state.dataIPShortCut->rNumericArgs(18) < 0)) {
2567 0 : ShowSevereError(state,
2568 0 : format("{} = {}: Use Side Effectiveness is out of bounds (0 to 1)",
2569 0 : state.dataIPShortCut->cCurrentModuleObject,
2570 0 : state.dataIPShortCut->cAlphaArgs(1)));
2571 0 : ErrorsFound = true;
2572 : }
2573 18 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(18);
2574 : } else {
2575 0 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
2576 : }
2577 :
2578 18 : if (NumNums > 18) {
2579 18 : if ((state.dataIPShortCut->rNumericArgs(19) > 1) || (state.dataIPShortCut->rNumericArgs(19) <= 0)) {
2580 0 : ShowSevereError(state,
2581 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
2582 0 : state.dataIPShortCut->cCurrentModuleObject,
2583 0 : state.dataIPShortCut->cAlphaArgs(1)));
2584 0 : ErrorsFound = true;
2585 : }
2586 18 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(19);
2587 : } else {
2588 0 : Tank.SourceEffectiveness = 1.0;
2589 : }
2590 :
2591 : // If no plant nodes are connected, simulate in stand-alone mode.
2592 42 : if (state.dataIPShortCut->cAlphaArgs(14).empty() && state.dataIPShortCut->cAlphaArgs(15).empty() &&
2593 42 : state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty()) {
2594 2 : Tank.StandAlone = true;
2595 : }
2596 :
2597 18 : if (!state.dataIPShortCut->lNumericFieldBlanks(20)) {
2598 11 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(20);
2599 11 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
2600 5 : Tank.UseDesignVolFlowRateWasAutoSized = true;
2601 : }
2602 : } else {
2603 7 : Tank.UseDesignVolFlowRate = 0.0;
2604 : }
2605 18 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2606 :
2607 18 : if (!state.dataIPShortCut->lNumericFieldBlanks(21)) {
2608 6 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(21);
2609 6 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
2610 4 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
2611 : }
2612 : } else {
2613 12 : Tank.SourceDesignVolFlowRate = 0.0;
2614 : }
2615 18 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2616 :
2617 18 : if (!state.dataIPShortCut->lNumericFieldBlanks(22)) {
2618 11 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(22);
2619 : } else {
2620 7 : Tank.SizingRecoveryTime = 1.5;
2621 : }
2622 :
2623 18 : if ((!state.dataIPShortCut->cAlphaArgs(14).empty()) || (!state.dataIPShortCut->cAlphaArgs(15).empty())) {
2624 6 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
2625 6 : state.dataIPShortCut->cAlphaArgs(14),
2626 : ErrorsFound,
2627 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2628 6 : state.dataIPShortCut->cAlphaArgs(1),
2629 : DataLoopNode::NodeFluidType::Water,
2630 : DataLoopNode::ConnectionType::Inlet,
2631 : NodeInputManager::CompFluidStream::Primary,
2632 : DataLoopNode::ObjectIsNotParent);
2633 6 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(14);
2634 6 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
2635 6 : state.dataIPShortCut->cAlphaArgs(15),
2636 : ErrorsFound,
2637 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2638 6 : state.dataIPShortCut->cAlphaArgs(1),
2639 : DataLoopNode::NodeFluidType::Water,
2640 : DataLoopNode::ConnectionType::Outlet,
2641 : NodeInputManager::CompFluidStream::Primary,
2642 : DataLoopNode::ObjectIsNotParent);
2643 6 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(15);
2644 :
2645 6 : if (state.dataIPShortCut->rNumericArgs(17) > 0) {
2646 8 : ShowWarningError(state,
2647 12 : format("{} = {}: Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used",
2648 4 : state.dataIPShortCut->cCurrentModuleObject,
2649 4 : state.dataIPShortCut->cAlphaArgs(1)));
2650 : }
2651 :
2652 6 : if (Tank.flowRateSched != nullptr) {
2653 0 : ShowWarningError(state,
2654 0 : format("{} = {}: Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used",
2655 0 : state.dataIPShortCut->cCurrentModuleObject,
2656 0 : state.dataIPShortCut->cAlphaArgs(1)));
2657 : }
2658 :
2659 6 : if (Tank.useInletTempSched != nullptr) {
2660 0 : ShowWarningError(state,
2661 0 : format("{} = {}: Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used",
2662 0 : state.dataIPShortCut->cCurrentModuleObject,
2663 0 : state.dataIPShortCut->cAlphaArgs(1)));
2664 : }
2665 : }
2666 :
2667 18 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
2668 16 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
2669 16 : state.dataIPShortCut->cAlphaArgs(16),
2670 : ErrorsFound,
2671 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2672 16 : state.dataIPShortCut->cAlphaArgs(1),
2673 : DataLoopNode::NodeFluidType::Water,
2674 : DataLoopNode::ConnectionType::Inlet,
2675 : NodeInputManager::CompFluidStream::Secondary,
2676 : DataLoopNode::ObjectIsNotParent);
2677 16 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(16);
2678 16 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
2679 16 : state.dataIPShortCut->cAlphaArgs(17),
2680 : ErrorsFound,
2681 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2682 16 : state.dataIPShortCut->cAlphaArgs(1),
2683 : DataLoopNode::NodeFluidType::Water,
2684 : DataLoopNode::ConnectionType::Outlet,
2685 : NodeInputManager::CompFluidStream::Secondary,
2686 : DataLoopNode::ObjectIsNotParent);
2687 16 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(17);
2688 : }
2689 :
2690 18 : if (!state.dataIPShortCut->lAlphaFieldBlanks(18)) {
2691 9 : Tank.SourceSideControlMode =
2692 9 : static_cast<SourceSideControl>(getEnumValue(SourceSideControlNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(18))));
2693 9 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
2694 0 : ShowSevereError(state,
2695 0 : format("{} = {}: Invalid Control Mode entered={}",
2696 0 : state.dataIPShortCut->cCurrentModuleObject,
2697 0 : state.dataIPShortCut->cAlphaArgs(1),
2698 0 : state.dataIPShortCut->cAlphaArgs(18)));
2699 0 : ErrorsFound = true;
2700 : }
2701 : } else {
2702 9 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
2703 : }
2704 :
2705 18 : if (state.dataIPShortCut->lAlphaFieldBlanks(19)) {
2706 2 : } else if ((Tank.sourceSideAltSetpointSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(19))) == nullptr) {
2707 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(19), state.dataIPShortCut->cAlphaArgs(19));
2708 0 : ErrorsFound = true;
2709 : }
2710 :
2711 18 : if (NumAlphas > 19) {
2712 8 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(20);
2713 : }
2714 :
2715 : } // WaterThermalTankNum
2716 :
2717 16 : return ErrorsFound;
2718 : }
2719 :
2720 14 : bool getWaterHeaterStratifiedInput(EnergyPlusData &state)
2721 : {
2722 14 : bool ErrorsFound = false;
2723 : static constexpr std::string_view routineName = "getWaterHeaterStratifiedInput";
2724 :
2725 14 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedWHModuleObj; //'WaterHeater:Stratified'
2726 :
2727 29 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + 1;
2728 29 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified;
2729 : ++WaterThermalTankNum) {
2730 : int NumAlphas;
2731 : int NumNums;
2732 : int IOStat;
2733 30 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2734 15 : state.dataIPShortCut->cCurrentModuleObject,
2735 15 : WaterThermalTankNum - state.dataWaterThermalTanks->numWaterHeaterMixed,
2736 15 : state.dataIPShortCut->cAlphaArgs,
2737 : NumAlphas,
2738 15 : state.dataIPShortCut->rNumericArgs,
2739 : NumNums,
2740 : IOStat,
2741 15 : state.dataIPShortCut->lNumericFieldBlanks,
2742 15 : state.dataIPShortCut->lAlphaFieldBlanks,
2743 15 : state.dataIPShortCut->cAlphaFieldNames,
2744 15 : state.dataIPShortCut->cNumericFieldNames);
2745 :
2746 15 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
2747 :
2748 15 : GlobalNames::VerifyUniqueInterObjectName(state,
2749 15 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2750 15 : state.dataIPShortCut->cAlphaArgs(1),
2751 15 : state.dataIPShortCut->cCurrentModuleObject,
2752 15 : state.dataIPShortCut->cAlphaFieldNames(1),
2753 : ErrorsFound);
2754 :
2755 15 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2756 :
2757 15 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2758 15 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2759 15 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterStratified;
2760 :
2761 15 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
2762 0 : ShowSevereError(state, "Fluid Properties for WATER not found.");
2763 0 : ErrorsFound = true;
2764 : }
2765 :
2766 : // default to always on
2767 15 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2768 15 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
2769 :
2770 15 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(2);
2771 :
2772 15 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2773 15 : if (Tank.Volume == DataSizing::AutoSize) {
2774 0 : Tank.VolumeWasAutoSized = true;
2775 : }
2776 15 : Real64 rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
2777 15 : Tank.Mass = Tank.Volume * rho;
2778 15 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
2779 15 : if (Tank.Height == DataSizing::AutoSize) {
2780 0 : Tank.HeightWasAutoSized = true;
2781 : }
2782 :
2783 15 : Tank.Shape = static_cast<TankShape>(getEnumValue(TankShapeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
2784 15 : switch (Tank.Shape) {
2785 15 : case TankShape::HorizCylinder:
2786 : case TankShape::VertCylinder: {
2787 15 : break;
2788 : }
2789 0 : case TankShape::Other: {
2790 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2791 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
2792 : } else {
2793 0 : ShowSevereError(state,
2794 0 : format("{} = {}: Tank Perimeter must be greater than zero for Tank Shape=OTHER",
2795 0 : state.dataIPShortCut->cCurrentModuleObject,
2796 0 : state.dataIPShortCut->cAlphaArgs(1)));
2797 0 : ErrorsFound = true;
2798 : }
2799 :
2800 0 : break;
2801 : }
2802 0 : default: {
2803 0 : ShowSevereError(state,
2804 0 : format("{} = {}: Invalid Tank Shape entered={}",
2805 0 : state.dataIPShortCut->cCurrentModuleObject,
2806 0 : state.dataIPShortCut->cAlphaArgs(1),
2807 0 : state.dataIPShortCut->cAlphaArgs(3)));
2808 0 : Tank.Shape = TankShape::VertCylinder;
2809 0 : ErrorsFound = true;
2810 0 : break;
2811 : }
2812 : }
2813 :
2814 15 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
2815 13 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(4);
2816 : } else {
2817 : // Default to very large number
2818 2 : Tank.TankTempLimit = 1.0e9;
2819 : }
2820 :
2821 : // Validate Heater Priority Control
2822 15 : Tank.StratifiedControlMode =
2823 15 : static_cast<PriorityControlMode>(getEnumValue(PriorityControlModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(4))));
2824 15 : if (Tank.StratifiedControlMode == PriorityControlMode::Invalid) {
2825 0 : ShowSevereError(state,
2826 0 : format("{} = {}: Invalid Heater Priority Control entered={}",
2827 0 : state.dataIPShortCut->cCurrentModuleObject,
2828 0 : state.dataIPShortCut->cAlphaArgs(1),
2829 0 : state.dataIPShortCut->cAlphaArgs(4)));
2830 0 : ErrorsFound = true;
2831 : }
2832 :
2833 15 : if (state.dataIPShortCut->lAlphaFieldBlanks(5)) {
2834 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5));
2835 0 : ErrorsFound = true;
2836 15 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(5))) == nullptr) {
2837 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5));
2838 0 : ErrorsFound = true;
2839 : }
2840 :
2841 15 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2842 15 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(5);
2843 : } else {
2844 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2845 0 : Tank.DeadBandDeltaTemp = 0.0001;
2846 : }
2847 :
2848 15 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(6);
2849 15 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2850 0 : Tank.MaxCapacityWasAutoSized = true;
2851 : }
2852 :
2853 15 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(7);
2854 :
2855 : // adjust tank height used in these calculations for testing heater height
2856 : Real64 tankHeightForTesting;
2857 15 : if (Tank.Shape == TankShape::HorizCylinder) {
2858 0 : tankHeightForTesting = 2.0 * sqrt((Tank.Volume / Tank.Height) / Constant::Pi);
2859 : } else {
2860 15 : tankHeightForTesting = Tank.Height;
2861 : }
2862 :
2863 : // Test if Heater height is within range
2864 15 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight1 > tankHeightForTesting)) {
2865 0 : ShowSevereError(state,
2866 0 : format("{} = {}: Heater 1 is located higher than overall tank height.",
2867 0 : state.dataIPShortCut->cCurrentModuleObject,
2868 0 : state.dataIPShortCut->cAlphaArgs(1)));
2869 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2870 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(7), state.dataIPShortCut->rNumericArgs(7)));
2871 0 : ErrorsFound = true;
2872 : }
2873 :
2874 15 : if (state.dataIPShortCut->lAlphaFieldBlanks(6)) {
2875 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(6));
2876 0 : ErrorsFound = true;
2877 15 : } else if ((Tank.setptTemp2Sched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(6))) == nullptr) {
2878 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6));
2879 0 : ErrorsFound = true;
2880 : }
2881 :
2882 15 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2883 15 : Tank.DeadBandDeltaTemp2 = state.dataIPShortCut->rNumericArgs(8);
2884 : } else {
2885 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2886 0 : Tank.DeadBandDeltaTemp2 = 0.0001;
2887 : }
2888 :
2889 15 : Tank.MaxCapacity2 = state.dataIPShortCut->rNumericArgs(9);
2890 15 : Tank.HeaterHeight2 = state.dataIPShortCut->rNumericArgs(10);
2891 :
2892 : // Test if Heater height is within range
2893 15 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight2 > tankHeightForTesting)) {
2894 0 : ShowSevereError(state,
2895 0 : format("{} = {}: Heater 2 is located higher than overall tank height.",
2896 0 : state.dataIPShortCut->cCurrentModuleObject,
2897 0 : state.dataIPShortCut->cAlphaArgs(1)));
2898 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2899 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
2900 0 : ErrorsFound = true;
2901 : }
2902 :
2903 : // Validate Heater Fuel Type
2904 15 : Tank.FuelType = static_cast<Constant::eFuel>(
2905 15 : getEnumValue(Constant::eFuelNamesUC,
2906 15 : state.dataIPShortCut->cAlphaArgs(
2907 : 7))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
2908 15 : if (Tank.FuelType == Constant::eFuel::Invalid) {
2909 0 : ShowSevereError(state,
2910 0 : format("{} = {}: Invalid Heater Fuel Type entered={}",
2911 0 : state.dataIPShortCut->cCurrentModuleObject,
2912 0 : state.dataIPShortCut->cAlphaArgs(1),
2913 0 : state.dataIPShortCut->cAlphaArgs(7)));
2914 : // Set to Electric to avoid errors when setting up output variables
2915 0 : Tank.FuelType = Constant::eFuel::Electricity;
2916 0 : ErrorsFound = true;
2917 : }
2918 :
2919 15 : if (state.dataIPShortCut->rNumericArgs(11) > 0.0) {
2920 15 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(11);
2921 15 : if (state.dataIPShortCut->rNumericArgs(11) > 1.0) {
2922 0 : ShowWarningError(state,
2923 0 : fmt::format("{} = {}: {}={} should not typically be greater than 1.",
2924 0 : state.dataIPShortCut->cCurrentModuleObject,
2925 0 : state.dataIPShortCut->cAlphaArgs(1),
2926 0 : state.dataIPShortCut->cNumericFieldNames(11),
2927 0 : state.dataIPShortCut->rNumericArgs(11)));
2928 : }
2929 : } else {
2930 0 : ShowSevereError(state,
2931 0 : format("{} = {}: Heater Thermal Efficiency must be greater than zero",
2932 0 : state.dataIPShortCut->cCurrentModuleObject,
2933 0 : state.dataIPShortCut->cAlphaArgs(1)));
2934 0 : ErrorsFound = true;
2935 : }
2936 :
2937 15 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(12);
2938 :
2939 : // Validate Off-Cycle Parasitic Fuel Type
2940 15 : Tank.OffCycParaFuelType = static_cast<Constant::eFuel>(
2941 15 : getEnumValue(Constant::eFuelNamesUC,
2942 15 : state.dataIPShortCut->cAlphaArgs(
2943 : 8))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
2944 15 : if (Tank.OffCycParaFuelType == Constant::eFuel::Invalid) {
2945 0 : if (state.dataIPShortCut->cAlphaArgs(8).empty()) {
2946 0 : Tank.OffCycParaFuelType = Tank.FuelType;
2947 : } else {
2948 0 : ShowSevereError(state,
2949 0 : format("{} = {}: Invalid Off-Cycle Parasitic Fuel Type entered={}",
2950 0 : state.dataIPShortCut->cCurrentModuleObject,
2951 0 : state.dataIPShortCut->cAlphaArgs(1),
2952 0 : state.dataIPShortCut->cAlphaArgs(8)));
2953 : // Set to Electric to avoid errors when setting up output variables
2954 0 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
2955 0 : ErrorsFound = true;
2956 : }
2957 : }
2958 :
2959 15 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(13);
2960 15 : Tank.OffCycParaHeight = state.dataIPShortCut->rNumericArgs(14);
2961 :
2962 15 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(15);
2963 :
2964 : // Validate On-Cycle Parasitic Fuel Type
2965 15 : Tank.OnCycParaFuelType = static_cast<Constant::eFuel>(
2966 15 : getEnumValue(Constant::eFuelNamesUC,
2967 15 : state.dataIPShortCut->cAlphaArgs(
2968 : 9))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported/empty
2969 15 : if (Tank.OnCycParaFuelType == Constant::eFuel::Invalid) {
2970 0 : if (state.dataIPShortCut->cAlphaArgs(9).empty()) {
2971 0 : Tank.OnCycParaFuelType = Tank.FuelType;
2972 : } else {
2973 0 : ShowSevereError(state,
2974 0 : format("{} = {}: Invalid On-Cycle Parasitic Fuel Type entered={}",
2975 0 : state.dataIPShortCut->cCurrentModuleObject,
2976 0 : state.dataIPShortCut->cAlphaArgs(1),
2977 0 : state.dataIPShortCut->cAlphaArgs(9)));
2978 : // Set to Electric to avoid errors when setting up output variables
2979 0 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
2980 0 : ErrorsFound = true;
2981 : }
2982 : }
2983 :
2984 15 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(16);
2985 15 : Tank.OnCycParaHeight = state.dataIPShortCut->rNumericArgs(17);
2986 :
2987 15 : Tank.AmbientTempIndicator =
2988 15 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(10))));
2989 15 : switch (Tank.AmbientTempIndicator) {
2990 :
2991 14 : case WTTAmbientTemp::Schedule: {
2992 14 : if (state.dataIPShortCut->lAlphaFieldBlanks(11)) {
2993 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(11));
2994 0 : ErrorsFound = true;
2995 14 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(11))) == nullptr) {
2996 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(11), state.dataIPShortCut->cAlphaArgs(11));
2997 0 : ErrorsFound = true;
2998 : }
2999 14 : } break;
3000 :
3001 1 : case WTTAmbientTemp::TempZone: {
3002 1 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(12), state.dataHeatBal->Zone);
3003 1 : if (Tank.AmbientTempZone == 0) {
3004 2 : ShowSevereError(state,
3005 4 : format("{} = {}: Ambient Temperature Zone not found = {}",
3006 1 : state.dataIPShortCut->cCurrentModuleObject,
3007 1 : state.dataIPShortCut->cAlphaArgs(1),
3008 1 : state.dataIPShortCut->cAlphaArgs(12)));
3009 1 : ErrorsFound = true;
3010 : }
3011 :
3012 1 : break;
3013 : }
3014 0 : case WTTAmbientTemp::OutsideAir: {
3015 0 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3016 0 : state.dataIPShortCut->cAlphaArgs(13),
3017 : ErrorsFound,
3018 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3019 0 : state.dataIPShortCut->cAlphaArgs(1),
3020 : DataLoopNode::NodeFluidType::Air,
3021 : DataLoopNode::ConnectionType::Inlet,
3022 : NodeInputManager::CompFluidStream::Primary,
3023 : DataLoopNode::ObjectIsNotParent);
3024 0 : if (!state.dataIPShortCut->cAlphaArgs(13).empty()) {
3025 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3026 0 : ShowSevereError(state,
3027 0 : format("{} = {}: Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node",
3028 0 : state.dataIPShortCut->cCurrentModuleObject,
3029 0 : state.dataIPShortCut->cAlphaArgs(1)));
3030 0 : ShowContinueError(state, format("...Referenced Node Name={}", state.dataIPShortCut->cAlphaArgs(13)));
3031 0 : ErrorsFound = true;
3032 : }
3033 : } else {
3034 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3035 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3036 0 : ErrorsFound = true;
3037 : }
3038 :
3039 0 : break;
3040 : }
3041 0 : default: {
3042 0 : ShowSevereError(state,
3043 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3044 0 : state.dataIPShortCut->cCurrentModuleObject,
3045 0 : state.dataIPShortCut->cAlphaArgs(1),
3046 0 : state.dataIPShortCut->cAlphaArgs(10)));
3047 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3048 0 : ErrorsFound = true;
3049 0 : break;
3050 : }
3051 : }
3052 :
3053 15 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(18);
3054 15 : Tank.SkinLossFracToZone = state.dataIPShortCut->rNumericArgs(19);
3055 15 : Tank.OffCycFlueLossCoeff = state.dataIPShortCut->rNumericArgs(20);
3056 15 : Tank.OffCycFlueLossFracToZone = state.dataIPShortCut->rNumericArgs(21);
3057 :
3058 : // this is temporary until we know fluid type
3059 15 : rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
3060 :
3061 15 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(22) * rho;
3062 :
3063 15 : if ((state.dataIPShortCut->cAlphaArgs(16).empty()) && (state.dataIPShortCut->cAlphaArgs(17).empty())) {
3064 10 : if (state.dataIPShortCut->lAlphaFieldBlanks(14)) {
3065 9 : } else if ((Tank.flowRateSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(14))) == nullptr) {
3066 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(14), state.dataIPShortCut->cAlphaArgs(14));
3067 0 : ErrorsFound = true;
3068 : }
3069 : }
3070 :
3071 15 : if (state.dataIPShortCut->lAlphaFieldBlanks(15)) {
3072 5 : } else if ((Tank.useInletTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(15))) == nullptr) {
3073 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(15), state.dataIPShortCut->cAlphaArgs(15));
3074 0 : ErrorsFound = true;
3075 : }
3076 :
3077 15 : if (NumNums > 22) {
3078 15 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(23);
3079 : } else {
3080 0 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
3081 : }
3082 :
3083 15 : if (NumNums > 23) {
3084 15 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(24);
3085 : } else {
3086 : // Defaults to bottom of tank
3087 0 : Tank.UseInletHeight = 0.0;
3088 : }
3089 15 : if ((!Tank.HeightWasAutoSized) && (Tank.UseInletHeight > Tank.Height)) {
3090 0 : ShowSevereError(state,
3091 0 : format("{} = {}: Use inlet is located higher than overall tank height.",
3092 0 : state.dataIPShortCut->cCurrentModuleObject,
3093 0 : state.dataIPShortCut->cAlphaArgs(1)));
3094 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3095 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(24), state.dataIPShortCut->rNumericArgs(24)));
3096 0 : ErrorsFound = true;
3097 : }
3098 :
3099 15 : if ((NumNums > 24) && (state.dataIPShortCut->rNumericArgs(25) != Constant::AutoCalculate)) {
3100 2 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(25);
3101 : } else {
3102 : // Defaults to top of tank
3103 13 : Tank.UseOutletHeight = Tank.Height;
3104 : }
3105 15 : if (Tank.UseOutletHeight == DataSizing::AutoSize) {
3106 0 : Tank.UseOutletHeightWasAutoSized = true;
3107 : }
3108 15 : if ((!Tank.HeightWasAutoSized) && (Tank.UseOutletHeight > Tank.Height)) {
3109 0 : ShowSevereError(state,
3110 0 : format("{} = {}: Use outlet is located higher than overall tank height.",
3111 0 : state.dataIPShortCut->cCurrentModuleObject,
3112 0 : state.dataIPShortCut->cAlphaArgs(1)));
3113 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3114 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(25), state.dataIPShortCut->rNumericArgs(25)));
3115 0 : ErrorsFound = true;
3116 : }
3117 :
3118 15 : if (NumNums > 25) {
3119 15 : if ((state.dataIPShortCut->rNumericArgs(26) > 1) || (state.dataIPShortCut->rNumericArgs(26) <= 0)) {
3120 0 : ShowSevereError(state,
3121 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3122 0 : state.dataIPShortCut->cCurrentModuleObject,
3123 0 : state.dataIPShortCut->cAlphaArgs(1)));
3124 0 : ErrorsFound = true;
3125 : }
3126 15 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(26);
3127 : } else {
3128 0 : Tank.SourceEffectiveness = 1.0;
3129 : }
3130 :
3131 15 : if ((NumNums > 26) && (state.dataIPShortCut->rNumericArgs(27) != Constant::AutoCalculate)) {
3132 5 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(27);
3133 : } else {
3134 : // Defaults to top of tank
3135 10 : Tank.SourceInletHeight = Tank.Height;
3136 : }
3137 15 : if (Tank.SourceInletHeight == DataSizing::AutoSize) {
3138 0 : Tank.SourceInletHeightWasAutoSized = true;
3139 : }
3140 15 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceInletHeight > Tank.Height)) {
3141 0 : ShowSevereError(state,
3142 0 : format("{} = {}: Source inlet is located higher than overall tank height.",
3143 0 : state.dataIPShortCut->cCurrentModuleObject,
3144 0 : state.dataIPShortCut->cAlphaArgs(1)));
3145 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3146 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(27), state.dataIPShortCut->rNumericArgs(27)));
3147 0 : ErrorsFound = true;
3148 : }
3149 :
3150 15 : if ((NumNums > 27) && (state.dataIPShortCut->rNumericArgs(28) != Constant::AutoCalculate)) {
3151 15 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(28);
3152 : } else {
3153 : // Defaults to bottom of tank
3154 0 : Tank.SourceOutletHeight = 0.0;
3155 : }
3156 15 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceOutletHeight > Tank.Height)) {
3157 0 : ShowSevereError(state,
3158 0 : format("{} = {}: Source outlet is located higher than overall tank height.",
3159 0 : state.dataIPShortCut->cCurrentModuleObject,
3160 0 : state.dataIPShortCut->cAlphaArgs(1)));
3161 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3162 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(28), state.dataIPShortCut->rNumericArgs(28)));
3163 0 : ErrorsFound = true;
3164 : }
3165 :
3166 : // If no plant nodes are connected, simulate in stand-alone mode.
3167 35 : if (state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty() &&
3168 35 : state.dataIPShortCut->cAlphaArgs(18).empty() && state.dataIPShortCut->cAlphaArgs(19).empty())
3169 9 : Tank.StandAlone = true;
3170 :
3171 15 : if (!state.dataIPShortCut->lNumericFieldBlanks(29)) {
3172 8 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(29);
3173 8 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3174 7 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3175 : }
3176 : } else {
3177 7 : Tank.UseDesignVolFlowRate = 0.0;
3178 : }
3179 :
3180 15 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3181 :
3182 15 : if (!state.dataIPShortCut->lNumericFieldBlanks(30)) {
3183 8 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(30);
3184 8 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3185 7 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3186 : }
3187 : } else {
3188 7 : Tank.SourceDesignVolFlowRate = 0.0;
3189 : }
3190 :
3191 15 : if (NumNums > 30) {
3192 15 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(31);
3193 : } else {
3194 0 : Tank.SizingRecoveryTime = 1.5;
3195 : }
3196 :
3197 15 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3198 :
3199 15 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
3200 5 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3201 5 : state.dataIPShortCut->cAlphaArgs(16),
3202 : ErrorsFound,
3203 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3204 5 : state.dataIPShortCut->cAlphaArgs(1),
3205 : DataLoopNode::NodeFluidType::Water,
3206 : DataLoopNode::ConnectionType::Inlet,
3207 : NodeInputManager::CompFluidStream::Primary,
3208 : DataLoopNode::ObjectIsNotParent);
3209 5 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(16);
3210 5 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3211 5 : state.dataIPShortCut->cAlphaArgs(17),
3212 : ErrorsFound,
3213 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3214 5 : state.dataIPShortCut->cAlphaArgs(1),
3215 : DataLoopNode::NodeFluidType::Water,
3216 : DataLoopNode::ConnectionType::Outlet,
3217 : NodeInputManager::CompFluidStream::Primary,
3218 : DataLoopNode::ObjectIsNotParent);
3219 5 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(17);
3220 :
3221 5 : if (state.dataIPShortCut->rNumericArgs(22) > 0) {
3222 4 : ShowWarningError(state,
3223 6 : format("{} = {}: Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used",
3224 2 : state.dataIPShortCut->cCurrentModuleObject,
3225 2 : state.dataIPShortCut->cAlphaArgs(1)));
3226 : }
3227 :
3228 5 : if (Tank.flowRateSched != nullptr) {
3229 0 : ShowWarningError(state,
3230 0 : format("{} = {}: Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used",
3231 0 : state.dataIPShortCut->cCurrentModuleObject,
3232 0 : state.dataIPShortCut->cAlphaArgs(1)));
3233 : }
3234 :
3235 5 : if (Tank.useInletTempSched != nullptr) {
3236 4 : ShowWarningError(state,
3237 6 : format("{} = {}: Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used",
3238 2 : state.dataIPShortCut->cCurrentModuleObject,
3239 2 : state.dataIPShortCut->cAlphaArgs(1)));
3240 : }
3241 : }
3242 :
3243 15 : if ((!state.dataIPShortCut->cAlphaArgs(18).empty()) || (!state.dataIPShortCut->cAlphaArgs(19).empty())) {
3244 2 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3245 2 : state.dataIPShortCut->cAlphaArgs(18),
3246 : ErrorsFound,
3247 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3248 2 : state.dataIPShortCut->cAlphaArgs(1),
3249 : DataLoopNode::NodeFluidType::Water,
3250 : DataLoopNode::ConnectionType::Inlet,
3251 : NodeInputManager::CompFluidStream::Secondary,
3252 : DataLoopNode::ObjectIsNotParent);
3253 2 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(18);
3254 2 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3255 2 : state.dataIPShortCut->cAlphaArgs(19),
3256 : ErrorsFound,
3257 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3258 2 : state.dataIPShortCut->cAlphaArgs(1),
3259 : DataLoopNode::NodeFluidType::Water,
3260 : DataLoopNode::ConnectionType::Outlet,
3261 : NodeInputManager::CompFluidStream::Secondary,
3262 : DataLoopNode::ObjectIsNotParent);
3263 2 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(19);
3264 : }
3265 :
3266 : // Validate inlet mode
3267 15 : Tank.InletMode =
3268 15 : static_cast<InletPositionMode>(getEnumValue(InletPositionModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(20))));
3269 :
3270 15 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(32);
3271 15 : int specifiedNodes = 0;
3272 15 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(33);
3273 :
3274 15 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
3275 15 : Tank.AdditionalLossCoeff = 0.0;
3276 103 : for (int NodeNum = 1; NodeNum <= 12; ++NodeNum) {
3277 99 : int index = 33 + NodeNum;
3278 99 : if (NumNums >= index) {
3279 88 : if (NodeNum <= Tank.Nodes) {
3280 68 : ++specifiedNodes;
3281 68 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(index);
3282 20 : } else if (!state.dataIPShortCut->lNumericFieldBlanks(index) && (state.dataIPShortCut->rNumericArgs(index) != 0)) {
3283 : // If either blank, or zero (default), then do not warn
3284 1 : ++specifiedNodes;
3285 : }
3286 : } else {
3287 11 : break;
3288 : }
3289 : }
3290 :
3291 15 : if (specifiedNodes > Tank.Nodes) {
3292 2 : ShowWarningError(
3293 : state,
3294 3 : format("{} = {}: More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used",
3295 1 : state.dataIPShortCut->cCurrentModuleObject,
3296 1 : state.dataIPShortCut->cAlphaArgs(1)));
3297 : }
3298 :
3299 15 : Tank.SetupStratifiedNodes(state);
3300 :
3301 15 : if (!state.dataIPShortCut->lAlphaFieldBlanks(21)) {
3302 4 : Tank.SourceSideControlMode =
3303 4 : static_cast<SourceSideControl>(getEnumValue(SourceSideControlNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(21))));
3304 4 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
3305 0 : ShowSevereError(state,
3306 0 : format("{} = {}: Invalid Control Mode entered={}",
3307 0 : state.dataIPShortCut->cCurrentModuleObject,
3308 0 : state.dataIPShortCut->cAlphaArgs(1),
3309 0 : state.dataIPShortCut->cAlphaArgs(21)));
3310 0 : ErrorsFound = true;
3311 : }
3312 : } else {
3313 11 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
3314 : }
3315 :
3316 15 : if (state.dataIPShortCut->lAlphaFieldBlanks(22)) {
3317 0 : } else if ((Tank.sourceSideAltSetpointSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(22))) == nullptr) {
3318 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(22), state.dataIPShortCut->cAlphaArgs(22));
3319 0 : ErrorsFound = true;
3320 : }
3321 : }
3322 :
3323 14 : return ErrorsFound;
3324 : }
3325 :
3326 0 : bool getWaterTankMixedInput(EnergyPlusData &state)
3327 : {
3328 : static constexpr std::string_view routineName = "getWaterTankMixedInput";
3329 0 : bool ErrorsFound = false;
3330 :
3331 0 : state.dataIPShortCut->cCurrentModuleObject = cMixedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Mixed'
3332 0 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified + 1;
3333 0 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3334 0 : state.dataWaterThermalTanks->numChilledWaterMixed;
3335 : ++WaterThermalTankNum) {
3336 : int NumAlphas;
3337 : int NumNums;
3338 : int IOStat;
3339 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
3340 : state,
3341 0 : state.dataIPShortCut->cCurrentModuleObject,
3342 0 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified),
3343 0 : state.dataIPShortCut->cAlphaArgs,
3344 : NumAlphas,
3345 0 : state.dataIPShortCut->rNumericArgs,
3346 : NumNums,
3347 : IOStat,
3348 0 : state.dataIPShortCut->lNumericFieldBlanks,
3349 0 : state.dataIPShortCut->lAlphaFieldBlanks,
3350 0 : state.dataIPShortCut->cAlphaFieldNames,
3351 0 : state.dataIPShortCut->cNumericFieldNames);
3352 :
3353 0 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
3354 :
3355 0 : GlobalNames::VerifyUniqueInterObjectName(state,
3356 0 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3357 0 : state.dataIPShortCut->cAlphaArgs(1),
3358 0 : state.dataIPShortCut->cCurrentModuleObject,
3359 0 : state.dataIPShortCut->cAlphaFieldNames(1),
3360 : ErrorsFound);
3361 :
3362 0 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3363 :
3364 0 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3365 0 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3366 0 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankMixed;
3367 :
3368 0 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
3369 0 : ShowSevereError(state, "Fluid Properties for WATER not found");
3370 0 : ErrorsFound = true;
3371 : }
3372 :
3373 0 : Tank.IsChilledWaterTank = true;
3374 0 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3375 :
3376 0 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3377 0 : if (Tank.Volume == DataSizing::AutoSize) {
3378 0 : Tank.VolumeWasAutoSized = true;
3379 : }
3380 :
3381 0 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
3382 : // Set volume to a really small number to continue simulation
3383 0 : Tank.Volume = 0.000001; // = 1 cm3
3384 : }
3385 :
3386 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
3387 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2));
3388 0 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
3389 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
3390 0 : ErrorsFound = true;
3391 : }
3392 :
3393 0 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
3394 0 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
3395 : } else {
3396 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3397 0 : Tank.DeadBandDeltaTemp = 0.5;
3398 : }
3399 :
3400 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3401 0 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
3402 : } else {
3403 : // default to just above freezing
3404 0 : Tank.TankTempLimit = 1.0;
3405 : }
3406 :
3407 0 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
3408 0 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3409 0 : Tank.MaxCapacityWasAutoSized = true;
3410 : }
3411 :
3412 0 : Tank.MinCapacity = 0.0;
3413 0 : Tank.ControlType = HeaterControlMode::Cycle;
3414 :
3415 0 : Tank.MassFlowRateMin = 0.0;
3416 0 : Tank.IgnitionDelay = 0.0;
3417 0 : Tank.FuelType = Constant::eFuel::Electricity;
3418 0 : Tank.Efficiency = 1.0;
3419 0 : Tank.PLFCurve = 0;
3420 0 : Tank.OffCycParaLoad = 0.0;
3421 0 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
3422 0 : Tank.OffCycParaFracToTank = 0.0;
3423 0 : Tank.OnCycParaLoad = 0.0;
3424 0 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3425 0 : Tank.OnCycParaFracToTank = 0.0;
3426 :
3427 0 : Tank.AmbientTempIndicator =
3428 0 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
3429 0 : switch (Tank.AmbientTempIndicator) {
3430 :
3431 0 : case WTTAmbientTemp::Schedule: {
3432 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(4)) {
3433 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(4));
3434 0 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(4))) == nullptr) {
3435 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4));
3436 0 : ErrorsFound = true;
3437 : }
3438 0 : } break;
3439 :
3440 0 : case WTTAmbientTemp::TempZone: {
3441 0 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(5), state.dataHeatBal->Zone);
3442 0 : if (Tank.AmbientTempZone == 0) {
3443 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
3444 0 : ShowContinueError(state,
3445 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3446 0 : ShowContinueError(state, "Zone was not found.");
3447 0 : ErrorsFound = true;
3448 : }
3449 :
3450 0 : break;
3451 : }
3452 0 : case WTTAmbientTemp::OutsideAir: {
3453 0 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3454 0 : state.dataIPShortCut->cAlphaArgs(6),
3455 : ErrorsFound,
3456 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3457 0 : state.dataIPShortCut->cAlphaArgs(1),
3458 : DataLoopNode::NodeFluidType::Air,
3459 : DataLoopNode::ConnectionType::OutsideAirReference,
3460 : NodeInputManager::CompFluidStream::Primary,
3461 : DataLoopNode::ObjectIsNotParent);
3462 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(6)) {
3463 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3464 0 : ShowSevereError(state,
3465 0 : format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)));
3466 0 : ShowContinueError(state,
3467 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3468 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3469 0 : ErrorsFound = true;
3470 : }
3471 : } else {
3472 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3473 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3474 0 : ErrorsFound = true;
3475 : }
3476 :
3477 0 : break;
3478 : }
3479 0 : default: {
3480 0 : ShowSevereError(state,
3481 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3482 0 : state.dataIPShortCut->cCurrentModuleObject,
3483 0 : state.dataIPShortCut->cAlphaArgs(1),
3484 0 : state.dataIPShortCut->cAlphaArgs(3)));
3485 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3486 0 : ErrorsFound = true;
3487 0 : break;
3488 : }
3489 : }
3490 :
3491 0 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3492 0 : Tank.OffCycLossFracToZone = 1.0;
3493 :
3494 0 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3495 0 : Tank.OnCycLossFracToZone = 1.0;
3496 :
3497 0 : Tank.MassFlowRateMax = 0.0;
3498 0 : Tank.flowRateSched = nullptr;
3499 0 : Tank.useInletTempSched = nullptr;
3500 :
3501 : // default to always on
3502 0 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3503 0 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3504 :
3505 0 : if ((state.dataIPShortCut->rNumericArgs(6) > 1) || (state.dataIPShortCut->rNumericArgs(6) < 0)) {
3506 0 : ShowSevereError(state,
3507 0 : format("{} = {}: Use Side Effectiveness is out of bounds (0 to 1)",
3508 0 : state.dataIPShortCut->cCurrentModuleObject,
3509 0 : state.dataIPShortCut->cAlphaArgs(1)));
3510 0 : ErrorsFound = true;
3511 : }
3512 0 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(6);
3513 :
3514 0 : if ((state.dataIPShortCut->rNumericArgs(8) > 1) || (state.dataIPShortCut->rNumericArgs(8) <= 0)) {
3515 0 : ShowSevereError(state,
3516 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3517 0 : state.dataIPShortCut->cCurrentModuleObject,
3518 0 : state.dataIPShortCut->cAlphaArgs(1)));
3519 0 : ErrorsFound = true;
3520 : }
3521 0 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(8);
3522 :
3523 0 : if (state.dataIPShortCut->lNumericFieldBlanks(7)) {
3524 0 : Tank.UseDesignVolFlowRate = 0.0;
3525 : } else {
3526 0 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(7);
3527 0 : if (Tank.UseDesignVolFlowRate) {
3528 0 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3529 : }
3530 : }
3531 :
3532 0 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3533 :
3534 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(9)) {
3535 0 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3536 0 : } else if ((Tank.useSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(9))) == nullptr) {
3537 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(9), state.dataIPShortCut->cAlphaArgs(9));
3538 0 : ErrorsFound = true;
3539 : }
3540 :
3541 0 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3542 :
3543 0 : if (state.dataIPShortCut->lNumericFieldBlanks(9)) {
3544 0 : Tank.SourceDesignVolFlowRate = 0.0;
3545 : } else {
3546 0 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(9);
3547 0 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3548 0 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3549 : }
3550 : }
3551 :
3552 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(12)) {
3553 0 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3554 0 : } else if ((Tank.sourceSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(12))) == nullptr) {
3555 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(12), state.dataIPShortCut->cAlphaArgs(12));
3556 0 : ErrorsFound = true;
3557 : }
3558 :
3559 0 : if (state.dataIPShortCut->lNumericFieldBlanks(10)) {
3560 0 : Tank.SizingRecoveryTime = 4.0;
3561 : } else {
3562 0 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(10);
3563 : }
3564 :
3565 0 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(7)) || (!state.dataIPShortCut->lAlphaFieldBlanks(8))) {
3566 0 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3567 0 : state.dataIPShortCut->cAlphaArgs(7),
3568 : ErrorsFound,
3569 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3570 0 : state.dataIPShortCut->cAlphaArgs(1),
3571 : DataLoopNode::NodeFluidType::Water,
3572 : DataLoopNode::ConnectionType::Inlet,
3573 : NodeInputManager::CompFluidStream::Primary,
3574 : DataLoopNode::ObjectIsNotParent);
3575 0 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(7);
3576 0 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3577 0 : state.dataIPShortCut->cAlphaArgs(8),
3578 : ErrorsFound,
3579 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3580 0 : state.dataIPShortCut->cAlphaArgs(1),
3581 : DataLoopNode::NodeFluidType::Water,
3582 : DataLoopNode::ConnectionType::Outlet,
3583 : NodeInputManager::CompFluidStream::Primary,
3584 : DataLoopNode::ObjectIsNotParent);
3585 0 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
3586 : }
3587 :
3588 0 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(10)) || (!state.dataIPShortCut->lAlphaFieldBlanks(11))) {
3589 0 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3590 0 : state.dataIPShortCut->cAlphaArgs(10),
3591 : ErrorsFound,
3592 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3593 0 : state.dataIPShortCut->cAlphaArgs(1),
3594 : DataLoopNode::NodeFluidType::Water,
3595 : DataLoopNode::ConnectionType::Inlet,
3596 : NodeInputManager::CompFluidStream::Secondary,
3597 : DataLoopNode::ObjectIsNotParent);
3598 0 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(10);
3599 0 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3600 0 : state.dataIPShortCut->cAlphaArgs(11),
3601 : ErrorsFound,
3602 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3603 0 : state.dataIPShortCut->cAlphaArgs(1),
3604 : DataLoopNode::NodeFluidType::Water,
3605 : DataLoopNode::ConnectionType::Outlet,
3606 : NodeInputManager::CompFluidStream::Secondary,
3607 : DataLoopNode::ObjectIsNotParent);
3608 0 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
3609 : }
3610 :
3611 0 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
3612 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
3613 : }
3614 :
3615 : } // WaterThermalTankNum
3616 :
3617 0 : return ErrorsFound;
3618 : }
3619 :
3620 1 : bool getWaterTankStratifiedInput(EnergyPlusData &state)
3621 : {
3622 1 : bool ErrorsFound = false;
3623 : static constexpr std::string_view routineName = "getWaterTankStratifiedInput";
3624 :
3625 1 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Stratified'
3626 :
3627 2 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3628 1 : state.dataWaterThermalTanks->numChilledWaterMixed + 1;
3629 2 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3630 2 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
3631 : ++WaterThermalTankNum) {
3632 : int NumNums;
3633 : int NumAlphas;
3634 : int IOStat;
3635 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3636 1 : state.dataIPShortCut->cCurrentModuleObject,
3637 1 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed +
3638 1 : state.dataWaterThermalTanks->numWaterHeaterStratified +
3639 1 : state.dataWaterThermalTanks->numChilledWaterMixed),
3640 1 : state.dataIPShortCut->cAlphaArgs,
3641 : NumAlphas,
3642 1 : state.dataIPShortCut->rNumericArgs,
3643 : NumNums,
3644 : IOStat,
3645 1 : state.dataIPShortCut->lNumericFieldBlanks,
3646 1 : state.dataIPShortCut->lAlphaFieldBlanks,
3647 1 : state.dataIPShortCut->cAlphaFieldNames,
3648 1 : state.dataIPShortCut->cNumericFieldNames);
3649 :
3650 1 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
3651 :
3652 1 : GlobalNames::VerifyUniqueInterObjectName(state,
3653 1 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3654 1 : state.dataIPShortCut->cAlphaArgs(1),
3655 1 : state.dataIPShortCut->cCurrentModuleObject,
3656 1 : state.dataIPShortCut->cAlphaFieldNames(1),
3657 : ErrorsFound);
3658 :
3659 1 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3660 :
3661 1 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3662 1 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3663 1 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankStratified;
3664 :
3665 1 : if ((Tank.water = Fluid::GetWater(state)) == nullptr) {
3666 0 : ShowSevereError(state, "Fluid properties for WATER not found");
3667 0 : ErrorsFound = true;
3668 : }
3669 :
3670 1 : Tank.IsChilledWaterTank = true;
3671 1 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3672 :
3673 1 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3674 1 : if (Tank.Volume == DataSizing::AutoSize) {
3675 0 : Tank.VolumeWasAutoSized = true;
3676 : }
3677 :
3678 1 : Real64 rho = Tank.water->getDensity(state, Constant::InitConvTemp, routineName);
3679 :
3680 1 : Tank.Mass = Tank.Volume * rho;
3681 1 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
3682 1 : if (Tank.Height == DataSizing::AutoSize) {
3683 0 : Tank.HeightWasAutoSized = true;
3684 : }
3685 :
3686 1 : Tank.Shape = static_cast<TankShape>(getEnumValue(TankShapeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(2))));
3687 1 : switch (Tank.Shape) {
3688 1 : case TankShape::HorizCylinder:
3689 : case TankShape::VertCylinder: {
3690 1 : break;
3691 : }
3692 0 : case TankShape::Other: {
3693 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3694 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
3695 : } else {
3696 0 : ShowSevereError(state,
3697 0 : format("{} = {}: Tank Perimeter must be greater than zero for Tank Shape=OTHER",
3698 0 : state.dataIPShortCut->cCurrentModuleObject,
3699 0 : state.dataIPShortCut->cAlphaArgs(1)));
3700 0 : ErrorsFound = true;
3701 : }
3702 0 : break;
3703 : }
3704 0 : default: {
3705 0 : ShowSevereError(state,
3706 0 : format("{} = {}: Invalid Tank Shape entered={}",
3707 0 : state.dataIPShortCut->cCurrentModuleObject,
3708 0 : state.dataIPShortCut->cAlphaArgs(1),
3709 0 : state.dataIPShortCut->cAlphaArgs(2)));
3710 0 : Tank.Shape = TankShape::VertCylinder;
3711 0 : ErrorsFound = true;
3712 0 : break;
3713 : }
3714 : }
3715 :
3716 1 : if (state.dataIPShortCut->rNumericArgs(6) > 0.0) {
3717 1 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(6);
3718 : } else {
3719 : // default to just above freezing
3720 0 : Tank.TankTempLimit = 1.0;
3721 : }
3722 :
3723 1 : if (state.dataIPShortCut->lAlphaFieldBlanks(3)) {
3724 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(3));
3725 0 : ErrorsFound = true;
3726 1 : } else if ((Tank.setptTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(3))) == nullptr) {
3727 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3));
3728 0 : ErrorsFound = true;
3729 : }
3730 :
3731 1 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
3732 1 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(4);
3733 : } else {
3734 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3735 0 : Tank.DeadBandDeltaTemp = 0.0001;
3736 : }
3737 :
3738 1 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(5);
3739 1 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(7);
3740 1 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3741 0 : Tank.MaxCapacityWasAutoSized = true;
3742 : }
3743 :
3744 1 : Tank.Efficiency = 1.0;
3745 1 : Tank.setptTemp2Sched = nullptr;
3746 1 : Tank.MaxCapacity2 = 0.0;
3747 1 : Tank.HeaterHeight2 = 0.0;
3748 1 : Tank.FuelType = Constant::eFuel::Electricity;
3749 :
3750 1 : Tank.OffCycParaLoad = 0.0;
3751 1 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
3752 1 : Tank.OffCycParaFracToTank = 0.0;
3753 1 : Tank.OffCycParaHeight = 0.0;
3754 1 : Tank.OnCycParaLoad = 0.0;
3755 1 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3756 1 : Tank.OnCycParaFracToTank = 0.0;
3757 1 : Tank.OnCycParaHeight = 0.0;
3758 :
3759 1 : Tank.AmbientTempIndicator =
3760 1 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(4))));
3761 1 : switch (Tank.AmbientTempIndicator) {
3762 :
3763 0 : case WTTAmbientTemp::Schedule: {
3764 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(5)) {
3765 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5));
3766 0 : ErrorsFound = true;
3767 0 : } else if ((Tank.ambientTempSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(5))) == nullptr) {
3768 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5));
3769 0 : ErrorsFound = true;
3770 : }
3771 0 : } break;
3772 :
3773 1 : case WTTAmbientTemp::TempZone: {
3774 1 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(6), state.dataHeatBal->Zone);
3775 1 : if (Tank.AmbientTempZone == 0) {
3776 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)));
3777 0 : ShowContinueError(state,
3778 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3779 0 : ShowContinueError(state, "Zone was not found.");
3780 0 : ErrorsFound = true;
3781 : }
3782 1 : Tank.OffCycLossFracToZone = 1.0;
3783 :
3784 1 : break;
3785 : }
3786 0 : case WTTAmbientTemp::OutsideAir: {
3787 0 : Tank.AmbientTempOutsideAirNode =
3788 0 : NodeInputManager::GetOnlySingleNode(state,
3789 0 : state.dataIPShortCut->cAlphaArgs(7),
3790 : ErrorsFound,
3791 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3792 0 : state.dataIPShortCut->cAlphaArgs(1),
3793 : DataLoopNode::NodeFluidType::Air,
3794 : DataLoopNode::ConnectionType::Inlet,
3795 : NodeInputManager::CompFluidStream::Primary,
3796 : DataLoopNode::ObjectIsNotParent);
3797 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(7)) {
3798 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3799 0 : ShowSevereError(state,
3800 0 : format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(7), state.dataIPShortCut->cAlphaArgs(7)));
3801 0 : ShowContinueError(state,
3802 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3803 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3804 0 : ErrorsFound = true;
3805 : }
3806 : } else {
3807 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3808 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3809 0 : ErrorsFound = true;
3810 : }
3811 :
3812 0 : break;
3813 : }
3814 0 : default: {
3815 0 : ShowSevereError(state,
3816 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3817 0 : state.dataIPShortCut->cCurrentModuleObject,
3818 0 : state.dataIPShortCut->cAlphaArgs(1),
3819 0 : state.dataIPShortCut->cAlphaArgs(4)));
3820 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3821 0 : ErrorsFound = true;
3822 0 : break;
3823 : }
3824 : }
3825 :
3826 1 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(8);
3827 1 : Tank.SkinLossFracToZone = 1.0;
3828 1 : Tank.OffCycFlueLossCoeff = 0.0;
3829 1 : Tank.OffCycFlueLossFracToZone = 0.0;
3830 :
3831 1 : Tank.MassFlowRateMax = 0.0;
3832 1 : Tank.flowRateSched = nullptr;
3833 1 : Tank.useInletTempSched = nullptr;
3834 1 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(9);
3835 1 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(10);
3836 :
3837 : // default to always on
3838 1 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3839 1 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3840 :
3841 1 : if (state.dataIPShortCut->rNumericArgs(10) == Constant::AutoCalculate) {
3842 0 : Tank.UseInletHeight = Tank.Height; // top of tank
3843 : }
3844 1 : if (Tank.UseInletHeight > Tank.Height) {
3845 0 : ShowSevereError(state,
3846 0 : format("{} = {}: Use inlet is located higher than overall tank height.",
3847 0 : state.dataIPShortCut->cCurrentModuleObject,
3848 0 : state.dataIPShortCut->cAlphaArgs(1)));
3849 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3850 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
3851 0 : ErrorsFound = true;
3852 : }
3853 :
3854 1 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(11);
3855 1 : if (Tank.UseOutletHeight > Tank.Height) {
3856 0 : ShowSevereError(state,
3857 0 : format("{} = {}: Use outlet is located higher than overall tank height.",
3858 0 : state.dataIPShortCut->cCurrentModuleObject,
3859 0 : state.dataIPShortCut->cAlphaArgs(1)));
3860 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3861 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(11), state.dataIPShortCut->rNumericArgs(11)));
3862 0 : ErrorsFound = true;
3863 : }
3864 :
3865 1 : if ((state.dataIPShortCut->rNumericArgs(13) > 1) || (state.dataIPShortCut->rNumericArgs(13) <= 0)) {
3866 0 : ShowSevereError(state,
3867 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3868 0 : state.dataIPShortCut->cCurrentModuleObject,
3869 0 : state.dataIPShortCut->cAlphaArgs(1)));
3870 0 : ErrorsFound = true;
3871 : }
3872 1 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(13);
3873 :
3874 1 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(14);
3875 1 : if (Tank.SourceInletHeight > Tank.Height) {
3876 0 : ShowSevereError(state,
3877 0 : format("{} = {}: Source inlet is located higher than overall tank height.",
3878 0 : state.dataIPShortCut->cCurrentModuleObject,
3879 0 : state.dataIPShortCut->cAlphaArgs(1)));
3880 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3881 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(14), state.dataIPShortCut->rNumericArgs(14)));
3882 0 : ErrorsFound = true;
3883 : }
3884 :
3885 1 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(15);
3886 1 : if (state.dataIPShortCut->rNumericArgs(15) == Constant::AutoCalculate) {
3887 0 : Tank.SourceOutletHeight = Tank.Height; // top of tank
3888 : }
3889 1 : if (Tank.SourceOutletHeight > Tank.Height) {
3890 0 : ShowSevereError(state,
3891 0 : format("{} = {}: Source outlet is located higher than overall tank height.",
3892 0 : state.dataIPShortCut->cCurrentModuleObject,
3893 0 : state.dataIPShortCut->cAlphaArgs(1)));
3894 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3895 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(15), state.dataIPShortCut->rNumericArgs(15)));
3896 0 : ErrorsFound = true;
3897 : }
3898 :
3899 1 : Tank.StandAlone = false;
3900 :
3901 1 : if (state.dataIPShortCut->lNumericFieldBlanks(12)) {
3902 0 : Tank.UseDesignVolFlowRate = 0.0;
3903 : } else {
3904 1 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(12);
3905 1 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3906 0 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3907 : }
3908 : }
3909 :
3910 1 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3911 :
3912 1 : if (state.dataIPShortCut->lNumericFieldBlanks(16)) {
3913 0 : Tank.SourceDesignVolFlowRate = 0.0;
3914 : } else {
3915 1 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(16);
3916 1 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3917 0 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3918 : }
3919 : }
3920 :
3921 1 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(17);
3922 :
3923 1 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3924 :
3925 1 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(8)) || (!state.dataIPShortCut->lAlphaFieldBlanks(9))) {
3926 1 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3927 1 : state.dataIPShortCut->cAlphaArgs(8),
3928 : ErrorsFound,
3929 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3930 1 : state.dataIPShortCut->cAlphaArgs(1),
3931 : DataLoopNode::NodeFluidType::Water,
3932 : DataLoopNode::ConnectionType::Inlet,
3933 : NodeInputManager::CompFluidStream::Primary,
3934 : DataLoopNode::ObjectIsNotParent);
3935 1 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
3936 1 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3937 1 : state.dataIPShortCut->cAlphaArgs(9),
3938 : ErrorsFound,
3939 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3940 1 : state.dataIPShortCut->cAlphaArgs(1),
3941 : DataLoopNode::NodeFluidType::Water,
3942 : DataLoopNode::ConnectionType::Outlet,
3943 : NodeInputManager::CompFluidStream::Primary,
3944 : DataLoopNode::ObjectIsNotParent);
3945 1 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(9);
3946 : }
3947 :
3948 1 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(11)) || (!state.dataIPShortCut->lAlphaFieldBlanks(12))) {
3949 1 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3950 1 : state.dataIPShortCut->cAlphaArgs(11),
3951 : ErrorsFound,
3952 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3953 1 : state.dataIPShortCut->cAlphaArgs(1),
3954 : DataLoopNode::NodeFluidType::Water,
3955 : DataLoopNode::ConnectionType::Inlet,
3956 : NodeInputManager::CompFluidStream::Secondary,
3957 : DataLoopNode::ObjectIsNotParent);
3958 1 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
3959 1 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3960 1 : state.dataIPShortCut->cAlphaArgs(12),
3961 : ErrorsFound,
3962 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3963 1 : state.dataIPShortCut->cAlphaArgs(1),
3964 : DataLoopNode::NodeFluidType::Water,
3965 : DataLoopNode::ConnectionType::Outlet,
3966 : NodeInputManager::CompFluidStream::Secondary,
3967 : DataLoopNode::ObjectIsNotParent);
3968 1 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(12);
3969 : }
3970 :
3971 1 : if (state.dataIPShortCut->lAlphaFieldBlanks(10)) {
3972 0 : Tank.useSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3973 1 : } else if ((Tank.useSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(10))) == nullptr) {
3974 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(10), state.dataIPShortCut->cAlphaArgs(10));
3975 0 : ErrorsFound = true;
3976 : }
3977 :
3978 1 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
3979 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
3980 : }
3981 :
3982 1 : if (state.dataIPShortCut->lAlphaFieldBlanks(13)) {
3983 0 : Tank.sourceSideAvailSched = Sched::GetScheduleAlwaysOn(state);
3984 1 : } else if ((Tank.sourceSideAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(13))) == nullptr) {
3985 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(13), state.dataIPShortCut->cAlphaArgs(13));
3986 0 : ErrorsFound = true;
3987 : }
3988 :
3989 : // Validate inlet mode
3990 1 : Tank.InletMode =
3991 1 : static_cast<InletPositionMode>(getEnumValue(InletPositionModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(14))));
3992 :
3993 1 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(18);
3994 1 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(19);
3995 :
3996 1 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
3997 1 : Tank.AdditionalLossCoeff = 0.0;
3998 1 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
3999 1 : if (NumNums > 19 + NodeNum) {
4000 0 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(19 + NodeNum);
4001 : } else {
4002 1 : break;
4003 : }
4004 : }
4005 :
4006 1 : if (NumNums > 19 + Tank.Nodes) {
4007 0 : ShowWarningError(
4008 : state,
4009 0 : format("{} = {}: More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used",
4010 0 : state.dataIPShortCut->cCurrentModuleObject,
4011 0 : state.dataIPShortCut->cAlphaArgs(1)));
4012 : }
4013 :
4014 1 : Tank.SetupStratifiedNodes(state);
4015 : }
4016 :
4017 1 : return ErrorsFound;
4018 : }
4019 :
4020 72 : bool GetWaterThermalTankInput(EnergyPlusData &state)
4021 : {
4022 :
4023 : // SUBROUTINE INFORMATION:
4024 : // AUTHOR Dan Fisher and Brandon Anderson
4025 : // DATE WRITTEN May 2000
4026 : // MODIFIED R. Raustad, June 2005, added HPWH and desuperheater water heating coils
4027 : // B. Griffith, Oct. 2007 extensions for indirect water heaters
4028 : // B. Griffith, Feb. 2008 extensions for autosizing water heaters
4029 : // BG Mar 2009. Trap for bad heater height input for stratified water heater CR7718
4030 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
4031 :
4032 : // PURPOSE OF THIS SUBROUTINE:
4033 : // Gets the water heater, HPWH, and/or desuperheater heating coil input from the input file.
4034 :
4035 72 : bool ErrorsFound = false;
4036 :
4037 : // Make sure refrigeration input is gotten before this input
4038 72 : RefrigeratedCase::CheckRefrigerationInput(state);
4039 :
4040 72 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
4041 72 : state.dataWaterThermalTanks->numWaterHeaterMixed = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedWHModuleObj);
4042 144 : state.dataWaterThermalTanks->numWaterHeaterStratified =
4043 72 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedWHModuleObj);
4044 144 : state.dataWaterThermalTanks->numChilledWaterMixed =
4045 72 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedCWTankModuleObj);
4046 144 : state.dataWaterThermalTanks->numChilledWaterStratified =
4047 72 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedCWTankModuleObj);
4048 144 : state.dataWaterThermalTanks->numWaterThermalTank =
4049 72 : state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
4050 72 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
4051 144 : state.dataWaterThermalTanks->numHeatPumpWaterHeater =
4052 72 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHPumpedCondenser) +
4053 72 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHWrappedCondenser);
4054 144 : state.dataWaterThermalTanks->numWaterHeaterDesuperheater =
4055 72 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCoilDesuperheater);
4056 :
4057 72 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4058 : static constexpr std::string_view Format_720(
4059 : "! <Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery Efficiency, "
4060 : "Standard Rated Energy Factor\n");
4061 : static constexpr std::string_view Format_721(
4062 : "! <Heat Pump Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery "
4063 : "Efficiency,Standard Rated Energy Factor,DX Coil Total Cooling Rate {{W}}\n");
4064 : static constexpr std::string_view Format_722(
4065 : "! <Water Heater Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},Maximum Capacity "
4066 : "{{W}},Off-Cycle UA {{W/K}},On-Cycle UA {{W/K}},Number Of Inlets,Number Of Outlets\n");
4067 : static constexpr std::string_view Format_725(
4068 : "! <Chilled Water Tank Information>,Type,Name,Volume {{m3}},Use Side Design Flow Rate {{m3/s}}, "
4069 : "Source Side Design Flow Rate {{m3/s}}\n");
4070 : static constexpr std::string_view Format_726(
4071 : "! <Chilled Water Tank Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},UA {{W/K}},Number Of "
4072 : "Inlets,Number Of Outlets\n");
4073 :
4074 : // Write water heater header for EIO
4075 27 : if ((state.dataWaterThermalTanks->numWaterHeaterMixed > 0) || (state.dataWaterThermalTanks->numWaterHeaterStratified > 0))
4076 26 : print(state.files.eio, Format_720);
4077 27 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) print(state.files.eio, Format_721);
4078 27 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) print(state.files.eio, Format_722);
4079 27 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) print(state.files.eio, Format_725);
4080 27 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) print(state.files.eio, Format_726);
4081 : }
4082 :
4083 72 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4084 27 : state.dataWaterThermalTanks->WaterThermalTank.allocate(state.dataWaterThermalTanks->numWaterThermalTank);
4085 27 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames.reserve(static_cast<unsigned>(state.dataWaterThermalTanks->numWaterThermalTank));
4086 : }
4087 72 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4088 10 : state.dataWaterThermalTanks->HPWaterHeater.allocate(state.dataWaterThermalTanks->numHeatPumpWaterHeater);
4089 : }
4090 :
4091 72 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4092 5 : state.dataWaterThermalTanks->WaterHeaterDesuperheater.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
4093 : }
4094 :
4095 : // ======= Get Coil:WaterHeating:Desuperheater ======================================================================
4096 72 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4097 5 : ErrorsFound |= getDesuperHtrInput(state);
4098 : }
4099 :
4100 : // ======= Get HEAT PUMP:WATER HEATER ===============================================================================
4101 72 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4102 10 : ErrorsFound |= getHPWaterHeaterInput(state);
4103 : }
4104 :
4105 : // ======= Get WATER HEATER:MIXED ===================================================================================
4106 72 : if (state.dataWaterThermalTanks->numWaterHeaterMixed > 0) {
4107 14 : ErrorsFound |= getWaterHeaterMixedInputs(state);
4108 : }
4109 :
4110 : // ======= Get WATER HEATER:STRATIFIED ==============================================================================
4111 72 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) {
4112 13 : ErrorsFound |= getWaterHeaterStratifiedInput(state);
4113 : }
4114 :
4115 : // ======= Get Chilled Water :MIXED ===================================================================================
4116 72 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) {
4117 0 : ErrorsFound |= getWaterTankMixedInput(state);
4118 : }
4119 :
4120 : // ======= Get 'ThermalStorage:ChilledWater:Stratified' =======================================================
4121 72 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) {
4122 1 : ErrorsFound |= getWaterTankStratifiedInput(state);
4123 : }
4124 :
4125 : // Loop through all desuperheating coils and then search all water heaters for the tank connected to the desuperheating coil
4126 72 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4127 5 : state.dataIPShortCut->cCurrentModuleObject = cCoilDesuperheater;
4128 11 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
4129 6 : auto &DesuperHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
4130 14 : for (int WtrHtrNum = 1; WtrHtrNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WtrHtrNum) {
4131 8 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WtrHtrNum);
4132 8 : if (!Util::SameString(DesuperHtr.TankName, Tank.Name) || !Util::SameString(DesuperHtr.TankType, Tank.Type)) continue;
4133 6 : Tank.DesuperheaterNum = DesuperheaterNum;
4134 6 : DesuperHtr.WaterHeaterTankNum = WtrHtrNum;
4135 6 : DesuperHtr.TankTypeNum = Tank.WaterThermalTankType;
4136 6 : DesuperHtr.BackupElementCapacity = Tank.MaxCapacity;
4137 6 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) DesuperHtr.StandAlone = true;
4138 :
4139 : // verify Desuperheater/tank source node connections
4140 6 : if (DesuperHtr.WaterInletNode != Tank.SourceOutletNode) {
4141 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4142 0 : ShowContinueError(state, "Desuperheater inlet node name does not match thermal tank source outlet node name.");
4143 0 : ShowContinueError(state,
4144 0 : format("Desuperheater water inlet and outlet node names = {} and {}",
4145 0 : DesuperHtr.InletNodeName1,
4146 0 : DesuperHtr.OutletNodeName1));
4147 0 : ShowContinueError(state,
4148 0 : format("Thermal tank source side inlet and outlet node names = {} and {}",
4149 0 : Tank.InletNodeName2,
4150 0 : Tank.OutletNodeName2));
4151 0 : ErrorsFound = true;
4152 : }
4153 :
4154 6 : if (DesuperHtr.WaterOutletNode != Tank.SourceInletNode) {
4155 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4156 0 : ShowContinueError(state, "Desuperheater water outlet node name does not match thermal tank source inlet node name.");
4157 0 : ShowContinueError(state,
4158 0 : format("Desuperheater water inlet and outlet node names = {} and {}",
4159 0 : DesuperHtr.InletNodeName1,
4160 0 : DesuperHtr.OutletNodeName1));
4161 0 : ShowContinueError(state,
4162 0 : format("Thermal tank source side inlet and outlet node names = {} and {}",
4163 0 : Tank.InletNodeName2,
4164 0 : Tank.OutletNodeName2));
4165 0 : ErrorsFound = true;
4166 : }
4167 : }
4168 :
4169 6 : if (DesuperHtr.WaterHeaterTankNum == 0) {
4170 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4171 0 : ShowContinueError(state, format(" Water heater tank = {} not found.", DesuperHtr.TankName));
4172 0 : ErrorsFound = true;
4173 : }
4174 : }
4175 : }
4176 :
4177 : // Loop through HPWH's and then search all water heaters for the tank connected to the HPWH
4178 72 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4179 :
4180 10 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
4181 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
4182 22 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
4183 :
4184 : // Create reference to current HPWH object in array.
4185 12 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
4186 12 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
4187 : // Pumped Condenser
4188 7 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
4189 : } else {
4190 : // Wrapped Condenser
4191 5 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
4192 : }
4193 :
4194 : // find the tank associated with the heat pump water heater and change its %TYPE to HEAT PUMP:WATER HEATER
4195 29 : for (int CheckWaterHeaterNum = 1; CheckWaterHeaterNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++CheckWaterHeaterNum) {
4196 :
4197 17 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(CheckWaterHeaterNum);
4198 :
4199 17 : if (!(Util::SameString(HPWH.TankName, Tank.Name) && Util::SameString(HPWH.TankType, Tank.Type))) continue;
4200 :
4201 : // save backup element and on/off-cycle parasitic properties for use during standard rating procedure
4202 12 : HPWH.BackupElementCapacity = Tank.MaxCapacity;
4203 12 : HPWH.BackupElementEfficiency = Tank.Efficiency;
4204 12 : HPWH.WHOnCycParaLoad = Tank.OnCycParaLoad;
4205 12 : HPWH.WHOffCycParaLoad = Tank.OffCycParaLoad;
4206 12 : HPWH.WHOnCycParaFracToTank = Tank.OnCycParaFracToTank;
4207 12 : HPWH.WHOffCycParaFracToTank = Tank.OffCycParaFracToTank;
4208 12 : HPWH.WHPLFCurve = Tank.PLFCurve;
4209 :
4210 12 : if (((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4211 6 : (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) ||
4212 6 : (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified)) {
4213 12 : HPWH.TankType = Tank.Type;
4214 12 : HPWH.HPWHTankType = Tank.WaterThermalTankType;
4215 : } else {
4216 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4217 0 : ShowContinueError(state, format("Invalid water heater tank type = {}", Tank.Type));
4218 0 : ErrorsFound = true;
4219 : }
4220 :
4221 : // Set up comp set for condenser water side nodes (reverse inlet/outlet for water heater)
4222 12 : if (HPWH.bIsIHP) {
4223 0 : BranchNodeConnections::SetUpCompSets(state,
4224 : HPWH.Type,
4225 : HPWH.Name,
4226 : HPWH.DXCoilType,
4227 0 : HPWH.DXCoilName + " Water Coil",
4228 : HPWH.InletNodeName1,
4229 : HPWH.OutletNodeName1,
4230 : "HPWH To Coil");
4231 : } else {
4232 12 : BranchNodeConnections::SetUpCompSets(
4233 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.InletNodeName1, HPWH.OutletNodeName1, "HPWH To Coil");
4234 : }
4235 12 : BranchNodeConnections::SetUpCompSets(
4236 : state, HPWH.Type, HPWH.Name, HPWH.TankType, HPWH.TankName, HPWH.OutletNodeName1, HPWH.InletNodeName1, "HPWH To Tank");
4237 :
4238 : // If WaterHeaterMixed: do not allow modulating control for HPWH's (i.e. modulating control usually used for tankless WH's)
4239 12 : if ((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4240 6 : (Tank.ControlType == HeaterControlMode::Modulate)) {
4241 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4242 0 : ShowContinueError(state, format("Heater Control Type for {} = {} must be CYCLE.", Tank.Type, Tank.Name));
4243 0 : ErrorsFound = true;
4244 : }
4245 :
4246 12 : Tank.HeatPumpNum = HPWaterHeaterNum;
4247 12 : HPWH.WaterHeaterTankNum = CheckWaterHeaterNum;
4248 12 : HPWH.FoundTank = true;
4249 :
4250 12 : if (Tank.DesuperheaterNum > 0) {
4251 0 : ShowSevereError(
4252 : state,
4253 0 : format("{} = {}and Coil:WaterHeating:Desuperheater = {}: cannot be connected to the same water heater tank = {}",
4254 0 : state.dataIPShortCut->cCurrentModuleObject,
4255 0 : HPWH.Name,
4256 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(CheckWaterHeaterNum).Name,
4257 0 : Tank.Name));
4258 : }
4259 :
4260 : // check that water heater source side effectiveness is greater than 0
4261 12 : if (Tank.SourceEffectiveness <= 0.0) {
4262 0 : ShowSevereError(state,
4263 0 : format("{} = {}: Invalid source side effectiveness for heat pump water heater = {:.3T}",
4264 0 : state.dataIPShortCut->cCurrentModuleObject,
4265 0 : HPWH.Name,
4266 0 : Tank.SourceEffectiveness));
4267 0 : ShowContinueError(state, " water heater source effectiveness will default to 1.0 and simulation continues.");
4268 0 : Tank.SourceEffectiveness = 1.0;
4269 : }
4270 :
4271 : // Set up the source side nodes for wrapped condensers
4272 12 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4273 5 : if (Tank.SourceInletNode > 0 || Tank.SourceOutletNode > 0) {
4274 0 : ShowSevereError(state, format("{} = {} has a source inlet or outlet node specified,", Tank.Type, Tank.Name));
4275 0 : ShowContinueError(
4276 0 : state, format("but it is attached to {} = {}, which doesn't permit source side connections.", HPWH.Type, HPWH.Name));
4277 0 : ShowContinueError(state, "Please leave the source side inlet and outlet fields blank.");
4278 0 : ErrorsFound = true;
4279 : } else {
4280 :
4281 : DataLoopNode::ConnectionObjectType objType = static_cast<DataLoopNode::ConnectionObjectType>(
4282 5 : getEnumValue(BranchNodeConnections::ConnectionObjectTypeNamesUC, Util::makeUPPER(Tank.Type)));
4283 :
4284 5 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
4285 5 : HPWH.OutletNodeName1,
4286 : ErrorsFound,
4287 : objType,
4288 5 : Tank.Name,
4289 : DataLoopNode::NodeFluidType::Water,
4290 : DataLoopNode::ConnectionType::Inlet,
4291 : NodeInputManager::CompFluidStream::Secondary,
4292 : DataLoopNode::ObjectIsNotParent);
4293 5 : Tank.InletNodeName2 = HPWH.OutletNodeName1;
4294 5 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
4295 5 : HPWH.InletNodeName1,
4296 : ErrorsFound,
4297 : objType,
4298 5 : Tank.Name,
4299 : DataLoopNode::NodeFluidType::Water,
4300 : DataLoopNode::ConnectionType::Outlet,
4301 : NodeInputManager::CompFluidStream::Secondary,
4302 : DataLoopNode::ObjectIsNotParent);
4303 5 : Tank.OutletNodeName2 = HPWH.InletNodeName1;
4304 : }
4305 :
4306 : // Mark the tank as not stand alone because it is connected now.
4307 5 : Tank.StandAlone = false;
4308 : }
4309 :
4310 : // Set HPWH structure variable StandAlone to TRUE if use nodes are not connected
4311 12 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) HPWH.StandAlone = true;
4312 :
4313 12 : if (HPWH.WHUseInletNode != Tank.UseInletNode || HPWH.WHUseOutletNode != Tank.UseOutletNode) {
4314 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4315 0 : ShowContinueError(state,
4316 0 : format("Heat pump water heater tank use side inlet and outlet node names must match the use side inlet and "
4317 : "outlet node names for water heater tank = {}: {}",
4318 0 : HPWH.TankType,
4319 0 : HPWH.TankName));
4320 0 : ShowContinueError(state,
4321 0 : format("Heat pump water heater use side inlet and outlet node names = {} and {}",
4322 0 : HPWH.InletNodeName2,
4323 0 : HPWH.OutletNodeName2));
4324 0 : ShowContinueError(state,
4325 0 : format("Water heater tank use side inlet and outlet node names = {} and {}",
4326 0 : Tank.InletNodeName1,
4327 0 : Tank.OutletNodeName1));
4328 0 : ErrorsFound = true;
4329 : } else {
4330 12 : if (!HPWH.StandAlone) {
4331 14 : BranchNodeConnections::TestCompSet(state, HPWH.Type, HPWH.Name, Tank.InletNodeName1, Tank.OutletNodeName1, "Water Nodes");
4332 : }
4333 : }
4334 :
4335 : // verify HP/tank source node connections
4336 12 : if (HPWH.CondWaterInletNode != Tank.SourceOutletNode) {
4337 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4338 0 : ShowContinueError(state,
4339 : "Heat Pump condenser water inlet node name does not match water heater tank source outlet node name.");
4340 0 : ShowContinueError(
4341 : state,
4342 0 : format("Heat pump condenser water inlet and outlet node names = {} and {}", HPWH.InletNodeName1, HPWH.OutletNodeName1));
4343 0 : ShowContinueError(state,
4344 0 : format("Water heater tank source side inlet and outlet node names = {} and {}",
4345 0 : Tank.InletNodeName2,
4346 0 : Tank.OutletNodeName2));
4347 0 : ErrorsFound = true;
4348 : }
4349 :
4350 12 : if (HPWH.CondWaterOutletNode != Tank.SourceInletNode) {
4351 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4352 0 : ShowContinueError(state,
4353 : "Heat Pump condenser water outlet node name does not match water heater tank source inlet node name.");
4354 0 : ShowContinueError(
4355 : state,
4356 0 : format("Heat pump condenser water inlet and outlet node names = {} and {}", HPWH.InletNodeName1, HPWH.OutletNodeName1));
4357 0 : ShowContinueError(state,
4358 0 : format("Water heater tank source side inlet and outlet node names = {} and {}",
4359 0 : Tank.InletNodeName2,
4360 0 : Tank.OutletNodeName2));
4361 0 : ErrorsFound = true;
4362 : }
4363 :
4364 : // verify wrapped condenser location
4365 12 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4366 : // make sure the top of the condenser is not above the tank height.
4367 5 : if (HPWH.WrappedCondenserTopLocation > Tank.Height) {
4368 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4369 0 : ShowContinueError(state, "The height of the top of the wrapped condenser is greater than the height of the tank.");
4370 0 : ErrorsFound = true;
4371 : }
4372 : }
4373 :
4374 : // Verify tank name is in a zone equipment list if HPWH Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
4375 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
4376 4 : if (allocated(state.dataZoneEquip->ZoneEquipConfig) && allocated(state.dataZoneEquip->ZoneEquipList)) {
4377 1 : bool FoundTankInList = false;
4378 1 : bool TankNotLowestPriority = false;
4379 1 : int ZoneEquipConfigNum = HPWH.AmbientTempZone;
4380 1 : int ZoneEquipListNum = state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigNum).EquipListIndex;
4381 1 : int TankCoolingPriority = 0;
4382 1 : int TankHeatingPriority = 0;
4383 1 : auto const &zoneEquipList = state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum);
4384 2 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= zoneEquipList.NumOfEquipTypes; ++EquipmentTypeNum) {
4385 2 : if (zoneEquipList.EquipName(EquipmentTypeNum) != HPWH.Name) continue;
4386 1 : FoundTankInList = true;
4387 1 : TankCoolingPriority = zoneEquipList.CoolingPriority(EquipmentTypeNum);
4388 1 : TankHeatingPriority = zoneEquipList.HeatingPriority(EquipmentTypeNum);
4389 1 : break;
4390 : } // EquipmentTypeNum
4391 1 : if (!FoundTankInList) {
4392 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4393 0 : ShowContinueError(state,
4394 : "Heat pump water heater type and name must be listed in the correct "
4395 : "ZoneHVAC:EquipmentList object when Inlet Air Configuration is equal to "
4396 : "ZoneAirOnly or ZoneAndOutdoorAir.");
4397 0 : ErrorsFound = true;
4398 : }
4399 : // check that tank has lower priority than all other non-HPWH objects in Zone
4400 : // Equipment List
4401 3 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= zoneEquipList.NumOfEquipTypes; ++EquipmentTypeNum) {
4402 2 : if (Util::SameString(zoneEquipList.EquipTypeName(EquipmentTypeNum), state.dataIPShortCut->cCurrentModuleObject))
4403 1 : continue;
4404 1 : if (TankCoolingPriority > zoneEquipList.CoolingPriority(EquipmentTypeNum) ||
4405 0 : TankHeatingPriority > zoneEquipList.HeatingPriority(EquipmentTypeNum)) {
4406 1 : TankNotLowestPriority = true;
4407 : }
4408 : } // EquipmentTypeNum
4409 1 : if (TankNotLowestPriority && FoundTankInList) {
4410 1 : ShowWarningError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4411 2 : ShowContinueError(state,
4412 : "Heat pump water heaters should be simulated first, before other space "
4413 : "conditioning equipment.");
4414 3 : ShowContinueError(state,
4415 : "Poor temperature control may result if the Heating/Cooling sequence number is "
4416 : "not 1 in the ZoneHVAC:EquipmentList.");
4417 : }
4418 : } else {
4419 3 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4420 6 : ShowContinueError(state,
4421 : "ZoneHVAC:EquipmentList and ZoneHVAC:EquipmentConnections objects are required when Inlet Air "
4422 : "Configuration is either ZoneAirOnly or ZoneAndOutdoorAir.");
4423 3 : ErrorsFound = true;
4424 : } // ALLOCATED
4425 : } // InletAirConfiguration
4426 :
4427 12 : if (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
4428 :
4429 : // Nodal heat distribution fraction for stratified tank wrapped condensers
4430 6 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4431 5 : if (Tank.Shape == TankShape::HorizCylinder) {
4432 0 : ShowWarningError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4433 0 : ShowContinueError(state, "A wrapped condenser HPWH model should not be used with a horizontal stratified tank.");
4434 0 : ShowContinueError(
4435 : state, "Ignoring condenser location and distributing heat evenly throughout the tank. Simulation continues.");
4436 0 : Real64 const SameFrac = 1.0 / Tank.Nodes;
4437 0 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4438 0 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac = SameFrac;
4439 : }
4440 : } else {
4441 5 : Real64 H0 = Tank.Height; // height of top of node
4442 : Real64 H; // height of bottom of node
4443 5 : Real64 SumFrac(0.0);
4444 : // Get the fraction of each stratified node that is wrapped by the condenser
4445 59 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4446 54 : StratifiedNodeData &CurNode = Tank.Node(NodeNum);
4447 54 : if (NodeNum == Tank.Nodes) {
4448 5 : H = 0.0;
4449 : } else {
4450 49 : H = H0 - CurNode.Height;
4451 : }
4452 54 : if (H < HPWH.WrappedCondenserBottomLocation && H0 > HPWH.WrappedCondenserBottomLocation) {
4453 : // The bottom of the condenser starts partway through this node.
4454 5 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0 - (HPWH.WrappedCondenserBottomLocation - H) / CurNode.Height;
4455 49 : } else if (H >= HPWH.WrappedCondenserBottomLocation && H <= HPWH.WrappedCondenserTopLocation) {
4456 27 : if (H0 > HPWH.WrappedCondenserTopLocation) {
4457 : // the top of the condenser ends partway through this node.
4458 5 : CurNode.HPWHWrappedCondenserHeatingFrac = (HPWH.WrappedCondenserTopLocation - H) / CurNode.Height;
4459 : } else {
4460 : // the entire node is wrapped by the condenser
4461 22 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0;
4462 : }
4463 : } else {
4464 22 : CurNode.HPWHWrappedCondenserHeatingFrac = 0.0;
4465 : }
4466 54 : SumFrac += CurNode.HPWHWrappedCondenserHeatingFrac;
4467 54 : H0 = H;
4468 : }
4469 : // Normalize the fractions so they sum to 1.
4470 59 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4471 54 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac /= SumFrac;
4472 : }
4473 : }
4474 : }
4475 :
4476 : // Stratified Tank HPWH control sensor node locations
4477 6 : if (HPWH.ControlSensor1Height < 0.0) {
4478 : // default to heater 1
4479 1 : HPWH.ControlSensor1Height = Tank.HeaterHeight1;
4480 : }
4481 6 : if (HPWH.ControlSensor2Height < 0.0) {
4482 : // default to heater 2
4483 0 : HPWH.ControlSensor2Height = Tank.HeaterHeight2;
4484 : }
4485 :
4486 : // Get the vertical tank height depending on the type of tank
4487 : Real64 TankHeight;
4488 6 : if (Tank.Shape == TankShape::VertCylinder || Tank.Shape == TankShape::Other) {
4489 6 : TankHeight = Tank.Height;
4490 : } else {
4491 0 : assert(Tank.Shape == TankShape::HorizCylinder);
4492 : // For horizontal cylinders, the tank "height" is actually the length.
4493 : // We need to calculate the height.
4494 0 : Real64 EndArea = Tank.Volume / Tank.Height;
4495 0 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
4496 0 : TankHeight = 2.0 * Radius; // actual vertical height
4497 : }
4498 :
4499 : // Make sure the control sensor locations are in the tank
4500 6 : if (HPWH.ControlSensor1Height < 0.0 || HPWH.ControlSensor1Height > TankHeight) {
4501 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4502 0 : ShowContinueError(state, "Control Sensor 1 is located outside the tank.");
4503 0 : ErrorsFound = true;
4504 : }
4505 6 : if (HPWH.ControlSensor2Height < 0.0 || HPWH.ControlSensor2Height > TankHeight) {
4506 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4507 0 : ShowContinueError(state, "Control Sensor 2 is located outside the tank.");
4508 0 : ErrorsFound = true;
4509 : }
4510 :
4511 : // Assign the control sensors to the appropriate nodes
4512 6 : Real64 H0 = TankHeight;
4513 : Real64 H;
4514 72 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4515 66 : StratifiedNodeData const &TankNode = Tank.Node(NodeNum);
4516 66 : if (NodeNum == Tank.Nodes) {
4517 6 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
4518 : } else {
4519 60 : H = H0 - TankNode.Height;
4520 : }
4521 :
4522 : // Control Sensor 1 Node
4523 66 : if (HPWH.ControlSensor1Height <= H0 && HPWH.ControlSensor1Height > H) {
4524 6 : HPWH.ControlSensor1Node = NodeNum;
4525 : }
4526 :
4527 : // Control Sensor 2 Node
4528 66 : if (HPWH.ControlSensor2Height <= H0 && HPWH.ControlSensor2Height > H) {
4529 6 : HPWH.ControlSensor2Node = NodeNum;
4530 : }
4531 :
4532 66 : H0 = H;
4533 : }
4534 : }
4535 :
4536 : } // DO CheckWaterHeaterNum = 1, NumWaterHeater
4537 :
4538 12 : if (!HPWH.FoundTank) {
4539 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4540 0 : ShowContinueError(state, format("Water heater tank object not found = {}, {}", HPWH.TankType, HPWH.TankName));
4541 0 : ErrorsFound = true;
4542 : }
4543 :
4544 : } // DO HPWaterHeaterNum = 1, NumHeatPumpWaterHeater
4545 : }
4546 :
4547 : // Get water heater sizing input.
4548 72 : state.dataIPShortCut->cCurrentModuleObject = "WaterHeater:Sizing";
4549 144 : state.dataWaterThermalTanks->numWaterHeaterSizing =
4550 72 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
4551 :
4552 72 : if (state.dataWaterThermalTanks->numWaterHeaterSizing > 0) {
4553 :
4554 2 : for (int WHsizingNum = 1; WHsizingNum <= state.dataWaterThermalTanks->numWaterHeaterSizing; ++WHsizingNum) {
4555 : int NumAlphas;
4556 : int NumNums;
4557 : int IOStat;
4558 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4559 1 : state.dataIPShortCut->cCurrentModuleObject,
4560 : WHsizingNum,
4561 1 : state.dataIPShortCut->cAlphaArgs,
4562 : NumAlphas,
4563 1 : state.dataIPShortCut->rNumericArgs,
4564 : NumNums,
4565 : IOStat);
4566 :
4567 : // find which water heater this object is for
4568 1 : int WaterThermalTankNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(1), state.dataWaterThermalTanks->WaterThermalTank);
4569 1 : if (WaterThermalTankNum == 0) {
4570 : // did not match name throw warning.
4571 0 : ShowSevereError(state,
4572 0 : format("{} object name: {} does not match any of the water heaters defined in the file",
4573 0 : state.dataIPShortCut->cCurrentModuleObject,
4574 0 : state.dataIPShortCut->cAlphaArgs(1)));
4575 0 : ErrorsFound = true;
4576 0 : continue;
4577 : } else { // we have a match
4578 : // store the sizing data in "sizing" nested derived type for the correct water heater
4579 :
4580 1 : if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PeakDraw")) {
4581 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PeakDraw;
4582 1 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "ResidentialHUD-FHAMinimum")) {
4583 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::ResidentialMin;
4584 1 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerPerson")) {
4585 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerPerson;
4586 1 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerFloorArea")) {
4587 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerFloorArea;
4588 1 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerUnit")) {
4589 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerUnit;
4590 1 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerSolarCollectorArea")) {
4591 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerSolarColArea;
4592 : } else {
4593 : // wrong design mode entered, throw error
4594 0 : ShowSevereError(state,
4595 0 : format("{} object named: {} contains an incorrect Design Mode of: {}",
4596 0 : state.dataIPShortCut->cCurrentModuleObject,
4597 0 : state.dataIPShortCut->cAlphaArgs(1),
4598 0 : state.dataIPShortCut->cAlphaArgs(2)));
4599 0 : ErrorsFound = true;
4600 : }
4601 :
4602 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime = state.dataIPShortCut->rNumericArgs(1);
4603 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryTime = state.dataIPShortCut->rNumericArgs(2);
4604 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow =
4605 1 : state.dataIPShortCut->rNumericArgs(3);
4606 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms =
4607 1 : int(state.dataIPShortCut->rNumericArgs(4));
4608 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBathrooms =
4609 1 : int(state.dataIPShortCut->rNumericArgs(5));
4610 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson =
4611 1 : state.dataIPShortCut->rNumericArgs(6);
4612 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson =
4613 1 : state.dataIPShortCut->rNumericArgs(7);
4614 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea =
4615 1 : state.dataIPShortCut->rNumericArgs(8);
4616 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea =
4617 1 : state.dataIPShortCut->rNumericArgs(9);
4618 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits = state.dataIPShortCut->rNumericArgs(10);
4619 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit =
4620 1 : state.dataIPShortCut->rNumericArgs(11);
4621 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit =
4622 1 : state.dataIPShortCut->rNumericArgs(12);
4623 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea =
4624 1 : state.dataIPShortCut->rNumericArgs(13);
4625 1 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.HeightAspectRatio =
4626 1 : state.dataIPShortCut->rNumericArgs(14);
4627 :
4628 1 : switch (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode) {
4629 :
4630 0 : case SizingMode::Invalid: {
4631 : // do nothing, error thrown if design mode not found
4632 0 : break;
4633 : }
4634 0 : case SizingMode::PeakDraw: { // need to have entered a reasonable value for TankDrawTime
4635 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime <= 0.0) {
4636 0 : ShowSevereError(state,
4637 0 : format("{}, named {}, design mode set to Peak Draw but needs a positive value for tank draw time",
4638 0 : state.dataIPShortCut->cCurrentModuleObject,
4639 0 : state.dataIPShortCut->cAlphaArgs(1)));
4640 0 : ErrorsFound = true;
4641 : }
4642 : // constrain crazy sizes by limiting to 10 years or 8760*10
4643 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime > 87600.0) {
4644 0 : ShowWarningError(state,
4645 0 : format("{}, named {}, has input with an unreasonably large Tank Draw Time, more than 10 years",
4646 0 : state.dataIPShortCut->cCurrentModuleObject,
4647 0 : state.dataIPShortCut->cAlphaArgs(1)));
4648 0 : ErrorsFound = true;
4649 : }
4650 : // if both volume and demand side flow connections are autosized, must be a good NominalVolForSizingDemandSideFlow
4651 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseSidePlantLoc.loopSideNum ==
4652 0 : DataPlant::LoopSideLocation::Demand) &&
4653 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseDesignVolFlowRateWasAutoSized)) {
4654 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4655 0 : ShowWarningError(state,
4656 0 : format("{}, named {} needs a value for Nominal Tank Volume for Autosizing Plant Connections",
4657 0 : state.dataIPShortCut->cCurrentModuleObject,
4658 0 : state.dataIPShortCut->cAlphaArgs(1)));
4659 0 : ErrorsFound = true;
4660 : }
4661 : }
4662 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SrcSidePlantLoc.loopSideNum ==
4663 0 : DataPlant::LoopSideLocation::Demand) &&
4664 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceDesignVolFlowRateWasAutoSized)) {
4665 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4666 0 : ShowWarningError(state,
4667 0 : format("{}, named {} needs a value for Nominal Tank Volume for Autosizing Plant Connections",
4668 0 : state.dataIPShortCut->cCurrentModuleObject,
4669 0 : state.dataIPShortCut->cAlphaArgs(1)));
4670 0 : ErrorsFound = true;
4671 : }
4672 : }
4673 :
4674 0 : break;
4675 : }
4676 0 : case SizingMode::ResidentialMin: {
4677 : // it would have to have at least on bedroom and any more than 10 is crazy for this mode
4678 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms < 1) {
4679 0 : ShowSevereError(state,
4680 0 : format("{}, named {}, mode needs at least one bedroom",
4681 0 : state.dataIPShortCut->cCurrentModuleObject,
4682 0 : state.dataIPShortCut->cAlphaArgs(1)));
4683 0 : ErrorsFound = true;
4684 : }
4685 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms > 10) {
4686 0 : ShowWarningError(state,
4687 0 : format("{}, named {}, probably has too many bedrooms for the selected design mode",
4688 0 : state.dataIPShortCut->cCurrentModuleObject,
4689 0 : state.dataIPShortCut->cAlphaArgs(1)));
4690 : }
4691 :
4692 0 : break;
4693 : }
4694 0 : case SizingMode::PerPerson: {
4695 :
4696 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4697 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson <= 0.0)) {
4698 0 : ShowSevereError(state,
4699 0 : format("{}, named {}, PerPerson mode needs positive value input for storage capacity per person",
4700 0 : state.dataIPShortCut->cCurrentModuleObject,
4701 0 : state.dataIPShortCut->cAlphaArgs(1)));
4702 0 : ErrorsFound = true;
4703 : }
4704 :
4705 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4706 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson <= 0.0)) {
4707 0 : ShowSevereError(state,
4708 0 : format("{}, named {}, PerPerson mode needs positive value input for recovery capacity per person",
4709 0 : state.dataIPShortCut->cCurrentModuleObject,
4710 0 : state.dataIPShortCut->cAlphaArgs(1)));
4711 0 : ErrorsFound = true;
4712 : }
4713 :
4714 0 : break;
4715 : }
4716 0 : case SizingMode::PerFloorArea: {
4717 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4718 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea <= 0.0)) {
4719 0 : ShowSevereError(state,
4720 0 : format("{}, named {}, PerArea mode needs positive value input for storage capacity per floor area",
4721 0 : state.dataIPShortCut->cCurrentModuleObject,
4722 0 : state.dataIPShortCut->cAlphaArgs(1)));
4723 0 : ErrorsFound = true;
4724 : }
4725 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4726 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea <= 0.0)) {
4727 0 : ShowSevereError(state,
4728 0 : format("{}, named {}, PerArea mode needs positive value input for recovery capacity per floor area",
4729 0 : state.dataIPShortCut->cCurrentModuleObject,
4730 0 : state.dataIPShortCut->cAlphaArgs(1)));
4731 0 : ErrorsFound = true;
4732 : }
4733 :
4734 0 : break;
4735 : }
4736 0 : case SizingMode::PerUnit: {
4737 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4738 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit <= 0.0)) {
4739 0 : ShowSevereError(state,
4740 0 : format("{}, named {}, PerUnit mode needs positive value input for storage capacity per unit",
4741 0 : state.dataIPShortCut->cCurrentModuleObject,
4742 0 : state.dataIPShortCut->cAlphaArgs(1)));
4743 0 : ErrorsFound = true;
4744 : }
4745 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4746 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4747 0 : ShowSevereError(state,
4748 0 : format("{}, named {}, PerUnit mode needs positive value input for number of units",
4749 0 : state.dataIPShortCut->cCurrentModuleObject,
4750 0 : state.dataIPShortCut->cAlphaArgs(1)));
4751 0 : ErrorsFound = true;
4752 : }
4753 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4754 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit <= 0.0)) {
4755 0 : ShowSevereError(state,
4756 0 : format("{}, named {}, PerUnit mode needs positive value input for recovery capacity per unit",
4757 0 : state.dataIPShortCut->cCurrentModuleObject,
4758 0 : state.dataIPShortCut->cAlphaArgs(1)));
4759 0 : ErrorsFound = true;
4760 : }
4761 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4762 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4763 0 : ShowSevereError(state,
4764 0 : format("{}, named {}, PerUnit mode needs positive value input for number of units",
4765 0 : state.dataIPShortCut->cCurrentModuleObject,
4766 0 : state.dataIPShortCut->cAlphaArgs(1)));
4767 0 : ErrorsFound = true;
4768 : }
4769 0 : break;
4770 : }
4771 1 : case SizingMode::PerSolarColArea: {
4772 2 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4773 1 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea <= 0.0)) {
4774 0 : ShowSevereError(
4775 : state,
4776 0 : format("{}, named {}, PerSolarCollectorArea mode needs positive value input for storage capacity per collector area",
4777 0 : state.dataIPShortCut->cCurrentModuleObject,
4778 0 : state.dataIPShortCut->cAlphaArgs(1)));
4779 0 : ErrorsFound = true;
4780 : }
4781 1 : break;
4782 : }
4783 0 : default:
4784 0 : break;
4785 : }
4786 :
4787 : } // found water heater num okay
4788 : } // loop over sizing objects
4789 :
4790 : } // any water heater sizing objects
4791 :
4792 : // now check that if water heater fields were autosized, that there was also a sizing object for that water heater
4793 72 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4794 58 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4795 :
4796 32 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4797 1 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4798 0 : ShowWarningError(
4799 : state,
4800 0 : format("Water heater named {}has tank volume set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4801 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4802 0 : ErrorsFound = true;
4803 : }
4804 31 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4805 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4806 0 : ShowWarningError(
4807 : state,
4808 0 : format("Water heater named {}has heater capacity set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4809 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4810 0 : ErrorsFound = true;
4811 : }
4812 31 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeightWasAutoSized) &&
4813 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4814 0 : ShowWarningError(
4815 : state,
4816 0 : format("Water heater named {}has tank height set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4817 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4818 0 : ErrorsFound = true;
4819 : }
4820 : }
4821 : }
4822 :
4823 : // now do calls to TestCompSet for tanks, depending on nodes and heat pump water heater
4824 72 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4825 58 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4826 40 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseInletNode > 0 &&
4827 9 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseOutletNode > 0) {
4828 9 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeatPumpNum > 0) {
4829 : // do nothing, Use nodes are tested for HeatPump:WaterHeater not tank
4830 : } else {
4831 6 : BranchNodeConnections::TestCompSet(state,
4832 2 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4833 2 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4834 2 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName1,
4835 2 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName1,
4836 : "Use Side Water Nodes");
4837 : }
4838 : }
4839 53 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceInletNode > 0 &&
4840 22 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceOutletNode > 0) {
4841 :
4842 66 : BranchNodeConnections::TestCompSet(state,
4843 22 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4844 22 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4845 22 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName2,
4846 22 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName2,
4847 : "Source Side Water Nodes");
4848 : }
4849 : }
4850 : }
4851 :
4852 72 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4853 58 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4854 :
4855 31 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).setupZoneInternalGains(state);
4856 :
4857 : } // WaterThermalTankNum
4858 : }
4859 : } // get input flag
4860 :
4861 72 : return ErrorsFound;
4862 : }
4863 :
4864 1 : void WaterThermalTankData::setupOutputVars(EnergyPlusData &state)
4865 : {
4866 1 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) ||
4867 1 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
4868 0 : this->setupChilledWaterTankOutputVars(state);
4869 : } else {
4870 : // moving setupWaterHeaterOutputVars to here causes big table diffs...
4871 1 : this->setupWaterHeaterOutputVars(state);
4872 : }
4873 : // moving setupZoneInternalGains to here causes math and table diffs...
4874 : // this->setupZoneInternalGains();
4875 1 : }
4876 :
4877 0 : void WaterThermalTankData::setupChilledWaterTankOutputVars(EnergyPlusData &state)
4878 : {
4879 :
4880 : // CurrentModuleObject='ThermalStorage:ChilledWater:Mixed/ThermalStorage:ChilledWater:Stratified'
4881 0 : SetupOutputVariable(state,
4882 : "Chilled Water Thermal Storage Tank Temperature",
4883 : Constant::Units::C,
4884 0 : this->TankTempAvg,
4885 : OutputProcessor::TimeStepType::System,
4886 : OutputProcessor::StoreType::Average,
4887 0 : this->Name);
4888 :
4889 0 : SetupOutputVariable(state,
4890 : "Chilled Water Thermal Storage Final Tank Temperature",
4891 : Constant::Units::C,
4892 0 : this->TankTemp,
4893 : OutputProcessor::TimeStepType::System,
4894 : OutputProcessor::StoreType::Average,
4895 0 : this->Name);
4896 :
4897 0 : SetupOutputVariable(state,
4898 : "Chilled Water Thermal Storage Tank Heat Gain Rate",
4899 : Constant::Units::W,
4900 0 : this->LossRate,
4901 : OutputProcessor::TimeStepType::System,
4902 : OutputProcessor::StoreType::Average,
4903 0 : this->Name);
4904 0 : SetupOutputVariable(state,
4905 : "Chilled Water Thermal Storage Tank Heat Gain Energy",
4906 : Constant::Units::J,
4907 0 : this->LossEnergy,
4908 : OutputProcessor::TimeStepType::System,
4909 : OutputProcessor::StoreType::Sum,
4910 0 : this->Name);
4911 :
4912 0 : SetupOutputVariable(state,
4913 : "Chilled Water Thermal Storage Use Side Mass Flow Rate",
4914 : Constant::Units::kg_s,
4915 0 : this->UseMassFlowRate,
4916 : OutputProcessor::TimeStepType::System,
4917 : OutputProcessor::StoreType::Average,
4918 0 : this->Name);
4919 :
4920 0 : SetupOutputVariable(state,
4921 : "Chilled Water Thermal Storage Use Side Inlet Temperature",
4922 : Constant::Units::C,
4923 0 : this->UseInletTemp,
4924 : OutputProcessor::TimeStepType::System,
4925 : OutputProcessor::StoreType::Average,
4926 0 : this->Name);
4927 :
4928 0 : SetupOutputVariable(state,
4929 : "Chilled Water Thermal Storage Use Side Outlet Temperature",
4930 : Constant::Units::C,
4931 0 : this->UseOutletTemp,
4932 : OutputProcessor::TimeStepType::System,
4933 : OutputProcessor::StoreType::Average,
4934 0 : this->Name);
4935 :
4936 0 : SetupOutputVariable(state,
4937 : "Chilled Water Thermal Storage Use Side Heat Transfer Rate",
4938 : Constant::Units::W,
4939 0 : this->UseRate,
4940 : OutputProcessor::TimeStepType::System,
4941 : OutputProcessor::StoreType::Average,
4942 0 : this->Name);
4943 0 : SetupOutputVariable(state,
4944 : "Chilled Water Thermal Storage Use Side Heat Transfer Energy",
4945 : Constant::Units::J,
4946 0 : this->UseEnergy,
4947 : OutputProcessor::TimeStepType::System,
4948 : OutputProcessor::StoreType::Sum,
4949 0 : this->Name);
4950 :
4951 0 : SetupOutputVariable(state,
4952 : "Chilled Water Thermal Storage Source Side Mass Flow Rate",
4953 : Constant::Units::kg_s,
4954 0 : this->SourceMassFlowRate,
4955 : OutputProcessor::TimeStepType::System,
4956 : OutputProcessor::StoreType::Average,
4957 0 : this->Name);
4958 :
4959 0 : SetupOutputVariable(state,
4960 : "Chilled Water Thermal Storage Source Side Inlet Temperature",
4961 : Constant::Units::C,
4962 0 : this->SourceInletTemp,
4963 : OutputProcessor::TimeStepType::System,
4964 : OutputProcessor::StoreType::Average,
4965 0 : this->Name);
4966 :
4967 0 : SetupOutputVariable(state,
4968 : "Chilled Water Thermal Storage Source Side Outlet Temperature",
4969 : Constant::Units::C,
4970 0 : this->SourceOutletTemp,
4971 : OutputProcessor::TimeStepType::System,
4972 : OutputProcessor::StoreType::Average,
4973 0 : this->Name);
4974 :
4975 0 : SetupOutputVariable(state,
4976 : "Chilled Water Thermal Storage Source Side Heat Transfer Rate",
4977 : Constant::Units::W,
4978 0 : this->SourceRate,
4979 : OutputProcessor::TimeStepType::System,
4980 : OutputProcessor::StoreType::Average,
4981 0 : this->Name);
4982 0 : SetupOutputVariable(state,
4983 : "Chilled Water Thermal Storage Source Side Heat Transfer Energy",
4984 : Constant::Units::J,
4985 0 : this->SourceEnergy,
4986 : OutputProcessor::TimeStepType::System,
4987 : OutputProcessor::StoreType::Sum,
4988 0 : this->Name);
4989 :
4990 0 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
4991 :
4992 0 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
4993 0 : SetupOutputVariable(state,
4994 0 : format("Chilled Water Thermal Storage Temperature Node {}", NodeNum),
4995 : Constant::Units::C,
4996 0 : this->Node(NodeNum).TempAvg,
4997 : OutputProcessor::TimeStepType::System,
4998 : OutputProcessor::StoreType::Average,
4999 0 : this->Name);
5000 : }
5001 :
5002 0 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5003 0 : SetupOutputVariable(state,
5004 0 : format("Chilled Water Thermal Storage Final Temperature Node {}", NodeNum),
5005 : Constant::Units::C,
5006 0 : this->Node(NodeNum).Temp,
5007 : OutputProcessor::TimeStepType::System,
5008 : OutputProcessor::StoreType::Average,
5009 0 : this->Name);
5010 : }
5011 : }
5012 :
5013 0 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
5014 :
5015 0 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5016 : static constexpr std::string_view Format_724("Chilled Water Tank Stratified Node Information,{},{:.4T},{:.4T},{:.4T},{},{}\n");
5017 :
5018 0 : print(state.files.eio,
5019 : Format_724,
5020 : NodeNum,
5021 0 : this->Node(NodeNum).Height,
5022 0 : this->Node(NodeNum).Volume,
5023 0 : this->Node(NodeNum).OffCycLossCoeff,
5024 0 : this->Node(NodeNum).Inlets,
5025 0 : this->Node(NodeNum).Outlets);
5026 : }
5027 : }
5028 0 : }
5029 :
5030 31 : void WaterThermalTankData::setupZoneInternalGains(EnergyPlusData &state)
5031 : {
5032 : // set up internal gains if tank is in a thermal zone
5033 31 : if (this->AmbientTempZone > 0) {
5034 3 : switch (this->WaterThermalTankType) {
5035 2 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
5036 2 : SetupZoneInternalGain(state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterMixed, &this->AmbientZoneGain);
5037 2 : break;
5038 : }
5039 0 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
5040 0 : SetupZoneInternalGain(
5041 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterStratified, &this->AmbientZoneGain);
5042 0 : break;
5043 : }
5044 0 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
5045 0 : SetupZoneInternalGain(
5046 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterMixed, &this->AmbientZoneGain);
5047 0 : break;
5048 : }
5049 1 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
5050 1 : SetupZoneInternalGain(
5051 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterStratified, &this->AmbientZoneGain);
5052 1 : break;
5053 : }
5054 0 : default:
5055 0 : break;
5056 : }
5057 : }
5058 31 : }
5059 :
5060 1 : void WaterThermalTankData::setupWaterHeaterOutputVars(EnergyPlusData &state)
5061 : {
5062 :
5063 : // Setup report variables for WaterHeater:Mixed
5064 : // CurrentModuleObject='WaterHeater:Mixed'
5065 2 : SetupOutputVariable(state,
5066 : "Water Heater Tank Temperature",
5067 : Constant::Units::C,
5068 1 : this->TankTempAvg,
5069 : OutputProcessor::TimeStepType::System,
5070 : OutputProcessor::StoreType::Average,
5071 1 : this->Name);
5072 :
5073 2 : SetupOutputVariable(state,
5074 : "Water Heater Final Tank Temperature",
5075 : Constant::Units::C,
5076 1 : this->TankTemp,
5077 : OutputProcessor::TimeStepType::System,
5078 : OutputProcessor::StoreType::Average,
5079 1 : this->Name);
5080 :
5081 2 : SetupOutputVariable(state,
5082 : "Water Heater Heat Loss Rate",
5083 : Constant::Units::W,
5084 1 : this->LossRate,
5085 : OutputProcessor::TimeStepType::System,
5086 : OutputProcessor::StoreType::Average,
5087 1 : this->Name);
5088 2 : SetupOutputVariable(state,
5089 : "Water Heater Heat Loss Energy",
5090 : Constant::Units::J,
5091 1 : this->LossEnergy,
5092 : OutputProcessor::TimeStepType::System,
5093 : OutputProcessor::StoreType::Sum,
5094 1 : this->Name);
5095 :
5096 2 : SetupOutputVariable(state,
5097 : "Water Heater Use Side Mass Flow Rate",
5098 : Constant::Units::kg_s,
5099 1 : this->UseMassFlowRate,
5100 : OutputProcessor::TimeStepType::System,
5101 : OutputProcessor::StoreType::Average,
5102 1 : this->Name);
5103 :
5104 2 : SetupOutputVariable(state,
5105 : "Water Heater Use Side Inlet Temperature",
5106 : Constant::Units::C,
5107 1 : this->UseInletTemp,
5108 : OutputProcessor::TimeStepType::System,
5109 : OutputProcessor::StoreType::Average,
5110 1 : this->Name);
5111 :
5112 2 : SetupOutputVariable(state,
5113 : "Water Heater Use Side Outlet Temperature",
5114 : Constant::Units::C,
5115 1 : this->UseOutletTemp,
5116 : OutputProcessor::TimeStepType::System,
5117 : OutputProcessor::StoreType::Average,
5118 1 : this->Name);
5119 :
5120 2 : SetupOutputVariable(state,
5121 : "Water Heater Use Side Heat Transfer Rate",
5122 : Constant::Units::W,
5123 1 : this->UseRate,
5124 : OutputProcessor::TimeStepType::System,
5125 : OutputProcessor::StoreType::Average,
5126 1 : this->Name);
5127 2 : SetupOutputVariable(state,
5128 : "Water Heater Use Side Heat Transfer Energy",
5129 : Constant::Units::J,
5130 1 : this->UseEnergy,
5131 : OutputProcessor::TimeStepType::System,
5132 : OutputProcessor::StoreType::Sum,
5133 1 : this->Name);
5134 :
5135 2 : SetupOutputVariable(state,
5136 : "Water Heater Source Side Mass Flow Rate",
5137 : Constant::Units::kg_s,
5138 1 : this->SourceMassFlowRate,
5139 : OutputProcessor::TimeStepType::System,
5140 : OutputProcessor::StoreType::Average,
5141 1 : this->Name);
5142 :
5143 2 : SetupOutputVariable(state,
5144 : "Water Heater Source Side Inlet Temperature",
5145 : Constant::Units::C,
5146 1 : this->SourceInletTemp,
5147 : OutputProcessor::TimeStepType::System,
5148 : OutputProcessor::StoreType::Average,
5149 1 : this->Name);
5150 :
5151 2 : SetupOutputVariable(state,
5152 : "Water Heater Source Side Outlet Temperature",
5153 : Constant::Units::C,
5154 1 : this->SourceOutletTemp,
5155 : OutputProcessor::TimeStepType::System,
5156 : OutputProcessor::StoreType::Average,
5157 1 : this->Name);
5158 :
5159 2 : SetupOutputVariable(state,
5160 : "Water Heater Source Side Heat Transfer Rate",
5161 : Constant::Units::W,
5162 1 : this->SourceRate,
5163 : OutputProcessor::TimeStepType::System,
5164 : OutputProcessor::StoreType::Average,
5165 1 : this->Name);
5166 2 : SetupOutputVariable(state,
5167 : "Water Heater Source Side Heat Transfer Energy",
5168 : Constant::Units::J,
5169 1 : this->SourceEnergy,
5170 : OutputProcessor::TimeStepType::System,
5171 : OutputProcessor::StoreType::Sum,
5172 1 : this->Name,
5173 : Constant::eResource::PlantLoopHeatingDemand,
5174 : OutputProcessor::Group::Plant,
5175 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5176 : this->EndUseSubcategoryName);
5177 :
5178 2 : SetupOutputVariable(state,
5179 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Rate",
5180 : Constant::Units::W,
5181 1 : this->OffCycParaRateToTank,
5182 : OutputProcessor::TimeStepType::System,
5183 : OutputProcessor::StoreType::Average,
5184 1 : this->Name);
5185 2 : SetupOutputVariable(state,
5186 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Energy",
5187 : Constant::Units::J,
5188 1 : this->OffCycParaEnergyToTank,
5189 : OutputProcessor::TimeStepType::System,
5190 : OutputProcessor::StoreType::Sum,
5191 1 : this->Name);
5192 :
5193 2 : SetupOutputVariable(state,
5194 : "Water Heater On Cycle Parasitic Tank Heat Transfer Rate",
5195 : Constant::Units::W,
5196 1 : this->OnCycParaRateToTank,
5197 : OutputProcessor::TimeStepType::System,
5198 : OutputProcessor::StoreType::Average,
5199 1 : this->Name);
5200 2 : SetupOutputVariable(state,
5201 : "Water Heater On Cycle Parasitic Tank Heat Transfer Energy",
5202 : Constant::Units::J,
5203 1 : this->OnCycParaEnergyToTank,
5204 : OutputProcessor::TimeStepType::System,
5205 : OutputProcessor::StoreType::Sum,
5206 1 : this->Name);
5207 :
5208 2 : SetupOutputVariable(state,
5209 : "Water Heater Total Demand Heat Transfer Rate",
5210 : Constant::Units::W,
5211 1 : this->TotalDemandRate,
5212 : OutputProcessor::TimeStepType::System,
5213 : OutputProcessor::StoreType::Average,
5214 1 : this->Name);
5215 2 : SetupOutputVariable(state,
5216 : "Water Heater Total Demand Heat Transfer Energy",
5217 : Constant::Units::J,
5218 1 : this->TotalDemandEnergy,
5219 : OutputProcessor::TimeStepType::System,
5220 : OutputProcessor::StoreType::Sum,
5221 1 : this->Name);
5222 :
5223 2 : SetupOutputVariable(state,
5224 : "Water Heater Heating Rate",
5225 : Constant::Units::W,
5226 1 : this->HeaterRate,
5227 : OutputProcessor::TimeStepType::System,
5228 : OutputProcessor::StoreType::Average,
5229 1 : this->Name);
5230 2 : SetupOutputVariable(state,
5231 : "Water Heater Heating Energy",
5232 : Constant::Units::J,
5233 1 : this->HeaterEnergy,
5234 : OutputProcessor::TimeStepType::System,
5235 : OutputProcessor::StoreType::Sum,
5236 1 : this->Name);
5237 :
5238 2 : SetupOutputVariable(state,
5239 : "Water Heater Unmet Demand Heat Transfer Rate",
5240 : Constant::Units::W,
5241 1 : this->UnmetRate,
5242 : OutputProcessor::TimeStepType::System,
5243 : OutputProcessor::StoreType::Average,
5244 1 : this->Name);
5245 2 : SetupOutputVariable(state,
5246 : "Water Heater Unmet Demand Heat Transfer Energy",
5247 : Constant::Units::J,
5248 1 : this->UnmetEnergy,
5249 : OutputProcessor::TimeStepType::System,
5250 : OutputProcessor::StoreType::Sum,
5251 1 : this->Name);
5252 :
5253 2 : SetupOutputVariable(state,
5254 : "Water Heater Venting Heat Transfer Rate",
5255 : Constant::Units::W,
5256 1 : this->VentRate,
5257 : OutputProcessor::TimeStepType::System,
5258 : OutputProcessor::StoreType::Average,
5259 1 : this->Name);
5260 2 : SetupOutputVariable(state,
5261 : "Water Heater Venting Heat Transfer Energy",
5262 : Constant::Units::J,
5263 1 : this->VentEnergy,
5264 : OutputProcessor::TimeStepType::System,
5265 : OutputProcessor::StoreType::Sum,
5266 1 : this->Name);
5267 :
5268 2 : SetupOutputVariable(state,
5269 : "Water Heater Net Heat Transfer Rate",
5270 : Constant::Units::W,
5271 1 : this->NetHeatTransferRate,
5272 : OutputProcessor::TimeStepType::System,
5273 : OutputProcessor::StoreType::Average,
5274 1 : this->Name);
5275 2 : SetupOutputVariable(state,
5276 : "Water Heater Net Heat Transfer Energy",
5277 : Constant::Units::J,
5278 1 : this->NetHeatTransferEnergy,
5279 : OutputProcessor::TimeStepType::System,
5280 : OutputProcessor::StoreType::Sum,
5281 1 : this->Name);
5282 :
5283 1 : SetupOutputVariable(state,
5284 : "Water Heater Cycle On Count",
5285 : Constant::Units::None,
5286 1 : this->CycleOnCount,
5287 : OutputProcessor::TimeStepType::System,
5288 : OutputProcessor::StoreType::Sum,
5289 1 : this->Name);
5290 2 : SetupOutputVariable(state,
5291 : "Water Heater Runtime Fraction",
5292 : Constant::Units::None,
5293 1 : this->RuntimeFraction,
5294 : OutputProcessor::TimeStepType::System,
5295 : OutputProcessor::StoreType::Average,
5296 1 : this->Name);
5297 2 : SetupOutputVariable(state,
5298 : "Water Heater Part Load Ratio",
5299 : Constant::Units::None,
5300 1 : this->PartLoadRatio,
5301 : OutputProcessor::TimeStepType::System,
5302 : OutputProcessor::StoreType::Average,
5303 1 : this->Name);
5304 :
5305 3 : SetupOutputVariable(state,
5306 2 : format("Water Heater {} Rate", Constant::eFuelNames[static_cast<int>(this->FuelType)]),
5307 : Constant::Units::W,
5308 1 : this->FuelRate,
5309 : OutputProcessor::TimeStepType::System,
5310 : OutputProcessor::StoreType::Average,
5311 1 : this->Name);
5312 3 : SetupOutputVariable(state,
5313 2 : format("Water Heater {} Energy", Constant::eFuelNames[static_cast<int>(this->FuelType)]),
5314 : Constant::Units::J,
5315 1 : this->FuelEnergy,
5316 : OutputProcessor::TimeStepType::System,
5317 : OutputProcessor::StoreType::Sum,
5318 1 : this->Name,
5319 1 : Constant::eFuel2eResource[(int)this->FuelType],
5320 : OutputProcessor::Group::Plant,
5321 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5322 : this->EndUseSubcategoryName);
5323 :
5324 3 : SetupOutputVariable(state,
5325 2 : format("Water Heater Off Cycle Parasitic {} Rate", Constant::eFuelNames[static_cast<int>(this->OffCycParaFuelType)]),
5326 : Constant::Units::W,
5327 1 : this->OffCycParaFuelRate,
5328 : OutputProcessor::TimeStepType::System,
5329 : OutputProcessor::StoreType::Average,
5330 1 : this->Name);
5331 3 : SetupOutputVariable(state,
5332 2 : format("Water Heater Off Cycle Parasitic {} Energy", Constant::eFuelNames[static_cast<int>(this->OffCycParaFuelType)]),
5333 : Constant::Units::J,
5334 1 : this->OffCycParaFuelEnergy,
5335 : OutputProcessor::TimeStepType::System,
5336 : OutputProcessor::StoreType::Sum,
5337 1 : this->Name,
5338 1 : Constant::eFuel2eResource[(int)this->OffCycParaFuelType],
5339 : OutputProcessor::Group::Plant,
5340 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5341 : this->EndUseSubcategoryName);
5342 :
5343 3 : SetupOutputVariable(state,
5344 2 : format("Water Heater On Cycle Parasitic {} Rate", Constant::eFuelNames[static_cast<int>(this->OnCycParaFuelType)]),
5345 : Constant::Units::W,
5346 1 : this->OnCycParaFuelRate,
5347 : OutputProcessor::TimeStepType::System,
5348 : OutputProcessor::StoreType::Average,
5349 1 : this->Name);
5350 3 : SetupOutputVariable(state,
5351 2 : format("Water Heater On Cycle Parasitic {} Energy", Constant::eFuelNames[static_cast<int>(this->OnCycParaFuelType)]),
5352 : Constant::Units::J,
5353 1 : this->OnCycParaFuelEnergy,
5354 : OutputProcessor::TimeStepType::System,
5355 : OutputProcessor::StoreType::Sum,
5356 1 : this->Name,
5357 1 : Constant::eFuel2eResource[(int)this->OnCycParaFuelType],
5358 : OutputProcessor::Group::Plant,
5359 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5360 : this->EndUseSubcategoryName);
5361 :
5362 2 : SetupOutputVariable(state,
5363 : "Water Heater Water Volume Flow Rate",
5364 : Constant::Units::m3_s,
5365 1 : this->VolFlowRate,
5366 : OutputProcessor::TimeStepType::System,
5367 : OutputProcessor::StoreType::Average,
5368 1 : this->Name);
5369 2 : SetupOutputVariable(state,
5370 : "Water Heater Water Volume",
5371 : Constant::Units::m3,
5372 1 : this->VolumeConsumed,
5373 : OutputProcessor::TimeStepType::System,
5374 : OutputProcessor::StoreType::Sum,
5375 1 : this->Name,
5376 : Constant::eResource::Water,
5377 : OutputProcessor::Group::Plant,
5378 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5379 : this->EndUseSubcategoryName);
5380 2 : SetupOutputVariable(state,
5381 : "Water Heater Mains Water Volume",
5382 : Constant::Units::m3,
5383 1 : this->VolumeConsumed,
5384 : OutputProcessor::TimeStepType::System,
5385 : OutputProcessor::StoreType::Sum,
5386 1 : this->Name,
5387 : Constant::eResource::MainsWater,
5388 : OutputProcessor::Group::Plant,
5389 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5390 : this->EndUseSubcategoryName);
5391 :
5392 1 : if (this->HeatPumpNum > 0) {
5393 : // CurrentModuleObject='WaterHeater:HeatPump:PumpedCondenser'
5394 1 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
5395 2 : SetupOutputVariable(state,
5396 : "Water Heater Compressor Part Load Ratio",
5397 : Constant::Units::None,
5398 1 : HPWH.HeatingPLR,
5399 : OutputProcessor::TimeStepType::System,
5400 : OutputProcessor::StoreType::Average,
5401 1 : HPWH.Name);
5402 2 : SetupOutputVariable(state,
5403 : "Water Heater Off Cycle Ancillary Electricity Rate",
5404 : Constant::Units::W,
5405 1 : HPWH.OffCycParaFuelRate,
5406 : OutputProcessor::TimeStepType::System,
5407 : OutputProcessor::StoreType::Average,
5408 1 : HPWH.Name);
5409 2 : SetupOutputVariable(state,
5410 : "Water Heater Off Cycle Ancillary Electricity Energy",
5411 : Constant::Units::J,
5412 1 : HPWH.OffCycParaFuelEnergy,
5413 : OutputProcessor::TimeStepType::System,
5414 : OutputProcessor::StoreType::Sum,
5415 1 : HPWH.Name,
5416 : Constant::eResource::Electricity,
5417 : OutputProcessor::Group::Plant,
5418 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5419 : "Water Heater Parasitic");
5420 2 : SetupOutputVariable(state,
5421 : "Water Heater On Cycle Ancillary Electricity Rate",
5422 : Constant::Units::W,
5423 1 : HPWH.OnCycParaFuelRate,
5424 : OutputProcessor::TimeStepType::System,
5425 : OutputProcessor::StoreType::Average,
5426 1 : HPWH.Name);
5427 2 : SetupOutputVariable(state,
5428 : "Water Heater On Cycle Ancillary Electricity Energy",
5429 : Constant::Units::J,
5430 1 : HPWH.OnCycParaFuelEnergy,
5431 : OutputProcessor::TimeStepType::System,
5432 : OutputProcessor::StoreType::Sum,
5433 1 : HPWH.Name,
5434 : Constant::eResource::Electricity,
5435 : OutputProcessor::Group::Plant,
5436 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5437 : "Water Heater Parasitic");
5438 2 : SetupOutputVariable(state,
5439 : "Water Heater Heat Pump Control Tank Temperature",
5440 : Constant::Units::C,
5441 1 : HPWH.ControlTempAvg,
5442 : OutputProcessor::TimeStepType::System,
5443 : OutputProcessor::StoreType::Average,
5444 1 : HPWH.Name);
5445 2 : SetupOutputVariable(state,
5446 : "Water Heater Heat Pump Control Tank Final Temperature",
5447 : Constant::Units::C,
5448 1 : HPWH.ControlTempFinal,
5449 : OutputProcessor::TimeStepType::System,
5450 : OutputProcessor::StoreType::Average,
5451 1 : HPWH.Name);
5452 : }
5453 :
5454 1 : if (this->DesuperheaterNum > 0) {
5455 : // CurrentModuleObject='Coil:WaterHeating:Desuperheater'
5456 0 : SetupOutputVariable(state,
5457 : "Water Heater Part Load Ratio",
5458 : Constant::Units::None,
5459 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR,
5460 : OutputProcessor::TimeStepType::System,
5461 : OutputProcessor::StoreType::Average,
5462 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5463 0 : SetupOutputVariable(state,
5464 : "Water Heater On Cycle Parasitic Electricity Rate",
5465 : Constant::Units::W,
5466 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelRate,
5467 : OutputProcessor::TimeStepType::System,
5468 : OutputProcessor::StoreType::Average,
5469 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5470 0 : SetupOutputVariable(state,
5471 : "Water Heater On Cycle Parasitic Electricity Energy",
5472 : Constant::Units::J,
5473 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelEnergy,
5474 : OutputProcessor::TimeStepType::System,
5475 : OutputProcessor::StoreType::Sum,
5476 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5477 : Constant::eResource::Electricity,
5478 : OutputProcessor::Group::Plant,
5479 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5480 : "Water Heater Parasitic");
5481 0 : SetupOutputVariable(state,
5482 : "Water Heater Off Cycle Parasitic Electricity Rate",
5483 : Constant::Units::W,
5484 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelRate,
5485 : OutputProcessor::TimeStepType::System,
5486 : OutputProcessor::StoreType::Average,
5487 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5488 0 : SetupOutputVariable(state,
5489 : "Water Heater Off Cycle Parasitic Electricity Energy",
5490 : Constant::Units::J,
5491 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelEnergy,
5492 : OutputProcessor::TimeStepType::System,
5493 : OutputProcessor::StoreType::Sum,
5494 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5495 : Constant::eResource::Electricity,
5496 : OutputProcessor::Group::Plant,
5497 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5498 : "Water Heater Parasitic");
5499 0 : SetupOutputVariable(state,
5500 : "Water Heater Heat Reclaim Efficiency Modifier Multiplier",
5501 : Constant::Units::None,
5502 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HEffFTempOutput,
5503 : OutputProcessor::TimeStepType::System,
5504 : OutputProcessor::StoreType::Average,
5505 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5506 0 : SetupOutputVariable(state,
5507 : "Water Heater Pump Electricity Rate",
5508 : Constant::Units::W,
5509 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpPower,
5510 : OutputProcessor::TimeStepType::System,
5511 : OutputProcessor::StoreType::Average,
5512 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5513 0 : SetupOutputVariable(state,
5514 : "Water Heater Pump Electricity Energy",
5515 : Constant::Units::J,
5516 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpEnergy,
5517 : OutputProcessor::TimeStepType::System,
5518 : OutputProcessor::StoreType::Sum,
5519 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5520 : Constant::eResource::Electricity,
5521 : OutputProcessor::Group::Plant,
5522 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5523 : "Desuperheater Pump");
5524 0 : SetupOutputVariable(state,
5525 : "Water Heater Heating Rate",
5526 : Constant::Units::W,
5527 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate,
5528 : OutputProcessor::TimeStepType::System,
5529 : OutputProcessor::StoreType::Average,
5530 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5531 0 : SetupOutputVariable(state,
5532 : "Water Heater Heating Energy",
5533 : Constant::Units::J,
5534 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterEnergy,
5535 : OutputProcessor::TimeStepType::System,
5536 : OutputProcessor::StoreType::Sum,
5537 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5538 : Constant::eResource::EnergyTransfer,
5539 : OutputProcessor::Group::Plant,
5540 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5541 : "Water Heater");
5542 : }
5543 :
5544 : // Setup report variables for WaterHeater:Stratified
5545 : // CurrentModuleObject='WaterHeater:Stratified'
5546 1 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5547 :
5548 0 : SetupOutputVariable(state,
5549 : "Water Heater Heater 1 Heating Rate",
5550 : Constant::Units::W,
5551 0 : this->HeaterRate1,
5552 : OutputProcessor::TimeStepType::System,
5553 : OutputProcessor::StoreType::Average,
5554 0 : this->Name);
5555 0 : SetupOutputVariable(state,
5556 : "Water Heater Heater 2 Heating Rate",
5557 : Constant::Units::W,
5558 0 : this->HeaterRate2,
5559 : OutputProcessor::TimeStepType::System,
5560 : OutputProcessor::StoreType::Average,
5561 0 : this->Name);
5562 :
5563 0 : SetupOutputVariable(state,
5564 : "Water Heater Heater 1 Heating Energy",
5565 : Constant::Units::J,
5566 0 : this->HeaterEnergy1,
5567 : OutputProcessor::TimeStepType::System,
5568 : OutputProcessor::StoreType::Sum,
5569 0 : this->Name);
5570 0 : SetupOutputVariable(state,
5571 : "Water Heater Heater 2 Heating Energy",
5572 : Constant::Units::J,
5573 0 : this->HeaterEnergy2,
5574 : OutputProcessor::TimeStepType::System,
5575 : OutputProcessor::StoreType::Sum,
5576 0 : this->Name);
5577 :
5578 0 : SetupOutputVariable(state,
5579 : "Water Heater Heater 1 Cycle On Count",
5580 : Constant::Units::None,
5581 0 : this->CycleOnCount1,
5582 : OutputProcessor::TimeStepType::System,
5583 : OutputProcessor::StoreType::Sum,
5584 0 : this->Name);
5585 0 : SetupOutputVariable(state,
5586 : "Water Heater Heater 2 Cycle On Count",
5587 : Constant::Units::None,
5588 0 : this->CycleOnCount2,
5589 : OutputProcessor::TimeStepType::System,
5590 : OutputProcessor::StoreType::Sum,
5591 0 : this->Name);
5592 :
5593 0 : SetupOutputVariable(state,
5594 : "Water Heater Heater 1 Runtime Fraction",
5595 : Constant::Units::None,
5596 0 : this->RuntimeFraction1,
5597 : OutputProcessor::TimeStepType::System,
5598 : OutputProcessor::StoreType::Average,
5599 0 : this->Name);
5600 0 : SetupOutputVariable(state,
5601 : "Water Heater Heater 2 Runtime Fraction",
5602 : Constant::Units::None,
5603 0 : this->RuntimeFraction2,
5604 : OutputProcessor::TimeStepType::System,
5605 : OutputProcessor::StoreType::Average,
5606 0 : this->Name);
5607 :
5608 0 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5609 0 : SetupOutputVariable(state,
5610 0 : format("Water Heater Temperature Node {}", NodeNum),
5611 : Constant::Units::C,
5612 0 : this->Node(NodeNum).TempAvg,
5613 : OutputProcessor::TimeStepType::System,
5614 : OutputProcessor::StoreType::Average,
5615 0 : this->Name);
5616 : }
5617 :
5618 0 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5619 0 : SetupOutputVariable(state,
5620 0 : format("Water Heater Final Temperature Node {}", NodeNum),
5621 : Constant::Units::C,
5622 0 : this->Node(NodeNum).Temp,
5623 : OutputProcessor::TimeStepType::System,
5624 : OutputProcessor::StoreType::Average,
5625 0 : this->Name);
5626 : }
5627 : }
5628 :
5629 1 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5630 :
5631 0 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5632 : static constexpr std::string_view Format_723("Water Heater Stratified Node Information,{},{:.4T},{:.4T},{:.3T},{:.4T},{:.4T},{},{}\n");
5633 0 : print(state.files.eio,
5634 : Format_723,
5635 : NodeNum,
5636 0 : this->Node(NodeNum).Height,
5637 0 : this->Node(NodeNum).Volume,
5638 0 : this->Node(NodeNum).MaxCapacity,
5639 0 : this->Node(NodeNum).OffCycLossCoeff,
5640 0 : this->Node(NodeNum).OnCycLossCoeff,
5641 0 : this->Node(NodeNum).Inlets,
5642 0 : this->Node(NodeNum).Outlets);
5643 : }
5644 : }
5645 1 : }
5646 :
5647 0 : void WaterThermalTankData::ValidatePLFCurve(EnergyPlusData &state, int const CurveIndex, bool &IsValid)
5648 : {
5649 :
5650 : // SUBROUTINE INFORMATION:
5651 : // AUTHOR Peter Graham Ellis
5652 : // DATE WRITTEN February 2005
5653 : // MODIFIED na
5654 : // RE-ENGINEERED na
5655 :
5656 : // PURPOSE OF THIS SUBROUTINE:
5657 : // Validates the Part Load Factor curve by making sure it can never be less than or equal to zero
5658 : // over the domain of Part Load Ratio inputs from 0 to 1.
5659 :
5660 : // METHODOLOGY EMPLOYED:
5661 : // Currently can only check 0 and 1. Need changes in CurveManager to be able to check minimums and
5662 : // maximums.
5663 :
5664 0 : IsValid = true;
5665 :
5666 : // Check 0 and 1
5667 0 : if (Curve::CurveValue(state, CurveIndex, 0.0) <= 0) IsValid = false;
5668 0 : if (Curve::CurveValue(state, CurveIndex, 1.0) <= 0) IsValid = false;
5669 0 : }
5670 :
5671 17 : void WaterThermalTankData::SetupStratifiedNodes(EnergyPlusData &state)
5672 : {
5673 :
5674 : // SUBROUTINE INFORMATION:
5675 : // AUTHOR Peter Graham Ellis
5676 : // DATE WRITTEN January 2007
5677 : // MODIFIED na
5678 : // RE-ENGINEERED na
5679 :
5680 : // PURPOSE OF THIS SUBROUTINE:
5681 : // Sets up node properties based on the tank shape, i.e., vertical cylinder, horizontal cylinder, or other.
5682 : // Node height, skin area, vertical conduction area, and loss coefficients are calculated and assigned.
5683 : // Heating elements, parasitics, and fluid inlet and outlet flows are assigned according to node height.
5684 :
5685 : // METHODOLOGY EMPLOYED:
5686 : // Tank is divided into nodes of equal mass. For horizontal cylinders, node heights are calculated using
5687 : // the Newton-Raphson iterative method. For vertical cylinders and other shapes, the node heights are calculated
5688 : // using basic geometry.
5689 :
5690 : static constexpr std::string_view RoutineName("GetWaterThermalTankInput");
5691 :
5692 17 : constexpr Real64 Tolerance(1.0e-8); // Tolerance for Newton-Raphson solution
5693 17 : constexpr Real64 FluidCond(0.6); // Conductivity of water (W/m-K)
5694 :
5695 17 : int NumNodes = this->Nodes;
5696 17 : this->Node.allocate(NumNodes);
5697 : Real64 rho;
5698 17 : if ((this->UseSidePlantLoc.loopNum > 0) && allocated(state.dataPlnt->PlantLoop)) {
5699 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
5700 : } else {
5701 17 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
5702 : }
5703 :
5704 17 : Real64 NodeMass = this->Volume * rho / NumNodes;
5705 : Real64 TankHeight;
5706 :
5707 : // Mixing rate set to 50% of the max value for dt = 1.0
5708 17 : this->InversionMixingRate = NodeMass * 0.5 * 1.0;
5709 :
5710 17 : if ((this->Shape == TankShape::VertCylinder) || (this->Shape == TankShape::Other)) {
5711 17 : TankHeight = this->Height;
5712 17 : Real64 EndArea = this->Volume / TankHeight;
5713 17 : Real64 NodeHeight = TankHeight / NumNodes;
5714 17 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * EndArea / NodeHeight;
5715 :
5716 : Real64 Perimeter_loc;
5717 17 : if (this->Shape == TankShape::VertCylinder) {
5718 17 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
5719 17 : Perimeter_loc = 2.0 * Constant::Pi * Radius;
5720 : } else { // TankShapeOther
5721 0 : Perimeter_loc = this->Perimeter;
5722 : }
5723 :
5724 187 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5725 170 : this->Node(NodeNum).Mass = NodeMass;
5726 170 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5727 170 : this->Node(NodeNum).Height = NodeHeight;
5728 170 : this->Node(NodeNum).CondCoeffUp = CondCoeff;
5729 170 : this->Node(NodeNum).CondCoeffDn = CondCoeff;
5730 :
5731 : Real64 SkinArea;
5732 170 : if ((NodeNum == 1) || (NodeNum == NumNodes)) {
5733 34 : SkinArea = Perimeter_loc * NodeHeight + EndArea;
5734 : } else {
5735 136 : SkinArea = Perimeter_loc * NodeHeight;
5736 : }
5737 :
5738 170 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5739 :
5740 170 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5741 :
5742 : } // NodeNum
5743 :
5744 17 : this->Node(1).CondCoeffUp = 0.0;
5745 17 : this->Node(NumNodes).CondCoeffDn = 0.0;
5746 :
5747 17 : } else { // Tank%Shape == TankShapeHorizCylinder
5748 0 : Real64 TankLength = this->Height; // Height is the length in the axial direction
5749 0 : Real64 EndArea = this->Volume / TankLength;
5750 0 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
5751 0 : TankHeight = 2.0 * Radius; // Actual vertical height
5752 0 : Real64 NodeEndArea = EndArea / NumNodes;
5753 :
5754 0 : Real64 R = Radius;
5755 0 : Real64 H0 = 0.0;
5756 0 : Real64 ChordLength = 0.0;
5757 0 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5758 0 : this->Node(NodeNum).Mass = NodeMass;
5759 0 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5760 : Real64 H;
5761 0 : if (NodeNum == NumNodes) {
5762 0 : H = TankHeight;
5763 : } else {
5764 : // Use the Newton-Raphson method to solve the nonlinear algebraic equation for node height
5765 0 : H = H0 + TankHeight / NumNodes; // Initial guess
5766 :
5767 : while (true) {
5768 0 : Real64 a = std::sqrt(H);
5769 0 : Real64 b = std::sqrt(2.0 * R - H);
5770 0 : Real64 c = 2.0 * R * R * std::atan(a / b) - (2.0 * R * R - 3.0 * H * R + H * H) * (a / b);
5771 : Real64 c0;
5772 0 : if (H0 > 0.0) {
5773 0 : Real64 a0 = std::sqrt(H0);
5774 0 : Real64 b0 = std::sqrt(2.0 * R - H0);
5775 0 : c0 = 2.0 * R * R * std::atan(a0 / b0) - (2.0 * R * R - 3.0 * H0 * R + H0 * H0) * (a0 / b0);
5776 : } else {
5777 0 : c0 = 0.0;
5778 : }
5779 :
5780 0 : Real64 ApproxEndArea = c - c0; // Area approximated by iteration
5781 0 : Real64 G = ApproxEndArea - NodeEndArea; // G is the function that should converge to zero
5782 :
5783 0 : if (std::abs(G) < Tolerance) {
5784 0 : break; // Converged !!!
5785 : } else {
5786 0 : H -= G / (2.0 * a * b); // Calculate next guess: H = Hprev - G/G'
5787 : }
5788 0 : } // Newton-Raphson
5789 : }
5790 :
5791 0 : this->Node(NodeNum).Height = H - H0;
5792 :
5793 0 : if (NodeNum > 1) {
5794 0 : Real64 CrossArea = 2.0 * ChordLength * TankLength; // Use old ChordLength from previous node
5795 0 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * CrossArea / (0.5 * (H - H0) + 0.5 * this->Node(NodeNum - 1).Height);
5796 0 : this->Node(NodeNum - 1).CondCoeffUp = CondCoeff; // Set for previous node
5797 0 : this->Node(NodeNum).CondCoeffDn = CondCoeff; // Set for this node
5798 : }
5799 :
5800 0 : ChordLength = std::sqrt(2.0 * R * H - H * H); // Calc new ChordLength to be used with next node
5801 :
5802 0 : Real64 Perimeter_loc = 2.0 * R * (std::acos((R - H) / R) - std::acos((R - H0) / R)); // Segments of circular perimeter
5803 0 : Real64 SkinArea = Perimeter_loc * TankLength + 2.0 * NodeEndArea;
5804 :
5805 0 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5806 :
5807 0 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5808 : // Although it doesn't make much sense to have a flue in a horizontal tank, keep it in anyway
5809 :
5810 0 : H0 = H;
5811 : } // NodeNum
5812 :
5813 0 : this->Node(1).CondCoeffUp = 0.0;
5814 0 : this->Node(NumNodes).CondCoeffDn = 0.0;
5815 : }
5816 :
5817 : // Loop through nodes again (from top to bottom this time) and assign heating elements, parasitics, flow inlets/outlets
5818 : // according to their vertical heights in the tank
5819 17 : Real64 H0 = TankHeight;
5820 187 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5821 : Real64 H;
5822 170 : if (NodeNum == NumNodes) {
5823 17 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
5824 : } else {
5825 153 : H = H0 - this->Node(NodeNum).Height;
5826 : }
5827 :
5828 : // Assign heater elements to the nodes at the specified heights
5829 170 : if ((this->HeaterHeight1 <= H0) && (this->HeaterHeight1 > H)) {
5830 : // sensor node will not get set if user enters 0 for this heater capacity
5831 : // (Tank%MaxCapacity > 0.0d0)) THEN
5832 17 : this->HeaterNode1 = NodeNum;
5833 17 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity;
5834 : }
5835 :
5836 170 : if ((this->HeaterHeight2 <= H0) && (this->HeaterHeight2 > H)) {
5837 : // sensor node will not get set if user enters 0 for this heater capacity
5838 : // .AND. (Tank%MaxCapacity2 > 0.0d0)) THEN
5839 17 : this->HeaterNode2 = NodeNum;
5840 :
5841 17 : if ((NodeNum == this->HeaterNode1) && (this->StratifiedControlMode == PriorityControlMode::Simultaneous)) {
5842 0 : this->Node(NodeNum).MaxCapacity += this->MaxCapacity2;
5843 : } else {
5844 17 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity2;
5845 : }
5846 : }
5847 :
5848 : // Assign parasitic heat gains to the nodes at the specified heights
5849 170 : if ((this->OffCycParaHeight <= H0) && (this->OffCycParaHeight > H)) {
5850 17 : this->Node(NodeNum).OffCycParaLoad = this->OffCycParaFracToTank * this->OffCycParaLoad;
5851 : }
5852 :
5853 170 : if ((this->OnCycParaHeight <= H0) && (this->OnCycParaHeight > H)) {
5854 17 : this->Node(NodeNum).OnCycParaLoad = this->OnCycParaFracToTank * this->OnCycParaLoad;
5855 : }
5856 :
5857 : // Assign inlets and outlets to the nodes at the specified heights
5858 170 : if ((this->UseInletHeight <= H0) && (this->UseInletHeight > H)) {
5859 17 : this->UseInletStratNode = NodeNum;
5860 :
5861 17 : if ((this->UseInletNode > 0) || (this->MassFlowRateMax > 0.0)) ++this->Node(NodeNum).Inlets;
5862 : }
5863 :
5864 170 : if ((this->UseOutletHeight <= H0) && (this->UseOutletHeight > H)) {
5865 17 : this->UseOutletStratNode = NodeNum;
5866 :
5867 17 : if ((this->UseOutletNode > 0) || (this->MassFlowRateMax > 0.0)) ++this->Node(NodeNum).Outlets;
5868 : }
5869 :
5870 170 : if ((this->SourceInletHeight <= H0) && (this->SourceInletHeight > H) && (this->SourceInletNode > 0)) {
5871 :
5872 5 : this->SourceInletStratNode = NodeNum;
5873 5 : ++this->Node(NodeNum).Inlets;
5874 : }
5875 :
5876 170 : if ((this->SourceOutletHeight <= H0) && (this->SourceOutletHeight > H) && (this->SourceOutletNode > 0)) {
5877 :
5878 5 : this->SourceOutletStratNode = NodeNum;
5879 5 : ++this->Node(NodeNum).Outlets;
5880 : }
5881 :
5882 170 : H0 = H;
5883 : } // NodeNum
5884 17 : }
5885 :
5886 11 : void WaterThermalTankData::initialize(EnergyPlusData &state, bool const FirstHVACIteration)
5887 : {
5888 :
5889 : // SUBROUTINE INFORMATION:
5890 : // AUTHOR Peter Graham Ellis
5891 : // DATE WRITTEN February 2004
5892 : // MODIFIED FSEC, July 2005
5893 : // Brent Griffith, October 2007 indirect fired water heater
5894 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
5895 : // RE-ENGINEERED na
5896 :
5897 : // PURPOSE OF THIS SUBROUTINE:
5898 : // Initialize the water heater, heat pump water heater, or desuperheater heating coil objects during the simulation.
5899 : // determine flow rates thru use side and source side plant connections (if any)
5900 :
5901 : // METHODOLOGY EMPLOYED:
5902 : // Inlet and outlet nodes are initialized. Scheduled values are retrieved for the current timestep.
5903 :
5904 11 : auto &ZoneEqSizing = state.dataSize->ZoneEqSizing;
5905 :
5906 : static constexpr std::string_view RoutineName("InitWaterThermalTank");
5907 : static constexpr std::string_view GetWaterThermalTankInput("GetWaterThermalTankInput");
5908 : static constexpr std::string_view SizeTankForDemand("SizeTankForDemandSide");
5909 :
5910 11 : if (this->scanPlantLoopsFlag && allocated(state.dataPlnt->PlantLoop)) {
5911 2 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
5912 0 : bool errFlag = false;
5913 0 : PlantUtilities::ScanPlantLoopsForObject(
5914 0 : state, this->Name, this->WaterThermalTankType, this->UseSidePlantLoc, errFlag, _, _, _, this->UseInletNode, _);
5915 0 : if (errFlag) {
5916 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5917 : }
5918 : }
5919 2 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
5920 : // this is a heat pump water heater, need a separate block because TypeOf_HeatPumpWtrHeater shows up on Branch
5921 : // (input should probably have been the associated tank )
5922 0 : bool errFlag = false;
5923 0 : PlantUtilities::ScanPlantLoopsForObject(state,
5924 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
5925 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType,
5926 0 : this->UseSidePlantLoc,
5927 : errFlag,
5928 : _,
5929 : _,
5930 : _,
5931 0 : this->UseInletNode,
5932 : _);
5933 0 : if (errFlag) {
5934 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5935 : }
5936 : }
5937 2 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
5938 1 : bool errFlag = false;
5939 3 : PlantUtilities::ScanPlantLoopsForObject(
5940 2 : state, this->Name, this->WaterThermalTankType, this->SrcSidePlantLoc, errFlag, _, _, _, this->SourceInletNode, _);
5941 1 : if (this->UseInletNode > 0) {
5942 0 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->UseSidePlantLoc, this->SrcSidePlantLoc, this->WaterThermalTankType, true);
5943 : }
5944 1 : if (errFlag) {
5945 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5946 : }
5947 : }
5948 2 : this->scanPlantLoopsFlag = false;
5949 : }
5950 :
5951 11 : if (this->SetLoopIndexFlag && allocated(state.dataPlnt->PlantLoop)) {
5952 2 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
5953 : Real64 rho =
5954 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
5955 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
5956 0 : this->Mass = this->Volume * rho;
5957 0 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
5958 0 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
5959 0 : ShowSevereError(state,
5960 0 : format("InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = {}", this->Name));
5961 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5962 : }
5963 : }
5964 2 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
5965 : Real64 rho =
5966 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
5967 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
5968 0 : this->Mass = this->Volume * rho;
5969 0 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
5970 0 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
5971 0 : ShowSevereError(state,
5972 0 : format("InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = {}", this->Name));
5973 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5974 : }
5975 : }
5976 2 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
5977 : Real64 rho =
5978 1 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
5979 1 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
5980 1 : this->SourceSidePlantSizNum = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).PlantSizNum;
5981 1 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0)) {
5982 0 : ShowSevereError(
5983 0 : state, format("InitWaterThermalTank: Did not find Sizing:Plant object for source side of plant thermal tank = {}", this->Name));
5984 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
5985 : }
5986 : }
5987 2 : if (((this->SourceInletNode > 0) && (this->DesuperheaterNum > 0)) || (this->HeatPumpNum > 0)) {
5988 1 : this->SetLoopIndexFlag = false;
5989 : }
5990 2 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) this->SetLoopIndexFlag = false;
5991 2 : if (this->StandAlone) {
5992 0 : this->SizeStandAloneWaterHeater(state);
5993 0 : this->SetLoopIndexFlag = false;
5994 : }
5995 9 : } else if (this->SetLoopIndexFlag && !state.dataGlobal->AnyPlantInModel) {
5996 5 : if (this->StandAlone) {
5997 0 : this->SizeStandAloneWaterHeater(state);
5998 : }
5999 5 : this->SetLoopIndexFlag = false;
6000 : }
6001 :
6002 11 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag && !this->SetLoopIndexFlag) {
6003 :
6004 2 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
6005 :
6006 0 : if (this->ControlType == HeaterControlMode::Cycle) {
6007 0 : this->MinCapacity = this->MaxCapacity;
6008 : }
6009 :
6010 : // check for sizing issues that model can not support
6011 :
6012 : // if stratified tank model, ensure that nominal change over rate is greater than one minute, avoid numerical problems.
6013 :
6014 0 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
6015 0 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
6016 0 : Real64 MaxSideVolFlow = max(this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
6017 :
6018 0 : if (MaxSideVolFlow > 0.0) { // protect div by zero
6019 0 : Real64 TankChangeRateScale = this->Volume / MaxSideVolFlow;
6020 0 : if (TankChangeRateScale < 60.0) { // nominal change over in less than one minute
6021 0 : ShowSevereError(state, "InitWaterThermalTank: Detected problem for stratified tank model. Model cannot be applied.");
6022 0 : ShowContinueError(state, format("Occurs for stratified tank name = {}", this->Name));
6023 0 : ShowContinueError(state, format("Tank volume = {:.4R} [m3]", this->Volume));
6024 0 : ShowContinueError(state, format("Tank use side volume flow rate = {:.4R} [m3/s]", this->UseDesignVolFlowRate));
6025 0 : ShowContinueError(state, format("Tank source side volume flow rate = {:.4R} [m3/s]", this->SourceDesignVolFlowRate));
6026 0 : ShowContinueError(state, format("Nominal tank change over rate = {:.2R} [s]", TankChangeRateScale));
6027 0 : ShowContinueError(
6028 : state, "Change over rate is too fast, increase tank volume, decrease connection flow rates or use mixed tank model");
6029 :
6030 0 : ShowFatalError(state, "InitWaterThermalTank: Simulation halted because of sizing problem in stratified tank model.");
6031 : }
6032 : }
6033 : }
6034 : }
6035 :
6036 : // Clear node initial conditions
6037 2 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
6038 0 : state.dataLoopNodes->Node(this->UseInletNode).Temp = 0.0;
6039 : Real64 rho =
6040 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
6041 0 : this->MassFlowRateMin = this->VolFlowRateMin * rho;
6042 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6043 0 : PlantUtilities::InitComponentNodes(state, this->MassFlowRateMin, this->PlantUseMassFlowRateMax, this->UseInletNode, this->UseOutletNode);
6044 0 : this->UseOutletTemp = 0.0;
6045 0 : this->UseMassFlowRate = 0.0;
6046 0 : this->SavedUseOutletTemp = 0.0;
6047 :
6048 0 : this->Mass = this->Volume * rho;
6049 0 : this->UseBranchControlType = DataPlant::CompData::getPlantComponent(state, this->UseSidePlantLoc).FlowCtrl;
6050 : }
6051 :
6052 2 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
6053 : Real64 rho =
6054 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, GetWaterThermalTankInput);
6055 0 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6056 0 : PlantUtilities::InitComponentNodes(state, 0.0, this->PlantSourceMassFlowRateMax, this->SourceInletNode, this->SourceOutletNode);
6057 :
6058 0 : this->SourceOutletTemp = 0.0;
6059 0 : this->SourceMassFlowRate = 0.0;
6060 0 : this->SavedSourceOutletTemp = 0.0;
6061 :
6062 0 : this->SourceBranchControlType = DataPlant::CompData::getPlantComponent(state, this->SrcSidePlantLoc).FlowCtrl;
6063 : }
6064 :
6065 2 : if ((this->SourceInletNode > 0) && ((this->DesuperheaterNum > 0) || (this->HeatPumpNum > 0))) {
6066 2 : state.dataLoopNodes->Node(this->SourceInletNode).Temp = 0.0;
6067 2 : this->SourceOutletTemp = 0.0;
6068 2 : this->SourceMassFlowRate = 0.0;
6069 2 : this->SavedSourceOutletTemp = 0.0;
6070 2 : Real64 rho = this->water->getDensity(state, Constant::InitConvTemp, SizeTankForDemand);
6071 2 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6072 : }
6073 :
6074 : // Initialize tank temperature to setpoint of first hour of warm up period
6075 : // (use HPWH or Desuperheater heating coil set point if applicable)
6076 2 : Sched::Schedule *sched = nullptr;
6077 2 : if (this->HeatPumpNum > 0) {
6078 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6079 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveMode = TankOperatingMode::Floating;
6080 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveWHMode = TankOperatingMode::Floating;
6081 0 : sched = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).setptTempSched;
6082 2 : } else if (this->DesuperheaterNum > 0) {
6083 2 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6084 2 : sched = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).setptTempSched;
6085 : } else {
6086 0 : sched = this->setptTempSched;
6087 : }
6088 :
6089 2 : if (sched != nullptr) {
6090 2 : this->TankTemp = sched->getCurrentVal();
6091 2 : this->SavedTankTemp = this->TankTemp;
6092 :
6093 2 : if (this->Nodes > 0) {
6094 26 : for (auto &e : this->Node) {
6095 24 : e.Temp = e.SavedTemp = this->TankTemp;
6096 : }
6097 : }
6098 : } else {
6099 0 : this->TankTemp = 20.0;
6100 0 : this->SavedTankTemp = this->TankTemp;
6101 :
6102 0 : if (this->Nodes > 0) {
6103 0 : for (auto &e : this->Node) {
6104 0 : e.Temp = e.SavedTemp = this->TankTemp;
6105 : }
6106 : }
6107 : }
6108 2 : this->SourceOutletTemp = this->SavedTankTemp;
6109 2 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6110 2 : this->UseOutletTemp = this->SavedTankTemp;
6111 2 : this->SavedUseOutletTemp = this->SavedTankTemp;
6112 2 : this->TankTempAvg = this->SavedTankTemp;
6113 :
6114 2 : this->SavedHeaterOn1 = false;
6115 2 : this->SavedHeaterOn2 = false;
6116 2 : this->Mode = TankOperatingMode::Floating;
6117 2 : this->SavedMode = TankOperatingMode::Floating;
6118 2 : this->FirstRecoveryDone = false;
6119 2 : this->FirstRecoveryFuel = 0.0;
6120 2 : this->UnmetEnergy = 0.0;
6121 2 : this->LossEnergy = 0.0;
6122 2 : this->FlueLossEnergy = 0.0;
6123 2 : this->UseEnergy = 0.0;
6124 2 : this->TotalDemandEnergy = 0.0;
6125 2 : this->SourceEnergy = 0.0;
6126 2 : this->HeaterEnergy = 0.0;
6127 2 : this->HeaterEnergy1 = 0.0;
6128 2 : this->HeaterEnergy2 = 0.0;
6129 2 : this->FuelEnergy = 0.0;
6130 2 : this->FuelEnergy1 = 0.0;
6131 2 : this->FuelEnergy2 = 0.0;
6132 2 : this->VentEnergy = 0.0;
6133 2 : this->OffCycParaFuelEnergy = 0.0;
6134 2 : this->OffCycParaEnergyToTank = 0.0;
6135 2 : this->OnCycParaFuelEnergy = 0.0;
6136 2 : this->OnCycParaEnergyToTank = 0.0;
6137 2 : this->NetHeatTransferEnergy = 0.0;
6138 : }
6139 :
6140 11 : if (!state.dataGlobal->BeginEnvrnFlag) this->MyEnvrnFlag = true;
6141 :
6142 11 : if (this->WarmupFlag && (!state.dataGlobal->WarmupFlag)) {
6143 : // reInitialize tank temperature to setpoint of first hour (use HPWH or Desuperheater heating coil set point if applicable)
6144 : // BG's interpretation here is that its better to reset initial condition to setpoint once warm up is over.
6145 : // (otherwise with a dynamic storage model it is difficult for the user to see the initial performance if it isn't periodic.)
6146 1 : Sched::Schedule *sched = nullptr;
6147 1 : if (this->HeatPumpNum > 0) {
6148 1 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6149 1 : sched = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).setptTempSched;
6150 0 : } else if (this->DesuperheaterNum > 0) {
6151 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6152 0 : sched = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).setptTempSched;
6153 : } else {
6154 0 : sched = this->setptTempSched;
6155 : }
6156 :
6157 1 : if (sched != nullptr) {
6158 1 : this->TankTemp = sched->getCurrentVal();
6159 1 : this->SavedTankTemp = this->TankTemp;
6160 :
6161 1 : if (this->Nodes > 0) {
6162 0 : for (auto &e : this->Node) {
6163 0 : e.Temp = e.SavedTemp = this->TankTemp;
6164 : }
6165 : }
6166 : } else {
6167 0 : this->TankTemp = 20.0;
6168 0 : this->SavedTankTemp = this->TankTemp;
6169 :
6170 0 : if (this->Nodes > 0) {
6171 0 : for (auto &e : this->Node) {
6172 0 : e.Temp = e.SavedTemp = this->TankTemp;
6173 : }
6174 : }
6175 : }
6176 1 : this->SourceOutletTemp = this->SavedTankTemp;
6177 1 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6178 1 : this->UseOutletTemp = this->SavedTankTemp;
6179 1 : this->SavedUseOutletTemp = this->SavedTankTemp;
6180 1 : this->SavedHeaterOn1 = false;
6181 1 : this->SavedHeaterOn2 = false;
6182 1 : this->Mode = TankOperatingMode::Floating;
6183 1 : this->SavedMode = TankOperatingMode::Floating;
6184 1 : this->WarmupFlag = false;
6185 : }
6186 11 : if (state.dataGlobal->WarmupFlag) this->WarmupFlag = true;
6187 :
6188 11 : if (FirstHVACIteration) {
6189 : // Get all scheduled values
6190 8 : this->SetPointTemp = this->setptTempSched->getCurrentVal();
6191 :
6192 8 : if (!this->IsChilledWaterTank) {
6193 8 : if (this->SetPointTemp > this->TankTempLimit) {
6194 : // Setpoint temperature scheduled higher than maximum tank temperature limit
6195 0 : this->SetPointTemp = this->TankTempLimit - 1.0;
6196 :
6197 0 : if (this->ShowSetPointWarning) {
6198 0 : ShowSevereError(
6199 : state,
6200 0 : format("Water heater = {}: Water heater tank set point temperature is greater than the maximum tank temperature limit.",
6201 0 : this->Name));
6202 0 : ShowContinueErrorTimeStamp(state,
6203 0 : format("Water heater tank set point temperature is reset to Tank Temperature Limit minus 1 C "
6204 : "({:.2T}) and simulation continues.",
6205 0 : this->SetPointTemp));
6206 0 : this->ShowSetPointWarning = false;
6207 : }
6208 : }
6209 : } else {
6210 0 : if (this->SetPointTemp < this->TankTempLimit) {
6211 : // Setpoint temperature scheduled lower than minimum tank temperature limit
6212 0 : this->SetPointTemp = this->TankTempLimit + 1.0;
6213 :
6214 0 : if (this->ShowSetPointWarning) {
6215 0 : ShowSevereError(
6216 : state,
6217 0 : format("Chilled Water Tank = {}: Water heater tank set point temperature is lower than the minimum tank temperature limit.",
6218 0 : this->Name));
6219 0 : ShowContinueErrorTimeStamp(state,
6220 0 : format("Chilled water tank set point temperature is reset to Tank Temperature Limit plus 1 C "
6221 : "({:.2T}) and simulation continues.",
6222 0 : this->SetPointTemp));
6223 0 : this->ShowSetPointWarning = false;
6224 : }
6225 : }
6226 : }
6227 :
6228 8 : if (this->setptTemp2Sched != nullptr) {
6229 1 : this->SetPointTemp2 = this->setptTemp2Sched->getCurrentVal();
6230 : }
6231 :
6232 8 : switch (this->AmbientTempIndicator) {
6233 7 : case WTTAmbientTemp::Schedule: {
6234 7 : this->AmbientTemp = this->ambientTempSched->getCurrentVal();
6235 7 : } break;
6236 :
6237 1 : case WTTAmbientTemp::TempZone: {
6238 1 : this->AmbientTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->AmbientTempZone).MAT;
6239 :
6240 1 : break;
6241 : }
6242 0 : case WTTAmbientTemp::OutsideAir: {
6243 0 : this->AmbientTemp = state.dataLoopNodes->Node(this->AmbientTempOutsideAirNode).Temp;
6244 0 : break;
6245 : }
6246 0 : default:
6247 0 : break;
6248 : }
6249 :
6250 8 : if (this->UseInletNode == 0) { // Stand-alone operation
6251 :
6252 4 : this->UseInletTemp = (this->useInletTempSched != nullptr) ? this->useInletTempSched->getCurrentVal() : state.dataEnvrn->WaterMainsTemp;
6253 :
6254 4 : this->UseMassFlowRate = this->MassFlowRateMax;
6255 4 : if (this->flowRateSched != nullptr) this->UseMassFlowRate *= this->flowRateSched->getCurrentVal();
6256 4 : this->VolFlowRate = this->UseMassFlowRate / Psychrometrics::RhoH2O(Constant::InitConvTemp);
6257 : }
6258 :
6259 8 : if (this->HeatPumpNum > 0) {
6260 4 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp =
6261 4 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).setptTempSched->getCurrentVal();
6262 4 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp >= this->TankTempLimit) {
6263 : // HP setpoint temperature scheduled equal to or higher than tank temperature limit
6264 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp = this->TankTempLimit - 1.0;
6265 :
6266 0 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning) {
6267 0 : ShowSevereError(state,
6268 0 : format("Heat Pump Water Heater = {}: Heat Pump water heater set point temperature is equal to or greater than "
6269 : "the maximum tank temperature limit.",
6270 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name));
6271 0 : ShowContinueErrorTimeStamp(state,
6272 0 : format("Heat Pump water heater tank set point temperature is reset to Tank Temperature Limit "
6273 : "minus 1 C ({:.2T}) and simulation continues.",
6274 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp));
6275 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning = false;
6276 : }
6277 : }
6278 : }
6279 :
6280 8 : if (this->DesuperheaterNum > 0) {
6281 1 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTemp =
6282 1 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).setptTempSched->getCurrentVal();
6283 : }
6284 :
6285 : } // first HVAC Iteration
6286 :
6287 11 : if (this->UseInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6288 : Real64 DeadBandTemp;
6289 5 : if (this->IsChilledWaterTank) {
6290 0 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6291 : } else {
6292 5 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6293 : }
6294 :
6295 10 : Real64 mdotUse = this->PlantMassFlowRatesFunc(state,
6296 : this->UseInletNode,
6297 : FirstHVACIteration,
6298 : WaterHeaterSide::Use,
6299 : this->UseSidePlantLoc.loopSideNum,
6300 5 : this->UseSideSeries,
6301 : this->UseBranchControlType,
6302 : this->SavedUseOutletTemp,
6303 : DeadBandTemp,
6304 5 : this->SetPointTemp);
6305 5 : PlantUtilities::SetComponentFlowRate(state, mdotUse, this->UseInletNode, this->UseOutletNode, this->UseSidePlantLoc);
6306 :
6307 5 : this->UseInletTemp = state.dataLoopNodes->Node(this->UseInletNode).Temp;
6308 5 : this->UseMassFlowRate = mdotUse;
6309 : }
6310 :
6311 11 : if (this->SourceInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6312 : Real64 DeadBandTemp;
6313 11 : if (this->IsChilledWaterTank) {
6314 0 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6315 : } else {
6316 11 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6317 : }
6318 :
6319 : Real64 sensedTemp;
6320 11 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
6321 0 : int tmpNodeNum = this->HeaterNode1;
6322 0 : sensedTemp = this->Node(tmpNodeNum).SavedTemp;
6323 : } else {
6324 11 : sensedTemp = this->SavedSourceOutletTemp;
6325 : }
6326 :
6327 22 : Real64 mdotSource = this->PlantMassFlowRatesFunc(state,
6328 : this->SourceInletNode,
6329 : FirstHVACIteration,
6330 : WaterHeaterSide::Source,
6331 : this->SrcSidePlantLoc.loopSideNum,
6332 11 : this->SourceSideSeries,
6333 : this->SourceBranchControlType,
6334 : sensedTemp,
6335 : DeadBandTemp,
6336 11 : this->SetPointTemp);
6337 11 : if (this->SrcSidePlantLoc.loopNum > 0) {
6338 1 : PlantUtilities::SetComponentFlowRate(state, mdotSource, this->SourceInletNode, this->SourceOutletNode, this->SrcSidePlantLoc);
6339 : } else { // not really plant connected (desuperheater or heat pump)
6340 10 : state.dataLoopNodes->Node(this->SourceInletNode).MassFlowRate = mdotSource;
6341 10 : state.dataLoopNodes->Node(this->SourceOutletNode).MassFlowRate = mdotSource;
6342 : }
6343 :
6344 11 : this->SourceInletTemp = state.dataLoopNodes->Node(this->SourceInletNode).Temp;
6345 11 : this->SourceMassFlowRate = mdotSource;
6346 : }
6347 :
6348 : // initialize HPWHs each iteration
6349 11 : if (this->HeatPumpNum > 0) {
6350 :
6351 5 : int HPNum = this->HeatPumpNum;
6352 :
6353 5 : if (this->MyHPSizeFlag) {
6354 : // autosize info must be calculated in GetWaterThermalTankInputFlag for use in StandardRating procedure
6355 : // (called at end of GetWaterThermalTankInputFlag)
6356 : // report autosizing information here (must be done after GetWaterThermalTankInputFlag is complete)
6357 6 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).WaterFlowRateAutoSized &&
6358 3 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6359 6 : BaseSizer::reportSizerOutput(state,
6360 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6361 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6362 : "Condenser water flow rate [m3/s]",
6363 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate);
6364 : }
6365 6 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).AirFlowRateAutoSized &&
6366 3 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6367 6 : BaseSizer::reportSizerOutput(state,
6368 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6369 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6370 : "Evaporator air flow rate [m3/s]",
6371 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate);
6372 : }
6373 3 : state.dataSize->DataNonZoneNonAirloopValue = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate;
6374 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate =
6375 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate * state.dataEnvrn->StdRhoAir;
6376 3 : if (state.dataSize->CurZoneEqNum > 0) {
6377 0 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirFlow = true;
6378 0 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirVolFlow = state.dataSize->DataNonZoneNonAirloopValue;
6379 : }
6380 3 : if (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated) this->MyHPSizeFlag = false;
6381 : }
6382 :
6383 5 : int HPAirInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode;
6384 5 : int HPAirOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirOutletNode;
6385 5 : int OutdoorAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode;
6386 5 : int ExhaustAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ExhaustAirNode;
6387 5 : int HPWaterInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode;
6388 5 : int HPWaterOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterOutletNode;
6389 5 : int InletAirMixerNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode;
6390 5 : int OutletAirSplitterNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutletAirSplitterNode;
6391 :
6392 5 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).CrankcaseTempIndicator) {
6393 1 : case CrankcaseHeaterControlTemp::Zone: {
6394 2 : state.dataHVACGlobal->HPWHCrankcaseDBTemp =
6395 1 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientTempZone).MAT;
6396 1 : break;
6397 : }
6398 4 : case CrankcaseHeaterControlTemp::Outdoors: {
6399 4 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = state.dataEnvrn->OutDryBulbTemp;
6400 4 : break;
6401 : }
6402 0 : case CrankcaseHeaterControlTemp::Schedule: {
6403 0 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = state.dataWaterThermalTanks->HPWaterHeater(HPNum).crankcaseTempSched->getCurrentVal();
6404 0 : break;
6405 : }
6406 0 : default:
6407 0 : break;
6408 : }
6409 :
6410 : // initialize HPWH report variables to 0 and set tank inlet node equal to outlet node
6411 5 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterSensibleCapacity = 0.0;
6412 5 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterLatentCapacity = 0.0;
6413 5 : this->SourceMassFlowRate = 0.0;
6414 5 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatingPLR = 0.0;
6415 5 : this->SourceInletTemp = this->SourceOutletTemp;
6416 :
6417 : // determine HPWH inlet air conditions based on inlet air configuration (Zone, ZoneAndOA, OutdoorAir, or Schedule)
6418 5 : Real64 HPInletDryBulbTemp = 0.0;
6419 5 : Real64 HPInletHumRat = 0.0;
6420 : Real64 HPInletRelHum;
6421 5 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirConfiguration) {
6422 1 : case WTTAmbientTemp::TempZone: {
6423 1 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6424 1 : HPInletDryBulbTemp = state.dataLoopNodes->Node(HPAirInletNode).Temp;
6425 1 : HPInletHumRat = state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6426 1 : break;
6427 : }
6428 0 : case WTTAmbientTemp::ZoneAndOA: {
6429 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).inletAirMixerSched != nullptr) {
6430 : // schedule values are checked for boundary of 0 and 1 in GetWaterThermalTankInputFlag
6431 0 : state.dataWaterThermalTanks->mixerInletAirSchedule =
6432 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).inletAirMixerSched->getCurrentVal();
6433 : } else {
6434 0 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6435 : }
6436 0 : HPInletDryBulbTemp = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).Temp +
6437 0 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).Temp;
6438 0 : HPInletHumRat = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).HumRat +
6439 0 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6440 0 : break;
6441 : }
6442 4 : case WTTAmbientTemp::OutsideAir: {
6443 4 : state.dataWaterThermalTanks->mixerInletAirSchedule = 1.0;
6444 4 : HPInletDryBulbTemp = state.dataLoopNodes->Node(OutdoorAirNode).Temp;
6445 4 : HPInletHumRat = state.dataLoopNodes->Node(OutdoorAirNode).HumRat;
6446 :
6447 4 : break;
6448 : }
6449 0 : case WTTAmbientTemp::Schedule: {
6450 0 : HPInletDryBulbTemp = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ambientTempSched->getCurrentVal();
6451 0 : HPInletRelHum = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ambientRHSched->getCurrentVal();
6452 0 : HPInletHumRat = Psychrometrics::PsyWFnTdbRhPb(state, HPInletDryBulbTemp, HPInletRelHum, state.dataEnvrn->OutBaroPress, RoutineName);
6453 0 : state.dataLoopNodes->Node(HPAirInletNode).Temp = HPInletDryBulbTemp;
6454 0 : state.dataLoopNodes->Node(HPAirInletNode).HumRat = HPInletHumRat;
6455 0 : state.dataLoopNodes->Node(HPAirInletNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6456 0 : state.dataLoopNodes->Node(HPAirInletNode).Press = state.dataEnvrn->OutBaroPress;
6457 :
6458 0 : break;
6459 : }
6460 0 : default:
6461 0 : assert(false);
6462 : break;
6463 : }
6464 :
6465 5 : state.dataWaterThermalTanks->mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
6466 :
6467 : // set up initial conditions on nodes
6468 5 : if (InletAirMixerNode > 0) {
6469 0 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate = 0.0;
6470 0 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6471 0 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6472 0 : state.dataLoopNodes->Node(InletAirMixerNode).Temp = HPInletDryBulbTemp;
6473 0 : state.dataLoopNodes->Node(InletAirMixerNode).HumRat = HPInletHumRat;
6474 0 : state.dataLoopNodes->Node(InletAirMixerNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6475 0 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6476 0 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6477 0 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6478 0 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6479 : } else {
6480 5 : if (OutdoorAirNode == 0) {
6481 1 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6482 1 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6483 1 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6484 1 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6485 : } else {
6486 4 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6487 4 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6488 4 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6489 4 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6490 : }
6491 : }
6492 :
6493 5 : if (OutletAirSplitterNode > 0) state.dataLoopNodes->Node(OutletAirSplitterNode).MassFlowRate = 0.0;
6494 : // these are water nodes are not managed by plant. the HP connects
6495 : // directly to the WH without using plant.
6496 5 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
6497 5 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
6498 5 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
6499 : }
6500 :
6501 : // set the max mass flow rate for outdoor fans
6502 5 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6503 5 : state.dataWaterThermalTanks->mdotAir;
6504 :
6505 : // Curve objects in DXCoils::CalcHPWHDXCoil will use inlet conditions to HPWH not inlet air conditions to DX Coil
6506 : // HPWHInletDBTemp and HPWHInletWBTemp are DataHVACGlobals to pass info to HPWHDXCoil
6507 5 : state.dataHVACGlobal->HPWHInletDBTemp = HPInletDryBulbTemp;
6508 10 : state.dataHVACGlobal->HPWHInletWBTemp =
6509 5 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, HPInletHumRat, state.dataEnvrn->OutBaroPress);
6510 :
6511 : // initialize flow rates at speed levels for variable-speed HPWH
6512 5 : if ((state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) &&
6513 0 : (0 == state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) // use SCWH coil represents
6514 : {
6515 0 : IntegratedHeatPump::SizeIHP(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum); //
6516 : // IntegratedHeatPump::SimIHP(modBlankString, HPWaterHeater(HPNum).DXCoilNum,
6517 : // 0, EMP1, EMP2, EMP3, 0, 0.0, 1, 0.0, 0.0, 0.0, false, 0.0); //conduct the sizing operation in the IHP
6518 0 : int VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6519 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6520 :
6521 5 : } else if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
6522 5 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") &&
6523 0 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed == 0)) {
6524 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
6525 0 : std::string(),
6526 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
6527 : HVAC::FanOp::Invalid, // Invalid instead of off?
6528 : HVAC::CompressorOp::Off,
6529 : 0.0,
6530 : 1,
6531 : 0.0,
6532 : 0.0,
6533 : 0.0,
6534 : 0.0); // conduct the sizing operation in the VS WSHP
6535 0 : int VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6536 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6537 : // below pass the flow rates from the VS coil to the water heater object
6538 : }
6539 :
6540 5 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed > 0) {
6541 : int VSCoilID;
6542 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP)
6543 0 : VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6544 : else
6545 0 : VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6546 :
6547 : // scale air flow rates
6548 0 : Real64 MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedAirVolFlowRate /
6549 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(
6550 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6551 0 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6552 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6553 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(Iter) * MulSpeedFlowScale;
6554 : }
6555 :
6556 : // check fan flow rate, should be larger than the max flow rate of the VS coil
6557 0 : Real64 FanVolFlow = state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->maxAirFlowRate;
6558 :
6559 0 : if (FanVolFlow < state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6560 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) { // but this is the not the scaled mas flow
6561 : // if ( FanVolFlow < HPWaterHeater( HPNum ).HPWHAirVolFlowRate( HPWaterHeater( HPNum ).NumofSpeed ) ) {
6562 :
6563 0 : ShowWarningError(state,
6564 0 : format("InitWaterThermalTank: -air flow rate = {:.7T} in fan object is less than the MSHP system air flow rate "
6565 : "when waterheating is required({:.7T}).",
6566 : FanVolFlow,
6567 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6568 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)));
6569 0 : ShowContinueError(state,
6570 : " The MSHP system flow rate when waterheating is required is reset to the"
6571 : " fan flow rate and the simulation continues.");
6572 0 : ShowContinueError(state, format(" Occurs in {}", state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name));
6573 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed) =
6574 : FanVolFlow;
6575 : // Check flow rates in other speeds and ensure flow rates are not above the max flow rate
6576 0 : for (int Iter = state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed - 1; Iter >= 1; --Iter) {
6577 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) >
6578 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1)) {
6579 0 : ShowContinueError(state,
6580 0 : format(" The MSHP system flow rate when waterheating is required is reset to the flow rate at higher "
6581 : "speed and the simulation continues at Speed{}.",
6582 : Iter));
6583 0 : ShowContinueError(state, format(" Occurs in {}", state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name));
6584 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6585 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1);
6586 : }
6587 : }
6588 : }
6589 :
6590 0 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6591 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSAirSpeedRatio(Iter) =
6592 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) /
6593 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6594 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6595 : }
6596 :
6597 : // scale water flow rates
6598 0 : MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedWaterVolFlowRate /
6599 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6600 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6601 0 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6602 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterVolFlowRate(Iter) =
6603 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) * MulSpeedFlowScale;
6604 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterMassFlowRate(Iter) =
6605 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterMassFlowRate(Iter) * MulSpeedFlowScale;
6606 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSWaterSpeedRatio(Iter) =
6607 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) /
6608 0 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6609 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6610 : }
6611 :
6612 0 : Real64 rhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, HPInletDryBulbTemp, HPInletHumRat);
6613 :
6614 0 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6615 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(Iter) =
6616 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) * rhoAir;
6617 : }
6618 :
6619 : // set the max mass flow rate for outdoor fans
6620 0 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6621 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6622 : }
6623 :
6624 : } // IF(WaterThermalTank(WaterThermalTankNum)%HeatPumpNum .GT. 0)THEN
6625 :
6626 : // calling CalcStandardRatings early bypasses fan sizing since DataSizing::DataNonZoneNonAirloopValue has not been set yet
6627 11 : if (!this->AlreadyRated) {
6628 7 : if (this->IsChilledWaterTank) {
6629 0 : this->AlreadyRated = true;
6630 : } else {
6631 7 : if (!state.dataGlobal->AnyPlantInModel || state.dataPlnt->PlantFirstSizesOkayToReport || this->MaxCapacity > 0.0 ||
6632 0 : this->HeatPumpNum > 0) {
6633 7 : this->CalcStandardRatings(state);
6634 : }
6635 : }
6636 : }
6637 11 : }
6638 :
6639 359 : void WaterThermalTankData::CalcWaterThermalTankMixed(EnergyPlusData &state) // Water Heater being simulated
6640 : {
6641 :
6642 : // SUBROUTINE INFORMATION:
6643 : // AUTHOR Peter Graham Ellis
6644 : // DATE WRITTEN January 2005
6645 : // MODIFIED na
6646 : // RE-ENGINEERED na
6647 :
6648 : // PURPOSE OF THIS SUBROUTINE:
6649 : // Simulates a well-mixed, single node water heater tank.
6650 :
6651 : // METHODOLOGY EMPLOYED:
6652 : // This model uses analytical calculations based on the differential equation describing the tank energy
6653 : // balance. The model operates in three different modes: heating, floating, and venting. Temperatures and
6654 : // energies change dynamically over the timestep. The final reported tank temperature is the average over
6655 : // the timestep. The final reported heat rates are averages based on the total energy transfer over the
6656 : // timestep.
6657 :
6658 : static constexpr std::string_view RoutineName("CalcWaterThermalTankMixed");
6659 :
6660 : Real64 TimeElapsed_loc =
6661 359 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
6662 :
6663 359 : if (this->TimeElapsed != TimeElapsed_loc) {
6664 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
6665 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
6666 11 : this->SavedTankTemp = this->TankTemp;
6667 11 : this->SavedMode = this->Mode;
6668 :
6669 : // Save outlet temperatures for demand-side flow control
6670 11 : this->SavedUseOutletTemp = this->UseOutletTemp;
6671 11 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
6672 :
6673 11 : this->TimeElapsed = TimeElapsed_loc;
6674 : }
6675 :
6676 359 : Real64 TankTemp_loc = this->SavedTankTemp;
6677 359 : TankOperatingMode Mode_loc = this->SavedMode;
6678 :
6679 359 : Real64 Qmaxcap = this->MaxCapacity;
6680 359 : Real64 Qmincap = this->MinCapacity;
6681 359 : Real64 Qoffcycfuel = this->OffCycParaLoad;
6682 359 : Real64 Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
6683 359 : Real64 Qoncycfuel = this->OnCycParaLoad;
6684 359 : Real64 Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
6685 :
6686 359 : Real64 SetPointTemp_loc = this->SetPointTemp;
6687 359 : Real64 DeadBandTemp = this->getDeadBandTemp();
6688 359 : Real64 MaxTemp = this->TankTempLimit;
6689 359 : Real64 AmbientTemp_loc = this->AmbientTemp;
6690 :
6691 359 : Real64 UseInletTemp_loc = this->UseInletTemp;
6692 359 : Real64 UseMassFlowRate_loc = this->UseMassFlowRate * this->UseEffectiveness;
6693 359 : Real64 SourceInletTemp_loc = this->SourceInletTemp;
6694 359 : Real64 SourceMassFlowRate_loc = this->SourceMassFlowRate * this->SourceEffectiveness;
6695 :
6696 : Real64 rho;
6697 359 : if (this->UseSidePlantLoc.loopNum > 0) {
6698 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, TankTemp_loc, RoutineName);
6699 : } else {
6700 359 : rho = this->water->getDensity(state, TankTemp_loc, RoutineName);
6701 : }
6702 :
6703 359 : Real64 TankMass = rho * this->Volume;
6704 :
6705 : Real64 Cp;
6706 359 : if (this->UseSidePlantLoc.loopNum > 0) {
6707 0 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, TankTemp_loc, RoutineName);
6708 : } else {
6709 359 : Cp = this->water->getSpecificHeat(state, TankTemp_loc, RoutineName);
6710 : }
6711 :
6712 359 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
6713 359 : Real64 TimeRemaining = SecInTimeStep;
6714 359 : int CycleOnCount_loc = 0;
6715 359 : int MaxCycles = SecInTimeStep;
6716 359 : Real64 Runtime = 0.0;
6717 359 : bool SetPointRecovered = false;
6718 :
6719 359 : Real64 Tsum = 0.0;
6720 359 : Real64 Eloss = 0.0;
6721 359 : Real64 Elosszone = 0.0;
6722 359 : Real64 Euse = 0.0;
6723 359 : Real64 Esource = 0.0;
6724 359 : Real64 Eheater = 0.0;
6725 359 : Real64 Event = 0.0;
6726 359 : Real64 Eneeded = 0.0;
6727 359 : Real64 Eunmet = 0.0;
6728 359 : Real64 Efuel = 0.0;
6729 359 : Real64 Eoncycfuel = 0.0;
6730 359 : Real64 Eoffcycfuel = 0.0;
6731 359 : Real64 PLR = 0.0;
6732 359 : Real64 PLRsum = 0.0;
6733 :
6734 359 : Real64 Qheat = 0.0;
6735 359 : Real64 Qheater = 0.0;
6736 359 : Real64 Qvent = 0.0;
6737 359 : Real64 Qneeded = 0.0;
6738 359 : Real64 Qunmet = 0.0;
6739 359 : Real64 Qfuel = 0.0;
6740 :
6741 : // Calculate the heating rate from the heat pump.
6742 359 : Real64 HPWHCondenserDeltaT = 0.0;
6743 :
6744 359 : if (this->HeatPumpNum > 0) {
6745 94 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
6746 94 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
6747 94 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
6748 94 : HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
6749 : }
6750 359 : assert(HPWHCondenserDeltaT >= 0);
6751 :
6752 : Real64 Qheatpump;
6753 : Real64 Qsource;
6754 359 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
6755 : HPWHCondenserDeltaT, SourceInletTemp_loc, Cp, SetPointTemp_loc, SourceMassFlowRate_loc, Qheatpump, Qsource);
6756 :
6757 : // Calculate steady-state use heat rate.
6758 359 : Real64 Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - SetPointTemp_loc);
6759 359 : Real64 Qloss = 0.0, PLF = 0.0;
6760 :
6761 941 : while (TimeRemaining > 0.0) {
6762 :
6763 582 : Real64 TimeNeeded = 0.0;
6764 :
6765 582 : Real64 NewTankTemp = TankTemp_loc;
6766 582 : Real64 LossCoeff_loc = 0.0;
6767 582 : Real64 LossFracToZone = 0.0;
6768 :
6769 582 : switch (Mode_loc) {
6770 :
6771 170 : case TankOperatingMode::Heating: // Heater is on
6772 :
6773 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
6774 170 : LossCoeff_loc = this->OnCycLossCoeff;
6775 170 : LossFracToZone = this->OnCycLossFracToZone;
6776 170 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
6777 170 : Qneeded = -Quse - Qsource - Qloss - Qoncycheat;
6778 :
6779 170 : if (TankTemp_loc > SetPointTemp_loc) {
6780 : // Heater is not needed after all, possibly due to step change in scheduled SetPointTemp
6781 :
6782 17 : Qheater = 0.0;
6783 17 : Qunmet = 0.0;
6784 17 : Mode_loc = TankOperatingMode::Floating;
6785 17 : continue;
6786 :
6787 153 : } else if (TankTemp_loc < SetPointTemp_loc) {
6788 : // Attempt to recover to the setpoint as quickly as possible by using maximum heater capacity
6789 :
6790 : // Qneeded is calculated above
6791 : // Qneeded does not account for the extra energy needed to recover to the setpoint
6792 88 : Qheater = Qmaxcap;
6793 88 : Qunmet = max(Qneeded - Qheater, 0.0);
6794 88 : Qheat = Qoncycheat + Qheater + Qheatpump;
6795 :
6796 : // Calculate time needed to recover to the setpoint at maximum heater capacity
6797 88 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
6798 : SetPointTemp_loc,
6799 : AmbientTemp_loc,
6800 : UseInletTemp_loc,
6801 : SourceInletTemp_loc,
6802 : TankMass,
6803 : Cp,
6804 : UseMassFlowRate_loc,
6805 : SourceMassFlowRate_loc,
6806 : LossCoeff_loc,
6807 : Qheat);
6808 :
6809 88 : if (TimeNeeded > TimeRemaining) {
6810 : // Heater is at maximum capacity and heats for all of the remaining time
6811 : // Setpoint temperature WILL NOT be recovered
6812 :
6813 23 : TimeNeeded = TimeRemaining;
6814 :
6815 23 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6816 : AmbientTemp_loc,
6817 : UseInletTemp_loc,
6818 : SourceInletTemp_loc,
6819 : TankMass,
6820 : Cp,
6821 : UseMassFlowRate_loc,
6822 : SourceMassFlowRate_loc,
6823 : LossCoeff_loc,
6824 : Qheat,
6825 : TimeNeeded);
6826 :
6827 : } else { // TimeNeeded <= TimeRemaining
6828 : // Heater is at maximum capacity but will not heat for all of the remaining time (at maximum anyway)
6829 : // Setpoint temperature WILL be recovered
6830 :
6831 65 : NewTankTemp = SetPointTemp_loc;
6832 :
6833 65 : SetPointRecovered = true;
6834 :
6835 : } // TimeNeeded > TimeRemaining
6836 :
6837 : } else { // TankTemp == SetPointTemp
6838 : // Attempt to maintain the setpoint by using the needed heater capacity (modulating, if allowed)
6839 :
6840 65 : if (Qneeded <= 0.0) {
6841 : // Heater is not needed
6842 :
6843 2 : Qneeded = 0.0;
6844 2 : Qheater = 0.0;
6845 2 : Qunmet = 0.0;
6846 2 : Mode_loc = TankOperatingMode::Floating;
6847 2 : continue;
6848 :
6849 63 : } else if (Qneeded < Qmincap) {
6850 : // Heater is required at less than the minimum capacity
6851 : // If cycling, Qmincap = Qmaxcap. Once the setpoint is reached, heater will almost always be shut off here
6852 :
6853 63 : if (this->ControlType == HeaterControlMode::Cycle) {
6854 : // Control will cycle on and off based on DeadBandTemp
6855 63 : Qheater = 0.0;
6856 63 : Qunmet = 0.0;
6857 63 : Mode_loc = TankOperatingMode::Floating;
6858 63 : continue;
6859 :
6860 0 : } else if (this->ControlType == HeaterControlMode::Modulate) {
6861 : // Control will cycle on and off based on DeadBandTemp until Qneeded > Qmincap again
6862 0 : Qheater = 0.0;
6863 0 : Qunmet = Qneeded;
6864 0 : Mode_loc = TankOperatingMode::Floating;
6865 0 : continue;
6866 :
6867 : // CASE (ControlTypeModulateWithOverheat) ! Not yet implemented
6868 : // Calculate time to reach steady-state temp; check for venting at MaxTemp limit
6869 : // Qheater = Qmincap
6870 :
6871 : // CASE (ControlTypeModulateWithUnderheat) ! Not yet implemented
6872 : // Heater must not come back on until Qneeded >= Qmincap
6873 : // Mode = modfloatMode
6874 : }
6875 :
6876 0 : } else if (Qneeded <= Qmaxcap) {
6877 : // Heater can exactly meet the needed heat rate (usually by modulating) and heats for all of the remaining time
6878 : // Setpoint temperature WILL be maintained
6879 :
6880 0 : TimeNeeded = TimeRemaining;
6881 :
6882 0 : Qheater = Qneeded;
6883 0 : Qunmet = 0.0;
6884 :
6885 0 : NewTankTemp = SetPointTemp_loc;
6886 :
6887 : } else { // Qneeded > Qmaxcap
6888 : // Heater is at maximum capacity and heats for all of the remaining time
6889 : // Setpoint temperature WILL NOT be maintained
6890 :
6891 0 : TimeNeeded = TimeRemaining;
6892 :
6893 0 : Qheater = Qmaxcap;
6894 0 : Qunmet = Qneeded - Qheater;
6895 0 : Qheat = Qoncycheat + Qheater + Qheatpump;
6896 :
6897 0 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6898 : AmbientTemp_loc,
6899 : UseInletTemp_loc,
6900 : SourceInletTemp_loc,
6901 : TankMass,
6902 : Cp,
6903 : UseMassFlowRate_loc,
6904 : SourceMassFlowRate_loc,
6905 : LossCoeff_loc,
6906 : Qheat,
6907 : TimeNeeded);
6908 :
6909 : } // Qneeded > Qmaxcap
6910 :
6911 : } // TankTemp > SetPointTemp
6912 :
6913 : // Update summed values
6914 88 : Eneeded += Qneeded * TimeNeeded;
6915 88 : Eheater += Qheater * TimeNeeded;
6916 88 : Eunmet += Qunmet * TimeNeeded;
6917 88 : Eoncycfuel += Qoncycfuel * TimeNeeded;
6918 :
6919 88 : if (Qmaxcap > 0.0) PLR = Qheater / Qmaxcap;
6920 88 : PLF = this->PartLoadFactor(state, PLR);
6921 88 : Efuel += Qheater * TimeNeeded / (PLF * this->Efficiency);
6922 :
6923 88 : Runtime += TimeNeeded;
6924 88 : PLRsum += PLR * TimeNeeded;
6925 :
6926 88 : if (!this->FirstRecoveryDone) {
6927 14 : this->FirstRecoveryFuel += Efuel + Eoffcycfuel + Eoncycfuel;
6928 14 : if (SetPointRecovered) this->FirstRecoveryDone = true;
6929 : }
6930 88 : break;
6931 :
6932 412 : case TankOperatingMode::Floating:
6933 : case TankOperatingMode::Cooling: // Heater is off
6934 :
6935 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
6936 412 : LossCoeff_loc = this->OffCycLossCoeff;
6937 412 : LossFracToZone = this->OffCycLossFracToZone;
6938 412 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
6939 412 : Qneeded = -Quse - Qsource - Qloss - Qoffcycheat;
6940 :
6941 : // This section really needs to work differently depending on ControlType
6942 : // CYCLE will look at TankTemp, MODULATE will look at Qneeded
6943 :
6944 412 : if ((TankTemp_loc < DeadBandTemp) && (!this->IsChilledWaterTank)) {
6945 : // Tank temperature is already below the minimum, possibly due to step change in scheduled SetPointTemp
6946 :
6947 32 : Mode_loc = TankOperatingMode::Heating;
6948 32 : ++CycleOnCount_loc;
6949 32 : continue;
6950 :
6951 380 : } else if ((TankTemp_loc >= DeadBandTemp) && (!this->IsChilledWaterTank)) {
6952 :
6953 380 : Qheat = Qoffcycheat + Qheatpump;
6954 :
6955 : // Calculate time needed for tank temperature to fall to minimum (setpoint - deadband)
6956 380 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
6957 : DeadBandTemp,
6958 : AmbientTemp_loc,
6959 : UseInletTemp_loc,
6960 : SourceInletTemp_loc,
6961 : TankMass,
6962 : Cp,
6963 : UseMassFlowRate_loc,
6964 : SourceMassFlowRate_loc,
6965 : LossCoeff_loc,
6966 : Qheat);
6967 :
6968 380 : if (TimeNeeded <= TimeRemaining) {
6969 : // Heating will be needed in this DataGlobals::TimeStep
6970 :
6971 44 : NewTankTemp = DeadBandTemp;
6972 44 : Mode_loc = TankOperatingMode::Heating;
6973 44 : ++CycleOnCount_loc;
6974 :
6975 : } else { // TimeNeeded > TimeRemaining
6976 : // Heating will not be needed for all of the remaining time
6977 :
6978 336 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6979 : AmbientTemp_loc,
6980 : UseInletTemp_loc,
6981 : SourceInletTemp_loc,
6982 : TankMass,
6983 : Cp,
6984 : UseMassFlowRate_loc,
6985 : SourceMassFlowRate_loc,
6986 : LossCoeff_loc,
6987 : Qheat,
6988 : TimeRemaining);
6989 :
6990 336 : if ((NewTankTemp < MaxTemp) || (this->IsChilledWaterTank)) {
6991 : // Neither heating nor venting is needed for all of the remaining time
6992 :
6993 335 : TimeNeeded = TimeRemaining;
6994 :
6995 : } else { // NewTankTemp >= MaxTemp
6996 : // Venting will be needed in this DataGlobals::TimeStep
6997 :
6998 : // Calculate time needed for tank temperature to rise to the maximum
6999 1 : TimeNeeded = CalcTimeNeeded(TankTemp_loc,
7000 : MaxTemp,
7001 : AmbientTemp_loc,
7002 : UseInletTemp_loc,
7003 : SourceInletTemp_loc,
7004 : TankMass,
7005 : Cp,
7006 : UseMassFlowRate_loc,
7007 : SourceMassFlowRate_loc,
7008 : LossCoeff_loc,
7009 : Qheat);
7010 :
7011 : // if limit NewTankTemp >= MaxTemp
7012 1 : if (TankTemp_loc >= MaxTemp) {
7013 1 : TimeNeeded = TimeRemaining;
7014 : }
7015 1 : NewTankTemp = MaxTemp;
7016 1 : Mode_loc = TankOperatingMode::Venting;
7017 :
7018 : } // NewTankTemp >= MaxTemp
7019 :
7020 : } // TimeNeeded <= TimeRemaining
7021 :
7022 0 : } else if ((TankTemp_loc > DeadBandTemp) && (this->IsChilledWaterTank)) {
7023 0 : Mode_loc = TankOperatingMode::Cooling;
7024 0 : Qheat = Qheatpump;
7025 :
7026 0 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
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 : TimeRemaining);
7037 0 : TimeNeeded = TimeRemaining;
7038 0 : } else if ((TankTemp_loc <= DeadBandTemp) && (this->IsChilledWaterTank)) {
7039 :
7040 0 : if (TankTemp_loc < SetPointTemp_loc) Mode_loc = TankOperatingMode::Floating;
7041 :
7042 0 : Qheat = Qheatpump;
7043 :
7044 0 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7045 : AmbientTemp_loc,
7046 : UseInletTemp_loc,
7047 : SourceInletTemp_loc,
7048 : TankMass,
7049 : Cp,
7050 : UseMassFlowRate_loc,
7051 : SourceMassFlowRate_loc,
7052 : LossCoeff_loc,
7053 : Qheat,
7054 : TimeRemaining);
7055 0 : TimeNeeded = TimeRemaining;
7056 : } // TankTemp vs DeadBandTemp for heaters and chilled water tanks
7057 :
7058 : // Update summed values
7059 380 : Eneeded += Qneeded * TimeNeeded;
7060 380 : Eunmet += Qunmet * TimeNeeded; // Qunmet may be propagated thru from the previous iteration
7061 380 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7062 380 : break;
7063 :
7064 0 : case TankOperatingMode::Venting: // Excess heat is vented
7065 :
7066 0 : LossCoeff_loc = this->OffCycLossCoeff;
7067 0 : LossFracToZone = this->OffCycLossFracToZone;
7068 0 : Qheat = Qoffcycheat + Qheatpump;
7069 :
7070 0 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7071 : AmbientTemp_loc,
7072 : UseInletTemp_loc,
7073 : SourceInletTemp_loc,
7074 : TankMass,
7075 : Cp,
7076 : UseMassFlowRate_loc,
7077 : SourceMassFlowRate_loc,
7078 : LossCoeff_loc,
7079 : Qheat,
7080 : TimeRemaining);
7081 :
7082 0 : if (NewTankTemp < MaxTemp) {
7083 : // Venting is no longer needed because conditions have changed
7084 :
7085 0 : Mode_loc = TankOperatingMode::Floating;
7086 0 : continue;
7087 :
7088 : } else { // NewTankTemp >= MaxTemp
7089 :
7090 0 : TimeNeeded = TimeRemaining;
7091 :
7092 : // Calculate the steady-state venting rate needed to maintain the tank at maximum temperature
7093 0 : Real64 Qloss = LossCoeff_loc * (AmbientTemp_loc - MaxTemp);
7094 0 : Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - MaxTemp);
7095 0 : Qsource = SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc - MaxTemp);
7096 0 : Qvent = -Quse - Qsource - Qloss - Qoffcycheat;
7097 :
7098 0 : NewTankTemp = MaxTemp;
7099 :
7100 : } // NewTankTemp < MaxTemp
7101 :
7102 : // Update summed values
7103 0 : Event += Qvent * TimeNeeded;
7104 0 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7105 0 : break;
7106 0 : default:
7107 0 : assert(false); // should never get here
7108 : }
7109 :
7110 468 : Real64 deltaTsum = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTempIntegral(TankTemp_loc,
7111 : NewTankTemp,
7112 : AmbientTemp_loc,
7113 : UseInletTemp_loc,
7114 : SourceInletTemp_loc,
7115 : TankMass,
7116 : Cp,
7117 : UseMassFlowRate_loc,
7118 : SourceMassFlowRate_loc,
7119 : LossCoeff_loc,
7120 : Qheat,
7121 : TimeNeeded);
7122 :
7123 : // Update summed values
7124 468 : Tsum += deltaTsum;
7125 468 : Eloss += LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7126 468 : Elosszone += LossFracToZone * LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7127 468 : Euse += UseMassFlowRate_loc * Cp * (UseInletTemp_loc * TimeNeeded - deltaTsum);
7128 468 : if (this->HeatPumpNum > 0) {
7129 132 : Esource += Qheatpump * TimeNeeded;
7130 : } else {
7131 336 : Esource += SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc * TimeNeeded - deltaTsum);
7132 : }
7133 :
7134 468 : TankTemp_loc = NewTankTemp; // Update tank temperature
7135 :
7136 468 : TimeRemaining -= TimeNeeded;
7137 :
7138 468 : if (CycleOnCount_loc > MaxCycles) {
7139 :
7140 0 : if (!state.dataGlobal->WarmupFlag) {
7141 0 : if (this->MaxCycleErrorIndex == 0) {
7142 0 : ShowWarningError(state, format("WaterHeater:Mixed = {}: Heater is cycling on and off more than once per second.", this->Name));
7143 0 : ShowContinueError(state, "Try increasing Deadband Temperature Difference or Tank Volume");
7144 0 : ShowContinueErrorTimeStamp(state, "");
7145 : }
7146 0 : ShowRecurringWarningErrorAtEnd(state,
7147 0 : "WaterHeater:Mixed = " + this->Name + " Heater is cycling on and off more than once per second:",
7148 0 : this->MaxCycleErrorIndex);
7149 : }
7150 :
7151 0 : break;
7152 :
7153 : } // CycleOnCount > MaxCycles
7154 :
7155 : } // TimeRemaining > 0.0
7156 :
7157 : // 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
7158 359 : Real64 TankTempAvg_loc = Tsum / SecInTimeStep;
7159 359 : Qloss = Eloss / SecInTimeStep;
7160 359 : Real64 Qlosszone = Elosszone / SecInTimeStep;
7161 359 : Quse = Euse / SecInTimeStep;
7162 359 : Qsource = Esource / SecInTimeStep;
7163 359 : Qheater = Eheater / SecInTimeStep;
7164 359 : Qoffcycfuel = Eoffcycfuel / SecInTimeStep;
7165 359 : Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
7166 359 : Qoncycfuel = Eoncycfuel / SecInTimeStep;
7167 359 : Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
7168 359 : Qvent = Event / SecInTimeStep;
7169 359 : Qneeded = Eneeded / SecInTimeStep;
7170 359 : Qunmet = Eunmet / SecInTimeStep;
7171 359 : Real64 RTF = Runtime / SecInTimeStep;
7172 359 : PLR = PLRsum / SecInTimeStep;
7173 :
7174 359 : if (this->ControlType == HeaterControlMode::Cycle) {
7175 : // Recalculate Part Load Factor and fuel energy based on Runtime Fraction, instead of Part Load Ratio
7176 359 : PLF = this->PartLoadFactor(state, RTF);
7177 359 : Efuel = Eheater / (PLF * this->Efficiency);
7178 : }
7179 :
7180 359 : Qfuel = Efuel / SecInTimeStep;
7181 :
7182 359 : this->Mode = Mode_loc; // Operating mode for carry-over to next DataGlobals::TimeStep
7183 359 : this->TankTemp = TankTemp_loc; // Final tank temperature for carry-over to next DataGlobals::TimeStep
7184 359 : this->TankTempAvg = TankTempAvg_loc; // Average tank temperature over the DataGlobals::TimeStep for reporting
7185 :
7186 359 : if (!state.dataGlobal->WarmupFlag) {
7187 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
7188 287 : if (this->TankTemp < 2) {
7189 1 : if (this->FreezingErrorIndex == 0) {
7190 2 : ShowWarningError(state,
7191 2 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
7192 : RoutineName,
7193 1 : this->Type,
7194 1 : this->Name,
7195 1 : this->TankTemp));
7196 3 : ShowContinueErrorTimeStamp(state, "");
7197 : }
7198 8 : ShowRecurringWarningErrorAtEnd(state,
7199 2 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
7200 1 : this->FreezingErrorIndex,
7201 1 : this->TankTemp, // Report Max
7202 1 : this->TankTemp, // Report Min
7203 : _, // Don't report Sum
7204 : "{C}", // Max Unit
7205 : "{C}"); // Min Unit
7206 : }
7207 : }
7208 359 : this->UseOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7209 359 : this->SourceOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7210 359 : if (this->HeatPumpNum > 0) {
7211 94 : this->SourceInletTemp =
7212 94 : TankTempAvg_loc + HPWHCondenserDeltaT; // Update the source inlet temperature to the average over the DataGlobals::TimeStep
7213 : }
7214 :
7215 359 : this->LossRate = Qloss;
7216 359 : this->UseRate = Quse;
7217 359 : this->SourceRate = Qsource;
7218 359 : this->OffCycParaRateToTank = Qoffcycheat;
7219 359 : this->OnCycParaRateToTank = Qoncycheat;
7220 359 : this->TotalDemandRate = -Quse - Qsource - Qloss - Qoffcycheat - Qoncycheat;
7221 359 : this->HeaterRate = Qheater;
7222 359 : this->UnmetRate = Qunmet;
7223 359 : this->VentRate = Qvent;
7224 359 : this->NetHeatTransferRate = Quse + Qsource + Qloss + Qoffcycheat + Qoncycheat + Qheater + Qvent;
7225 :
7226 359 : this->CycleOnCount = CycleOnCount_loc;
7227 359 : this->RuntimeFraction = RTF;
7228 359 : this->PartLoadRatio = PLR;
7229 :
7230 359 : this->FuelRate = Qfuel;
7231 359 : this->OffCycParaFuelRate = Qoffcycfuel;
7232 359 : this->OnCycParaFuelRate = Qoncycfuel;
7233 :
7234 : // Add water heater skin losses and venting losses to ambient zone, if specified
7235 359 : if (this->AmbientTempZone > 0) this->AmbientZoneGain = -Qlosszone - Qvent;
7236 359 : }
7237 :
7238 361 : void WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
7239 : Real64 HPWHCondenserDeltaT, // input, The temperature difference (C) across the heat pump, zero if
7240 : // there is no heat pump or if the heat pump is off
7241 : Real64 SourceInletTemp, // input, Source inlet temperature (C)
7242 : Real64 Cp, // Specific heat of fluid (J/kg deltaC)
7243 : Real64 SetPointTemp, // input, Mixed tank set point temperature
7244 : Real64 &SourceMassFlowRate, // source mass flow rate (kg/s)
7245 : Real64 &Qheatpump, // heat transfer rate from heat pump
7246 : Real64 &Qsource // steady state heat transfer rate from a constant temperature source side flow
7247 : )
7248 : {
7249 : // Function Information:
7250 : // Author: Noel Merket
7251 : // Date Written: January 2015
7252 : // Modified: na
7253 : // Re-engineered: na
7254 :
7255 : // Purpose of this function:
7256 : // Determines if the source side heat transfer is coming from a heat pump.
7257 : // If so it treats the source side heat transfer as a constant heat source
7258 : // If it is not coming from a heat pump it treats the source side heat transfer
7259 : // as a constant temperature.
7260 :
7261 : // Determine if the source side heating is coming from a heat pump.
7262 361 : Qheatpump = SourceMassFlowRate * Cp * HPWHCondenserDeltaT;
7263 361 : if (Qheatpump > 0.0) {
7264 12 : SourceMassFlowRate = 0.0; // Handle this heating as a constant heat source
7265 12 : Qsource = Qheatpump;
7266 : } else {
7267 349 : Qsource = SourceMassFlowRate * Cp * (SourceInletTemp - SetPointTemp);
7268 : }
7269 361 : }
7270 :
7271 469 : Real64 WaterThermalTankData::CalcTimeNeeded(Real64 const Ti, // Initial tank temperature (C)
7272 : Real64 const Tf, // Final tank temperature (C)
7273 : Real64 const Ta, // Ambient environment temperature (C)
7274 : Real64 const T1, // Temperature of flow 1 (C)
7275 : Real64 const T2, // Temperature of flow 2 (C)
7276 : Real64 const m, // Mass of tank fluid (kg)
7277 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7278 : Real64 const m1, // Mass flow rate 1 (kg/s)
7279 : Real64 const m2, // Mass flow rate 2 (kg/s)
7280 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7281 : Real64 const Q // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7282 : )
7283 : {
7284 :
7285 : // SUBROUTINE INFORMATION:
7286 : // AUTHOR Peter Graham Ellis
7287 : // DATE WRITTEN February 2005
7288 : // MODIFIED na
7289 : // RE-ENGINEERED na
7290 :
7291 : // PURPOSE OF THIS SUBROUTINE:
7292 : // Calculates the time needed for the tank temperature to change from Ti to Tf given heat loss,
7293 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7294 :
7295 : // METHODOLOGY EMPLOYED:
7296 : // Equations are derived by solving the differential equation governing the tank energy balance.
7297 : // Special cases which cause the natural logarithm to blow up are trapped and interpreted as
7298 : // requiring an infinite amount of time because Tf can never be reached under the given conditions.
7299 :
7300 : // Return value
7301 : Real64 CalcTimeNeeded;
7302 :
7303 : // SUBROUTINE PARAMETER DEFINITIONS:
7304 469 : Real64 constexpr Infinity(99999999.9); // A time interval much larger than any single DataGlobals::TimeStep (s)
7305 :
7306 : Real64 t; // Time elapsed from Ti to Tf (s)
7307 :
7308 469 : if (Tf == Ti) {
7309 : // Already at Tf; no time is needed
7310 0 : t = 0.0;
7311 :
7312 : } else {
7313 : Real64 a; // Intermediate variable
7314 : Real64 b; // Intermediate variable
7315 : Real64 Tm; // Mixed temperature after an infinite amount of time has passed (C)
7316 : Real64 quotient; // Intermediate variable
7317 :
7318 469 : if (UA / Cp + m1 + m2 == 0.0) {
7319 :
7320 55 : if (Q == 0.0) {
7321 : // With no mass flow and no heat flow and Tf<>Ti, then Tf can never be reached
7322 42 : t = Infinity;
7323 :
7324 : } else {
7325 13 : a = Q / (m * Cp);
7326 :
7327 13 : t = (Tf - Ti) / a;
7328 : }
7329 :
7330 : } else {
7331 414 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7332 414 : b = -(UA / Cp + m1 + m2) / m;
7333 :
7334 : // Calculate the mixed temperature Tm of the tank after an infinite amount of time has passed
7335 414 : Tm = -a / b;
7336 :
7337 414 : if (Tm == Ti) {
7338 : // Mixed temperature is the same as Ti; if Tf<>Ti, then Tf can never be reached
7339 2 : t = Infinity;
7340 :
7341 412 : } else if (Tm == Tf) {
7342 : // Tf only approaches Tm; it can never actually get there in finite time (also avoids divide by zero error)
7343 0 : t = Infinity;
7344 :
7345 : } else {
7346 412 : quotient = (Tf - Tm) / (Ti - Tm);
7347 :
7348 412 : if (quotient <= 0.0) { // Autodesk:Num Changed < to <= to elim poss floating point error in LOG call
7349 : // Tm is in between Ti and Tf; Tf can never be reached
7350 7 : t = Infinity;
7351 :
7352 : } else {
7353 405 : t = std::log(quotient) / b;
7354 : }
7355 : }
7356 : }
7357 :
7358 469 : if (t < 0.0) t = Infinity; // If negative time, Tf can never be reached in the future
7359 : }
7360 :
7361 469 : CalcTimeNeeded = t;
7362 :
7363 469 : return CalcTimeNeeded;
7364 : }
7365 :
7366 359 : Real64 WaterThermalTankData::CalcTankTemp(Real64 const Ti, // Initial tank temperature (C)
7367 : Real64 const Ta, // Ambient environment temperature (C)
7368 : Real64 const T1, // Temperature of flow 1 (C)
7369 : Real64 const T2, // Temperature of flow 2 (C)
7370 : Real64 const m, // Mass of tank fluid (kg)
7371 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7372 : Real64 const m1, // Mass flow rate 1 (kg/s)
7373 : Real64 const m2, // Mass flow rate 2 (kg/s)
7374 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7375 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7376 : Real64 const t // Time elapsed from Ti to Tf (s)
7377 : )
7378 : {
7379 :
7380 : // SUBROUTINE INFORMATION:
7381 : // AUTHOR Peter Graham Ellis
7382 : // DATE WRITTEN February 2005
7383 : // MODIFIED na
7384 : // RE-ENGINEERED na
7385 :
7386 : // PURPOSE OF THIS SUBROUTINE:
7387 : // Calculates the final tank temperature Tf after time t has elapsed given heat loss,
7388 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7389 :
7390 : // METHODOLOGY EMPLOYED:
7391 : // Equations are derived by solving the differential equation governing the tank energy balance.
7392 :
7393 : // Return value
7394 : Real64 CalcTankTemp;
7395 :
7396 : // Locals
7397 : // SUBROUTINE ARGUMENT DEFINITIONS:
7398 :
7399 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7400 : Real64 a; // Intermediate variable
7401 : Real64 b; // Intermediate variable
7402 : Real64 Tf; // Final tank temperature (C)
7403 :
7404 359 : if (UA / Cp + m1 + m2 == 0.0) {
7405 53 : a = Q / (m * Cp);
7406 :
7407 53 : Tf = a * t + Ti;
7408 :
7409 : } else {
7410 306 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7411 306 : b = -(UA / Cp + m1 + m2) / m;
7412 :
7413 306 : Tf = (a / b + Ti) * std::exp(b * t) - a / b;
7414 : }
7415 :
7416 359 : CalcTankTemp = Tf;
7417 :
7418 359 : return CalcTankTemp;
7419 : }
7420 :
7421 473 : Real64 WaterThermalTankData::CalcTempIntegral(Real64 const Ti, // Initial tank temperature (C)
7422 : Real64 const Tf, // Final tank temperature (C)
7423 : Real64 const Ta, // Ambient environment temperature (C)
7424 : Real64 const T1, // Temperature of flow 1 (C)
7425 : Real64 const T2, // Temperature of flow 2 (C)
7426 : Real64 const m, // Mass of tank fluid (kg)
7427 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7428 : Real64 const m1, // Mass flow rate 1 (kg/s)
7429 : Real64 const m2, // Mass flow rate 2 (kg/s)
7430 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7431 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7432 : Real64 const t // Time elapsed from Ti to Tf (s)
7433 : )
7434 : {
7435 :
7436 : // SUBROUTINE INFORMATION:
7437 : // AUTHOR Peter Graham Ellis
7438 : // DATE WRITTEN February 2005
7439 : // MODIFIED na
7440 : // RE-ENGINEERED na
7441 :
7442 : // PURPOSE OF THIS SUBROUTINE:
7443 : // Calculates the integral of the tank temperature from Ti to Tf. The integral is added to a sum which is
7444 : // later divided by the elapsed time to yield the average tank temperature over the DataGlobals::TimeStep.
7445 :
7446 : // METHODOLOGY EMPLOYED:
7447 : // Equations are the mathematical integrals of the governing differential equations.
7448 :
7449 : // Return value
7450 : Real64 CalcTempIntegral;
7451 :
7452 : // Locals
7453 : // SUBROUTINE ARGUMENT DEFINITIONS:
7454 :
7455 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7456 : Real64 a; // Intermediate variable
7457 : Real64 b; // Intermediate variable
7458 : Real64 dTsum; // Integral of tank temperature (C s)
7459 :
7460 473 : if (t == 0.0) {
7461 1 : dTsum = 0.0;
7462 :
7463 472 : } else if (Tf == Ti) { // Steady-state conditions
7464 45 : dTsum = Tf * t;
7465 :
7466 427 : } else if (UA / Cp + m1 + m2 == 0.0) {
7467 14 : a = Q / (m * Cp);
7468 :
7469 : // Integral of T(t) = a * t + Ti, evaluated from 0 to t
7470 14 : dTsum = 0.5 * a * t * t + Ti * t;
7471 :
7472 : } else {
7473 413 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7474 413 : b = -(UA / Cp + m1 + m2) / m;
7475 :
7476 : // Integral of T(t) = (a / b + Ti) * EXP(b * t) - a / b, evaluated from 0 to t
7477 413 : dTsum = (a / b + Ti) * (std::exp(b * t) - 1.0) / b - a * t / b;
7478 : }
7479 :
7480 473 : CalcTempIntegral = dTsum;
7481 :
7482 473 : return CalcTempIntegral;
7483 : }
7484 :
7485 447 : Real64 WaterThermalTankData::PartLoadFactor(EnergyPlusData &state, Real64 const PartLoadRatio_loc)
7486 : {
7487 :
7488 : // SUBROUTINE INFORMATION:
7489 : // AUTHOR Peter Graham Ellis
7490 : // DATE WRITTEN January 2005
7491 : // MODIFIED na
7492 : // RE-ENGINEERED na
7493 :
7494 : // PURPOSE OF THIS SUBROUTINE:
7495 : // Calculates the Part Load Factor (PLF) based on a curve correlated to Part Load Ratio, if Heater Control Type
7496 : // is MODULATE, or correlated to Runtime Fraction, if Heater Control Type is CYCLE.
7497 :
7498 447 : if (this->PLFCurve > 0) {
7499 103 : return max(Curve::CurveValue(state, this->PLFCurve, PartLoadRatio_loc), 0.1);
7500 : } else {
7501 344 : return 1.0;
7502 : }
7503 : }
7504 :
7505 1462 : void WaterThermalTankData::CalcWaterThermalTankStratified(EnergyPlusData &state)
7506 : {
7507 : // SUBROUTINE INFORMATION:
7508 : // AUTHOR Noel Merket, originally by Peter Graham Ellis
7509 : // DATE WRITTEN January 2007
7510 : // MODIFIED Nov 2011, BAN; modified the use and source outlet temperature calculation
7511 : // RE-ENGINEERED Noel Merket, November 2018
7512 :
7513 : // PURPOSE OF THIS SUBROUTINE:
7514 : // Simulates a stratified, multi-node water heater tank with up to two heating elements.
7515 :
7516 : // METHODOLOGY EMPLOYED:
7517 : // This model uses a numerical calculation based on an analytical solution of the ODE dT/dt = a*T + b.
7518 : // A heat balance is calculated for each node.
7519 : // Temperatures and energies change dynamically over the system time step.
7520 : // Final node temperatures are reported as final instantaneous values as well as averages over the
7521 : // time step. Heat transfer rates are averages over the time step.
7522 :
7523 : static constexpr std::string_view RoutineName("CalcWaterThermalTankStratified");
7524 1462 : constexpr Real64 TemperatureConvergenceCriteria = 0.0001;
7525 1462 : constexpr Real64 SubTimestepMax = 60.0 * 10.0; // seconds
7526 1462 : constexpr Real64 SubTimestepMin = 10.0; // seconds
7527 : Real64 dt;
7528 :
7529 : // Tank object reference
7530 1462 : const Real64 &nTankNodes = this->Nodes;
7531 :
7532 : // Fraction of the current hour that has elapsed (h)
7533 : const Real64 TimeElapsed_loc =
7534 1462 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
7535 :
7536 : // Seconds in one DataGlobals::TimeStep (s)
7537 1462 : const Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
7538 :
7539 : // Advance tank simulation to the next system DataGlobals::TimeStep, if applicable
7540 1462 : if (this->TimeElapsed != TimeElapsed_loc) {
7541 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
7542 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
7543 57 : for (auto &e : this->Node)
7544 52 : e.SavedTemp = e.Temp;
7545 :
7546 5 : this->SavedHeaterOn1 = this->HeaterOn1;
7547 5 : this->SavedHeaterOn2 = this->HeaterOn2;
7548 :
7549 : // Save outlet temperatures for demand-side flow control
7550 5 : this->SavedUseOutletTemp = this->UseOutletTemp;
7551 5 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
7552 :
7553 5 : this->TimeElapsed = TimeElapsed_loc;
7554 : }
7555 :
7556 : // Reset node temperatures to what they were at the beginning of the system DataGlobals::TimeStep.
7557 18996 : for (auto &e : this->Node)
7558 17534 : e.Temp = e.SavedTemp;
7559 :
7560 1462 : this->HeaterOn1 = this->SavedHeaterOn1;
7561 1462 : this->HeaterOn2 = this->SavedHeaterOn2;
7562 :
7563 : // Condenser configuration of heat pump water heater
7564 : const DataPlant::PlantEquipmentType HPWHCondenserConfig =
7565 1462 : this->HeatPumpNum > 0 ? state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType : DataPlant::PlantEquipmentType::Invalid;
7566 :
7567 : // Heat rate from the heat pump (W)
7568 0 : const Real64 Qheatpump = [&, this] { // BLB
7569 1462 : if (this->HeatPumpNum == 0) return 0.0;
7570 9 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
7571 : Real64 CoilTotalHeatingEnergyRate;
7572 9 : if (HPWH.NumofSpeed > 0) {
7573 : // VSHPWH
7574 0 : VariableSpeedCoils::VariableSpeedCoilData const &Coil = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum);
7575 0 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7576 : } else {
7577 : // Single speed HPWH
7578 9 : DXCoils::DXCoilData const &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
7579 9 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7580 : }
7581 9 : return CoilTotalHeatingEnergyRate * this->SourceEffectiveness;
7582 1462 : }();
7583 :
7584 : // Minimum tank temperatures
7585 1462 : const Real64 MinTemp1 = this->SetPointTemp - this->DeadBandDeltaTemp;
7586 1462 : const Real64 MinTemp2 = this->SetPointTemp2 - this->DeadBandDeltaTemp2;
7587 :
7588 : // Specific Heat of water (J/kg K)
7589 0 : const Real64 Cp = [&] {
7590 1462 : if (this->UseSidePlantLoc.loopNum > 0) {
7591 0 : return state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, this->TankTemp, RoutineName);
7592 : } else {
7593 1462 : return this->water->getSpecificHeat(state, this->TankTemp, RoutineName);
7594 : }
7595 1462 : }();
7596 :
7597 1462 : Real64 Eloss = 0.0; // Energy change due to ambient losses over the DataGlobals::TimeStep (J)
7598 1462 : Real64 Euse = 0.0; // Energy change due to use side mass flow over the DataGlobals::TimeStep (J)
7599 1462 : Real64 Esource = 0.0; // Energy change due to source side mass flow over the DataGlobals::TimeStep (J)
7600 1462 : Real64 Eheater1 = 0.0; // Energy change due to heater 1 over the DataGlobals::TimeStep (J)
7601 1462 : Real64 Eheater2 = 0.0; // Energy change due to heater 2 over the DataGlobals::TimeStep (J)
7602 1462 : Real64 Eunmet = 0.0; // Energy change unmet over the DataGlobals::TimeStep (J)
7603 1462 : Real64 Event = 0.0; // Energy change due to venting over the DataGlobals::TimeStep (J)
7604 1462 : int CycleOnCount1_loc = 0; // Number of times heater 1 cycles on in the current time step
7605 1462 : int CycleOnCount2_loc = 0; // Number of times heater 2 cycles on in the current time step
7606 1462 : Real64 Runtime = 0.0; // Time that either heater is running (s)
7607 1462 : Real64 Runtime1 = 0.0; // Time that heater 1 is running (s)
7608 1462 : Real64 Runtime2 = 0.0; // Time that heater 2 is running (s)
7609 1462 : bool SetPointRecovered = false; // Flag to indicate when set point is recovered for the first time
7610 : // Added three variables for desuperheater sourceinlet temperature update
7611 : Real64 MdotDesuperheaterWater; // mass flow rate of desuperheater source side water, kg/s
7612 1462 : Real64 DesuperheaterPLR = 0.0; // Desuperheater part load ratio
7613 1462 : Real64 DesuperheaterHeaterRate = 0.0; // Desuperheater heater rate (W)
7614 1462 : Real64 SourceInletTempSum = 0.0; // Sum the source inlet temperature in sub time step to calculate average tempearature
7615 : Real64 Qheater1; // Heating rate of burner or electric heating element 1 (W)
7616 : Real64 Qheater2; // Heating rate of burner or electric heating element 2 (W)
7617 :
7618 1462 : if (this->InletMode == InletPositionMode::Fixed) CalcNodeMassFlows(InletPositionMode::Fixed);
7619 :
7620 : // Time remaining in the current DataGlobals::TimeStep (s)
7621 1462 : Real64 TimeRemaining = SecInTimeStep;
7622 :
7623 : // Diff Eq. Coefficients for each node
7624 1462 : std::vector<Real64> A;
7625 1462 : A.resize(nTankNodes);
7626 1462 : std::vector<Real64> B;
7627 1462 : B.resize(nTankNodes);
7628 :
7629 : // Temperature at the end of the internal DataGlobals::TimeStep
7630 1462 : std::vector<Real64> Tfinal;
7631 1462 : Tfinal.resize(nTankNodes);
7632 :
7633 : // Average temperature of each node over the internal DataGlobals::TimeStep
7634 1462 : std::vector<Real64> Tavg;
7635 1462 : Tavg.resize(nTankNodes);
7636 :
7637 1462 : int SubTimestepCount = 0;
7638 :
7639 4895 : while (TimeRemaining > 0.0) {
7640 :
7641 3433 : ++SubTimestepCount;
7642 :
7643 3433 : bool PrevHeaterOn1 = this->HeaterOn1;
7644 3433 : bool PrevHeaterOn2 = this->HeaterOn2;
7645 :
7646 3433 : if (this->InletMode == InletPositionMode::Seeking) CalcNodeMassFlows(InletPositionMode::Seeking);
7647 :
7648 : // Heater control logic
7649 3433 : if (this->IsChilledWaterTank) {
7650 : // Chilled Water Tank, no heating
7651 21 : Qheater1 = 0.0;
7652 21 : Qheater2 = 0.0;
7653 : } else {
7654 : // Control the first heater element (master)
7655 3412 : if (this->MaxCapacity > 0.0) {
7656 3408 : const Real64 &NodeTemp = this->Node(this->HeaterNode1).Temp;
7657 :
7658 3408 : if (this->HeaterOn1) {
7659 30 : if (NodeTemp >= this->SetPointTemp) {
7660 1 : this->HeaterOn1 = false;
7661 1 : SetPointRecovered = true;
7662 : }
7663 : } else { // Heater is off
7664 3378 : if (NodeTemp < MinTemp1) {
7665 2 : this->HeaterOn1 = true;
7666 2 : ++CycleOnCount1_loc;
7667 : }
7668 : }
7669 : }
7670 :
7671 3412 : if (this->HeaterOn1) {
7672 31 : Qheater1 = this->MaxCapacity;
7673 : } else {
7674 3381 : Qheater1 = 0.0;
7675 : }
7676 :
7677 : // Control the second heater element (slave)
7678 3412 : if (this->MaxCapacity2 > 0.0) {
7679 3410 : if ((this->StratifiedControlMode == PriorityControlMode::MasterSlave) && this->HeaterOn1) {
7680 31 : this->HeaterOn2 = false;
7681 :
7682 : } else {
7683 3379 : const Real64 &NodeTemp = this->Node(this->HeaterNode2).Temp;
7684 :
7685 3379 : if (this->HeaterOn2) {
7686 755 : if (NodeTemp >= this->SetPointTemp2) {
7687 19 : this->HeaterOn2 = false;
7688 19 : SetPointRecovered = true;
7689 : }
7690 : } else { // Heater is off
7691 2624 : if (NodeTemp < MinTemp2) {
7692 20 : this->HeaterOn2 = true;
7693 20 : ++CycleOnCount2_loc;
7694 : }
7695 : }
7696 : }
7697 : }
7698 :
7699 3412 : if (this->HeaterOn2) {
7700 756 : Qheater2 = this->MaxCapacity2;
7701 : } else {
7702 2656 : Qheater2 = 0.0;
7703 : }
7704 : }
7705 :
7706 3433 : if (SubTimestepCount == 1) {
7707 1462 : dt = SubTimestepMin;
7708 : } else {
7709 :
7710 : // Set the maximum tank temperature change allowed
7711 1971 : Real64 dT_max = std::numeric_limits<Real64>::max();
7712 1971 : if (this->HeaterOn1) {
7713 31 : if (this->Node(this->HeaterNode1).Temp < this->SetPointTemp) {
7714 : // Node temperature is less than setpoint and heater is on
7715 31 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7716 : } else {
7717 : // Node temperature is greater than or equal to setpoint and heater is on
7718 : // Heater will turn off next time around, calculate assuming that
7719 0 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7720 : }
7721 : } else { // Heater off
7722 1940 : if (this->Node(this->HeaterNode1).Temp >= MinTemp1) {
7723 : // Node temperature is greater than or equal to cut in temperature and heater is off
7724 1940 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7725 : } else {
7726 : // Heater will turn on next time around, calculate to setpoint
7727 0 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7728 : }
7729 : }
7730 1971 : if (this->HeaterOn2) {
7731 590 : if (this->Node(this->HeaterNode2).Temp < this->SetPointTemp2) {
7732 : // Node temperature is less than setpoint and heater is on
7733 590 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7734 : } else {
7735 : // Node temperature is greater than or equal to setpoint and heater is on
7736 : // Heater will turn off next time around, calculate assuming that
7737 0 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7738 : }
7739 : } else { // Heater off
7740 1381 : if (this->Node(this->HeaterNode2).Temp >= MinTemp2) {
7741 : // Node temperature is greater than or equal to cut in temperature and heater is off
7742 1338 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7743 : } else {
7744 : // Heater will turn on next time around, calculate to setpoint
7745 43 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7746 : }
7747 : }
7748 :
7749 : // Make adjustments to A and B to account for heaters being on or off now
7750 1971 : if (this->HeaterOn1 && !PrevHeaterOn1) {
7751 : // If heater 1 is on now and wasn't before add the heat rate to the B term
7752 2 : B[this->HeaterNode1 - 1] += Qheater1 / (this->Node(this->HeaterNode1).Mass * Cp);
7753 1969 : } else if (!this->HeaterOn1 && PrevHeaterOn1) {
7754 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7755 1 : B[this->HeaterNode1 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode1).Mass * Cp);
7756 : }
7757 1971 : if (this->HeaterOn2 && !PrevHeaterOn2) {
7758 : // If heater 2 is on now and wasn't before add the heat rate to the B term
7759 20 : B[this->HeaterNode2 - 1] += Qheater2 / (this->Node(this->HeaterNode2).Mass * Cp);
7760 1951 : } else if (!this->HeaterOn2 && PrevHeaterOn2) {
7761 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7762 19 : B[this->HeaterNode2 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode2).Mass * Cp);
7763 : }
7764 :
7765 1971 : if ((this->HeaterOn1 || this->HeaterOn2) && !(PrevHeaterOn1 || PrevHeaterOn2)) {
7766 : // Remove off cycle loads
7767 : // Apply on cycle loads
7768 273 : for (int i = 0; i < nTankNodes; i++) {
7769 252 : auto const &node = this->Node[i];
7770 252 : Real64 NodeCapacitance = node.Mass * Cp;
7771 252 : A[i] += (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7772 252 : B[i] += (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7773 : NodeCapacitance;
7774 : }
7775 1971 : } else if (!(this->HeaterOn1 || this->HeaterOn2) && (PrevHeaterOn1 || PrevHeaterOn2)) {
7776 : // Remove on cycle loads
7777 : // Apply off cycle loads
7778 247 : for (int i = 0; i < nTankNodes; i++) {
7779 228 : auto const &node = this->Node[i];
7780 228 : Real64 NodeCapacitance = node.Mass * Cp;
7781 228 : A[i] -= (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7782 228 : B[i] -= (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7783 : NodeCapacitance;
7784 : }
7785 : }
7786 :
7787 : // Set the sub DataGlobals::TimeStep (dt)
7788 1971 : dt = TimeRemaining;
7789 25499 : for (int i = 0; i < nTankNodes; ++i) {
7790 23528 : const Real64 Denominator = fabs(A[i] * Tavg[i] + B[i]);
7791 23528 : if (Denominator != 0.0) dt = min(dt, dT_max / Denominator);
7792 : }
7793 1971 : dt = max(min(SubTimestepMin, TimeRemaining), dt);
7794 1971 : dt = min(SubTimestepMax, dt);
7795 : }
7796 :
7797 : // Make initial guess that average and final temperatures over the DataGlobals::TimeStep are equal to the starting temperatures
7798 44495 : for (int i = 0; i < nTankNodes; i++) {
7799 41062 : const auto &NodeTemp = this->Node[i].Temp;
7800 41062 : Tfinal[i] = NodeTemp;
7801 41062 : Tavg[i] = NodeTemp;
7802 : }
7803 :
7804 7686 : for (int ConvergenceCounter = 1; ConvergenceCounter <= 10; ConvergenceCounter++) {
7805 :
7806 7684 : std::fill(A.begin(), A.end(), 0.0);
7807 7684 : std::fill(B.begin(), B.end(), 0.0);
7808 :
7809 : // Heater Coefficients
7810 7684 : B[this->HeaterNode1 - 1] += Qheater1;
7811 7684 : B[this->HeaterNode2 - 1] += Qheater2;
7812 :
7813 99620 : for (int i = 0; i < nTankNodes; i++) {
7814 91936 : const int NodeNum = i + 1;
7815 91936 : const auto &tank_node = this->Node(NodeNum);
7816 :
7817 : // Parasitic Loads and Losses to Ambient
7818 91936 : if (this->HeaterOn1 || this->HeaterOn2) {
7819 : // Parasitic Loads
7820 27120 : B[i] += tank_node.OnCycParaLoad;
7821 : // Losses to Ambient
7822 27120 : A[i] += -tank_node.OnCycLossCoeff;
7823 27120 : B[i] += tank_node.OnCycLossCoeff * this->AmbientTemp;
7824 : } else {
7825 : // Parasitic Loads
7826 64816 : B[i] += tank_node.OffCycParaLoad;
7827 : // Losses to Ambient
7828 64816 : A[i] += -tank_node.OffCycLossCoeff;
7829 64816 : B[i] += tank_node.OffCycLossCoeff * this->AmbientTemp;
7830 : }
7831 :
7832 : // Conduction to adjacent nodes
7833 91936 : A[i] += -(tank_node.CondCoeffDn + tank_node.CondCoeffUp);
7834 91936 : if (NodeNum > 1) B[i] += tank_node.CondCoeffUp * Tavg[i - 1];
7835 91936 : if (NodeNum < nTankNodes) B[i] += tank_node.CondCoeffDn * Tavg[i + 1];
7836 :
7837 : // Use side plant connection
7838 91936 : const Real64 use_e_mdot_cp = tank_node.UseMassFlowRate * Cp;
7839 91936 : A[i] += -use_e_mdot_cp;
7840 91936 : B[i] += use_e_mdot_cp * this->UseInletTemp;
7841 :
7842 : // Source side heat transfer rate
7843 91936 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
7844 : // Pumped Condenser Heat Pump Water Heater
7845 0 : if (tank_node.SourceMassFlowRate > 0.0) B[i] += Qheatpump;
7846 : } else {
7847 : // Source side plant connection (constant temperature)
7848 91936 : const Real64 src_e_mdot_cp = tank_node.SourceMassFlowRate * Cp;
7849 91936 : A[i] += -src_e_mdot_cp;
7850 91936 : B[i] += src_e_mdot_cp * this->SourceInletTemp;
7851 : }
7852 :
7853 : // Wrapped condenser heat pump water heater
7854 91936 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped)) {
7855 624 : B[i] += Qheatpump * tank_node.HPWHWrappedCondenserHeatingFrac;
7856 : }
7857 :
7858 : // Internodal flow
7859 91936 : A[i] += -(tank_node.MassFlowFromUpper + tank_node.MassFlowFromLower) * Cp;
7860 91936 : if (NodeNum > 1) B[i] += tank_node.MassFlowFromUpper * Cp * Tavg[i - 1];
7861 91936 : if (NodeNum < nTankNodes) B[i] += tank_node.MassFlowFromLower * Cp * Tavg[i + 1];
7862 :
7863 : // Divide by mass and specific heat
7864 : // m * cp * dT/dt = q_net => dT/dt = a * T + b
7865 91936 : A[i] /= tank_node.Mass * Cp;
7866 91936 : B[i] /= tank_node.Mass * Cp;
7867 :
7868 : } // end for each node
7869 :
7870 : // Calculate the average and final temperatures over the interval
7871 7684 : Real64 TfinalDiff = 0.0;
7872 99620 : for (int i = 0; i < nTankNodes; ++i) {
7873 91936 : const Real64 Tstart = this->Node[i].Temp;
7874 91936 : const Real64 b_a = B[i] / A[i];
7875 91936 : const Real64 e_a_dt = exp(A[i] * dt);
7876 91936 : Tavg[i] = (Tstart + b_a) * (e_a_dt - 1.0) / (A[i] * dt) - b_a;
7877 91936 : const Real64 Tfinal_old = Tfinal[i];
7878 91936 : Tfinal[i] = (Tstart + b_a) * e_a_dt - b_a;
7879 91936 : TfinalDiff = max(fabs(Tfinal[i] - Tfinal_old), TfinalDiff);
7880 : }
7881 :
7882 7684 : if (TfinalDiff < TemperatureConvergenceCriteria) break;
7883 :
7884 4253 : if (this->DesuperheaterNum > 0) {
7885 3869 : DesuperheaterPLR = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR;
7886 3869 : DesuperheaterHeaterRate = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate;
7887 3869 : MdotDesuperheaterWater = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OperatingWaterFlowRate *
7888 3869 : Psychrometrics::RhoH2O(Tavg[this->SourceOutletStratNode - 1]);
7889 3869 : if (DesuperheaterPLR > 0.0 && MdotDesuperheaterWater > 0.0) {
7890 17 : this->SourceInletTemp =
7891 17 : Tavg[this->SourceOutletStratNode - 1] + (DesuperheaterHeaterRate / DesuperheaterPLR) / (MdotDesuperheaterWater * Cp);
7892 : } else {
7893 3852 : this->SourceInletTemp = Tavg[this->SourceOutletStratNode - 1];
7894 : }
7895 : }
7896 : } // end temperature convergence loop
7897 :
7898 : // Inversion mixing
7899 : bool HasInversion;
7900 8108 : do {
7901 8108 : HasInversion = false;
7902 : // Starting from the top of the tank check if the node below has a temperature inversion.
7903 56318 : for (int j = 0; j < nTankNodes - 1; ++j) {
7904 52885 : if (Tfinal[j] < Tfinal[j + 1]) {
7905 :
7906 : // Temperature inversion!
7907 4675 : HasInversion = true;
7908 :
7909 : // From the node above the inversion, move down calculating a weighted average
7910 : // of node temperatures until the node below the group of mixed nodes isn't hotter
7911 : // or we hit the bottom of the tank.
7912 4675 : Real64 Tmixed = 0.0;
7913 4675 : Real64 MassMixed = 0.0;
7914 : int m;
7915 32778 : for (m = j; m < nTankNodes; ++m) {
7916 32778 : Tmixed += Tfinal[m] * this->Node[m].Mass;
7917 32778 : MassMixed += this->Node[m].Mass;
7918 32778 : if ((m == nTankNodes - 1) || (Tmixed / MassMixed > Tfinal[m + 1])) break;
7919 : }
7920 4675 : Tmixed /= MassMixed;
7921 :
7922 : // Now we have a range of nodes (j = top, m = bottom) that are mixed
7923 : // and the mixed temperature (Tmixed).
7924 : // Move through the mixed nodes and set the final temperature to the mixed temperature.
7925 : // Also calculate a corrected average temperature for each node.
7926 37453 : for (int k = j; k <= m; ++k) {
7927 : Real64 FinalFactorMixing;
7928 : Real64 AvgFactorMixing;
7929 32778 : const Real64 NodeCapacitance = this->Node[k].Mass * Cp;
7930 32778 : if (A[k] == 0.0) {
7931 0 : FinalFactorMixing = dt / NodeCapacitance;
7932 0 : AvgFactorMixing = FinalFactorMixing / 2.0;
7933 : } else {
7934 32778 : FinalFactorMixing = (exp(A[k] * dt) - 1.0) / A[k] / NodeCapacitance;
7935 32778 : AvgFactorMixing = ((exp(A[k] * dt) - 1.0) / A[k] / dt - 1.0) / A[k] / NodeCapacitance;
7936 : }
7937 32778 : const Real64 Q_AdiabaticMixing = (Tmixed - Tfinal[k]) / FinalFactorMixing;
7938 32778 : Tfinal[k] = Tmixed;
7939 32778 : Tavg[k] += Q_AdiabaticMixing * AvgFactorMixing;
7940 : }
7941 :
7942 : // Since we mixed, get out of here and start from the top to check again for mixing.
7943 4675 : break;
7944 : }
7945 : }
7946 : } while (HasInversion);
7947 :
7948 : // Venting
7949 3433 : if (!this->IsChilledWaterTank) {
7950 3412 : if (Tfinal[0] > this->TankTempLimit) {
7951 13 : for (int i = 0; i < nTankNodes; ++i) {
7952 12 : if (Tfinal[i] > this->TankTempLimit) {
7953 5 : Event += this->Node[i].Mass * Cp * (this->TankTempLimit - Tfinal[i]);
7954 5 : Tfinal[i] = this->TankTempLimit;
7955 : }
7956 : }
7957 : }
7958 : }
7959 :
7960 : // Increment to next internal time step
7961 3433 : TimeRemaining -= dt;
7962 3433 : Real64 Qloss = 0.0;
7963 44495 : for (int i = 0; i < nTankNodes; ++i) {
7964 41062 : auto &node = this->Node[i];
7965 41062 : node.Temp = Tfinal[i];
7966 41062 : node.TempSum += Tavg[i] * dt;
7967 :
7968 : // Bookkeeping for reporting variables, mostly for Qunmet.
7969 41062 : Real64 Qloss_node = (this->AmbientTemp - Tavg[i]);
7970 : Real64 Qheat_node;
7971 41062 : const Real64 Quse_node = node.UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[i]);
7972 123186 : const Real64 Qsource_node = [&] {
7973 41062 : if (this->HeatPumpNum > 0) {
7974 216 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
7975 0 : if (node.SourceMassFlowRate > 0.0) {
7976 0 : return Qheatpump;
7977 : } else {
7978 0 : return 0.0;
7979 : }
7980 : } else {
7981 216 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
7982 216 : return Qheatpump * node.HPWHWrappedCondenserHeatingFrac;
7983 : }
7984 : } else {
7985 40846 : return node.SourceMassFlowRate * Cp * (this->SourceInletTemp - Tavg[i]);
7986 : }
7987 41062 : }();
7988 :
7989 41062 : if (this->HeaterOn1 || this->HeaterOn2) {
7990 9444 : Qloss_node *= node.OnCycLossCoeff;
7991 9444 : Qheat_node = node.OnCycParaLoad * this->OnCycParaFracToTank;
7992 : } else {
7993 31618 : Qloss_node *= node.OffCycLossCoeff;
7994 31618 : Qheat_node = node.OffCycParaLoad * this->OffCycParaFracToTank;
7995 : }
7996 41062 : Qloss += Qloss_node;
7997 41062 : const Real64 Qneeded_node = max(-Quse_node - Qsource_node - Qloss_node - Qheat_node, 0.0);
7998 41062 : const Real64 Qunmet_node = max(Qneeded_node - Qheater1 - Qheater2, 0.0);
7999 41062 : Eunmet += Qunmet_node * dt;
8000 : }
8001 3433 : SourceInletTempSum += this->SourceInletTemp * dt;
8002 : // More bookkeeping for reporting variables
8003 3433 : Eloss += Qloss * dt;
8004 3433 : const Real64 Quse = (this->UseOutletStratNode > 0)
8005 3433 : ? this->UseEffectiveness * this->UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[this->UseOutletStratNode - 1])
8006 3433 : : 0.0;
8007 3433 : Euse += Quse * dt;
8008 10299 : const Real64 Qsource = [&] {
8009 3433 : if (this->HeatPumpNum > 0) {
8010 18 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8011 0 : return Qheatpump;
8012 : } else {
8013 18 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
8014 18 : return 0.0;
8015 : }
8016 : } else {
8017 3415 : if (this->SourceOutletStratNode > 0) {
8018 3335 : return this->SourceEffectiveness * this->SourceMassFlowRate * Cp *
8019 3335 : (this->SourceInletTemp - Tavg[this->SourceOutletStratNode - 1]);
8020 : } else {
8021 80 : return 0.0;
8022 : }
8023 : }
8024 3433 : }();
8025 3433 : Esource += Qsource * dt;
8026 3433 : if (this->HeaterOn1) Runtime1 += dt;
8027 3433 : if (this->HeaterOn2) Runtime2 += dt;
8028 3433 : if (this->HeaterOn1 || this->HeaterOn2) Runtime += dt;
8029 3433 : Eheater1 += Qheater1 * dt;
8030 3433 : Eheater2 += Qheater2 * dt;
8031 :
8032 : // Calculation for standard ratings
8033 3433 : if (!this->FirstRecoveryDone) {
8034 : Real64 Qrecovery;
8035 163 : if (this->HeaterOn1 || this->HeaterOn2) {
8036 86 : Qrecovery = (Qheater1 + Qheater1) / this->Efficiency + this->OnCycParaLoad;
8037 : } else {
8038 77 : Qrecovery = this->OffCycParaLoad;
8039 : }
8040 163 : this->FirstRecoveryFuel += Qrecovery * dt;
8041 163 : if (SetPointRecovered) this->FirstRecoveryDone = true;
8042 : }
8043 : } // end while TimeRemaining > 0.0
8044 :
8045 18996 : for (auto &e : this->Node) {
8046 17534 : e.TempAvg = e.TempSum / SecInTimeStep;
8047 17534 : e.TempSum = 0.0;
8048 : }
8049 :
8050 1462 : this->TankTemp = sum(this->Node, &StratifiedNodeData::Temp) / this->Nodes;
8051 1462 : this->TankTempAvg = sum(this->Node, &StratifiedNodeData::TempAvg) / this->Nodes;
8052 :
8053 1462 : if (!state.dataGlobal->WarmupFlag) {
8054 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
8055 1462 : if (this->TankTemp < 2) {
8056 1 : if (this->FreezingErrorIndex == 0) {
8057 2 : ShowWarningError(state,
8058 2 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
8059 : RoutineName,
8060 1 : this->Type,
8061 1 : this->Name,
8062 1 : this->TankTemp));
8063 3 : ShowContinueErrorTimeStamp(state, "");
8064 : }
8065 8 : ShowRecurringWarningErrorAtEnd(state,
8066 2 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
8067 1 : this->FreezingErrorIndex,
8068 1 : this->TankTemp, // Report Max
8069 1 : this->TankTemp, // Report Min
8070 : _, // Don't report Sum
8071 : "{C}", // Max Unit
8072 : "{C}"); // Min Unit
8073 : }
8074 : }
8075 :
8076 1462 : if (this->UseOutletStratNode > 0) {
8077 1462 : this->UseOutletTemp = this->Node(this->UseOutletStratNode).TempAvg;
8078 : // Revised use outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8079 1462 : if (this->UseMassFlowRate > 0.0) {
8080 10 : this->UseOutletTemp = this->UseInletTemp * (1.0 - this->UseEffectiveness) + this->UseOutletTemp * this->UseEffectiveness;
8081 : }
8082 : }
8083 :
8084 1462 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
8085 : // If we have a wrapped condenser HPWH, set the source outlet to the weighted average of the node
8086 : // temperatures the condenser sees
8087 9 : Real64 WeightedAverageSourceOutletTemp(0.0);
8088 117 : for (int i = 1; i <= this->Nodes; ++i) {
8089 108 : WeightedAverageSourceOutletTemp += this->Node(i).TempAvg * this->Node(i).HPWHWrappedCondenserHeatingFrac;
8090 : }
8091 9 : this->SourceOutletTemp = WeightedAverageSourceOutletTemp;
8092 1453 : } else if (this->SourceOutletStratNode > 0) {
8093 : // otherwise set it to the temperature of the source outlet node
8094 1447 : this->SourceOutletTemp = this->Node(this->SourceOutletStratNode).TempAvg;
8095 : // Output the average inlet temperature for the DataGlobals::TimeStep
8096 1447 : this->SourceInletTemp = SourceInletTempSum / SecInTimeStep;
8097 : }
8098 1462 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8099 : // For pumped condensers, set the source inlet and outlets to match the delta T
8100 : // across the water side of the DX coil.
8101 0 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8102 0 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
8103 0 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
8104 0 : Real64 const HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
8105 0 : this->SourceInletTemp = this->SourceOutletTemp + HPWHCondenserDeltaT;
8106 : }
8107 :
8108 : // Revised source outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8109 1462 : if (this->SourceOutletStratNode > 0) {
8110 1447 : if (this->SourceMassFlowRate > 0.0) {
8111 5 : this->SourceOutletTemp = this->SourceInletTemp * (1.0 - this->SourceEffectiveness) + this->SourceOutletTemp * this->SourceEffectiveness;
8112 : }
8113 : }
8114 :
8115 1462 : this->LossRate = Eloss / SecInTimeStep;
8116 1462 : this->UseRate = Euse / SecInTimeStep;
8117 1462 : Real64 WrappedCondenserHeatPumpRate = 0.0;
8118 1462 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
8119 0 : this->SourceRate = Qheatpump;
8120 : } else {
8121 1462 : this->SourceRate = Esource / SecInTimeStep;
8122 1462 : WrappedCondenserHeatPumpRate = Qheatpump;
8123 : }
8124 :
8125 1462 : this->OffCycParaFuelRate = this->OffCycParaLoad * (SecInTimeStep - Runtime) / SecInTimeStep;
8126 1462 : this->OnCycParaFuelRate = this->OnCycParaLoad * Runtime / SecInTimeStep;
8127 1462 : this->OffCycParaRateToTank = this->OffCycParaFuelRate * this->OffCycParaFracToTank;
8128 1462 : this->OnCycParaRateToTank = this->OnCycParaFuelRate * this->OnCycParaFracToTank;
8129 1462 : this->TotalDemandRate =
8130 1462 : -this->UseRate - this->SourceRate - this->LossRate - this->OffCycParaRateToTank - this->OnCycParaRateToTank - WrappedCondenserHeatPumpRate;
8131 1462 : this->HeaterRate1 = Eheater1 / SecInTimeStep;
8132 1462 : this->HeaterRate2 = Eheater2 / SecInTimeStep;
8133 1462 : this->HeaterRate = this->HeaterRate1 + this->HeaterRate2;
8134 :
8135 1462 : this->UnmetRate = Eunmet / SecInTimeStep;
8136 1462 : this->VentRate = Event / SecInTimeStep;
8137 1462 : this->NetHeatTransferRate = this->UseRate + this->SourceRate + this->LossRate + this->OffCycParaRateToTank + this->OnCycParaRateToTank +
8138 1462 : this->HeaterRate + this->VentRate + WrappedCondenserHeatPumpRate;
8139 :
8140 1462 : this->CycleOnCount = CycleOnCount1_loc + CycleOnCount2_loc;
8141 1462 : this->CycleOnCount1 = CycleOnCount1_loc;
8142 1462 : this->CycleOnCount2 = CycleOnCount2_loc;
8143 :
8144 1462 : this->RuntimeFraction = Runtime / SecInTimeStep;
8145 1462 : this->RuntimeFraction1 = Runtime1 / SecInTimeStep;
8146 1462 : this->RuntimeFraction2 = Runtime2 / SecInTimeStep;
8147 :
8148 1462 : this->FuelRate = (Eheater1 + Eheater2) / this->Efficiency / SecInTimeStep;
8149 :
8150 : // Add water heater skin losses and venting losses to ambient zone, if specified
8151 1462 : if (this->AmbientTempZone > 0) this->AmbientZoneGain = -this->LossRate * this->SkinLossFracToZone - this->VentRate;
8152 1462 : }
8153 :
8154 1482 : void WaterThermalTankData::CalcNodeMassFlows(InletPositionMode inletMode)
8155 : {
8156 :
8157 : // SUBROUTINE INFORMATION:
8158 : // AUTHOR Peter Graham Ellis
8159 : // DATE WRITTEN January 2007
8160 : // MODIFIED na
8161 : // RE-ENGINEERED na
8162 :
8163 : // PURPOSE OF THIS SUBROUTINE:
8164 : // Determines mass flow rates between nodes according to the locations of the use- and source-side inlet and outlet
8165 : // nodes.
8166 :
8167 : // METHODOLOGY EMPLOYED:
8168 : // In 'Seeking' mode, nodes are searched between the user-specified inlet and outlet nodes to find the node closest
8169 : // in temperature to the inlet fluid temperature. In 'Fixed' mode, the user-specified nodes are always used.
8170 : // Upward and downward flows are added to each node between an inlet and outlet. Flows in both directions cancel out
8171 : // to leave only the net flow in one direction.
8172 :
8173 1482 : int useInletStratNod = this->UseInletStratNode;
8174 1482 : int useOutletStratNode = this->UseOutletStratNode;
8175 1482 : int sourceInletStratNode = this->SourceInletStratNode;
8176 1482 : int sourceOutletStratNode = this->SourceOutletStratNode;
8177 :
8178 1482 : Real64 useMassFlowRate = this->UseMassFlowRate * this->UseEffectiveness;
8179 1482 : Real64 sourceMassFlowRate = this->SourceMassFlowRate * this->SourceEffectiveness;
8180 :
8181 19136 : for (auto &e : this->Node) {
8182 17654 : e.UseMassFlowRate = 0.0;
8183 17654 : e.SourceMassFlowRate = 0.0;
8184 17654 : e.MassFlowFromUpper = 0.0;
8185 17654 : e.MassFlowFromLower = 0.0;
8186 17654 : e.MassFlowToUpper = 0.0;
8187 17654 : e.MassFlowToLower = 0.0;
8188 : }
8189 :
8190 1482 : if (inletMode == InletPositionMode::Seeking) {
8191 : // 'Seek' the node with the temperature closest to the inlet temperature
8192 : // Start at the user-specified inlet node and search to the user-specified outlet node
8193 : int Step;
8194 21 : if (useMassFlowRate > 0.0) {
8195 21 : if (useInletStratNod > useOutletStratNode) {
8196 0 : Step = -1;
8197 : } else {
8198 21 : Step = 1;
8199 : }
8200 21 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8201 21 : int const NodeNum_stop(floop_end(useInletStratNod, useOutletStratNode, Step));
8202 47 : for (int NodeNum = useInletStratNod; NodeNum != NodeNum_stop; NodeNum += Step) {
8203 46 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->UseInletTemp);
8204 46 : if (DeltaTemp < MinDeltaTemp) {
8205 21 : MinDeltaTemp = DeltaTemp;
8206 21 : useInletStratNod = NodeNum;
8207 25 : } else if (DeltaTemp > MinDeltaTemp) {
8208 20 : break;
8209 : }
8210 : }
8211 : }
8212 :
8213 21 : if (sourceMassFlowRate > 0.0) {
8214 21 : if (sourceInletStratNode > sourceOutletStratNode) {
8215 21 : Step = -1;
8216 : } else {
8217 0 : Step = 1;
8218 : }
8219 21 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8220 21 : int const NodeNum_stop(floop_end(sourceInletStratNode, sourceOutletStratNode, Step));
8221 47 : for (int NodeNum = sourceInletStratNode; NodeNum != NodeNum_stop; NodeNum += Step) {
8222 46 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->SourceInletTemp);
8223 46 : if (DeltaTemp < MinDeltaTemp) {
8224 21 : MinDeltaTemp = DeltaTemp;
8225 21 : sourceInletStratNode = NodeNum;
8226 25 : } else if (DeltaTemp > MinDeltaTemp) {
8227 20 : break;
8228 : }
8229 : }
8230 : }
8231 : }
8232 :
8233 1482 : if (useInletStratNod > 0) this->Node(useInletStratNod).UseMassFlowRate = useMassFlowRate;
8234 1482 : if (sourceInletStratNode > 0) this->Node(sourceInletStratNode).SourceMassFlowRate = sourceMassFlowRate;
8235 :
8236 1482 : if (useMassFlowRate > 0.0) {
8237 30 : if (useOutletStratNode > useInletStratNod) {
8238 : // Use-side flow is down
8239 136 : for (int NodeNum = useInletStratNod; NodeNum <= useOutletStratNode - 1; ++NodeNum) {
8240 113 : this->Node(NodeNum).MassFlowToLower += useMassFlowRate;
8241 : }
8242 136 : for (int NodeNum = useInletStratNod + 1; NodeNum <= useOutletStratNode; ++NodeNum) {
8243 113 : this->Node(NodeNum).MassFlowFromUpper += useMassFlowRate;
8244 : }
8245 :
8246 7 : } else if (useOutletStratNode < useInletStratNod) {
8247 : // Use-side flow is up
8248 84 : for (int NodeNum = useOutletStratNode; NodeNum <= useInletStratNod - 1; ++NodeNum) {
8249 77 : this->Node(NodeNum).MassFlowFromLower += useMassFlowRate;
8250 : }
8251 84 : for (int NodeNum = useOutletStratNode + 1; NodeNum <= useInletStratNod; ++NodeNum) {
8252 77 : this->Node(NodeNum).MassFlowToUpper += useMassFlowRate;
8253 : }
8254 :
8255 : } else {
8256 : // Use-side flow is across the node; no flow to other nodes
8257 : }
8258 : }
8259 :
8260 1482 : if (sourceMassFlowRate > 0.0) {
8261 25 : if (sourceOutletStratNode > sourceInletStratNode) {
8262 : // Source-side flow is down
8263 41 : for (int NodeNum = sourceInletStratNode; NodeNum <= sourceOutletStratNode - 1; ++NodeNum) {
8264 37 : this->Node(NodeNum).MassFlowToLower += sourceMassFlowRate;
8265 : }
8266 41 : for (int NodeNum = sourceInletStratNode + 1; NodeNum <= sourceOutletStratNode; ++NodeNum) {
8267 37 : this->Node(NodeNum).MassFlowFromUpper += sourceMassFlowRate;
8268 : }
8269 :
8270 21 : } else if (sourceOutletStratNode < sourceInletStratNode) {
8271 : // Source-side flow is up
8272 126 : for (int NodeNum = sourceOutletStratNode; NodeNum <= sourceInletStratNode - 1; ++NodeNum) {
8273 105 : this->Node(NodeNum).MassFlowFromLower += sourceMassFlowRate;
8274 : }
8275 126 : for (int NodeNum = sourceOutletStratNode + 1; NodeNum <= sourceInletStratNode; ++NodeNum) {
8276 105 : this->Node(NodeNum).MassFlowToUpper += sourceMassFlowRate;
8277 : }
8278 :
8279 : } else {
8280 : // Source-side flow is across the node; no flow to other nodes
8281 : }
8282 : }
8283 :
8284 : // Cancel out any up and down flows
8285 19136 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
8286 17654 : this->Node(NodeNum).MassFlowFromUpper = max((this->Node(NodeNum).MassFlowFromUpper - this->Node(NodeNum).MassFlowToUpper), 0.0);
8287 17654 : this->Node(NodeNum).MassFlowFromLower = max((this->Node(NodeNum).MassFlowFromLower - this->Node(NodeNum).MassFlowToLower), 0.0);
8288 : }
8289 1482 : }
8290 :
8291 11 : void WaterThermalTankData::CalcDesuperheaterWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8292 : {
8293 :
8294 : // SUBROUTINE INFORMATION:
8295 : // AUTHOR Richard Raustad
8296 : // DATE WRITTEN July 2005
8297 : // MODIFIED na
8298 : // RE-ENGINEERED na
8299 :
8300 : // PURPOSE OF THIS SUBROUTINE:
8301 : // Simulates a refrigerant desuperheater to heat water
8302 :
8303 : // METHODOLOGY EMPLOYED:
8304 : // This model uses the rated heat reclaim recovery efficiency, recovery efficiency modifier curve,
8305 : // set point temperature, and dead band temperature difference to simulate the desuperheater coil
8306 : // and sets up inputs to the tank model associated with the desuperheater coil
8307 :
8308 11 : int constexpr MaxIte(500); // Maximum number of iterations for RegulaFalsi
8309 :
8310 11 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum);
8311 :
8312 11 : int WaterInletNode = DesupHtr.WaterInletNode;
8313 11 : int WaterOutletNode = DesupHtr.WaterOutletNode;
8314 :
8315 : // store first iteration tank temperature and desuperheater mode of operation
8316 11 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && DesupHtr.FirstTimeThroughFlag) {
8317 : // Save conditions from end of previous system timestep
8318 : // Every iteration that does not advance time should reset to these values
8319 6 : this->SavedTankTemp = this->TankTemp;
8320 6 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
8321 6 : DesupHtr.SaveMode = DesupHtr.Mode;
8322 6 : DesupHtr.FirstTimeThroughFlag = false;
8323 : }
8324 :
8325 5 : else if (!FirstHVACIteration) {
8326 4 : DesupHtr.FirstTimeThroughFlag = true;
8327 : }
8328 :
8329 : // initialize variables before invoking any RETURN statement
8330 11 : this->SourceMassFlowRate = 0.0;
8331 : // reset tank inlet temp from previous time step
8332 11 : this->SourceInletTemp = this->SavedSourceOutletTemp;
8333 11 : DesupHtr.DesuperheaterPLR = 0.0;
8334 :
8335 11 : state.dataLoopNodes->Node(WaterInletNode).MassFlowRate = 0.0;
8336 11 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = 0.0;
8337 11 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SavedSourceOutletTemp;
8338 :
8339 11 : DesupHtr.DesuperheaterPLR = 0.0;
8340 11 : DesupHtr.OnCycParaFuelRate = 0.0;
8341 11 : DesupHtr.OnCycParaFuelEnergy = 0.0;
8342 11 : DesupHtr.OffCycParaFuelRate = 0.0;
8343 11 : DesupHtr.OffCycParaFuelEnergy = 0.0;
8344 11 : DesupHtr.HEffFTempOutput = 0.0;
8345 11 : DesupHtr.HeaterRate = 0.0;
8346 11 : DesupHtr.HeaterEnergy = 0.0;
8347 11 : DesupHtr.PumpPower = 0.0;
8348 11 : DesupHtr.PumpEnergy = 0.0;
8349 :
8350 : // simulate only the water heater tank if the desuperheater coil is scheduled off
8351 11 : Real64 AvailSchedule = DesupHtr.availSched->getCurrentVal();
8352 11 : if (AvailSchedule == 0.0) {
8353 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8354 0 : this->CalcWaterThermalTank(state);
8355 1 : return;
8356 : }
8357 :
8358 : // simulate only the water heater tank if the lowest temperature available from the desuperheater coil
8359 : // is less than water inlet temperature if the reclaim source is a refrigeration condenser
8360 11 : if (DesupHtr.ValidSourceType) {
8361 11 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8362 11 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8363 0 : if (state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailTemperature <= this->SourceInletTemp) {
8364 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8365 0 : this->CalcWaterThermalTank(state);
8366 0 : ShowRecurringWarningErrorAtEnd(state,
8367 0 : "WaterHeating:Desuperheater " + DesupHtr.Name +
8368 : " - Waste heat source temperature was too low to be useful.",
8369 0 : DesupHtr.InsuffTemperatureWarn);
8370 0 : return;
8371 : } // Temp too low
8372 : } // desuperheater source is condenser_refrigeration
8373 : } // validsourcetype
8374 :
8375 11 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad;
8376 11 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8377 :
8378 : // check that water heater tank cut-in temp is greater than desuperheater cut-in temp
8379 11 : Real64 desupHtrSetPointTemp = DesupHtr.SetPointTemp;
8380 11 : Real64 DeadBandTempDiff = DesupHtr.DeadBandTempDiff;
8381 11 : if ((desupHtrSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
8382 0 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
8383 0 : Real64 MinTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8384 0 : ++DesupHtr.SetPointError;
8385 0 : if (DesupHtr.SetPointError < 5) {
8386 0 : ShowWarningError(state,
8387 0 : format("{} \"{}\": Water heater tank set point temperature is greater than or equal to the cut-in temperature of "
8388 : "the desuperheater. Desuperheater will be disabled.",
8389 0 : DesupHtr.Type,
8390 0 : DesupHtr.Name));
8391 0 : ShowContinueErrorTimeStamp(state, format(" ...Desuperheater cut-in temperature = {:.2R}", MinTemp));
8392 : } else {
8393 0 : ShowRecurringWarningErrorAtEnd(state,
8394 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8395 : "\": Water heater tank set point temperature is greater than or equal to the cut-in "
8396 : "temperature of the desuperheater. Desuperheater will be disabled warning continues...",
8397 0 : DesupHtr.SetPointErrIndex1,
8398 : MinTemp,
8399 : MinTemp);
8400 : }
8401 : }
8402 :
8403 : // Simulate tank if desuperheater unavailable for water heating
8404 0 : this->CalcWaterThermalTank(state);
8405 0 : return;
8406 : }
8407 :
8408 11 : Real64 Effic = DesupHtr.HeatReclaimRecoveryEff;
8409 :
8410 11 : state.dataLoopNodes->Node(WaterInletNode).Temp = this->SavedSourceOutletTemp;
8411 11 : DesupHtr.Mode = DesupHtr.SaveMode;
8412 :
8413 : Real64 HEffFTemp;
8414 11 : if (DesupHtr.HEffFTemp > 0) {
8415 0 : HEffFTemp = max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, this->SavedTankTemp, state.dataEnvrn->OutDryBulbTemp));
8416 : } else {
8417 11 : HEffFTemp = 1.0;
8418 : }
8419 :
8420 : // set limits on heat recovery efficiency
8421 11 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8422 0 : if ((HEffFTemp * Effic) > 0.9) HEffFTemp = 0.9 / Effic;
8423 : } else { // max is 0.3 for all other sources
8424 11 : if ((HEffFTemp * Effic) > 0.3) HEffFTemp = 0.3 / Effic;
8425 : } // setting limits on heat recovery efficiency
8426 :
8427 : // Access the appropriate structure to find the average heating capacity of the desuperheater heating coil
8428 11 : Real64 AverageWasteHeat = 0.0;
8429 11 : if (DesupHtr.ValidSourceType) {
8430 11 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8431 11 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8432 : // Refrigeration systems are solved outside the time step iteration, so the
8433 : // appropriate decrement for other waste heat applications is handled differently
8434 0 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).AvailCapacity -
8435 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8436 0 : DesupHtr.DXSysPLR = 1.0;
8437 11 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8438 0 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailCapacity -
8439 0 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8440 0 : DesupHtr.DXSysPLR = 1.0;
8441 11 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8442 6 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8443 4 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8444 7 : AverageWasteHeat = state.dataHeatBal->HeatReclaimDXCoil(SourceID).AvailCapacity -
8445 7 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8446 7 : DesupHtr.DXSysPLR = state.dataDXCoils->DXCoil(SourceID).PartLoadRatio;
8447 4 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling ||
8448 4 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpVSEQ) {
8449 2 : AverageWasteHeat = state.dataHeatBal->HeatReclaimVS_Coil(SourceID).AvailCapacity -
8450 2 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8451 2 : DesupHtr.DXSysPLR = state.dataVariableSpeedCoils->VarSpeedCoil(SourceID).PartLoadRatio;
8452 2 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8453 2 : AverageWasteHeat = state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).AvailCapacity -
8454 2 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8455 2 : DesupHtr.DXSysPLR = state.dataWaterToAirHeatPumpSimple->SimpleWatertoAirHP(SourceID).PartLoadRatio;
8456 0 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8457 0 : AverageWasteHeat =
8458 0 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.AvailCapacity -
8459 0 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.HVACDesuperheaterReclaimedHeatTotal;
8460 0 : DesupHtr.DXSysPLR = state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].partLoadRatioReport;
8461 : }
8462 : } else {
8463 0 : AverageWasteHeat = 0.0;
8464 : }
8465 :
8466 : // simulate only water heater tank if reclaim heating source is off
8467 11 : if (DesupHtr.DXSysPLR == 0.0) {
8468 1 : this->CalcWaterThermalTank(state);
8469 1 : return;
8470 : }
8471 :
8472 : // If the set point is higher than the maximum water temp, reset both the set point and the dead band temperature difference
8473 10 : if (desupHtrSetPointTemp > DesupHtr.MaxInletWaterTemp) {
8474 0 : Real64 CutInTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8475 0 : desupHtrSetPointTemp = DesupHtr.MaxInletWaterTemp;
8476 0 : DeadBandTempDiff = max(0.0, (desupHtrSetPointTemp - CutInTemp));
8477 : }
8478 :
8479 : Real64 Acc; // Accuracy of result from RegulaFalsi
8480 10 : if (DesupHtr.TankTypeNum == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8481 1 : Acc = 0.001;
8482 : } else {
8483 9 : Acc = 0.00001;
8484 : }
8485 :
8486 : // set the water-side mass flow rate
8487 10 : Real64 CpWater = Psychrometrics::CPHW(state.dataLoopNodes->Node(WaterInletNode).Temp);
8488 10 : Real64 MdotWater = DesupHtr.OperatingWaterFlowRate * Psychrometrics::RhoH2O(state.dataLoopNodes->Node(WaterInletNode).Temp);
8489 10 : Real64 QHeatRate = 0.0;
8490 10 : if (state.dataLoopNodes->Node(WaterInletNode).Temp <= DesupHtr.MaxInletWaterTemp + Acc) {
8491 8 : QHeatRate = ((AverageWasteHeat * Effic * HEffFTemp) / DesupHtr.DXSysPLR) + (DesupHtr.PumpElecPower * DesupHtr.PumpFracToWater);
8492 : }
8493 :
8494 : // change to tanktypenum using parameters?
8495 10 : Real64 partLoadRatio = 0.0;
8496 : Real64 NewTankTemp;
8497 : {
8498 10 : DataPlant::PlantEquipmentType const TankType = DesupHtr.TankTypeNum;
8499 :
8500 10 : if (TankType == DataPlant::PlantEquipmentType::WtrHeaterMixed || TankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8501 :
8502 10 : DesupHtr.SaveWHMode = this->Mode;
8503 10 : Real64 PreTankAvgTemp = this->TankTempAvg;
8504 10 : Real64 NewTankAvgTemp = 0.0; // Initialization
8505 10 : int max_count = 200;
8506 10 : int count = 0;
8507 10 : bool firstThrough = true;
8508 10 : switch (DesupHtr.Mode) {
8509 8 : case TankOperatingMode::Heating:
8510 : // Calculate until consistency of desuperheater and tank source side energy transfer achieved
8511 79 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > HVAC::SmallTempDiff || firstThrough) && count < max_count) {
8512 71 : count++;
8513 71 : firstThrough = false;
8514 71 : PreTankAvgTemp = this->TankTempAvg;
8515 71 : partLoadRatio = DesupHtr.DXSysPLR;
8516 71 : if (MdotWater > 0.0) {
8517 71 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8518 : } else {
8519 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8520 : }
8521 :
8522 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8523 71 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8524 :
8525 : // set the source mass flow rate for the tank
8526 71 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8527 :
8528 71 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8529 71 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8530 71 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8531 71 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8532 71 : this->CalcWaterThermalTank(state);
8533 71 : Real64 NewTankTemp = this->TankTemp;
8534 :
8535 71 : if (NewTankTemp > desupHtrSetPointTemp) {
8536 : // Only revert to floating mode if the tank temperature is higher than the cut out temperature
8537 12 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8538 12 : DesupHtr.Mode = TankOperatingMode::Floating;
8539 : }
8540 : int SolFla;
8541 12 : std::string IterNum;
8542 169 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8543 169 : this->Mode = DesupHtr.SaveWHMode;
8544 169 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8545 169 : this->CalcWaterThermalTank(state);
8546 169 : Real64 NewTankTemp = this->TankTemp;
8547 169 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8548 169 : return PLRResidualWaterThermalTank;
8549 12 : };
8550 12 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8551 12 : if (SolFla == -1) {
8552 0 : IterNum = fmt::to_string(MaxIte);
8553 0 : if (!state.dataGlobal->WarmupFlag) {
8554 0 : ++DesupHtr.IterLimitExceededNum1;
8555 0 : if (DesupHtr.IterLimitExceededNum1 == 1) {
8556 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8557 0 : ShowContinueError(state,
8558 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8559 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8560 : IterNum,
8561 : partLoadRatio));
8562 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8563 : } else {
8564 0 : ShowRecurringWarningErrorAtEnd(state,
8565 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8566 : "\": Iteration limit exceeded in heating mode warning continues. "
8567 : "Part-load ratio statistics follow.",
8568 0 : DesupHtr.IterLimitErrIndex1,
8569 : partLoadRatio,
8570 : partLoadRatio);
8571 : }
8572 : }
8573 12 : } else if (SolFla == -2) {
8574 0 : partLoadRatio =
8575 0 : max(0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8576 0 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8577 0 : this->CalcWaterThermalTank(state);
8578 0 : if (!state.dataGlobal->WarmupFlag) {
8579 0 : ++DesupHtr.RegulaFalsiFailedNum1;
8580 0 : if (DesupHtr.RegulaFalsiFailedNum1 == 1) {
8581 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8582 0 : ShowContinueError(state,
8583 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to 1 "
8584 : "exceeded. Part-load ratio used = {:.3R}",
8585 : partLoadRatio));
8586 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8587 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8588 : } else {
8589 0 : ShowRecurringWarningErrorAtEnd(state,
8590 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8591 : "\": Part-load ratio calculation failed in heating mode warning "
8592 : "continues. Part-load ratio statistics follow.",
8593 0 : DesupHtr.RegulaFalsiFailedIndex1,
8594 : partLoadRatio,
8595 : partLoadRatio);
8596 : }
8597 : }
8598 : }
8599 12 : } else {
8600 59 : partLoadRatio = DesupHtr.DXSysPLR;
8601 : }
8602 71 : NewTankAvgTemp = this->TankTempAvg;
8603 : }
8604 8 : break;
8605 2 : case TankOperatingMode::Floating:
8606 2 : if (MdotWater > 0.0) {
8607 2 : state.dataLoopNodes->Node(WaterOutletNode).Temp =
8608 2 : state.dataLoopNodes->Node(WaterInletNode).Temp + QHeatRate / (MdotWater * CpWater);
8609 : } else {
8610 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = state.dataLoopNodes->Node(WaterInletNode).Temp;
8611 : }
8612 : // check tank temperature by setting source inlet mass flow rate to zero
8613 2 : partLoadRatio = 0.0;
8614 :
8615 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8616 2 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8617 :
8618 : // check tank temperature by setting source inlet mass flow rate to zero
8619 2 : this->SourceMassFlowRate = 0.0;
8620 :
8621 : // disable the tank heater to find PLR of the HPWH
8622 2 : this->MaxCapacity = 0.0;
8623 2 : this->MinCapacity = 0.0;
8624 2 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8625 2 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8626 2 : this->CalcWaterThermalTank(state);
8627 2 : NewTankTemp = this->TankTemp;
8628 :
8629 2 : if (NewTankTemp <= (desupHtrSetPointTemp - DeadBandTempDiff)) {
8630 0 : this->Mode = DesupHtr.SaveWHMode;
8631 0 : if ((this->SavedTankTemp - NewTankTemp) != 0.0) {
8632 0 : partLoadRatio =
8633 0 : min(DesupHtr.DXSysPLR,
8634 0 : max(0.0, ((desupHtrSetPointTemp - DeadBandTempDiff) - NewTankTemp) / (this->SavedTankTemp - NewTankTemp)));
8635 : } else {
8636 0 : partLoadRatio = DesupHtr.DXSysPLR;
8637 : }
8638 0 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > HVAC::SmallTempDiff || firstThrough) && count < max_count) {
8639 0 : count++;
8640 0 : firstThrough = false;
8641 0 : PreTankAvgTemp = this->TankTempAvg;
8642 0 : DesupHtr.Mode = TankOperatingMode::Heating;
8643 0 : if (MdotWater > 0.0) {
8644 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8645 : } else {
8646 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8647 : }
8648 :
8649 : // set the full load outlet temperature on the water heater source inlet node
8650 0 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8651 :
8652 : // set the source mass flow rate for the tank and enable backup heating element
8653 0 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8654 0 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8655 0 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8656 0 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8657 0 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8658 0 : this->CalcWaterThermalTank(state);
8659 0 : NewTankTemp = this->TankTemp;
8660 :
8661 0 : if (NewTankTemp > desupHtrSetPointTemp) {
8662 : // Only revert to floating mode if the tank temperature is higher than the cut-out temperature
8663 0 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8664 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8665 : }
8666 : int SolFla;
8667 0 : std::string IterNum;
8668 0 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8669 0 : this->Mode = DesupHtr.SaveWHMode;
8670 0 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8671 0 : this->CalcWaterThermalTank(state);
8672 0 : Real64 NewTankTemp = this->TankTemp;
8673 0 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8674 0 : return PLRResidualWaterThermalTank;
8675 0 : };
8676 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8677 0 : if (SolFla == -1) {
8678 0 : IterNum = fmt::to_string(MaxIte);
8679 0 : if (!state.dataGlobal->WarmupFlag) {
8680 0 : ++DesupHtr.IterLimitExceededNum2;
8681 0 : if (DesupHtr.IterLimitExceededNum2 == 1) {
8682 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8683 0 : ShowContinueError(state,
8684 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8685 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8686 : IterNum,
8687 : partLoadRatio));
8688 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8689 : } else {
8690 0 : ShowRecurringWarningErrorAtEnd(state,
8691 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8692 : "\": Iteration limit exceeded in float mode warning continues. "
8693 : "Part-load ratio statistics follow.",
8694 0 : DesupHtr.IterLimitErrIndex2,
8695 : partLoadRatio,
8696 : partLoadRatio);
8697 : }
8698 : }
8699 0 : } else if (SolFla == -2) {
8700 0 : partLoadRatio = max(
8701 0 : 0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8702 0 : if (!state.dataGlobal->WarmupFlag) {
8703 0 : ++DesupHtr.RegulaFalsiFailedNum2;
8704 0 : if (DesupHtr.RegulaFalsiFailedNum2 == 1) {
8705 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8706 0 : ShowContinueError(state,
8707 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to "
8708 : "1 exceeded. Part-load ratio used = {:.3R}",
8709 : partLoadRatio));
8710 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8711 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8712 : } else {
8713 0 : ShowRecurringWarningErrorAtEnd(
8714 : state,
8715 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8716 : "\": Part-load ratio calculation failed in float mode warning "
8717 : "continues. Part-load ratio statistics follow.",
8718 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).RegulaFalsiFailedIndex2,
8719 : partLoadRatio,
8720 : partLoadRatio);
8721 : }
8722 : }
8723 : }
8724 0 : }
8725 0 : NewTankAvgTemp = this->TankTempAvg;
8726 : }
8727 : } else {
8728 2 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8729 2 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8730 : }
8731 2 : break;
8732 0 : default:
8733 0 : break;
8734 : }
8735 :
8736 : // should never get here, case is checked in GetWaterThermalTankInput
8737 10 : } else {
8738 0 : ShowFatalError(state,
8739 0 : format("Coil:WaterHeating:Desuperheater = {}: invalid water heater tank type and name entered = {}, {}",
8740 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).Name,
8741 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankType,
8742 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankName));
8743 : }
8744 : }
8745 :
8746 10 : if (QHeatRate == 0) partLoadRatio = 0.0;
8747 :
8748 10 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = MdotWater * partLoadRatio;
8749 10 : DesupHtr.HEffFTempOutput = HEffFTemp;
8750 10 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8751 10 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8752 :
8753 10 : if (partLoadRatio == 0) {
8754 3 : this->SourceInletTemp = this->SourceOutletTemp;
8755 3 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8756 3 : DesupHtr.HEffFTempOutput = 0.0;
8757 3 : DesupHtr.HeaterRate = 0.0;
8758 : }
8759 :
8760 10 : DesupHtr.HeaterEnergy = DesupHtr.HeaterRate * state.dataHVACGlobal->TimeStepSysSec;
8761 10 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8762 10 : DesupHtr.OnCycParaFuelRate = DesupHtr.OnCycParaLoad * partLoadRatio;
8763 10 : DesupHtr.OnCycParaFuelEnergy = DesupHtr.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8764 10 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad * (1 - partLoadRatio);
8765 10 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8766 10 : DesupHtr.PumpPower = DesupHtr.PumpElecPower * (partLoadRatio);
8767 10 : DesupHtr.PumpEnergy = DesupHtr.PumpPower * state.dataHVACGlobal->TimeStepSysSec;
8768 :
8769 : // Update used waste heat (just in case multiple users of waste heat use same source)
8770 10 : if (DesupHtr.ValidSourceType) {
8771 10 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8772 10 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8773 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8774 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8775 0 : for (auto const &num : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8776 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8777 10 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8778 0 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8779 0 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8780 0 : for (auto const &num : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8781 0 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8782 10 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8783 5 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8784 3 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8785 7 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8786 7 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8787 16 : for (auto const &num : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8788 9 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8789 10 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling ||
8790 3 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpVSEQ) {
8791 2 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8792 2 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8793 4 : for (auto const &num : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8794 2 : state.dataHeatBal->HeatReclaimVS_Coil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8795 3 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8796 1 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8797 1 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8798 2 : for (auto const &num : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8799 1 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8800 0 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8801 0 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat(
8802 0 : DesuperheaterNum) = DesupHtr.HeaterRate;
8803 0 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal =
8804 : 0.0;
8805 0 : for (auto const &num :
8806 0 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat)
8807 0 : state.dataCoilCoolingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum]
8808 0 : .reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8809 : }
8810 : }
8811 : }
8812 :
8813 8 : void WaterThermalTankData::CalcHeatPumpWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8814 : {
8815 :
8816 : // SUBROUTINE INFORMATION:
8817 : // AUTHOR Richard Raustad
8818 : // DATE WRITTEN March 2005
8819 : // MODIFIED B. Griffith, Jan 2012 for stratified tank
8820 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
8821 : // RE-ENGINEERED na
8822 :
8823 : // PURPOSE OF THIS SUBROUTINE:
8824 : // Simulates a heat pump water heater
8825 :
8826 : // METHODOLOGY EMPLOYED:
8827 : // Simulate the water heater tank, DX coil, and fan to meet the water heating requirements.
8828 :
8829 8 : int constexpr MaxIte(500); // maximum number of iterations
8830 8 : Real64 constexpr Acc(0.001); // Accuracy of result from RegulaFalsi
8831 :
8832 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8833 : Real64 MdotWater; // mass flow rate of condenser water, kg/s
8834 8 : IntegratedHeatPump::IHPOperationMode IHPMode(IntegratedHeatPump::IHPOperationMode::Idle); // IHP working mode
8835 :
8836 : // References to objects used in this function
8837 8 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8838 :
8839 : // initialize local variables
8840 8 : int AvailSchedule = HeatPump.availSched->getCurrentVal();
8841 8 : int HPAirInletNode = HeatPump.HeatPumpAirInletNode;
8842 8 : int HPAirOutletNode = HeatPump.HeatPumpAirOutletNode;
8843 8 : int OutdoorAirNode = HeatPump.OutsideAirNode;
8844 8 : int ExhaustAirNode = HeatPump.ExhaustAirNode;
8845 8 : int HPWaterInletNode = HeatPump.CondWaterInletNode;
8846 8 : int HPWaterOutletNode = HeatPump.CondWaterOutletNode;
8847 8 : int InletAirMixerNode = HeatPump.InletAirMixerNode;
8848 8 : int OutletAirSplitterNode = HeatPump.OutletAirSplitterNode;
8849 8 : int DXCoilAirInletNode = HeatPump.DXCoilAirInletNode;
8850 8 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
8851 8 : HVAC::CompressorOp compressorOp = HVAC::CompressorOp::Off; // DX compressor operation; 1=on, 0=off
8852 8 : HeatPump.OnCycParaFuelRate = 0.0;
8853 8 : HeatPump.OnCycParaFuelEnergy = 0.0;
8854 8 : HeatPump.OffCycParaFuelRate = 0.0;
8855 8 : HeatPump.OffCycParaFuelEnergy = 0.0;
8856 8 : state.dataLoopNodes->Node(HPWaterOutletNode) = state.dataLoopNodes->Node(HPWaterInletNode);
8857 8 : int MaxSpeedNum = HeatPump.NumofSpeed; // speed number of variable speed HPWH coil
8858 :
8859 : // assign set point temperature (cut-out) and dead band temp diff (cut-in = cut-out minus dead band temp diff)
8860 8 : Real64 HPSetPointTemp = HeatPump.SetPointTemp;
8861 8 : Real64 DeadBandTempDiff = HeatPump.DeadBandTempDiff;
8862 8 : Real64 RhoWater = Psychrometrics::RhoH2O(HPSetPointTemp); // initialize
8863 :
8864 : // store first iteration tank temperature and HP mode of operation
8865 : // this code can be called more than once with FirstHVACIteration = .TRUE., use FirstTimeThroughFlag to control save
8866 8 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && HeatPump.FirstTimeThroughFlag) {
8867 2 : this->SavedTankTemp = this->TankTemp;
8868 2 : HeatPump.SaveMode = HeatPump.Mode;
8869 2 : HeatPump.SaveWHMode = this->Mode;
8870 2 : HeatPump.FirstTimeThroughFlag = false;
8871 : }
8872 :
8873 8 : if (!FirstHVACIteration) HeatPump.FirstTimeThroughFlag = true;
8874 :
8875 : // check if HPWH is off for some reason and simulate HPWH air- and water-side mass flow rates of 0
8876 : // simulate only water heater tank if HP compressor is scheduled off
8877 : // simulate only water heater tank if HP compressor cut-out temperature is lower than the tank's cut-in temperature
8878 : // simulate only water heater tank if HP inlet air temperature is below minimum temperature for HP compressor operation
8879 : // if the tank maximum temperature limit is less than the HPWH set point temp, disable HPWH
8880 8 : if (AvailSchedule == 0.0 || (HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp ||
8881 6 : state.dataHVACGlobal->HPWHInletDBTemp < HeatPump.MinAirTempForHPOperation ||
8882 6 : state.dataHVACGlobal->HPWHInletDBTemp > HeatPump.MaxAirTempForHPOperation || HPSetPointTemp >= this->TankTempLimit ||
8883 6 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime && this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed &&
8884 16 : this->SavedMode == TankOperatingMode::Heating) ||
8885 6 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime &&
8886 1 : this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified && (this->SavedHeaterOn1 || this->SavedHeaterOn2))) {
8887 : // revert to float mode any time HPWH compressor is OFF
8888 2 : HeatPump.Mode = TankOperatingMode::Floating;
8889 2 : if (InletAirMixerNode > 0) {
8890 0 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
8891 : }
8892 : // pass node info and simulate crankcase heater
8893 2 : if (MaxSpeedNum > 0) {
8894 0 : int VSCoilNum = HeatPump.DXCoilNum;
8895 :
8896 0 : if (HeatPump.bIsIHP) {
8897 0 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(VSCoilNum).SCWHCoilIndex;
8898 : }
8899 : // set the SCWH mode
8900 0 : Real64 SpeedRatio = 1.0; // speed ratio for interpolating between two speed levels
8901 0 : int SpeedNum = 1;
8902 0 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
8903 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
8904 :
8905 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
8906 0 : if (HeatPump.bIsIHP)
8907 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8908 : "",
8909 : VSCoilNum,
8910 : HVAC::FanOp::Cycling,
8911 : HVAC::CompressorOp::On,
8912 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8913 : SpeedNum,
8914 : SpeedRatio,
8915 : 0.0,
8916 : 0.0,
8917 : 1.0);
8918 : else
8919 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8920 : HeatPump.DXCoilName,
8921 : VSCoilNum,
8922 : HVAC::FanOp::Cycling,
8923 : HVAC::CompressorOp::On,
8924 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8925 : SpeedNum,
8926 : SpeedRatio,
8927 : 0.0,
8928 : 0.0,
8929 : 1.0);
8930 : } else {
8931 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
8932 0 : if (HeatPump.bIsIHP)
8933 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8934 : "",
8935 : VSCoilNum,
8936 : HVAC::FanOp::Cycling,
8937 : HVAC::CompressorOp::On,
8938 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8939 : SpeedNum,
8940 : SpeedRatio,
8941 : 0.0,
8942 : 0.0,
8943 : 1.0);
8944 : else
8945 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8946 : HeatPump.DXCoilName,
8947 : VSCoilNum,
8948 : HVAC::FanOp::Cycling,
8949 : HVAC::CompressorOp::On,
8950 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8951 : SpeedNum,
8952 : SpeedRatio,
8953 : 0.0,
8954 : 0.0,
8955 : 1.0);
8956 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
8957 : }
8958 :
8959 : // set the DWH mode
8960 0 : if (HeatPump.bIsIHP) {
8961 0 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
8962 :
8963 0 : if (VSCoilNum > 0) // if DWH coil exists
8964 : {
8965 0 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
8966 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
8967 :
8968 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
8969 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8970 : "",
8971 : VSCoilNum,
8972 : HVAC::FanOp::Cycling,
8973 : HVAC::CompressorOp::On,
8974 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8975 : SpeedNum,
8976 : SpeedRatio,
8977 : 0.0,
8978 : 0.0,
8979 : 1.0);
8980 : } else {
8981 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
8982 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
8983 : "",
8984 : VSCoilNum,
8985 : HVAC::FanOp::Cycling,
8986 : HVAC::CompressorOp::On,
8987 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
8988 : SpeedNum,
8989 : SpeedRatio,
8990 : 0.0,
8991 : 0.0,
8992 : 1.0);
8993 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
8994 : }
8995 : }
8996 : }
8997 :
8998 : } else {
8999 2 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9000 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9001 :
9002 0 : DXCoils::SimDXCoil(state,
9003 : HeatPump.DXCoilName,
9004 : compressorOp,
9005 : FirstHVACIteration,
9006 0 : HeatPump.DXCoilNum,
9007 : HVAC::FanOp::Cycling,
9008 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9009 : } else {
9010 4 : DXCoils::SimDXCoil(state,
9011 : HeatPump.DXCoilName,
9012 : compressorOp,
9013 : FirstHVACIteration,
9014 2 : HeatPump.DXCoilNum,
9015 : HVAC::FanOp::Cycling,
9016 2 : state.dataWaterThermalTanks->hpPartLoadRatio);
9017 2 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9018 : }
9019 : }
9020 :
9021 2 : if (OutletAirSplitterNode > 0) {
9022 0 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
9023 : }
9024 :
9025 : // Simulate tank if HP compressor unavailable for water heating
9026 2 : this->CalcWaterThermalTank(state);
9027 :
9028 : // If HPWH compressor is available and unit is off for another reason, off-cycle parasitics are calculated
9029 2 : if (AvailSchedule != 0) {
9030 2 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
9031 2 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
9032 : }
9033 :
9034 : // Warn if HPWH compressor cut-in temperature is less than the water heater tank's set point temp
9035 2 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
9036 2 : if ((HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
9037 2 : Real64 HPMinTemp = HPSetPointTemp - DeadBandTempDiff;
9038 2 : const std::string HPMinTempChar = fmt::to_string(HPMinTemp);
9039 2 : ++HeatPump.HPSetPointError;
9040 : // add logic for warmup, DataGlobals::KickOffSimulation and doing sizing here
9041 2 : if (HeatPump.HPSetPointError == 1) {
9042 4 : ShowWarningError(state,
9043 4 : format("{} \"{}: Water heater tank set point temperature is greater than or equal to the cut-in temperature of "
9044 : "the heat pump water heater. Heat Pump will be disabled and simulation continues.",
9045 2 : HeatPump.Type,
9046 2 : HeatPump.Name));
9047 2 : ShowContinueErrorTimeStamp(state, format(" ...Heat Pump cut-in temperature={}", HPMinTempChar));
9048 : } else {
9049 0 : ShowRecurringWarningErrorAtEnd(state,
9050 0 : HeatPump.Type + " \"" + HeatPump.Name +
9051 : ": Water heater tank set point temperature is greater than or equal to the cut-in "
9052 : "temperature of the heat pump water heater. Heat Pump will be disabled error continues...",
9053 0 : HeatPump.HPSetPointErrIndex1,
9054 : HPMinTemp,
9055 : HPMinTemp);
9056 : }
9057 2 : }
9058 : }
9059 2 : return;
9060 : }
9061 6 : Real64 savedTankTemp = this->SavedTankTemp;
9062 6 : HeatPump.Mode = HeatPump.SaveMode;
9063 :
9064 6 : RhoWater = Psychrometrics::RhoH2O(savedTankTemp); // update water density using tank temp
9065 :
9066 : // set the heat pump air- and water-side mass flow rate
9067 6 : MdotWater = HeatPump.OperatingWaterFlowRate * Psychrometrics::RhoH2O(savedTankTemp);
9068 :
9069 : // Select mode of operation (float mode or heat mode) from last iteration.
9070 : // Determine if heating will occur this iteration and get an estimate of the PLR
9071 6 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9072 : // HPWH was heating last iteration and will continue to heat until the set point is reached
9073 3 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9074 3 : if (savedTankTemp > HPSetPointTemp) { // tank set point temp may have been reduced since last iteration and float mode may be needed
9075 0 : HeatPump.Mode = TankOperatingMode::Floating;
9076 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9077 : // check to see if HP needs to operate
9078 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9079 0 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9080 0 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9081 0 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9082 0 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9083 0 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9084 0 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9085 : }
9086 0 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9087 0 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9088 :
9089 : // Check tank temperature by setting source inlet mass flow rate to zero.
9090 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9091 :
9092 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9093 0 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9094 :
9095 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9096 0 : this->MaxCapacity = 0.0;
9097 0 : this->MinCapacity = 0.0;
9098 0 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9099 0 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9100 0 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9101 0 : this->CalcWaterThermalTank(state);
9102 0 : this->SourceEffectiveness = SourceEffectivenessBackup;
9103 0 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9104 :
9105 : // Reset the tank's internal heating element capacity.
9106 0 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9107 0 : this->MinCapacity = HeatPump.BackupElementCapacity;
9108 :
9109 : // Check to see if the tank drifts below set point if no heating happens.
9110 0 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9111 :
9112 : // HPWH is now in heating mode
9113 0 : HeatPump.Mode = TankOperatingMode::Heating;
9114 :
9115 : // Reset the water heater's mode (call above may have changed modes)
9116 0 : this->Mode = HeatPump.SaveWHMode;
9117 :
9118 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9119 : }
9120 : } else { // or use side nodes may meet set point without need for heat pump compressor operation
9121 : // check to see if HP needs to operate
9122 3 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9123 3 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9124 3 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9125 0 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9126 0 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9127 0 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9128 : }
9129 : // Check tank temperature by setting source inlet mass flow rate to zero.
9130 3 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9131 3 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9132 :
9133 3 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9134 :
9135 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9136 3 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9137 :
9138 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9139 3 : this->MaxCapacity = 0.0;
9140 3 : this->MinCapacity = 0.0;
9141 3 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9142 3 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9143 3 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9144 3 : this->CalcWaterThermalTank(state);
9145 3 : this->SourceEffectiveness = SourceEffectivenessBackup;
9146 3 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9147 :
9148 : // Reset the tank's internal heating element capacity.
9149 3 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9150 3 : this->MinCapacity = HeatPump.BackupElementCapacity;
9151 :
9152 : // Check to see if the tank meets set point if no heating happens.
9153 3 : if (NewTankTemp > HPSetPointTemp) {
9154 :
9155 : // HPWH is now in floating mode
9156 1 : HeatPump.Mode = TankOperatingMode::Floating;
9157 :
9158 : } else {
9159 :
9160 : // HPWH remains in heating mode
9161 2 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9162 : }
9163 :
9164 : // Reset the water heater's mode (call above may have changed modes)
9165 3 : this->Mode = HeatPump.SaveWHMode;
9166 : }
9167 : } else {
9168 3 : assert(HeatPump.Mode == TankOperatingMode::Floating);
9169 : // HPWH was floating last iteration and will continue to float until the cut-in temperature is reached
9170 :
9171 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9172 3 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9173 2 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9174 2 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9175 1 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9176 1 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9177 1 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9178 : }
9179 3 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9180 3 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9181 :
9182 : // Check tank temperature by setting source inlet mass flow rate to zero.
9183 3 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9184 :
9185 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9186 3 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9187 :
9188 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9189 3 : this->MaxCapacity = 0.0;
9190 3 : this->MinCapacity = 0.0;
9191 3 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9192 3 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9193 3 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9194 3 : this->CalcWaterThermalTank(state);
9195 3 : this->SourceEffectiveness = SourceEffectivenessBackup;
9196 3 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9197 :
9198 : // Reset the tank's internal heating element capacity.
9199 3 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9200 3 : this->MinCapacity = HeatPump.BackupElementCapacity;
9201 :
9202 : // Check to see if the tank drifts below set point if no heating happens.
9203 3 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9204 :
9205 : // HPWH is now in heating mode
9206 1 : HeatPump.Mode = TankOperatingMode::Heating;
9207 :
9208 : // Reset the water heater's mode (call above may have changed modes)
9209 1 : this->Mode = HeatPump.SaveWHMode;
9210 :
9211 1 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9212 : }
9213 : }
9214 :
9215 6 : if (HeatPump.bIsIHP) // mark the water heating call, if existing
9216 : {
9217 0 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9218 0 : if (HeatPump.Mode == TankOperatingMode::Heating)
9219 0 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = true;
9220 : else
9221 0 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = false;
9222 : }
9223 : }
9224 :
9225 : // If the HPWH was in heating mode during the last DataGlobals::TimeStep or if it was determined that
9226 : // heating would be needed during this DataGlobals::TimeStep to maintain setpoint, do the heating calculation.
9227 6 : int SpeedNum = 0;
9228 6 : Real64 SpeedRatio = 0.0;
9229 6 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9230 :
9231 : // set up air flow on DX coil inlet node
9232 3 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate =
9233 3 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9234 :
9235 : // set the condenser inlet node mass flow rate prior to calling the DXCoils::CalcHPWHDXCoil
9236 3 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9237 3 : this->SourceMassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9238 :
9239 : // Do the coil and tank calculations at full PLR to see if it overshoots setpoint.
9240 3 : bool bIterSpeed = false;
9241 3 : if (MaxSpeedNum > 0) { // lowest speed of VS HPWH coil
9242 0 : SpeedRatio = 1.0;
9243 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9244 0 : bIterSpeed = true; // prepare for iterating between speed levels
9245 0 : SpeedNum = 1;
9246 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9247 :
9248 0 : if (HeatPump.bIsIHP) {
9249 0 : bIterSpeed = false; // don't iterate speed unless match conditions below
9250 0 : IHPMode = IntegratedHeatPump::GetCurWorkMode(state, HeatPump.DXCoilNum);
9251 :
9252 0 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9253 : int VSCoilNum;
9254 0 : if (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) {
9255 0 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
9256 0 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode =
9257 : IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg;
9258 : } else {
9259 0 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).SCWHCoilIndex;
9260 0 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IntegratedHeatPump::IHPOperationMode::SCWHMatchWH;
9261 : }
9262 :
9263 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9264 :
9265 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9266 : "",
9267 : VSCoilNum,
9268 : HVAC::FanOp::Cycling,
9269 : HVAC::CompressorOp::On,
9270 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9271 : SpeedNum,
9272 : SpeedRatio,
9273 : 0.0,
9274 : 0.0,
9275 : 1.0);
9276 :
9277 0 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IHPMode;
9278 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9279 : } else {
9280 0 : SpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9281 :
9282 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9283 0 : IntegratedHeatPump::SimIHP(state,
9284 : HeatPump.DXCoilName,
9285 0 : HeatPump.DXCoilNum,
9286 : HVAC::FanOp::Cycling,
9287 : HVAC::CompressorOp::On,
9288 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9289 : SpeedNum,
9290 : SpeedRatio,
9291 : 0.0,
9292 : 0.0,
9293 : true,
9294 : false,
9295 0 : 1.0);
9296 :
9297 0 : if ((IntegratedHeatPump::IHPOperationMode::SCWHMatchWH == IHPMode) ||
9298 : (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode)) {
9299 0 : bIterSpeed = true;
9300 : } else {
9301 0 : this->SourceMassFlowRate = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TankSourceWaterMassFlowRate;
9302 0 : MdotWater = this->SourceMassFlowRate;
9303 : }
9304 :
9305 0 : if (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) // turn off heater element
9306 : {
9307 0 : this->MaxCapacity = 0.0;
9308 0 : this->MinCapacity = 0.0;
9309 : }
9310 : }
9311 : } else {
9312 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9313 : HeatPump.DXCoilName,
9314 0 : HeatPump.DXCoilNum,
9315 : HVAC::FanOp::Cycling,
9316 : HVAC::CompressorOp::On,
9317 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9318 : SpeedNum,
9319 : SpeedRatio,
9320 : 0.0,
9321 : 0.0,
9322 : 1.0);
9323 : }
9324 :
9325 0 : this->CalcWaterThermalTank(state);
9326 : } else {
9327 3 : this->ConvergeSingleSpeedHPWHCoilAndTank(state, state.dataWaterThermalTanks->hpPartLoadRatio);
9328 : }
9329 :
9330 3 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9331 3 : Real64 LowSpeedTankTemp = NewTankTemp;
9332 3 : Real64 HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9333 :
9334 3 : if (NewTankTemp > HPSetPointTemp) {
9335 2 : HeatPump.Mode = TankOperatingMode::Floating;
9336 2 : TankOperatingMode tmpMode = HeatPump.SaveWHMode;
9337 6 : auto f = [&state, this, HPSetPointTemp, tmpMode, MdotWater](Real64 const HPPartLoadRatio) {
9338 6 : return this->PLRResidualHPWH(state, HPPartLoadRatio, HPSetPointTemp, tmpMode, MdotWater);
9339 2 : };
9340 2 : Real64 zeroResidual = 1.0;
9341 2 : if (MaxSpeedNum > 0) {
9342 : // square the solving, and avoid warning
9343 : // due to very small capacity at lowest speed of VSHPWH coil
9344 0 : if (bIterSpeed)
9345 0 : zeroResidual = this->PLRResidualHPWH(state, 0.0, HPSetPointTemp, tmpMode, MdotWater);
9346 : else
9347 0 : zeroResidual = -1.0;
9348 : }
9349 :
9350 2 : if (zeroResidual > 0.0) { // then iteration
9351 : int SolFla;
9352 2 : General::SolveRoot(state, Acc, MaxIte, SolFla, state.dataWaterThermalTanks->hpPartLoadRatio, f, 0.0, 1.0);
9353 2 : if (SolFla == -1) {
9354 0 : std::string IterNum;
9355 0 : IterNum = fmt::to_string(MaxIte);
9356 0 : if (!state.dataGlobal->WarmupFlag) {
9357 0 : ++HeatPump.IterLimitExceededNum2;
9358 0 : if (HeatPump.IterLimitExceededNum2 == 1) {
9359 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9360 0 : ShowContinueError(state,
9361 0 : format("Iteration limit exceeded calculating heat pump water heater compressor part-load ratio, "
9362 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
9363 : IterNum,
9364 0 : state.dataWaterThermalTanks->hpPartLoadRatio));
9365 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9366 : } else {
9367 0 : ShowRecurringWarningErrorAtEnd(
9368 : state,
9369 0 : HeatPump.Type + " \"" + HeatPump.Name +
9370 : "\": Iteration limit exceeded in float mode warning continues. Part-load ratio statistics follow.",
9371 0 : HeatPump.IterLimitErrIndex2,
9372 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9373 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9374 : }
9375 : }
9376 2 : } else if (SolFla == -2) {
9377 0 : state.dataWaterThermalTanks->hpPartLoadRatio =
9378 0 : max(0.0, min(1.0, (HPSetPointTemp - savedTankTemp) / (NewTankTemp - savedTankTemp)));
9379 0 : if (!state.dataGlobal->WarmupFlag) {
9380 0 : ++HeatPump.RegulaFalsiFailedNum2;
9381 0 : if (HeatPump.RegulaFalsiFailedNum2 == 1) {
9382 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9383 0 : ShowContinueError(state,
9384 0 : format("Heat pump water heater compressor part-load ratio calculation failed: PLR limits of 0 to 1 "
9385 : "exceeded. Part-load ratio used = {:.3R}",
9386 0 : state.dataWaterThermalTanks->hpPartLoadRatio));
9387 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9388 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9389 : } else {
9390 0 : ShowRecurringWarningErrorAtEnd(
9391 : state,
9392 0 : HeatPump.Type + " \"" + HeatPump.Name +
9393 : "\": Part-load ratio calculation failed in float mode warning continues. Part-load ratio statistics follow.",
9394 0 : HeatPump.RegulaFalsiFailedIndex2,
9395 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9396 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9397 : }
9398 : }
9399 : }
9400 : } else {
9401 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9402 : }
9403 :
9404 : // Re-calculate the HPWH Coil to get the correct heat transfer rate.
9405 2 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9406 2 : if (MaxSpeedNum > 0) {
9407 0 : SpeedRatio = 1.0;
9408 0 : SpeedNum = 1;
9409 :
9410 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9411 :
9412 0 : if (HeatPump.bIsIHP) {
9413 0 : if (bIterSpeed) {
9414 0 : IntegratedHeatPump::SimIHP(state,
9415 : HeatPump.DXCoilName,
9416 0 : HeatPump.DXCoilNum,
9417 : HVAC::FanOp::Cycling,
9418 : HVAC::CompressorOp::On,
9419 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9420 : SpeedNum,
9421 : SpeedRatio,
9422 : 0.0,
9423 : 0.0,
9424 : true,
9425 : false,
9426 0 : 1.0);
9427 : }
9428 : } else {
9429 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9430 : HeatPump.DXCoilName,
9431 0 : HeatPump.DXCoilNum,
9432 : HVAC::FanOp::Cycling,
9433 : HVAC::CompressorOp::On,
9434 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9435 : SpeedNum,
9436 : SpeedRatio,
9437 : 0.0,
9438 : 0.0,
9439 : 1.0);
9440 : }
9441 :
9442 : } else {
9443 2 : DXCoils::CalcHPWHDXCoil(state, HeatPump.DXCoilNum, state.dataWaterThermalTanks->hpPartLoadRatio);
9444 : }
9445 1 : } else if (bIterSpeed) {
9446 0 : for (int loopIter = 1; loopIter <= 4; ++loopIter) {
9447 0 : HeatPump.Mode = TankOperatingMode::Heating; // modHeatMode is important for system convergence
9448 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9449 0 : SpeedRatio = 1.0;
9450 0 : int LowSpeedNum = 2;
9451 0 : if (HeatPump.bIsIHP) {
9452 0 : LowSpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9453 0 : MaxSpeedNum = IntegratedHeatPump::GetMaxSpeedNumIHP(state, HeatPump.DXCoilNum);
9454 : }
9455 :
9456 0 : for (int i = LowSpeedNum; i <= MaxSpeedNum; ++i) {
9457 0 : SpeedNum = i;
9458 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9459 0 : if (HeatPump.bIsIHP) {
9460 0 : IntegratedHeatPump::SimIHP(state,
9461 : HeatPump.DXCoilName,
9462 0 : HeatPump.DXCoilNum,
9463 : HVAC::FanOp::Cycling,
9464 : HVAC::CompressorOp::On,
9465 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9466 : SpeedNum,
9467 : SpeedRatio,
9468 : 0.0,
9469 : 0.0,
9470 : true,
9471 : false,
9472 0 : 1.0);
9473 : } else {
9474 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9475 : HeatPump.DXCoilName,
9476 0 : HeatPump.DXCoilNum,
9477 : HVAC::FanOp::Cycling,
9478 : HVAC::CompressorOp::On,
9479 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9480 : SpeedNum,
9481 : SpeedRatio,
9482 : 0.0,
9483 : 0.0,
9484 : 1.0);
9485 : }
9486 :
9487 : // HPWH condenser water temperature difference
9488 0 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9489 :
9490 : // move the full load outlet temperature rate to the water heater structure variables
9491 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has
9492 : // been called)
9493 0 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9494 : // this CALL does not update node temps, must use WaterThermalTank variables
9495 : // select tank type
9496 0 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9497 0 : this->CalcWaterThermalTankMixed(state);
9498 0 : NewTankTemp = this->TankTemp;
9499 0 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9500 0 : this->CalcWaterThermalTankStratified(state);
9501 0 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9502 : }
9503 :
9504 0 : if (NewTankTemp > HPSetPointTemp) {
9505 0 : SpeedNum = i;
9506 0 : break;
9507 : } else {
9508 0 : LowSpeedTankTemp = NewTankTemp;
9509 : }
9510 : }
9511 :
9512 0 : if (NewTankTemp > HPSetPointTemp) {
9513 : int SolFla;
9514 0 : std::string IterNum;
9515 0 : auto f = [&state, this, SpeedNum, HPWaterInletNode, HPWaterOutletNode, RhoWater, HPSetPointTemp, &HeatPump, FirstHVACIteration](
9516 : Real64 const SpeedRatio) {
9517 0 : return this->PLRResidualIterSpeed(state,
9518 : SpeedRatio,
9519 0 : this->HeatPumpNum,
9520 : SpeedNum,
9521 : HPWaterInletNode,
9522 : HPWaterOutletNode,
9523 : RhoWater,
9524 : HPSetPointTemp,
9525 0 : HeatPump.SaveWHMode,
9526 0 : FirstHVACIteration);
9527 0 : };
9528 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, SpeedRatio, f, 1.0e-10, 1.0);
9529 :
9530 0 : if (SolFla == -1) {
9531 0 : IterNum = fmt::to_string(MaxIte);
9532 0 : if (!state.dataGlobal->WarmupFlag) {
9533 0 : ++HeatPump.IterLimitExceededNum1;
9534 0 : if (HeatPump.IterLimitExceededNum1 == 1) {
9535 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9536 0 : ShowContinueError(state,
9537 0 : format("Iteration limit exceeded calculating heat pump water heater speed speed ratio ratio, "
9538 : "maximum iterations = {}. speed ratio returned = {:.3R}",
9539 : IterNum,
9540 : SpeedRatio));
9541 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9542 : } else {
9543 0 : ShowRecurringWarningErrorAtEnd(
9544 : state,
9545 0 : HeatPump.Type + " \"" + HeatPump.Name +
9546 : "\": Iteration limit exceeded in heating mode warning continues. speed ratio statistics follow.",
9547 0 : HeatPump.IterLimitErrIndex1,
9548 : SpeedRatio,
9549 : SpeedRatio);
9550 : }
9551 : }
9552 0 : } else if (SolFla == -2) {
9553 0 : SpeedRatio = max(0.0, min(1.0, (HPSetPointTemp - LowSpeedTankTemp) / (NewTankTemp - LowSpeedTankTemp)));
9554 0 : if (!state.dataGlobal->WarmupFlag) {
9555 0 : ++HeatPump.RegulaFalsiFailedNum1;
9556 0 : if (HeatPump.RegulaFalsiFailedNum1 == 1) {
9557 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9558 0 : ShowContinueError(state,
9559 0 : format("Heat pump water heater speed ratio calculation failed: speed ratio limits of 0 to 1 "
9560 : "exceeded. speed ratio used = {:.3R}",
9561 : SpeedRatio));
9562 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9563 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9564 : } else {
9565 0 : ShowRecurringWarningErrorAtEnd(
9566 : state,
9567 0 : HeatPump.Type + " \"" + HeatPump.Name +
9568 : "\": Speed ratio calculation failed in heating mode warning continues. Speed ratio statistics follow.",
9569 0 : HeatPump.RegulaFalsiFailedIndex1,
9570 : SpeedRatio,
9571 : SpeedRatio);
9572 : }
9573 : }
9574 : }
9575 0 : } else {
9576 0 : SpeedNum = MaxSpeedNum;
9577 0 : SpeedRatio = 1.0;
9578 : }
9579 :
9580 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9581 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9582 :
9583 0 : if (HeatPump.bIsIHP) {
9584 0 : IntegratedHeatPump::SimIHP(state,
9585 : HeatPump.DXCoilName,
9586 0 : HeatPump.DXCoilNum,
9587 : HVAC::FanOp::Cycling,
9588 : HVAC::CompressorOp::On,
9589 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9590 : SpeedNum,
9591 : SpeedRatio,
9592 : 0.0,
9593 : 0.0,
9594 : true,
9595 : false,
9596 0 : 1.0);
9597 : } else {
9598 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9599 : HeatPump.DXCoilName,
9600 0 : HeatPump.DXCoilNum,
9601 : HVAC::FanOp::Cycling,
9602 : HVAC::CompressorOp::On,
9603 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9604 : SpeedNum,
9605 : SpeedRatio,
9606 : 0.0,
9607 : 0.0,
9608 : 1.0);
9609 : }
9610 :
9611 : // HPWH condenser water temperature difference
9612 0 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9613 :
9614 : // move the full load outlet temperature rate to the water heater structure variables
9615 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been
9616 : // called)
9617 0 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9618 : // this CALL does not update node temps, must use WaterThermalTank variables
9619 : // select tank type
9620 0 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9621 0 : this->CalcWaterThermalTankMixed(state);
9622 0 : NewTankTemp = this->TankTemp;
9623 0 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9624 0 : this->CalcWaterThermalTankStratified(state);
9625 0 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9626 : }
9627 : // update inlet temp
9628 0 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9629 0 : if (std::abs(state.dataLoopNodes->Node(HPWaterInletNode).Temp - HPWHCondInletNodeLast) < HVAC::SmallTempDiff) break;
9630 0 : HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9631 : }
9632 :
9633 : } else {
9634 : // Set the PLR to 1 if we're not going to reach setpoint during this DataGlobals::TimeStep.
9635 1 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9636 : }
9637 : }
9638 :
9639 6 : if (HeatPump.bIsIHP) {
9640 0 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9641 0 : IntegratedHeatPump::ClearCoils(state, HeatPump.DXCoilNum); // clear node info when checking the heating load
9642 : }
9643 : }
9644 :
9645 : // set air-side mass flow rate for final calculation
9646 6 : if (InletAirMixerNode > 0) {
9647 0 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate =
9648 0 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9649 0 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio *
9650 0 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule);
9651 0 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9652 0 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * state.dataWaterThermalTanks->mixerInletAirSchedule;
9653 : // IF HPWH is off, pass zone node conditions through HPWH air-side
9654 0 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0)
9655 0 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
9656 : } else {
9657 6 : if (OutdoorAirNode == 0) {
9658 1 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate =
9659 1 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9660 : } else {
9661 5 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9662 5 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9663 : }
9664 : }
9665 6 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0) this->SourceInletTemp = this->SourceOutletTemp;
9666 :
9667 : // set water-side mass flow rate for final calculation
9668 6 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9669 :
9670 6 : if (MaxSpeedNum > 0) {
9671 :
9672 : // it is important to use mdotAir to reset the notes, otherwise, could fail to converge
9673 0 : if (InletAirMixerNode > 0) {
9674 0 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9675 0 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9676 : } else {
9677 0 : if (OutdoorAirNode == 0) {
9678 0 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9679 0 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9680 : } else {
9681 0 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9682 0 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9683 : }
9684 : }
9685 :
9686 : // set the max mass flow rate for outdoor fans
9687 0 : state.dataLoopNodes->Node(HeatPump.FanOutletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9688 :
9689 0 : if (HeatPump.bIsIHP) {
9690 : // pass node information using resulting PLR
9691 0 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9692 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9693 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9694 :
9695 0 : IntegratedHeatPump::SimIHP(state,
9696 : HeatPump.DXCoilName,
9697 0 : HeatPump.DXCoilNum,
9698 : HVAC::FanOp::Cycling,
9699 : compressorOp,
9700 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9701 : SpeedNum,
9702 : SpeedRatio,
9703 : 0.0,
9704 : 0.0,
9705 : true,
9706 : false,
9707 0 : 1.0);
9708 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9709 :
9710 0 : IntegratedHeatPump::SimIHP(state,
9711 : HeatPump.DXCoilName,
9712 0 : HeatPump.DXCoilNum,
9713 : HVAC::FanOp::Cycling,
9714 : compressorOp,
9715 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9716 : SpeedNum,
9717 : SpeedRatio,
9718 : 0.0,
9719 : 0.0,
9720 : true,
9721 : false,
9722 0 : 1.0);
9723 : } else {
9724 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9725 0 : IntegratedHeatPump::SimIHP(state,
9726 : HeatPump.DXCoilName,
9727 0 : HeatPump.DXCoilNum,
9728 : HVAC::FanOp::Cycling,
9729 : compressorOp,
9730 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9731 : SpeedNum,
9732 : SpeedRatio,
9733 : 0.0,
9734 : 0.0,
9735 : true,
9736 : false,
9737 0 : 1.0);
9738 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9739 :
9740 0 : IntegratedHeatPump::SimIHP(state,
9741 : HeatPump.DXCoilName,
9742 0 : HeatPump.DXCoilNum,
9743 : HVAC::FanOp::Cycling,
9744 : compressorOp,
9745 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9746 : SpeedNum,
9747 : SpeedRatio,
9748 : 0.0,
9749 : 0.0,
9750 : true,
9751 : false,
9752 0 : 1.0);
9753 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9754 : }
9755 : } else {
9756 : // pass node information using resulting PLR
9757 0 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9758 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9759 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9760 :
9761 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9762 : HeatPump.DXCoilName,
9763 0 : HeatPump.DXCoilNum,
9764 : HVAC::FanOp::Cycling,
9765 : compressorOp,
9766 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9767 : SpeedNum,
9768 : SpeedRatio,
9769 : 0.0,
9770 : 0.0,
9771 : 1.0);
9772 :
9773 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9774 :
9775 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9776 : HeatPump.DXCoilName,
9777 0 : HeatPump.DXCoilNum,
9778 : HVAC::FanOp::Cycling,
9779 : compressorOp,
9780 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9781 : SpeedNum,
9782 : SpeedRatio,
9783 : 0.0,
9784 : 0.0,
9785 : 1.0);
9786 : } else {
9787 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9788 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9789 : HeatPump.DXCoilName,
9790 0 : HeatPump.DXCoilNum,
9791 : HVAC::FanOp::Cycling,
9792 : compressorOp,
9793 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9794 : SpeedNum,
9795 : SpeedRatio,
9796 : 0.0,
9797 : 0.0,
9798 : 1.0);
9799 :
9800 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9801 :
9802 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9803 : HeatPump.DXCoilName,
9804 0 : HeatPump.DXCoilNum,
9805 : HVAC::FanOp::Cycling,
9806 : compressorOp,
9807 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9808 : SpeedNum,
9809 : SpeedRatio,
9810 : 0.0,
9811 : 0.0,
9812 : 1.0);
9813 :
9814 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9815 : }
9816 : }
9817 : } else { // single speed
9818 :
9819 : // pass node information using resulting PLR
9820 6 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9821 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9822 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9823 :
9824 0 : DXCoils::SimDXCoil(state,
9825 : HeatPump.DXCoilName,
9826 : compressorOp,
9827 : FirstHVACIteration,
9828 0 : HeatPump.DXCoilNum,
9829 : HVAC::FanOp::Cycling,
9830 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9831 :
9832 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9833 :
9834 0 : DXCoils::SimDXCoil(state,
9835 : HeatPump.DXCoilName,
9836 : compressorOp,
9837 : FirstHVACIteration,
9838 0 : HeatPump.DXCoilNum,
9839 : HVAC::FanOp::Cycling,
9840 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9841 : } else {
9842 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9843 12 : DXCoils::SimDXCoil(state,
9844 : HeatPump.DXCoilName,
9845 : compressorOp,
9846 : FirstHVACIteration,
9847 6 : HeatPump.DXCoilNum,
9848 : HVAC::FanOp::Cycling,
9849 6 : state.dataWaterThermalTanks->hpPartLoadRatio);
9850 :
9851 6 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9852 :
9853 12 : DXCoils::SimDXCoil(state,
9854 : HeatPump.DXCoilName,
9855 : compressorOp,
9856 : FirstHVACIteration,
9857 6 : HeatPump.DXCoilNum,
9858 : HVAC::FanOp::Cycling,
9859 6 : state.dataWaterThermalTanks->hpPartLoadRatio);
9860 :
9861 6 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9862 : }
9863 : }
9864 :
9865 : // Call the tank one more time with the final PLR
9866 6 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9867 5 : this->CalcWaterThermalTankMixed(state);
9868 1 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9869 1 : this->CalcWaterThermalTankStratified(state);
9870 : } else {
9871 0 : assert(0);
9872 : }
9873 :
9874 : // set HPWH outlet node equal to the outlet air splitter node conditions if outlet air splitter node exists
9875 6 : if (OutletAirSplitterNode > 0) {
9876 0 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
9877 0 : state.dataLoopNodes->Node(ExhaustAirNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
9878 : }
9879 :
9880 : // Check schedule to divert air-side cooling to outdoors.
9881 6 : if (HeatPump.outletAirSplitterSched != nullptr) {
9882 0 : Real64 OutletAirSplitterSch = HeatPump.outletAirSplitterSched->getCurrentVal();
9883 0 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate =
9884 0 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * (1.0 - OutletAirSplitterSch);
9885 0 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate =
9886 0 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * OutletAirSplitterSch;
9887 : }
9888 :
9889 6 : HeatPump.HeatingPLR = state.dataWaterThermalTanks->hpPartLoadRatio;
9890 6 : HeatPump.OnCycParaFuelRate = HeatPump.OnCycParaLoad * state.dataWaterThermalTanks->hpPartLoadRatio;
9891 6 : HeatPump.OnCycParaFuelEnergy = HeatPump.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
9892 6 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
9893 6 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
9894 6 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9895 5 : HeatPump.ControlTempAvg = this->TankTempAvg;
9896 5 : HeatPump.ControlTempFinal = this->TankTemp;
9897 1 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9898 1 : HeatPump.ControlTempAvg = this->FindStratifiedTankSensedTemp(state, true);
9899 1 : HeatPump.ControlTempFinal = this->FindStratifiedTankSensedTemp(state);
9900 : } else {
9901 0 : assert(0);
9902 : }
9903 :
9904 6 : switch (HeatPump.InletAirConfiguration) {
9905 :
9906 : // no sensible capacity to zone for outdoor and scheduled HPWH
9907 6 : case WTTAmbientTemp::OutsideAir:
9908 : case WTTAmbientTemp::Schedule: {
9909 6 : HeatPump.HPWaterHeaterSensibleCapacity = 0.0;
9910 6 : HeatPump.HPWaterHeaterLatentCapacity = 0.0;
9911 :
9912 : // calculate sensible capacity to zone for inlet air configuration equals Zone Only or Zone And Outdoor Air configurations
9913 6 : break;
9914 : }
9915 0 : default:
9916 :
9917 0 : Real64 CpAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(HPAirInletNode).HumRat);
9918 :
9919 : // add parasitics to zone heat balance if parasitic heat load is to zone otherwise neglect parasitics
9920 0 : if (HeatPump.ParasiticTempIndicator == WTTAmbientTemp::TempZone) {
9921 0 : HeatPump.HPWaterHeaterSensibleCapacity =
9922 0 : (state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
9923 0 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp)) +
9924 0 : HeatPump.OnCycParaFuelRate + HeatPump.OffCycParaFuelRate;
9925 : } else {
9926 0 : HeatPump.HPWaterHeaterSensibleCapacity =
9927 0 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
9928 0 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp);
9929 : }
9930 :
9931 0 : HeatPump.HPWaterHeaterLatentCapacity = state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate *
9932 0 : (state.dataLoopNodes->Node(HPAirOutletNode).HumRat - state.dataLoopNodes->Node(HPAirInletNode).HumRat);
9933 0 : break;
9934 : }
9935 : }
9936 :
9937 264 : void WaterThermalTankData::CalcWaterThermalTank(EnergyPlusData &state)
9938 : {
9939 264 : switch (this->WaterThermalTankType) {
9940 253 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
9941 253 : this->CalcWaterThermalTankMixed(state);
9942 253 : break;
9943 11 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
9944 11 : this->CalcWaterThermalTankStratified(state);
9945 11 : break;
9946 0 : default:
9947 0 : assert(false);
9948 : }
9949 264 : }
9950 :
9951 15 : Real64 WaterThermalTankData::GetHPWHSensedTankTemp(EnergyPlusData &state)
9952 : {
9953 15 : switch (this->WaterThermalTankType) {
9954 10 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
9955 10 : return this->TankTemp;
9956 : break;
9957 5 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
9958 5 : return this->FindStratifiedTankSensedTemp(state);
9959 : break;
9960 0 : default:
9961 0 : assert(false);
9962 : return 0.0; // silence compiler
9963 : }
9964 : }
9965 :
9966 3 : void WaterThermalTankData::ConvergeSingleSpeedHPWHCoilAndTank(EnergyPlusData &state, Real64 const partLoadRatio)
9967 : {
9968 3 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
9969 3 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
9970 :
9971 3 : Real64 PrevTankTemp = this->SourceOutletTemp;
9972 11 : for (int i = 1; i <= 10; ++i) {
9973 :
9974 11 : DXCoils::CalcHPWHDXCoil(state, HPWH.DXCoilNum, partLoadRatio);
9975 11 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWH.CondWaterOutletNode).Temp;
9976 :
9977 11 : this->CalcWaterThermalTank(state);
9978 11 : state.dataLoopNodes->Node(Coil.WaterInNode).Temp = this->SourceOutletTemp;
9979 :
9980 11 : if (std::abs(this->SourceOutletTemp - PrevTankTemp) < HVAC::SmallTempDiff) {
9981 3 : break;
9982 : }
9983 :
9984 8 : PrevTankTemp = this->SourceOutletTemp;
9985 : }
9986 3 : }
9987 :
9988 0 : void WaterThermalTankData::SetVSHPWHFlowRates(EnergyPlusData &state,
9989 : HeatPumpWaterHeaterData &HPWH, // heat pump coil
9990 : int const SpeedNum, // upper speed number
9991 : Real64 const SpeedRatio, // interpolation ration between upper and lower speed
9992 : Real64 const WaterDens, // tank water density
9993 : Real64 &MdotWater, // water flow rate
9994 : bool const FirstHVACIteration)
9995 : {
9996 : // FUNCTION INFORMATION:
9997 : // AUTHOR B.Shen, ORNL, 12/2014
9998 : // DATE WRITTEN May 2005
9999 : // MODIFIED
10000 : // RE-ENGINEERED
10001 :
10002 : // PURPOSE OF THIS FUNCTION:
10003 : // set water and air flow rates driven by the variable-speed HPWH coil
10004 : // calculate resultant HPWH coil output
10005 :
10006 0 : int SpeedLow = SpeedNum - 1;
10007 0 : if (SpeedLow < 1) SpeedLow = 1;
10008 :
10009 0 : int HPWaterInletNode = HPWH.CondWaterInletNode;
10010 0 : int DXCoilAirInletNode = HPWH.DXCoilAirInletNode;
10011 0 : if (HPWH.bIsIHP) {
10012 0 : HPWH.OperatingWaterFlowRate = IntegratedHeatPump::GetWaterVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio);
10013 0 : state.dataWaterThermalTanks->mdotAir = IntegratedHeatPump::GetAirMassFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10014 0 : HPWH.OperatingAirFlowRate = IntegratedHeatPump::GetAirVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10015 0 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10016 0 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10017 0 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10018 : } else {
10019 0 : HPWH.OperatingWaterFlowRate = HPWH.HPWHWaterVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHWaterVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10020 0 : HPWH.OperatingAirFlowRate = HPWH.HPWHAirVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10021 0 : state.dataWaterThermalTanks->mdotAir =
10022 0 : HPWH.HPWHAirMassFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirMassFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10023 : }
10024 :
10025 0 : MdotWater = HPWH.OperatingWaterFlowRate * WaterDens;
10026 0 : this->SourceMassFlowRate = MdotWater;
10027 :
10028 0 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10029 0 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater;
10030 0 : this->SourceMassFlowRate = MdotWater;
10031 :
10032 0 : if (HPWH.InletAirMixerNode > 0) {
10033 0 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10034 0 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10035 : } else {
10036 0 : if (HPWH.OutsideAirNode == 0) {
10037 0 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10038 0 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10039 : } else {
10040 0 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10041 0 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10042 : }
10043 : }
10044 :
10045 : // put fan component first, regardless placement, to calculate fan power
10046 0 : int FanInNode = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
10047 :
10048 0 : state.dataLoopNodes->Node(FanInNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10049 0 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10050 0 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10051 0 : if (HPWH.fanType != HVAC::FanType::SystemModel) {
10052 0 : state.dataFans->fans(HPWH.FanNum)->massFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10053 : } // system fan will use the inlet node max avail.
10054 :
10055 0 : state.dataFans->fans(HPWH.FanNum)->simulate(state, FirstHVACIteration, _, _);
10056 0 : }
10057 :
10058 0 : Real64 WaterThermalTankData::PLRResidualIterSpeed(EnergyPlusData &state,
10059 : Real64 const SpeedRatio, // speed ratio between two speed levels
10060 : int const HPNum,
10061 : int const SpeedNum,
10062 : int const HPWaterInletNode,
10063 : int const HPWaterOutletNode,
10064 : Real64 const RhoWater,
10065 : Real64 const desTankTemp,
10066 : TankOperatingMode const mode,
10067 : bool const FirstHVACIteration)
10068 : {
10069 : // FUNCTION INFORMATION:
10070 : // AUTHOR B.Shen, ORNL, 12/2014
10071 : // MODIFIED
10072 : // RE-ENGINEERED
10073 :
10074 : // PURPOSE OF THIS FUNCTION:
10075 : // Calculates residual function (desired tank temp - actual tank temp), when iterating speed ration between two speed levels
10076 : // HP water heater output depends on the speed ratio which is being varied to zero the residual.
10077 :
10078 : // METHODOLOGY EMPLOYED:
10079 : // Calls residuals to get tank temperature at the given speed ratio between a lower and an upper speed levels
10080 : // and calculates the residual as defined respectively for DataPlant::PlantEquipmentType::WtrHeaterMixed or
10081 : // DataPlant::PlantEquipmentType::WtrHeaterStratified
10082 :
10083 0 : this->Mode = mode;
10084 0 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
10085 0 : Real64 MdotWater = 0.0;
10086 :
10087 0 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
10088 :
10089 0 : this->SetVSHPWHFlowRates(state, HPWH, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
10090 :
10091 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
10092 0 : IntegratedHeatPump::SimIHP(state,
10093 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10094 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10095 : HVAC::FanOp::Cycling,
10096 : HVAC::CompressorOp::On,
10097 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
10098 : SpeedNum,
10099 : SpeedRatio,
10100 : 0.0,
10101 : 0.0,
10102 : true,
10103 : false,
10104 0 : 1.0);
10105 : } else {
10106 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
10107 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10108 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10109 : HVAC::FanOp::Cycling,
10110 : HVAC::CompressorOp::On,
10111 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
10112 : SpeedNum,
10113 : SpeedRatio,
10114 : 0.0,
10115 : 0.0,
10116 : 1.0);
10117 : }
10118 :
10119 : Real64 CondenserDeltaT;
10120 0 : CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
10121 :
10122 : // move the full load outlet temperature rate to the water heater structure variables
10123 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been called)
10124 0 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
10125 :
10126 : // this CALL does not update node temps, must use WaterThermalTank variables
10127 : // select tank type
10128 0 : Real64 NewTankTemp = 0.0;
10129 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10130 0 : this->CalcWaterThermalTankMixed(state);
10131 0 : NewTankTemp = this->TankTemp;
10132 0 : } else if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10133 0 : this->CalcWaterThermalTankStratified(state);
10134 0 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
10135 : }
10136 :
10137 0 : return desTankTemp - NewTankTemp;
10138 : }
10139 :
10140 6 : Real64 WaterThermalTankData::PLRResidualHPWH(
10141 : EnergyPlusData &state, Real64 const HPPartLoadRatio, Real64 const desTankTemp, TankOperatingMode const mode, Real64 const mDotWater)
10142 : {
10143 : // FUNCTION INFORMATION:
10144 : // AUTHOR B.Griffith, Richard Raustad
10145 : // DATE WRITTEN Jan 2012
10146 : // MODIFIED
10147 : // RE-ENGINEERED Noel Merket, Oct 2015
10148 :
10149 : // PURPOSE OF THIS FUNCTION:
10150 : // Calculates residual function (desired tank temp - actual tank temp)
10151 : // HP water heater output depends on the part load ratio which is being varied to zero the residual.
10152 :
10153 : // METHODOLOGY EMPLOYED:
10154 : // Calls with CalcWaterThermalTankMixed or CalcWaterThermalTankStratified to get tank temperature at the given part load ratio (source water
10155 : // mass flow rate) and calculates the residual as defined above
10156 :
10157 6 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
10158 6 : bool const isVariableSpeed = (HeatPump.NumofSpeed > 0);
10159 6 : this->Mode = mode;
10160 : // Apply the PLR
10161 6 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10162 : // For a mixed tank, the PLR is applied to the source mass flow rate.
10163 3 : this->SourceMassFlowRate = mDotWater * HPPartLoadRatio;
10164 3 : this->CalcWaterThermalTankMixed(state);
10165 : } else {
10166 3 : assert(this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified);
10167 : // For a stratified tank, the PLR is applied to the Coil.TotalHeatingEnergyRate
10168 : // whether that's a VarSpeedCoil or DXCoils::DXCoil.
10169 : // Here we create a pointer to the TotalHeatingEnergyRate for the appropriate coil type.
10170 : Real64 *CoilTotalHeatingEnergyRatePtr;
10171 3 : if (isVariableSpeed) {
10172 0 : if (HeatPump.bIsIHP)
10173 0 : CoilTotalHeatingEnergyRatePtr = &state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TotalWaterHeatingRate;
10174 : else
10175 0 : CoilTotalHeatingEnergyRatePtr = &state.dataVariableSpeedCoils->VarSpeedCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10176 : } else {
10177 3 : CoilTotalHeatingEnergyRatePtr = &state.dataDXCoils->DXCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10178 : }
10179 : // Copy the value of the total heating energy rate
10180 3 : Real64 const CoilTotalHeatingEnergyRateBackup = *CoilTotalHeatingEnergyRatePtr;
10181 : // Apply the PLR
10182 3 : *CoilTotalHeatingEnergyRatePtr *= HPPartLoadRatio;
10183 : // Tank Calculation
10184 3 : this->CalcWaterThermalTankStratified(state);
10185 : // Restore the original value
10186 3 : *CoilTotalHeatingEnergyRatePtr = CoilTotalHeatingEnergyRateBackup;
10187 : }
10188 6 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
10189 6 : return desTankTemp - NewTankTemp;
10190 : }
10191 :
10192 5 : bool WaterThermalTankData::SourceHeatNeed([[maybe_unused]] EnergyPlusData &state,
10193 : Real64 const OutletTemp,
10194 : Real64 const DeadBandTemp,
10195 : Real64 const SetPointTemp_loc)
10196 : {
10197 : // FUNCTION INFORMATION:
10198 : // AUTHOR Yueyue Zhou
10199 : // DATE WRITTEN May 2019
10200 : // MODIFIED na
10201 : // RE-ENGINEERED na
10202 :
10203 : // PURPOSE OF THIS FUNCTION:
10204 : // Determine by tank type, tank temperature and control mode if source side flow is needed
10205 :
10206 : // return value initialization
10207 5 : bool NeedsHeatOrCool = false;
10208 :
10209 5 : if (!this->IsChilledWaterTank) {
10210 5 : if (this->SourceSideControlMode == SourceSideControl::IndirectHeatPrimarySetpoint) {
10211 1 : if (OutletTemp < DeadBandTemp) {
10212 1 : NeedsHeatOrCool = true;
10213 0 : } else if ((OutletTemp >= DeadBandTemp) && (OutletTemp < SetPointTemp_loc)) {
10214 : // inside the deadband, use saved mode from water heater calcs
10215 0 : if (this->SavedMode == TankOperatingMode::Heating) {
10216 0 : NeedsHeatOrCool = true;
10217 0 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10218 0 : NeedsHeatOrCool = false;
10219 : }
10220 :
10221 0 : } else if (OutletTemp >= SetPointTemp_loc) {
10222 0 : NeedsHeatOrCool = false;
10223 : }
10224 4 : } else if (this->SourceSideControlMode == SourceSideControl::IndirectHeatAltSetpoint) {
10225 : // get alternate setpoint
10226 4 : Real64 const AltSetpointTemp = this->sourceSideAltSetpointSched->getCurrentVal();
10227 4 : Real64 const AltDeadBandTemp = AltSetpointTemp - this->DeadBandDeltaTemp;
10228 4 : if (OutletTemp < AltDeadBandTemp) {
10229 3 : NeedsHeatOrCool = true;
10230 1 : } else if ((OutletTemp >= AltDeadBandTemp) && (OutletTemp < AltSetpointTemp)) {
10231 : // inside the deadband, use saved mode from water heater calcs
10232 0 : if (this->SavedMode == TankOperatingMode::Heating) {
10233 0 : NeedsHeatOrCool = true;
10234 0 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10235 0 : NeedsHeatOrCool = false;
10236 : }
10237 :
10238 1 : } else if (OutletTemp >= AltSetpointTemp) {
10239 1 : NeedsHeatOrCool = false;
10240 : }
10241 0 : } else if (this->SourceSideControlMode == SourceSideControl::StorageTank) {
10242 0 : if (OutletTemp < this->TankTempLimit) {
10243 0 : NeedsHeatOrCool = true;
10244 : } else {
10245 0 : NeedsHeatOrCool = false;
10246 : }
10247 : }
10248 : } else { // is a chilled water tank so flip logic
10249 0 : if (OutletTemp > DeadBandTemp) {
10250 0 : NeedsHeatOrCool = true;
10251 0 : } else if ((OutletTemp <= DeadBandTemp) && (OutletTemp > SetPointTemp_loc)) {
10252 : // inside the deadband, use saved mode from water thermal tank calcs (modes only for mixed)
10253 0 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) {
10254 0 : if (this->SavedMode == TankOperatingMode::Cooling) {
10255 0 : NeedsHeatOrCool = true;
10256 0 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10257 0 : NeedsHeatOrCool = false;
10258 : }
10259 0 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
10260 0 : NeedsHeatOrCool = true;
10261 : }
10262 :
10263 0 : } else if (OutletTemp <= SetPointTemp_loc) {
10264 0 : NeedsHeatOrCool = false;
10265 : }
10266 : }
10267 5 : return NeedsHeatOrCool;
10268 : }
10269 :
10270 17 : Real64 WaterThermalTankData::PlantMassFlowRatesFunc(EnergyPlusData &state,
10271 : int const InNodeNum,
10272 : bool const FirstHVACIteration,
10273 : WaterHeaterSide const WaterThermalTankSide,
10274 : const DataPlant::LoopSideLocation PlantLoopSide,
10275 : [[maybe_unused]] bool const PlumbedInSeries,
10276 : DataBranchAirLoopPlant::ControlType const BranchControlType,
10277 : Real64 const OutletTemp,
10278 : Real64 const DeadBandTemp,
10279 : Real64 const SetPointTemp_loc)
10280 : {
10281 :
10282 : // FUNCTION INFORMATION:
10283 : // AUTHOR Brent Griffith
10284 : // DATE WRITTEN October 2007
10285 : // MODIFIED na
10286 : // RE-ENGINEERED na
10287 :
10288 : // PURPOSE OF THIS FUNCTION:
10289 : // collect routines for setting flow rates for Water heaters
10290 : // with plant connections.
10291 :
10292 : // determine current mode. there are three possible
10293 : // 1. passing thru what was given to inlet node
10294 : // 2. potentially making a flow request
10295 : // 3. throttling flow in response to Plant's restrictions (MassFlowRateMaxAvail)
10296 : // init default mode changed to Unassigned
10297 17 : FlowMode CurrentMode = FlowMode::Invalid; // default
10298 :
10299 17 : if (PlantLoopSide == DataPlant::LoopSideLocation::Invalid) {
10300 14 : CurrentMode = FlowMode::PassingFlowThru;
10301 3 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Supply) {
10302 : // If FlowLock is False (0), the tank sets the plant loop mdot
10303 : // If FlowLock is True (1), the new resolved plant loop mdot is used
10304 1 : if (this->UseCurrentFlowLock == DataPlant::FlowLock::Unlocked) {
10305 1 : CurrentMode = FlowMode::PassingFlowThru;
10306 1 : if ((this->UseSideLoadRequested > 0.0) && (WaterThermalTankSide == WaterHeaterSide::Use)) {
10307 0 : CurrentMode = FlowMode::MaybeRequestingFlow;
10308 : }
10309 : } else {
10310 0 : CurrentMode = FlowMode::PassingFlowThru;
10311 : }
10312 1 : if (WaterThermalTankSide == WaterHeaterSide::Source) {
10313 1 : CurrentMode = FlowMode::MaybeRequestingFlow;
10314 : }
10315 2 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Demand) {
10316 :
10317 : // 2. Might be Requesting Flow.
10318 2 : if (FirstHVACIteration) {
10319 1 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10320 0 : CurrentMode = FlowMode::PassingFlowThru;
10321 : } else {
10322 1 : CurrentMode = FlowMode::MaybeRequestingFlow;
10323 : }
10324 : } else {
10325 1 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10326 0 : CurrentMode = FlowMode::PassingFlowThru;
10327 : } else {
10328 1 : CurrentMode = FlowMode::ThrottlingFlow;
10329 : }
10330 : }
10331 : }
10332 :
10333 : // evaluate Availability schedule,
10334 17 : bool ScheduledAvail = true;
10335 17 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10336 6 : if (this->useSideAvailSched->getCurrentVal() == 0.0) {
10337 0 : ScheduledAvail = false;
10338 : }
10339 11 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10340 11 : if (this->sourceSideAvailSched->getCurrentVal() == 0.0) {
10341 0 : ScheduledAvail = false;
10342 : }
10343 : }
10344 :
10345 : // now act based on current mode
10346 17 : Real64 FlowResult = 0.0;
10347 17 : switch (CurrentMode) {
10348 :
10349 14 : case FlowMode::PassingFlowThru: {
10350 14 : if (!ScheduledAvail) {
10351 0 : FlowResult = 0.0;
10352 : } else {
10353 14 : FlowResult = state.dataLoopNodes->Node(InNodeNum).MassFlowRate;
10354 : }
10355 :
10356 14 : break;
10357 : }
10358 1 : case FlowMode::ThrottlingFlow: {
10359 : // first determine what mass flow would be if it is to requested
10360 1 : Real64 MassFlowRequest = 0.0;
10361 1 : if (!ScheduledAvail) {
10362 0 : MassFlowRequest = 0.0;
10363 : } else {
10364 1 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10365 0 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10366 1 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10367 1 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10368 : } else {
10369 0 : assert(false);
10370 : }
10371 : }
10372 :
10373 : // next determine if tank temperature is such that source side flow might be requested
10374 1 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10375 :
10376 1 : if (MassFlowRequest > 0.0) {
10377 1 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10378 0 : FlowResult = MassFlowRequest;
10379 1 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10380 1 : if (NeedsHeatOrCool) {
10381 1 : FlowResult = MassFlowRequest;
10382 : } else {
10383 0 : FlowResult = 0.0;
10384 : }
10385 : } else {
10386 0 : assert(false);
10387 : }
10388 : } else {
10389 0 : FlowResult = 0.0;
10390 : }
10391 :
10392 : // now throttle against MassFlowRateMaxAvail, MassFlowRateMinAvail, MassFlowRateMax, and MassFlowRateMin
10393 : // see notes about reverse dd compliance (specifically 5ZoneWaterSystems file)
10394 1 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMinAvail, FlowResult); // okay for compliance (reverse dd)
10395 1 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMin, FlowResult); // okay for compliance (reverse dd)
10396 1 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMaxAvail, FlowResult);
10397 : //=> following might take out of reverse dd compliance
10398 1 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMax, FlowResult);
10399 :
10400 1 : break;
10401 : }
10402 2 : case FlowMode::MaybeRequestingFlow: {
10403 :
10404 : // first determine what mass flow would be if it is to requested
10405 2 : Real64 MassFlowRequest = 0.0;
10406 2 : if (!ScheduledAvail) {
10407 0 : MassFlowRequest = 0.0;
10408 : } else {
10409 2 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10410 0 : if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested > 0.0)) {
10411 0 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10412 0 : } else if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested == 0.0)) {
10413 0 : MassFlowRequest = 0.0;
10414 : } else {
10415 0 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10416 : }
10417 :
10418 2 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10419 2 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10420 : }
10421 : }
10422 :
10423 2 : if (WaterThermalTankSide == WaterHeaterSide::Source) { // temperature dependent controls for indirect heating/cooling
10424 2 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10425 2 : if (MassFlowRequest > 0.0) {
10426 2 : if (NeedsHeatOrCool) {
10427 2 : FlowResult = MassFlowRequest;
10428 : } else {
10429 0 : FlowResult = 0.0;
10430 : }
10431 : } else {
10432 0 : FlowResult = 0.0;
10433 : }
10434 : } else { // end source side, begin use side
10435 0 : if (MassFlowRequest > 0.0) {
10436 0 : FlowResult = MassFlowRequest;
10437 : } else {
10438 0 : FlowResult = 0.0;
10439 : }
10440 : }
10441 2 : break;
10442 : }
10443 0 : default:
10444 0 : break;
10445 : }
10446 :
10447 17 : if (FlowResult < HVAC::VerySmallMassFlow) FlowResult = 0.0; // Catch underflow problems
10448 :
10449 17 : return FlowResult;
10450 : }
10451 :
10452 1 : void WaterThermalTankData::MinePlantStructForInfo(EnergyPlusData &state)
10453 : {
10454 :
10455 : // SUBROUTINE INFORMATION:
10456 : // AUTHOR Brent Griffith
10457 : // DATE WRITTEN October 2007
10458 : // MODIFIED na
10459 : // RE-ENGINEERED na
10460 :
10461 : // PURPOSE OF THIS SUBROUTINE:
10462 : // get information from plant loop data structure
10463 : // check what we can learn from plant structure against user inputs
10464 :
10465 1 : bool ErrorsFound = false;
10466 :
10467 1 : if (allocated(state.dataPlnt->PlantLoop) && this->UseSidePlantLoc.loopNum > 0) {
10468 :
10469 : // check plant structure for useful data.
10470 :
10471 0 : int PlantLoopNum = this->UseSidePlantLoc.loopNum;
10472 0 : DataPlant::LoopSideLocation LoopSideNum = this->UseSidePlantLoc.loopSideNum;
10473 :
10474 0 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
10475 0 : ShowSevereError(state,
10476 0 : format("Water heater = {} for autosizing Use side flow rate, did not find Sizing:Plant object {}",
10477 0 : this->Name,
10478 0 : state.dataPlnt->PlantLoop(PlantLoopNum).Name));
10479 0 : ErrorsFound = true;
10480 : }
10481 : // Is this wh Use side plumbed in series (default) or are there other branches in parallel?
10482 0 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.Exists) {
10483 0 : if (any_eq(state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.NodeNumOut,
10484 0 : this->UseInletNode)) { // this wh is on the splitter
10485 0 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.TotalOutletNodes > 1) {
10486 0 : this->UseSideSeries = false;
10487 : }
10488 : }
10489 : }
10490 : }
10491 :
10492 1 : if (allocated(state.dataPlnt->PlantLoop) && this->SrcSidePlantLoc.loopNum > 0) {
10493 : // was user's input correct for plant loop name?
10494 1 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0) && (this->DesuperheaterNum == 0) &&
10495 0 : (this->HeatPumpNum == 0)) {
10496 0 : ShowSevereError(state,
10497 0 : format("Water heater = {}for autosizing Source side flow rate, did not find Sizing:Plant object {}",
10498 0 : this->Name,
10499 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).Name));
10500 0 : ErrorsFound = true;
10501 : }
10502 : // Is this wh Source side plumbed in series (default) or are there other branches in parallel?
10503 1 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.Exists) {
10504 0 : if (any_eq(state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.NodeNumOut,
10505 0 : this->SourceInletNode)) { // this wh is on the splitter
10506 0 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.TotalOutletNodes >
10507 : 1) {
10508 0 : this->SourceSideSeries = false;
10509 : }
10510 : }
10511 : }
10512 : }
10513 :
10514 1 : if (ErrorsFound) {
10515 0 : ShowFatalError(state, "Preceding water heater input errors cause program termination");
10516 : }
10517 1 : }
10518 :
10519 1 : void WaterThermalTankData::SizeSupplySidePlantConnections(EnergyPlusData &state, const int loopNum)
10520 : {
10521 :
10522 : // SUBROUTINE INFORMATION:
10523 : // AUTHOR Brent Griffith
10524 : // DATE WRITTEN October 2007
10525 : // MODIFIED na
10526 : // RE-ENGINEERED na
10527 :
10528 : // PURPOSE OF THIS SUBROUTINE:
10529 : // This subroutine is for sizing water heater plant connection flow rates
10530 : // on the supply that have not been specified in the input.
10531 :
10532 : // METHODOLOGY EMPLOYED:
10533 : // This routine is called later in the simulation than the sizing routine for the demand side
10534 : // because the simulation needs to be further along before the needed data are available.
10535 : // For water heaters sides on Supply LoopSide, obtains hot water flow rate from the plant sizing array
10536 : // (adapted approach from boiler sizing routines)
10537 :
10538 : static constexpr std::string_view RoutineName("SizeSupplySidePlantConnections");
10539 :
10540 1 : auto &PlantSizData = state.dataSize->PlantSizData;
10541 :
10542 1 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
10543 1 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
10544 :
10545 1 : if ((this->UseInletNode > 0) && (loopNum == this->UseSidePlantLoc.loopNum)) {
10546 0 : if (this->UseDesignVolFlowRateWasAutoSized) {
10547 0 : int PltSizNum = this->UseSidePlantSizNum;
10548 0 : if (PltSizNum > 0) { // we have a Plant Sizing Object
10549 0 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10550 0 : if (PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
10551 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10552 0 : this->UseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10553 : } else {
10554 0 : tmpUseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10555 : }
10556 : } else {
10557 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10558 0 : this->UseDesignVolFlowRate = 0.0;
10559 : } else {
10560 0 : tmpUseDesignVolFlowRate = 0.0;
10561 : }
10562 : }
10563 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10564 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10565 : }
10566 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10567 0 : BaseSizer::reportSizerOutput(
10568 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10569 : }
10570 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10571 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10572 : } else {
10573 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
10574 : }
10575 :
10576 : Real64 rho =
10577 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10578 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10579 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10580 : } else {
10581 0 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
10582 : }
10583 : }
10584 : } else {
10585 : // do nothing
10586 : } // plant sizing object
10587 : } else {
10588 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10589 : Real64 rho;
10590 0 : if (this->UseSidePlantLoc.loopNum > 0) {
10591 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10592 : } else {
10593 0 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
10594 : }
10595 :
10596 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10597 :
10598 : } // autosizing needed.
10599 : } // connected to plant
10600 :
10601 1 : if ((this->SourceInletNode > 0) && (loopNum == this->SrcSidePlantLoc.loopNum)) {
10602 1 : if (this->SourceDesignVolFlowRateWasAutoSized) {
10603 0 : int PltSizNum = this->SourceSidePlantSizNum;
10604 0 : if (PltSizNum > 0) {
10605 0 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10606 0 : if (PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
10607 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10608 0 : this->SourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10609 : } else {
10610 0 : tmpSourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10611 : }
10612 : } else {
10613 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10614 0 : this->SourceDesignVolFlowRate = 0.0;
10615 : } else {
10616 0 : tmpSourceDesignVolFlowRate = 0.0;
10617 : }
10618 : }
10619 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10620 0 : BaseSizer::reportSizerOutput(
10621 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10622 : }
10623 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10624 0 : BaseSizer::reportSizerOutput(
10625 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10626 : }
10627 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10628 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10629 : } else {
10630 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
10631 : }
10632 : Real64 rho =
10633 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10634 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10635 0 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10636 : } else {
10637 0 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
10638 : }
10639 : } // plant loop allocation
10640 : } else {
10641 : // do nothing
10642 : } // plant sizing object
10643 : } else {
10644 1 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10645 1 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10646 : Real64 rho;
10647 1 : if (this->SrcSidePlantLoc.loopNum > 0) {
10648 1 : rho = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
10649 : } else {
10650 0 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
10651 : }
10652 1 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10653 : }
10654 : } // autosizing needed.
10655 : } // connected to plant
10656 1 : }
10657 :
10658 1 : void WaterThermalTankData::SizeTankForDemandSide(EnergyPlusData &state)
10659 : {
10660 :
10661 : // SUBROUTINE INFORMATION:
10662 : // AUTHOR Brent Griffith
10663 : // DATE WRITTEN February 2008
10664 : // MODIFIED na
10665 : // RE-ENGINEERED na
10666 :
10667 : // PURPOSE OF THIS SUBROUTINE:
10668 : // This subroutine is for sizing water heater tank volume and heater
10669 : // as best we can at this point in simulation. (prior to demand side
10670 : // sizing that needs volume).
10671 :
10672 : // METHODOLOGY EMPLOYED:
10673 : // depending on the sizing design mode...
10674 :
10675 : // REFERENCES:
10676 : // BA benchmark report for residential design mode
10677 :
10678 : // SUBROUTINE PARAMETER DEFINITIONS:
10679 : static constexpr std::string_view RoutineName("SizeTankForDemandSide");
10680 1 : Real64 constexpr GalTocubicMeters(0.0037854);
10681 1 : Real64 constexpr kBtuPerHrToWatts(293.1);
10682 :
10683 1 : Real64 Tstart = 14.44;
10684 1 : Real64 Tfinish = 57.22;
10685 :
10686 1 : Real64 tmpTankVolume = this->Volume;
10687 1 : Real64 tmpMaxCapacity = this->MaxCapacity;
10688 :
10689 1 : switch (this->Sizing.DesignMode) {
10690 :
10691 0 : case SizingMode::Invalid:
10692 : case SizingMode::PeakDraw: {
10693 :
10694 0 : break;
10695 : }
10696 0 : case SizingMode::ResidentialMin: {
10697 :
10698 : // assume can propagate rules for gas to other fuels.
10699 0 : bool FuelTypeIsLikeGas = false;
10700 0 : switch (this->FuelType) {
10701 0 : case Constant::eFuel::NaturalGas:
10702 : case Constant::eFuel::Diesel:
10703 : case Constant::eFuel::Gasoline:
10704 : case Constant::eFuel::Coal:
10705 : case Constant::eFuel::FuelOilNo1:
10706 : case Constant::eFuel::FuelOilNo2:
10707 : case Constant::eFuel::Propane:
10708 : case Constant::eFuel::OtherFuel1:
10709 : case Constant::eFuel::OtherFuel2:
10710 : case Constant::eFuel::DistrictHeatingWater:
10711 : case Constant::eFuel::DistrictHeatingSteam:
10712 0 : FuelTypeIsLikeGas = true;
10713 0 : break;
10714 0 : default: // FuelTypeIsLikeGas stays false
10715 0 : break;
10716 : }
10717 :
10718 0 : if (this->Sizing.NumberOfBedrooms == 1) {
10719 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10720 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
10721 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
10722 0 : } else if (FuelTypeIsLikeGas) {
10723 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
10724 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
10725 : }
10726 :
10727 0 : } else if (this->Sizing.NumberOfBedrooms == 2) {
10728 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10729 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10730 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10731 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
10732 0 : } else if (FuelTypeIsLikeGas) {
10733 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10734 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10735 : }
10736 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10737 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10738 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10739 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10740 0 : } else if (FuelTypeIsLikeGas) {
10741 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10742 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10743 : }
10744 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10745 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10746 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10747 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10748 0 : } else if (FuelTypeIsLikeGas) {
10749 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10750 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10751 : }
10752 : }
10753 0 : } else if (this->Sizing.NumberOfBedrooms == 3) {
10754 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10755 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10756 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10757 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10758 0 : } else if (FuelTypeIsLikeGas) {
10759 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10760 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10761 : }
10762 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10763 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10764 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10765 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10766 0 : } else if (FuelTypeIsLikeGas) {
10767 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10768 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10769 : }
10770 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10771 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10772 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10773 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10774 0 : } else if (FuelTypeIsLikeGas) {
10775 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10776 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10777 : }
10778 : }
10779 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
10780 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10781 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10782 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10783 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10784 0 : } else if (FuelTypeIsLikeGas) {
10785 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10786 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10787 : }
10788 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10789 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10790 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10791 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10792 0 : } else if (FuelTypeIsLikeGas) {
10793 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10794 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10795 : }
10796 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10797 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10798 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10799 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10800 0 : } else if (FuelTypeIsLikeGas) {
10801 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10802 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10803 : }
10804 : }
10805 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
10806 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10807 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10808 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10809 0 : } else if (FuelTypeIsLikeGas) {
10810 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10811 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
10812 : }
10813 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
10814 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10815 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10816 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10817 0 : } else if (FuelTypeIsLikeGas) {
10818 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10819 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
10820 : }
10821 : }
10822 :
10823 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10824 0 : this->Volume = tmpTankVolume;
10825 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10826 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
10827 : }
10828 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10829 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
10830 : }
10831 : }
10832 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10833 0 : this->MaxCapacity = tmpMaxCapacity;
10834 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10835 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
10836 : }
10837 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10838 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
10839 : }
10840 : }
10841 0 : break;
10842 : }
10843 0 : case SizingMode::PerPerson: {
10844 : // how to get number of people?
10845 :
10846 : // MJW TODO: this won't compile now: Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
10847 0 : Real64 SumPeopleAllZones = 0.0;
10848 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
10849 0 : SumPeopleAllZones += thisZone.TotOccupants;
10850 : }
10851 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
10852 :
10853 0 : if (this->MaxCapacityWasAutoSized) {
10854 : Real64 rho;
10855 : Real64 Cp;
10856 0 : if (this->UseSidePlantLoc.loopNum > 0) {
10857 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10858 0 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10859 : } else {
10860 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10861 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10862 : }
10863 :
10864 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) * rho *
10865 : Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
10866 : }
10867 :
10868 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10869 0 : this->Volume = tmpTankVolume;
10870 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10871 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
10872 : }
10873 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10874 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
10875 : }
10876 : }
10877 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10878 0 : this->MaxCapacity = tmpMaxCapacity;
10879 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10880 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
10881 : }
10882 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10883 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
10884 : }
10885 : }
10886 0 : break;
10887 : }
10888 0 : case SizingMode::PerFloorArea: {
10889 :
10890 : // MJW TODO: this won't compile now: Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
10891 0 : Real64 SumFloorAreaAllZones = 0.0;
10892 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
10893 0 : SumFloorAreaAllZones += thisZone.FloorArea;
10894 : }
10895 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
10896 0 : if (this->MaxCapacityWasAutoSized) {
10897 : Real64 rho;
10898 : Real64 Cp;
10899 0 : if (this->UseSidePlantLoc.loopNum > 0) {
10900 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10901 0 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10902 : } else {
10903 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10904 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10905 : }
10906 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) * rho *
10907 : Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
10908 : }
10909 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10910 0 : this->Volume = tmpTankVolume;
10911 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10912 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
10913 : }
10914 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10915 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
10916 : }
10917 : }
10918 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10919 0 : this->MaxCapacity = tmpMaxCapacity;
10920 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10921 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
10922 : }
10923 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10924 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
10925 : }
10926 : }
10927 0 : break;
10928 : }
10929 0 : case SizingMode::PerUnit: {
10930 :
10931 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
10932 :
10933 0 : if (this->MaxCapacityWasAutoSized) {
10934 : Real64 rho;
10935 : Real64 Cp;
10936 0 : if (this->UseSidePlantLoc.loopNum > 0) {
10937 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10938 0 : Cp = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10939 : } else {
10940 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10941 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
10942 : }
10943 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) *
10944 : rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
10945 : }
10946 :
10947 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10948 0 : this->Volume = tmpTankVolume;
10949 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10950 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
10951 : }
10952 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10953 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
10954 : }
10955 : }
10956 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10957 0 : this->MaxCapacity = tmpMaxCapacity;
10958 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10959 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
10960 : }
10961 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10962 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
10963 : }
10964 : }
10965 0 : break;
10966 : }
10967 1 : case SizingMode::PerSolarColArea: {
10968 1 : break;
10969 : }
10970 0 : default:
10971 0 : break;
10972 : }
10973 :
10974 1 : if (this->MaxCapacityWasAutoSized) this->setBackupElementCapacity(state);
10975 :
10976 : // if stratified, might set height.
10977 1 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
10978 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
10979 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
10980 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / Constant::Pi, 0.3333333333333333);
10981 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10982 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
10983 : }
10984 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10985 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
10986 : }
10987 : // check if Constant::AutoCalculate() Use outlet and source inlet are still set to autosize by earlier
10988 0 : if (this->UseOutletHeightWasAutoSized) {
10989 0 : this->UseOutletHeight = this->Height;
10990 : }
10991 0 : if (this->SourceInletHeightWasAutoSized) {
10992 0 : this->SourceInletHeight = this->Height;
10993 : }
10994 : }
10995 : }
10996 1 : }
10997 :
10998 1 : void WaterThermalTankData::SizeTankForSupplySide(EnergyPlusData &state)
10999 : {
11000 :
11001 : // SUBROUTINE INFORMATION:
11002 : // AUTHOR Brent Griffith
11003 : // DATE WRITTEN February 2008
11004 : // MODIFIED na
11005 : // RE-ENGINEERED na
11006 :
11007 : // PURPOSE OF THIS SUBROUTINE:
11008 : // This subroutine is for sizing water heater tank volume and heater
11009 : // at a later point in the simulation when more of the plant is ready.
11010 :
11011 : // METHODOLOGY EMPLOYED:
11012 : // depending on the sizing design mode...
11013 :
11014 : // REFERENCES:
11015 : // BA benchmark report for residential design mode
11016 :
11017 : static constexpr std::string_view RoutineName("SizeTankForSupplySide");
11018 :
11019 1 : Real64 tmpTankVolume = this->Volume;
11020 1 : Real64 tmpMaxCapacity = this->MaxCapacity;
11021 :
11022 1 : if (this->Sizing.DesignMode == SizingMode::PeakDraw) {
11023 0 : if (this->VolumeWasAutoSized)
11024 0 : tmpTankVolume = this->Sizing.TankDrawTime * this->UseDesignVolFlowRate * Constant::rSecsInHour; // hours | m3/s | (3600 s/1 hour)
11025 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11026 0 : this->Volume = tmpTankVolume;
11027 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11028 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11029 : }
11030 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11031 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11032 : }
11033 : }
11034 0 : if (this->MaxCapacityWasAutoSized) {
11035 0 : if (this->Sizing.RecoveryTime > 0.0) {
11036 0 : Real64 rho = 0.0;
11037 0 : Real64 Cp = 0.0;
11038 0 : constexpr Real64 Tstart = 14.44;
11039 0 : constexpr Real64 Tfinish = 57.22;
11040 :
11041 0 : if (this->SrcSidePlantLoc.loopNum > 0) {
11042 0 : rho = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11043 0 : Cp = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum)
11044 0 : .glycol->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11045 : } else {
11046 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11047 0 : Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), RoutineName);
11048 : }
11049 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11050 0 : (this->Sizing.RecoveryTime * Constant::rSecsInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11051 : } else {
11052 0 : ShowFatalError(
11053 0 : state, format("{}: Tank=\"{}\", requested sizing for max capacity but entered Recovery Time is zero.", RoutineName, this->Name));
11054 : }
11055 : }
11056 :
11057 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11058 0 : this->MaxCapacity = tmpMaxCapacity;
11059 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11060 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11061 : }
11062 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11063 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11064 : }
11065 : }
11066 1 : } else if (this->Sizing.DesignMode == SizingMode::PerSolarColArea) {
11067 :
11068 1 : this->Sizing.TotalSolarCollectorArea = 0.0;
11069 :
11070 1 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11071 0 : auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
11072 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
11073 : }
11074 :
11075 2 : for (int CollectorNum = 1; CollectorNum <= state.dataPhotovoltaicThermalCollector->NumPVT; ++CollectorNum) {
11076 1 : auto const &collector = state.dataPhotovoltaicThermalCollector->PVT(CollectorNum);
11077 1 : this->Sizing.TotalSolarCollectorArea += collector.AreaCol;
11078 : }
11079 :
11080 1 : if (this->VolumeWasAutoSized) {
11081 1 : if (this->Sizing.TotalSolarCollectorArea > 0) {
11082 1 : tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11083 : } else {
11084 0 : ShowFatalError(state,
11085 0 : format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
11086 : "area of Collectors is zero.",
11087 : RoutineName,
11088 0 : this->Name));
11089 : }
11090 : }
11091 1 : if (this->MaxCapacityWasAutoSized) {
11092 0 : tmpMaxCapacity = 0.0;
11093 : }
11094 1 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11095 1 : this->Volume = tmpTankVolume;
11096 1 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11097 1 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11098 : }
11099 1 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11100 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11101 : }
11102 : }
11103 1 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11104 0 : this->MaxCapacity = tmpMaxCapacity;
11105 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11106 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11107 : }
11108 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11109 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11110 : }
11111 : }
11112 : }
11113 :
11114 1 : if (this->MaxCapacityWasAutoSized) {
11115 0 : this->setBackupElementCapacity(state);
11116 : }
11117 :
11118 1 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
11119 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
11120 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
11121 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / Constant::Pi, 0.3333333333333333);
11122 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11123 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
11124 : }
11125 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11126 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
11127 : }
11128 : }
11129 : }
11130 1 : }
11131 :
11132 1 : void WaterThermalTankData::SizeDemandSidePlantConnections(EnergyPlusData &state)
11133 : {
11134 :
11135 : // SUBROUTINE INFORMATION:
11136 : // AUTHOR Brent Griffith
11137 : // DATE WRITTEN October 2007
11138 : // MODIFIED na
11139 : // RE-ENGINEERED na
11140 :
11141 : // PURPOSE OF THIS SUBROUTINE:
11142 : // This subroutine is for sizing water heater plant connection flow rates
11143 : // on the demand side that have not been specified in the input.
11144 :
11145 : // METHODOLOGY EMPLOYED:
11146 : // For water heater sides on the Demand side, hot water flow rates are modeled entirely from user input data
11147 : // because the plant loop is not yet set up nor is plant sizing info populated.
11148 : // sizing is done by calculating an initial
11149 : // recovery rate that if continued would reheat tank in user specified amount of time.
11150 : // initial and final tank temperatures are 14.44 and reheat to 57.22 (values from CalcStandardRatings routine)
11151 :
11152 : static constexpr std::string_view RoutineName("SizeDemandSidePlantConnections");
11153 :
11154 1 : auto &PlantSizData = state.dataSize->PlantSizData;
11155 :
11156 1 : Real64 tankRecoverhours = this->SizingRecoveryTime;
11157 1 : bool ErrorsFound = false;
11158 1 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
11159 1 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
11160 :
11161 : Real64 Tstart;
11162 : Real64 Tfinish;
11163 1 : if (!this->IsChilledWaterTank) {
11164 1 : Tstart = 14.44;
11165 1 : Tfinish = 57.22;
11166 : } else {
11167 0 : Tstart = 14.44;
11168 0 : Tfinish = 9.0;
11169 : }
11170 :
11171 : // determine tank volume to use for sizing.
11172 1 : Real64 TankVolume = this->Volume;
11173 1 : if (this->VolumeWasAutoSized) {
11174 1 : TankVolume = this->Sizing.NominalVolForSizingDemandSideFlow;
11175 : }
11176 :
11177 1 : if (this->UseInletNode > 0) {
11178 0 : if (this->UseDesignVolFlowRateWasAutoSized) {
11179 0 : int PltSizNum = this->UseSidePlantSizNum;
11180 0 : if (PltSizNum > 0) { // we have a Plant Sizing Object
11181 0 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11182 : // probably shouldn't come here as Use side is unlikley to be on demand side (?)
11183 : // but going to treat component with symetry so if connections are reversed it'll still work
11184 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11185 : // in user specified hours.
11186 : // using the plant inlet design temp for sizing.
11187 0 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11188 0 : Real64 eff = this->UseEffectiveness;
11189 0 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11190 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11191 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11192 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11193 : } else {
11194 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11195 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11196 : }
11197 0 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11198 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11199 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11200 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11201 : } else {
11202 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11203 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11204 : }
11205 : } else {
11206 0 : if (!this->IsChilledWaterTank) {
11207 : // plant sizing object design temperature is set too low throw warning.
11208 0 : ShowSevereError(state,
11209 : "Autosizing of Use side water heater design flow rate requires Sizing:Plant object to have an exit "
11210 : "temperature >= 58C");
11211 0 : ShowContinueError(state, format("Occurs for water heater object={}", this->Name));
11212 : } else {
11213 : // plant sizing object design temperature is set too hi throw warning.
11214 0 : ShowSevereError(state,
11215 : "Autosizing of Use side chilled water tank design flow rate requires Sizing:Plant object to have an "
11216 : "exit temperature <= 8C");
11217 0 : ShowContinueError(state, format("Occurs for chilled water storage tank object={}", this->Name));
11218 : }
11219 0 : ErrorsFound = true;
11220 : }
11221 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11222 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11223 : }
11224 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11225 0 : BaseSizer::reportSizerOutput(
11226 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11227 : }
11228 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11229 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11230 : } else {
11231 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
11232 : }
11233 : Real64 rho =
11234 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11235 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11236 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11237 : } else {
11238 0 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
11239 : }
11240 : } // Demand side
11241 : } else {
11242 : // do nothing
11243 : } // plant sizing object
11244 :
11245 : } else {
11246 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11247 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11248 : Real64 rho;
11249 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11250 0 : rho = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11251 : } else {
11252 0 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
11253 : }
11254 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11255 : } // autosizing needed.
11256 : } // connected to plant
11257 :
11258 1 : if (this->SourceInletNode > 0) {
11259 1 : if (this->SourceDesignVolFlowRateWasAutoSized) {
11260 0 : int PltSizNum = this->SourceSidePlantSizNum;
11261 0 : if (PltSizNum > 0) {
11262 0 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11263 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11264 : // in user specified hours.
11265 : // using the plant inlet design temp for sizing.
11266 0 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11267 0 : Real64 eff = this->SourceEffectiveness;
11268 0 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11269 :
11270 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11271 0 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11272 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11273 : } else {
11274 0 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11275 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11276 : }
11277 0 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11278 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11279 0 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11280 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11281 : } else {
11282 0 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::rSecsInHour * eff)) *
11283 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11284 : }
11285 : } else {
11286 0 : if (!this->IsChilledWaterTank) {
11287 : // plant sizing object design temperature is set too low throw warning.
11288 0 : ShowSevereError(state,
11289 : "Autosizing of Source side water heater design flow rate requires Sizing:Plant object to have an "
11290 : "exit temperature >= 58C");
11291 0 : ShowContinueError(state, format("Occurs for WaterHeater:Mixed object={}", this->Name));
11292 : } else {
11293 : // plant sizing object design temperature is set too hi throw warning.
11294 0 : ShowSevereError(state,
11295 : "Autosizing of Source side chilled water tank design flow rate requires Sizing:Plant object to have "
11296 : "an exit temperature <= 8C");
11297 0 : ShowContinueError(state, format("Occurs for chilled water storage tank object={}", this->Name));
11298 : }
11299 0 : ErrorsFound = true;
11300 : }
11301 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11302 0 : BaseSizer::reportSizerOutput(
11303 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11304 : }
11305 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11306 0 : BaseSizer::reportSizerOutput(
11307 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11308 : }
11309 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11310 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11311 : } else {
11312 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
11313 : }
11314 : Real64 rho =
11315 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11316 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11317 0 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11318 : } else {
11319 0 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
11320 : }
11321 : } // demand side
11322 : } else {
11323 : // do nothing
11324 : } // plant sizing object
11325 :
11326 : } else {
11327 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11328 1 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11329 : Real64 rho;
11330 1 : if (this->SrcSidePlantLoc.loopNum > 0) {
11331 1 : rho = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
11332 : } else {
11333 0 : rho = this->water->getDensity(state, Constant::InitConvTemp, RoutineName);
11334 : }
11335 1 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11336 : } // autosizing needed.
11337 : } // connected to plant
11338 :
11339 1 : if (ErrorsFound) {
11340 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
11341 : }
11342 1 : }
11343 :
11344 0 : void WaterThermalTankData::SizeStandAloneWaterHeater(EnergyPlusData &state)
11345 : {
11346 :
11347 : // SUBROUTINE INFORMATION:
11348 : // AUTHOR B. Griffith
11349 : // DATE WRITTEN October 2013
11350 : // MODIFIED na
11351 : // RE-ENGINEERED na
11352 :
11353 : // PURPOSE OF THIS SUBROUTINE:
11354 : // allow autosizing of tank volume and heat capacity for stand alone tanks
11355 :
11356 : // METHODOLOGY EMPLOYED:
11357 : // same as for plant connected water heaters, only draws are scheduled.
11358 :
11359 : // SUBROUTINE PARAMETER DEFINITIONS:
11360 0 : Real64 constexpr GalTocubicMeters(0.0037854);
11361 0 : Real64 constexpr kBtuPerHrToWatts(293.1);
11362 : static constexpr std::string_view routineName = "SizeStandAloneWaterHeater";
11363 :
11364 0 : Real64 Tstart = 14.44;
11365 0 : Real64 Tfinish = 57.22;
11366 0 : Real64 tmpTankVolume = this->Volume;
11367 0 : Real64 tmpMaxCapacity = this->MaxCapacity;
11368 :
11369 0 : if (this->VolumeWasAutoSized || this->MaxCapacityWasAutoSized) {
11370 :
11371 0 : switch (this->Sizing.DesignMode) {
11372 :
11373 0 : case SizingMode::PeakDraw: {
11374 : // get draw rate from maximum in schedule
11375 :
11376 0 : Real64 rho = this->water->getDensity(state, Constant::InitConvTemp, routineName);
11377 0 : Real64 DrawDesignVolFlowRate = this->flowRateSched->getCurrentVal() * this->MassFlowRateMax / rho;
11378 :
11379 0 : if (this->VolumeWasAutoSized) {
11380 0 : tmpTankVolume = this->Sizing.TankDrawTime * DrawDesignVolFlowRate * Constant::rSecsInHour; // hours | m3/s | (3600 s/1 hour)
11381 0 : this->Volume = tmpTankVolume;
11382 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11383 : }
11384 0 : if (this->MaxCapacityWasAutoSized) {
11385 0 : if (this->Sizing.RecoveryTime > 0.0) {
11386 0 : rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11387 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11388 :
11389 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11390 0 : (this->Sizing.RecoveryTime * Constant::rSecsInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11391 : } else {
11392 0 : ShowFatalError(
11393 : state,
11394 0 : format("{}: Tank=\"{}\", requested sizing for max capacity but entered Recovery Time is zero.", routineName, this->Name));
11395 : }
11396 0 : this->MaxCapacity = tmpMaxCapacity;
11397 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11398 : }
11399 :
11400 0 : break;
11401 : }
11402 0 : case SizingMode::ResidentialMin: {
11403 : // assume can propagate rules for gas to other fuels.
11404 0 : bool FuelTypeIsLikeGas = false;
11405 0 : switch (this->FuelType) {
11406 0 : case Constant::eFuel::NaturalGas:
11407 : case Constant::eFuel::Diesel:
11408 : case Constant::eFuel::Gasoline:
11409 : case Constant::eFuel::Coal:
11410 : case Constant::eFuel::FuelOilNo1:
11411 : case Constant::eFuel::FuelOilNo2:
11412 : case Constant::eFuel::Propane:
11413 : case Constant::eFuel::OtherFuel1:
11414 : case Constant::eFuel::OtherFuel2:
11415 : case Constant::eFuel::DistrictHeatingWater:
11416 : case Constant::eFuel::DistrictHeatingSteam:
11417 0 : FuelTypeIsLikeGas = true;
11418 0 : break;
11419 0 : default: // FuelTypeIsLikeGas stays false
11420 0 : break;
11421 : }
11422 :
11423 0 : if (this->Sizing.NumberOfBedrooms == 1) {
11424 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11425 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
11426 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
11427 0 : } else if (FuelTypeIsLikeGas) {
11428 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
11429 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
11430 : }
11431 :
11432 0 : } else if (this->Sizing.NumberOfBedrooms == 2) {
11433 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11434 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11435 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11436 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
11437 0 : } else if (FuelTypeIsLikeGas) {
11438 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11439 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11440 : }
11441 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11442 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11443 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11444 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11445 0 : } else if (FuelTypeIsLikeGas) {
11446 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11447 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11448 : }
11449 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11450 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11451 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11452 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11453 0 : } else if (FuelTypeIsLikeGas) {
11454 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11455 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11456 : }
11457 : }
11458 0 : } else if (this->Sizing.NumberOfBedrooms == 3) {
11459 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11460 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11461 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11462 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11463 0 : } else if (FuelTypeIsLikeGas) {
11464 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11465 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11466 : }
11467 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11468 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11469 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11470 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11471 0 : } else if (FuelTypeIsLikeGas) {
11472 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11473 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11474 : }
11475 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11476 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11477 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11478 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11479 0 : } else if (FuelTypeIsLikeGas) {
11480 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11481 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11482 : }
11483 : }
11484 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
11485 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11486 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11487 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11488 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11489 0 : } else if (FuelTypeIsLikeGas) {
11490 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11491 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11492 : }
11493 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11494 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11495 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11496 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11497 0 : } else if (FuelTypeIsLikeGas) {
11498 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11499 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11500 : }
11501 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11502 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11503 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11504 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11505 0 : } else if (FuelTypeIsLikeGas) {
11506 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11507 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11508 : }
11509 : }
11510 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
11511 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11512 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11513 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11514 0 : } else if (FuelTypeIsLikeGas) {
11515 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11516 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
11517 : }
11518 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
11519 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11520 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11521 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11522 0 : } else if (FuelTypeIsLikeGas) {
11523 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11524 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
11525 : }
11526 : }
11527 0 : if (this->VolumeWasAutoSized) {
11528 0 : this->Volume = tmpTankVolume;
11529 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11530 : }
11531 0 : if (this->MaxCapacityWasAutoSized) {
11532 0 : this->MaxCapacity = tmpMaxCapacity;
11533 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11534 : }
11535 :
11536 0 : break;
11537 : }
11538 0 : case SizingMode::PerPerson: {
11539 : // how to get number of people?
11540 :
11541 : // MJW TODO: this won't compile now: Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
11542 0 : Real64 SumPeopleAllZones = 0.0;
11543 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
11544 0 : SumPeopleAllZones += thisZone.TotOccupants;
11545 : }
11546 0 : if (this->VolumeWasAutoSized) {
11547 0 : tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
11548 : }
11549 0 : if (this->MaxCapacityWasAutoSized) {
11550 0 : Real64 rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11551 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11552 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) *
11553 : rho * Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11554 : }
11555 :
11556 0 : if (this->VolumeWasAutoSized) {
11557 0 : this->Volume = tmpTankVolume;
11558 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11559 : }
11560 0 : if (this->MaxCapacityWasAutoSized) {
11561 0 : this->MaxCapacity = tmpMaxCapacity;
11562 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11563 : }
11564 :
11565 0 : break;
11566 : }
11567 0 : case SizingMode::PerFloorArea: {
11568 :
11569 : // MJW TODO: this won't compile now: Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
11570 0 : Real64 SumFloorAreaAllZones = 0.0;
11571 0 : for (auto const &thisZone : state.dataHeatBal->Zone) {
11572 0 : SumFloorAreaAllZones += thisZone.FloorArea;
11573 : }
11574 0 : if (this->VolumeWasAutoSized) {
11575 0 : tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
11576 : }
11577 :
11578 0 : if (this->MaxCapacityWasAutoSized) {
11579 0 : Real64 rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11580 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11581 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) * (1.0 / Constant::rSecsInHour) *
11582 : rho * Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11583 : }
11584 0 : if (this->VolumeWasAutoSized) {
11585 0 : this->Volume = tmpTankVolume;
11586 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11587 : }
11588 0 : if (this->MaxCapacityWasAutoSized) {
11589 0 : this->MaxCapacity = tmpMaxCapacity;
11590 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11591 : }
11592 0 : break;
11593 : }
11594 0 : case SizingMode::PerUnit: {
11595 :
11596 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
11597 :
11598 0 : if (this->MaxCapacityWasAutoSized) {
11599 0 : Real64 rho = this->water->getDensity(state, ((Tfinish + Tstart) / 2.0), routineName);
11600 0 : Real64 Cp = this->water->getSpecificHeat(state, ((Tfinish + Tstart) / 2.0), routineName);
11601 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) *
11602 0 : (1.0 / Constant::rSecsInHour) * rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11603 : }
11604 :
11605 0 : if (this->VolumeWasAutoSized) {
11606 0 : this->Volume = tmpTankVolume;
11607 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11608 : }
11609 0 : if (this->MaxCapacityWasAutoSized) {
11610 0 : this->MaxCapacity = tmpMaxCapacity;
11611 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11612 : }
11613 0 : break;
11614 : }
11615 0 : case SizingMode::PerSolarColArea: {
11616 :
11617 0 : this->Sizing.TotalSolarCollectorArea = 0.0;
11618 :
11619 0 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11620 0 : auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
11621 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
11622 : }
11623 :
11624 0 : for (int CollectorNum = 1; CollectorNum <= state.dataPhotovoltaicThermalCollector->NumPVT; ++CollectorNum) {
11625 0 : auto const &collector = state.dataPhotovoltaicThermalCollector->PVT(CollectorNum);
11626 0 : this->Sizing.TotalSolarCollectorArea += collector.AreaCol;
11627 : }
11628 :
11629 0 : if (this->VolumeWasAutoSized) {
11630 0 : if (this->Sizing.TotalSolarCollectorArea > 0) {
11631 0 : tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11632 : } else {
11633 0 : ShowFatalError(state,
11634 0 : format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
11635 : "area of Collectors is zero.",
11636 : routineName,
11637 0 : this->Name));
11638 : }
11639 : }
11640 0 : if (this->MaxCapacityWasAutoSized) {
11641 0 : tmpMaxCapacity = 0.0;
11642 : }
11643 :
11644 0 : if (this->VolumeWasAutoSized) {
11645 0 : this->Volume = tmpTankVolume;
11646 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11647 : }
11648 0 : if (this->MaxCapacityWasAutoSized) {
11649 0 : this->MaxCapacity = tmpMaxCapacity;
11650 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11651 : }
11652 0 : break;
11653 : }
11654 0 : default:
11655 0 : if (this->MaxCapacityWasAutoSized) {
11656 0 : this->setBackupElementCapacity(state);
11657 : }
11658 0 : break;
11659 : }
11660 : }
11661 0 : }
11662 :
11663 7 : void WaterThermalTankData::UpdateWaterThermalTank(EnergyPlusData &state)
11664 : {
11665 :
11666 : // SUBROUTINE INFORMATION:
11667 : // AUTHOR Brandon Anderson
11668 : // DATE WRITTEN May 2000
11669 : // MODIFIED na
11670 : // Nov 2011, BAN; removed the use and source heat rate re-calculation for stratified tank
11671 : // for energy conservation verification.
11672 : // RE-ENGINEERED Feb 2004, PGE
11673 :
11674 : // PURPOSE OF THIS SUBROUTINE:
11675 : // Updates the node variables with local variables.
11676 :
11677 7 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
11678 6 : state.dataLoopNodes->Node(UseOutletNode) = state.dataLoopNodes->Node(this->UseInletNode); // this could wipe out setpoints on outlet node
11679 :
11680 6 : state.dataLoopNodes->Node(this->UseOutletNode).Temp = this->UseOutletTemp;
11681 : }
11682 :
11683 7 : if (this->SourceInletNode > 0 && this->SourceOutletNode > 0) {
11684 7 : state.dataLoopNodes->Node(this->SourceOutletNode) = state.dataLoopNodes->Node(this->SourceInletNode);
11685 :
11686 7 : state.dataLoopNodes->Node(this->SourceOutletNode).Temp = this->SourceOutletTemp;
11687 : }
11688 7 : }
11689 :
11690 1 : void WaterThermalTankData::ReportWaterThermalTank(EnergyPlusData &state)
11691 : {
11692 :
11693 : // SUBROUTINE INFORMATION:
11694 : // AUTHOR Brandon Anderson
11695 : // DATE WRITTEN May 2000
11696 : // MODIFIED na
11697 : // RE-ENGINEERED Feb 2004, PGE
11698 :
11699 1 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
11700 :
11701 1 : this->UnmetEnergy = this->UnmetRate * SecInTimeStep;
11702 1 : this->LossEnergy = this->LossRate * SecInTimeStep;
11703 1 : this->FlueLossEnergy = this->FlueLossRate * SecInTimeStep;
11704 1 : this->UseEnergy = this->UseRate * SecInTimeStep;
11705 1 : this->TotalDemandEnergy = this->TotalDemandRate * SecInTimeStep;
11706 1 : this->SourceEnergy = this->SourceRate * SecInTimeStep;
11707 1 : this->HeaterEnergy = this->HeaterRate * SecInTimeStep;
11708 1 : this->HeaterEnergy1 = this->HeaterRate1 * SecInTimeStep;
11709 1 : this->HeaterEnergy2 = this->HeaterRate2 * SecInTimeStep;
11710 1 : this->FuelEnergy = this->FuelRate * SecInTimeStep;
11711 1 : this->VentEnergy = this->VentRate * SecInTimeStep;
11712 1 : this->OffCycParaFuelEnergy = this->OffCycParaFuelRate * SecInTimeStep;
11713 1 : this->OffCycParaEnergyToTank = this->OffCycParaRateToTank * SecInTimeStep;
11714 1 : this->OnCycParaFuelEnergy = this->OnCycParaFuelRate * SecInTimeStep;
11715 1 : this->OnCycParaEnergyToTank = this->OnCycParaRateToTank * SecInTimeStep;
11716 1 : this->NetHeatTransferEnergy = this->NetHeatTransferRate * SecInTimeStep;
11717 1 : this->VolumeConsumed = this->VolFlowRate * SecInTimeStep;
11718 1 : }
11719 :
11720 8 : void WaterThermalTankData::CalcStandardRatings(EnergyPlusData &state)
11721 : {
11722 :
11723 : // SUBROUTINE INFORMATION:
11724 : // AUTHOR Peter Graham Ellis
11725 : // DATE WRITTEN January 2005
11726 : // MODIFIED R. Raustad, July 2005 - added HPWH to ratings procedure
11727 : // RE-ENGINEERED na
11728 :
11729 : // PURPOSE OF THIS SUBROUTINE:
11730 : // Calculates the water heater standard ratings, such as Energy Factor and Recovery Efficiency. Results are written
11731 : // to the EIO file. Standard ratings are not calculated for storage-only tanks, i.e., MaxCapacity = 0, nor for Integrated Heat Pumps
11732 :
11733 : // METHODOLOGY EMPLOYED:
11734 : // Water heater inputs are set to the specified test conditions. For HPWHs, the heating capacity and COP are assumed
11735 : // to be the primary element in the water heater and are used during the rating procedure. CalcWaterThermalTankMixed
11736 : // is iteratively called in a self-contained, 24 hour simulation of the standard test procedure.
11737 :
11738 : // REFERENCES:
11739 : // Title 10, Code of Federal Regulations, Part 430- Energy Conservation Program for Consumer Products, Appendix E to
11740 : // Subpart B- Uniform Test Procedure for Measuring the Energy Consumption of Water Heaters, January 1, 2004.
11741 :
11742 8 : if (this->AlreadyRated) { // bail we already did this one
11743 1 : return;
11744 : }
11745 :
11746 : bool FirstTimeFlag; // used during HPWH rating procedure
11747 7 : bool bIsVSCoil = false;
11748 : Real64 RecoveryEfficiency;
11749 : Real64 EnergyFactor;
11750 7 : Real64 RatedDXCoilTotalCapacity = 0.0;
11751 7 : if (this->MaxCapacity > 0.0 || this->HeatPumpNum > 0) {
11752 : // Set test conditions
11753 5 : this->AmbientTemp = 19.7222; // 67.5 F
11754 5 : this->UseInletTemp = 14.4444; // 58 F
11755 5 : this->SetPointTemp = 57.2222; // 135 F
11756 5 : this->SetPointTemp2 = 57.2222; // 135 F
11757 5 : this->TankTemp = 57.2222; // Initialize tank temperature
11758 5 : if (this->Nodes > 0)
11759 13 : for (auto &e : this->Node)
11760 12 : e.Temp = 57.2222;
11761 :
11762 5 : Real64 TotalDrawMass = 0.243402 * Psychrometrics::RhoH2O(Constant::InitConvTemp); // 64.3 gal * rho
11763 5 : Real64 DrawMass = TotalDrawMass / 6.0; // 6 equal draws
11764 5 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
11765 5 : Real64 DrawMassFlowRate = DrawMass / SecInTimeStep;
11766 5 : Real64 FuelEnergy_loc = 0.0;
11767 5 : FirstTimeFlag = true;
11768 :
11769 5 : int TimeStepPerHour = int(1.0 / state.dataHVACGlobal->TimeStepSys);
11770 : // Simulate 24 hour test
11771 1541 : for (int Step = 1; Step <= TimeStepPerHour * 24; ++Step) {
11772 :
11773 1536 : if (Step == 1 || Step == (1 + TimeStepPerHour) || Step == (1 + TimeStepPerHour * 2) || Step == (1 + TimeStepPerHour * 3) ||
11774 1516 : Step == (1 + TimeStepPerHour * 4) || Step == (1 + TimeStepPerHour * 5)) { // Hour 1 | Hour 2 | Hour 3 | Hour 4 | Hour 5 | Hour 6
11775 :
11776 30 : this->UseMassFlowRate = DrawMassFlowRate;
11777 : } else {
11778 1506 : this->UseMassFlowRate = 0.0;
11779 : }
11780 :
11781 1536 : this->SavedTankTemp = this->TankTemp;
11782 1536 : this->SavedMode = this->Mode;
11783 1536 : if (this->Nodes > 0) {
11784 18720 : for (auto &e : this->Node)
11785 17280 : e.SavedTemp = e.Temp;
11786 1440 : this->SavedHeaterOn1 = this->HeaterOn1;
11787 1440 : this->SavedHeaterOn2 = this->HeaterOn2;
11788 : }
11789 :
11790 1536 : if (this->HeatPumpNum == 0) {
11791 :
11792 1464 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
11793 24 : this->CalcWaterThermalTankMixed(state);
11794 :
11795 1440 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
11796 1440 : this->CalcWaterThermalTankStratified(state);
11797 : }
11798 :
11799 : } else {
11800 :
11801 72 : int HPNum = this->HeatPumpNum; // Convenience variable
11802 72 : Real64 AmbientHumRat = 0.00717; // Humidity ratio at 67.5 F / 50% RH
11803 :
11804 : // set the heat pump air- and water-side mass flow rate
11805 72 : Real64 MdotWater = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate * Psychrometrics::RhoH2O(this->TankTemp);
11806 72 : Real64 mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
11807 :
11808 : // ?? why is HPWH condenser inlet node temp reset inside the for loop? shouldn't it chnage with the tank temp throughout these
11809 : // iterations?
11810 72 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
11811 : // set the condenser inlet node mass flow rate and temperature
11812 72 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).MassFlowRate = MdotWater;
11813 72 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
11814 : }
11815 :
11816 : // initialize temperatures for HPWH DX Coil heating capacity and COP curves
11817 72 : state.dataHVACGlobal->HPWHInletDBTemp = this->AmbientTemp;
11818 144 : state.dataHVACGlobal->HPWHInletWBTemp =
11819 72 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, AmbientHumRat, state.dataEnvrn->OutBaroPress);
11820 :
11821 : // set up full air flow on DX coil inlet node
11822 72 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode > 0) {
11823 0 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRate = mdotAir;
11824 0 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRateMaxAvail = mdotAir;
11825 0 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Temp = this->AmbientTemp;
11826 0 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).HumRat = AmbientHumRat;
11827 0 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Enthalpy =
11828 0 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
11829 : } else {
11830 72 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode == 0) {
11831 24 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRate = mdotAir;
11832 24 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRateMaxAvail =
11833 : mdotAir;
11834 24 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Temp = this->AmbientTemp;
11835 24 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).HumRat = AmbientHumRat;
11836 24 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Enthalpy =
11837 24 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
11838 : } else {
11839 48 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRate = mdotAir;
11840 48 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRateMaxAvail = mdotAir;
11841 48 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Temp = this->AmbientTemp;
11842 48 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).HumRat = AmbientHumRat;
11843 48 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Enthalpy =
11844 48 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
11845 : }
11846 : }
11847 :
11848 72 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = this->AmbientTemp;
11849 :
11850 72 : if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
11851 144 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") ||
11852 72 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP)) {
11853 0 : bIsVSCoil = true;
11854 0 : std::string VSCoilName = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName;
11855 0 : int VSCoilNum = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
11856 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
11857 0 : VSCoilNum =
11858 0 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
11859 : VSCoilName =
11860 0 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilName;
11861 : }
11862 :
11863 0 : Real64 RhoWater = Psychrometrics::RhoH2O(this->TankTemp);
11864 0 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
11865 0 : this->SetVSHPWHFlowRates(
11866 : state,
11867 : HPWH,
11868 0 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
11869 : 1.0,
11870 : RhoWater,
11871 : MdotWater,
11872 : true);
11873 : // simulate the HPWH coil/fan to find heating capacity
11874 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).fanPlace == HVAC::FanPlace::BlowThru) {
11875 : // simulate fan and DX coil twice
11876 0 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
11877 :
11878 0 : VariableSpeedCoils::SimVariableSpeedCoils(
11879 : state,
11880 : VSCoilName,
11881 : VSCoilNum,
11882 : HVAC::FanOp::Cycling,
11883 : HVAC::CompressorOp::On,
11884 : 1.0,
11885 0 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
11886 : 1.0,
11887 : 0.0,
11888 : 0.0,
11889 : 1.0);
11890 :
11891 0 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
11892 :
11893 0 : VariableSpeedCoils::SimVariableSpeedCoils(
11894 : state,
11895 : VSCoilName,
11896 : VSCoilNum,
11897 : HVAC::FanOp::Cycling,
11898 : HVAC::CompressorOp::On,
11899 : 1.0,
11900 0 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
11901 : 1.0,
11902 : 0.0,
11903 : 0.0,
11904 : 1.0);
11905 : } else {
11906 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
11907 0 : VariableSpeedCoils::SimVariableSpeedCoils(
11908 : state,
11909 : VSCoilName,
11910 : VSCoilNum,
11911 : HVAC::FanOp::Cycling,
11912 : HVAC::CompressorOp::On,
11913 : 1.0,
11914 0 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
11915 : 1.0,
11916 : 0.0,
11917 : 0.0,
11918 : 1.0);
11919 :
11920 0 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
11921 :
11922 0 : VariableSpeedCoils::SimVariableSpeedCoils(
11923 : state,
11924 : VSCoilName,
11925 : VSCoilNum,
11926 : HVAC::FanOp::Cycling,
11927 : HVAC::CompressorOp::On,
11928 : 1.0,
11929 0 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
11930 : 1.0,
11931 : 0.0,
11932 : 0.0,
11933 : 1.0);
11934 :
11935 0 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
11936 : }
11937 :
11938 0 : this->MaxCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
11939 0 : this->MinCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
11940 0 : this->Efficiency = state.dataVariableSpeedCoils->VSHPWHHeatingCOP;
11941 0 : } else {
11942 72 : bIsVSCoil = false;
11943 : // simulate the HPWH coil/fan to find heating capacity
11944 72 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).fanPlace == HVAC::FanPlace::BlowThru) {
11945 0 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
11946 : // reset water inlet temp. If already sized, no harm.
11947 0 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
11948 :
11949 0 : DXCoils::SimDXCoil(state,
11950 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
11951 : HVAC::CompressorOp::On,
11952 : true,
11953 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
11954 : HVAC::FanOp::Cycling,
11955 0 : 1.0);
11956 0 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
11957 : }
11958 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
11959 : // PLR=1 here.
11960 0 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
11961 :
11962 0 : DXCoils::SimDXCoil(state,
11963 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
11964 : HVAC::CompressorOp::On,
11965 : true,
11966 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
11967 : HVAC::FanOp::Cycling,
11968 0 : 1.0);
11969 :
11970 0 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
11971 :
11972 0 : DXCoils::SimDXCoil(state,
11973 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
11974 : HVAC::CompressorOp::On,
11975 : true,
11976 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
11977 : HVAC::FanOp::Cycling,
11978 0 : 1.0);
11979 : } else {
11980 72 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
11981 : // reset water inlet temp. If already sized, no harm.
11982 9 : DXCoils::SimDXCoil(state,
11983 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
11984 : HVAC::CompressorOp::On,
11985 : true,
11986 3 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
11987 : HVAC::FanOp::Cycling,
11988 6 : 1.0);
11989 3 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
11990 : }
11991 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
11992 : // PLR=1 here.
11993 216 : DXCoils::SimDXCoil(state,
11994 72 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
11995 : HVAC::CompressorOp::On,
11996 : true,
11997 72 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
11998 : HVAC::FanOp::Cycling,
11999 144 : 1.0);
12000 :
12001 72 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12002 :
12003 216 : DXCoils::SimDXCoil(state,
12004 72 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12005 : HVAC::CompressorOp::On,
12006 : true,
12007 72 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12008 : HVAC::FanOp::Cycling,
12009 144 : 1.0);
12010 :
12011 72 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12012 : }
12013 :
12014 72 : this->MaxCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12015 72 : this->MinCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12016 72 : this->Efficiency = state.dataDXCoils->HPWHHeatingCOP;
12017 : }
12018 :
12019 72 : if (FirstTimeFlag) {
12020 3 : RatedDXCoilTotalCapacity = state.dataHVACGlobal->DXCoilTotalCapacity;
12021 3 : FirstTimeFlag = false;
12022 : }
12023 :
12024 : // Switch the HPWH info with the tank info and call CalcWaterThermalTankMixed to get Standard Rating
12025 : // (backup element is assumed to be disabled during the rating procedure)
12026 72 : this->SourceMassFlowRate = 0.0;
12027 72 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OnCycParaLoad;
12028 72 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OffCycParaLoad;
12029 72 : this->OffCycParaFracToTank = 0.0;
12030 72 : this->OnCycParaFracToTank = 0.0;
12031 72 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilPLFFPLR;
12032 :
12033 72 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
12034 72 : if (this->Efficiency > 0.0) this->CalcWaterThermalTankMixed(state);
12035 :
12036 0 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12037 0 : if (this->Efficiency > 0.0) this->CalcWaterThermalTankStratified(state);
12038 : }
12039 :
12040 : // reset the water heater data to original values
12041 72 : this->MaxCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12042 72 : this->MinCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12043 72 : this->Efficiency = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementEfficiency;
12044 72 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaLoad;
12045 72 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaLoad;
12046 72 : this->OnCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaFracToTank;
12047 72 : this->OffCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaFracToTank;
12048 72 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHPLFCurve;
12049 : }
12050 :
12051 1536 : FuelEnergy_loc += (this->FuelRate + this->OffCycParaFuelRate + this->OnCycParaFuelRate) * SecInTimeStep;
12052 :
12053 : } // Step
12054 :
12055 5 : if (this->FirstRecoveryDone && this->FirstRecoveryFuel > 0.0) {
12056 : // Calculate Recovery Efficiency based on energy used to recover from the first draw
12057 : // FirstRecoveryFuel is recorded inside the CalcWaterThermalTank subroutine
12058 3 : RecoveryEfficiency = DrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / this->FirstRecoveryFuel;
12059 :
12060 : // Calculate Energy Factor based on total energy (including parasitics) used over entire test
12061 3 : EnergyFactor = TotalDrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / FuelEnergy_loc;
12062 :
12063 : } else {
12064 2 : RecoveryEfficiency = 0.0;
12065 2 : EnergyFactor = 0.0;
12066 : // If this a regular tank, or an HPWH that's not an Integrated one
12067 2 : if ((this->HeatPumpNum == 0) || !state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).bIsIHP) {
12068 4 : ShowWarningError(
12069 : state,
12070 4 : format("Water heater = {}: Recovery Efficiency and Energy Factor could not be calculated during the test for standard ratings",
12071 2 : this->Name));
12072 6 : ShowContinueError(state, "Setpoint was never recovered and/or heater never turned on");
12073 : }
12074 : }
12075 :
12076 5 : } else {
12077 :
12078 : // Storage-only tank
12079 2 : RecoveryEfficiency = 0.0;
12080 2 : EnergyFactor = 0.0;
12081 :
12082 : } // WaterThermalTank(WaterThermalTankNum)%MaxCapacity > 0.0
12083 :
12084 : // create predefined report
12085 : // Store values for the input verification and summary report
12086 7 : std::string equipName;
12087 7 : if (this->HeatPumpNum == 0) {
12088 4 : equipName = this->Name;
12089 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHType, equipName, this->Type);
12090 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12091 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, this->MaxCapacity);
12092 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12093 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12094 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12095 : } else {
12096 3 : equipName = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name;
12097 6 : OutputReportPredefined::PreDefTableEntry(
12098 6 : state, state.dataOutRptPredefined->pdchSWHType, equipName, state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type);
12099 3 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12100 3 : if (bIsVSCoil) {
12101 0 : OutputReportPredefined::PreDefTableEntry(
12102 0 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataVariableSpeedCoils->VSHPWHHeatingCapacity);
12103 : } else {
12104 9 : OutputReportPredefined::PreDefTableEntry(
12105 6 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataDXCoils->HPWHHeatingCapacity);
12106 : }
12107 3 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12108 3 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12109 3 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12110 : }
12111 :
12112 : // Write test results
12113 7 : if (this->HeatPumpNum == 0) {
12114 : Real64 MaxCapacity_loc;
12115 4 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12116 1 : if (this->StratifiedControlMode == PriorityControlMode::MasterSlave) {
12117 1 : MaxCapacity_loc = max(this->MaxCapacity, this->MaxCapacity2);
12118 : } else { // PrioritySimultaneous
12119 0 : MaxCapacity_loc = this->MaxCapacity + this->MaxCapacity2;
12120 : }
12121 : } else { // WaterHeaterMixed
12122 3 : MaxCapacity_loc = this->MaxCapacity;
12123 : }
12124 :
12125 : static constexpr std::string_view Format_720("Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T}\n");
12126 4 : print(state.files.eio, Format_720, this->Type, this->Name, this->Volume, MaxCapacity_loc, RecoveryEfficiency, EnergyFactor);
12127 : } else {
12128 : static constexpr std::string_view Format_721("Heat Pump Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T},{:.0T}\n");
12129 3 : print(state.files.eio,
12130 : Format_721,
12131 3 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type,
12132 3 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
12133 3 : this->Volume,
12134 3 : state.dataDXCoils->HPWHHeatingCapacity,
12135 : RecoveryEfficiency,
12136 : EnergyFactor,
12137 : RatedDXCoilTotalCapacity);
12138 : }
12139 :
12140 7 : this->AlreadyRated = true;
12141 7 : }
12142 :
12143 0 : void WaterThermalTankData::ReportCWTankInits(EnergyPlusData &state)
12144 : {
12145 :
12146 : // SUBROUTINE INFORMATION:
12147 : // AUTHOR B. Griffith
12148 : // DATE WRITTEN March 2009
12149 : // MODIFIED na
12150 : // RE-ENGINEERED na
12151 :
12152 : // PURPOSE OF THIS SUBROUTINE:
12153 : // send chilled water tank info to EIO
12154 :
12155 0 : if (this->myOneTimeInitFlag) {
12156 0 : this->setupOutputVars(state);
12157 0 : this->myOneTimeInitFlag = false;
12158 : }
12159 :
12160 0 : if (this->AlreadyReported) { // bail we already did this one
12161 0 : return;
12162 : }
12163 :
12164 : static constexpr std::string_view Format_728("Chilled Water Tank Information,{},{},{:.4T},{:.4T},{:.4T}\n");
12165 0 : print(state.files.eio, Format_728, this->Type, this->Name, this->Volume, this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
12166 :
12167 0 : this->AlreadyReported = true;
12168 : }
12169 :
12170 7 : Real64 WaterThermalTankData::FindStratifiedTankSensedTemp(EnergyPlusData &state, bool UseAverage)
12171 : {
12172 :
12173 : // FUNCTION INFORMATION:
12174 : // AUTHOR B. Griffith
12175 : // DATE WRITTEN March 2012
12176 : // MODIFIED na
12177 : // RE-ENGINEERED Noel Merket, April 2015
12178 :
12179 : // PURPOSE OF THIS FUNCTION:
12180 : // find tank temperature depending on how sensed
12181 :
12182 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
12183 7 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
12184 : Real64 ControlSensor1Temp;
12185 : Real64 ControlSensor2Temp;
12186 :
12187 7 : if (UseAverage) {
12188 1 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).TempAvg;
12189 1 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).TempAvg;
12190 : } else {
12191 6 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).Temp;
12192 6 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).Temp;
12193 : }
12194 :
12195 7 : Real64 SensedTemp = ControlSensor1Temp * HPWH.ControlSensor1Weight + ControlSensor2Temp * HPWH.ControlSensor2Weight;
12196 :
12197 7 : return SensedTemp;
12198 : }
12199 :
12200 361 : Real64 WaterThermalTankData::getDeadBandTemp()
12201 : {
12202 361 : if (this->IsChilledWaterTank) {
12203 1 : return (this->SetPointTemp + this->DeadBandDeltaTemp);
12204 : } else {
12205 360 : return (this->SetPointTemp - this->DeadBandDeltaTemp);
12206 : }
12207 : }
12208 0 : void WaterThermalTankData::oneTimeInit(EnergyPlusData &state)
12209 : {
12210 0 : if (this->myOneTimeInitFlag) {
12211 0 : this->setupOutputVars(state);
12212 0 : this->myOneTimeInitFlag = false;
12213 : }
12214 0 : }
12215 :
12216 6 : void WaterThermalTankData::setBackupElementCapacity(EnergyPlusData &state)
12217 : {
12218 : // Fix for #9001: The BackupElementCapacity was not being reset from the autosize value (-99999) which resulted in
12219 : // negative electric consumption. Using a test for any negative numbers here instead of just -99999 for safety.
12220 : // Only reset the backup element capacity if a problem has been occured.
12221 6 : if (this->HeatPumpNum > 0) {
12222 3 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) return;
12223 2 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity < 0.0) {
12224 1 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity = this->MaxCapacity;
12225 : }
12226 3 : } else if (this->DesuperheaterNum > 0) {
12227 2 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity < 0.0) {
12228 1 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity = this->MaxCapacity;
12229 : }
12230 : }
12231 : }
12232 :
12233 42 : bool GetHeatPumpWaterHeaterNodeNumber(EnergyPlusData &state, int const NodeNumber)
12234 : {
12235 : // PURPOSE OF THIS FUNCTION:
12236 : // Check if a node is used by a heat pump water heater
12237 : // and can be excluded from an airflow network.
12238 :
12239 : // Return value
12240 : bool HeatPumpWaterHeaterNodeException;
12241 :
12242 : int HeatPumpWaterHeaterIndex;
12243 :
12244 42 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
12245 5 : GetWaterThermalTankInput(state);
12246 5 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
12247 : }
12248 :
12249 42 : HeatPumpWaterHeaterNodeException = false;
12250 :
12251 46 : for (HeatPumpWaterHeaterIndex = 1; HeatPumpWaterHeaterIndex <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HeatPumpWaterHeaterIndex) {
12252 :
12253 : // Get heat pump water heater data
12254 6 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpWaterHeaterIndex);
12255 :
12256 : // "Zone and outdoor air" configuration is expected break the conservation of mass
12257 6 : if (HPWH.InletAirConfiguration != WTTAmbientTemp::ZoneAndOA) {
12258 :
12259 : // Air outlet node
12260 3 : if (NodeNumber == HPWH.HeatPumpAirOutletNode) {
12261 0 : HeatPumpWaterHeaterNodeException = true;
12262 0 : break;
12263 : }
12264 :
12265 : // Air inlet node
12266 3 : if (NodeNumber == HPWH.HeatPumpAirInletNode) {
12267 0 : HeatPumpWaterHeaterNodeException = true;
12268 0 : break;
12269 : }
12270 :
12271 : // Get fan inlet node index
12272 3 : int FanInletNodeIndex = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
12273 :
12274 : // Fan inlet node
12275 3 : if (NodeNumber == FanInletNodeIndex) {
12276 1 : HeatPumpWaterHeaterNodeException = true;
12277 1 : break;
12278 : }
12279 :
12280 : // Fan outlet node
12281 2 : if (NodeNumber == HPWH.FanOutletNode) {
12282 1 : HeatPumpWaterHeaterNodeException = true;
12283 1 : break;
12284 : }
12285 :
12286 : // Outside air node
12287 1 : if (NodeNumber == HPWH.OutsideAirNode) {
12288 0 : HeatPumpWaterHeaterNodeException = true;
12289 0 : break;
12290 : }
12291 :
12292 : // Exhaust air node
12293 1 : if (NodeNumber == HPWH.ExhaustAirNode) {
12294 0 : HeatPumpWaterHeaterNodeException = true;
12295 0 : break;
12296 : }
12297 : }
12298 : }
12299 :
12300 42 : return HeatPumpWaterHeaterNodeException;
12301 : }
12302 :
12303 0 : int getHeatPumpWaterHeaterIndex(EnergyPlusData &state, std::string_view CompName)
12304 : {
12305 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
12306 0 : GetWaterThermalTankInput(state);
12307 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
12308 : }
12309 :
12310 0 : for (int HPNum = 1; HPNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPNum) {
12311 0 : if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name, CompName)) {
12312 0 : return HPNum;
12313 : }
12314 : }
12315 :
12316 0 : return 0;
12317 : }
12318 :
12319 : } // namespace EnergyPlus::WaterThermalTanks
|