Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // 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 : std::string const fluidNameWater = "WATER";
135 :
136 142 : PlantComponent *WaterThermalTankData::factory(EnergyPlusData &state, std::string const &objectName)
137 : {
138 : // Process the input data
139 142 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
140 10 : GetWaterThermalTankInput(state);
141 10 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
142 : }
143 :
144 : // Now look for this object in the list
145 163 : for (auto &tank : state.dataWaterThermalTanks->WaterThermalTank) {
146 163 : if (tank.Name == objectName) {
147 142 : return &tank;
148 : }
149 : }
150 : // If we didn't find it, fatal
151 : ShowFatalError(state, format("LocalWaterTankFactory: Error getting inputs for tank named: {}", objectName)); // LCOV_EXCL_LINE
152 : // Shut up the compiler
153 : return nullptr; // LCOV_EXCL_LINE
154 : }
155 :
156 771 : void WaterThermalTankData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
157 : {
158 771 : this->initialize(state, true);
159 771 : this->MinePlantStructForInfo(state);
160 771 : if (calledFromLocation.loopNum > 0) {
161 771 : if ((this->SrcSidePlantLoc.loopNum == calledFromLocation.loopNum) || (this->UseSidePlantLoc.loopNum == calledFromLocation.loopNum)) {
162 766 : this->SizeTankForDemandSide(state);
163 766 : this->SizeDemandSidePlantConnections(state);
164 766 : this->SizeSupplySidePlantConnections(state, calledFromLocation.loopNum);
165 766 : this->SizeTankForSupplySide(state);
166 : } else {
167 5 : return;
168 : }
169 : } else {
170 0 : this->SizeTankForDemandSide(state);
171 0 : this->SizeDemandSidePlantConnections(state);
172 0 : this->SizeSupplySidePlantConnections(state, this->SrcSidePlantLoc.loopNum);
173 0 : this->SizeTankForSupplySide(state);
174 : }
175 :
176 766 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
177 166 : if (!this->IsChilledWaterTank) {
178 156 : this->CalcStandardRatings(state);
179 : } else {
180 10 : this->ReportCWTankInits(state);
181 : }
182 : }
183 : }
184 :
185 721 : void WaterThermalTankData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
186 : [[maybe_unused]] const PlantLocation &calledFromLocation,
187 : Real64 &MaxLoad,
188 : Real64 &MinLoad,
189 : Real64 &OptLoad)
190 : {
191 721 : MinLoad = 0.0;
192 721 : MaxLoad = this->MaxCapacity;
193 721 : OptLoad = this->MaxCapacity;
194 721 : }
195 :
196 0 : int getTankIDX(EnergyPlusData &state, std::string_view CompName, int &CompIndex)
197 : {
198 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
199 0 : GetWaterThermalTankInput(state);
200 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
201 : }
202 :
203 : int CompNum;
204 :
205 0 : if (CompIndex == 0) {
206 0 : CompNum = Util::FindItem(CompName, state.dataWaterThermalTanks->WaterThermalTank);
207 0 : if (CompNum == 0) {
208 0 : ShowFatalError(state, format("SimWaterThermalTank_WaterTank: Unit not found={}", CompName));
209 : }
210 0 : CompIndex = CompNum;
211 : } else {
212 0 : CompNum = CompIndex;
213 0 : if (CompNum > state.dataWaterThermalTanks->numWaterThermalTank || CompNum < 1) {
214 0 : ShowFatalError(state,
215 0 : format("SimWaterThermalTank_WaterTank: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
216 : CompNum,
217 0 : state.dataWaterThermalTanks->numWaterThermalTank,
218 : CompName));
219 : }
220 0 : if (state.dataWaterThermalTanks->WaterThermalTank(CompNum).CheckWTTEquipName) {
221 0 : if (CompName != state.dataWaterThermalTanks->WaterThermalTank(CompNum).Name) {
222 0 : ShowFatalError(state,
223 0 : format("SimWaterThermalTank_WaterTank: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
224 : CompNum,
225 : CompName,
226 0 : state.dataWaterThermalTanks->WaterThermalTank(CompNum).Name));
227 : }
228 0 : state.dataWaterThermalTanks->WaterThermalTank(CompNum).CheckWTTEquipName = false;
229 : }
230 : }
231 :
232 0 : return CompNum;
233 : }
234 :
235 5022 : int getHPTankIDX(EnergyPlusData &state, std::string_view CompName, int &CompIndex)
236 : {
237 5022 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
238 0 : GetWaterThermalTankInput(state);
239 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
240 : }
241 :
242 : int CompNum;
243 :
244 5022 : if (CompIndex == 0) {
245 0 : CompNum = Util::FindItem(CompName, state.dataWaterThermalTanks->HPWaterHeater);
246 0 : if (CompNum == 0) {
247 0 : ShowFatalError(state, format("SimWaterThermalTank_HeatPump: Unit not found={}", CompName));
248 : }
249 0 : CompIndex = CompNum;
250 : } else {
251 5022 : CompNum = CompIndex;
252 5022 : if (CompNum > state.dataWaterThermalTanks->numWaterThermalTank || CompNum < 1) {
253 0 : ShowFatalError(state,
254 0 : format("SimWaterThermalTank_HeatPump: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
255 : CompNum,
256 0 : state.dataWaterThermalTanks->numHeatPumpWaterHeater,
257 : CompName));
258 : }
259 5022 : if (state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName) {
260 1 : if (CompName != state.dataWaterThermalTanks->HPWaterHeater(CompNum).Name) {
261 0 : ShowFatalError(state,
262 0 : format("SimWaterThermalTank_HeatPump: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
263 : CompNum,
264 : CompName,
265 0 : state.dataWaterThermalTanks->HPWaterHeater(CompNum).Name));
266 : }
267 1 : state.dataWaterThermalTanks->HPWaterHeater(CompNum).CheckHPWHEquipName = false;
268 : }
269 : }
270 :
271 5022 : return CompNum;
272 : }
273 :
274 5835179 : void WaterThermalTankData::simulate(
275 : EnergyPlusData &state, const PlantLocation &calledFromLocation, bool FirstHVACIteration, Real64 &CurLoad, [[maybe_unused]] bool RunFlag)
276 : {
277 : // SUBROUTINE INFORMATION:
278 : // AUTHOR Brandon Anderson
279 : // DATE WRITTEN May 2000
280 : // MODIFIED FSEC, July 2005
281 : // RE-ENGINEERED na
282 :
283 : // set the caller loop num to mimic what was happening in plant loop equip
284 5835179 : this->callerLoopNum = calledFromLocation.loopNum;
285 :
286 5835179 : this->oneTimeInit(state);
287 :
288 5835179 : if (this->MyOneTimeFlagWH) {
289 174 : this->MyOneTimeFlagWH = false;
290 : } else {
291 5835005 : if (this->MyTwoTimeFlagWH) {
292 174 : this->MinePlantStructForInfo(state); // call it again to get control types filled out
293 174 : this->MyTwoTimeFlagWH = false;
294 : }
295 : }
296 5835179 : this->UseSideLoadRequested = std::abs(CurLoad);
297 11338444 : if (this->UseSidePlantLoc.loopNum > 0 && this->UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
298 5503265 : !state.dataGlobal->KickOffSimulation) {
299 5482664 : this->UseCurrentFlowLock = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).LoopSide(this->UseSidePlantLoc.loopSideNum).FlowLock;
300 : } else {
301 352515 : this->UseCurrentFlowLock = DataPlant::FlowLock::Locked;
302 : }
303 5835179 : this->initialize(state, FirstHVACIteration);
304 : // Plant connected water heaters may have a desuperheater heating coil attached
305 5835179 : if (this->DesuperheaterNum == 0) {
306 5805695 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) ||
307 656022 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed)) {
308 5547453 : this->CalcWaterThermalTankMixed(state);
309 258242 : } else if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
310 175445 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
311 258242 : this->CalcWaterThermalTankStratified(state);
312 : }
313 29484 : } else if (this->DesuperheaterNum > 0) {
314 29484 : this->CalcDesuperheaterWaterHeater(state, FirstHVACIteration);
315 : }
316 5835179 : this->UpdateWaterThermalTank(state);
317 5835179 : this->ReportWaterThermalTank(state);
318 : // reset the caller loop num to mimic what was happening in PlantLoopEquip
319 5835179 : this->callerLoopNum = 0;
320 5835179 : }
321 :
322 10 : PlantComponent *HeatPumpWaterHeaterData::factory(EnergyPlusData &state, std::string const &objectName)
323 : {
324 : // Process the input data
325 10 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
326 3 : GetWaterThermalTankInput(state);
327 3 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
328 : }
329 :
330 : // Now look for this object in the list
331 28 : for (auto &HPWH : state.dataWaterThermalTanks->HPWaterHeater) {
332 28 : if (HPWH.Name == objectName) {
333 10 : return &HPWH;
334 : }
335 : }
336 : // If we didn't find it, fatal
337 : ShowFatalError(state, format("LocalHeatPumpWaterHeaterFactory: Error getting inputs for object named: {}", objectName)); // LCOV_EXCL_LINE
338 : // Shut up the compiler
339 : return nullptr; // LCOV_EXCL_LINE
340 : }
341 :
342 50 : void HeatPumpWaterHeaterData::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &calledFromLocation)
343 : {
344 50 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
345 50 : Tank.onInitLoopEquip(state, calledFromLocation);
346 50 : }
347 :
348 50 : void HeatPumpWaterHeaterData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
349 : [[maybe_unused]] const PlantLocation &calledFromLocation,
350 : Real64 &MaxLoad,
351 : Real64 &MinLoad,
352 : Real64 &OptLoad)
353 : {
354 50 : MinLoad = 0.0;
355 50 : MaxLoad = this->Capacity;
356 50 : OptLoad = this->Capacity;
357 50 : }
358 :
359 448686 : void HeatPumpWaterHeaterData::simulate(
360 : EnergyPlusData &state, const PlantLocation &calledFromLocation, bool FirstHVACIteration, Real64 &CurLoad, [[maybe_unused]] bool RunFlag)
361 : {
362 : // SUBROUTINE INFORMATION:
363 : // AUTHOR Brandon Anderson
364 : // DATE WRITTEN May 2000
365 : // MODIFIED FSEC, July 2005
366 : // RE-ENGINEERED na
367 :
368 448686 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(this->WaterHeaterTankNum);
369 :
370 : // set caller loop num to mimic what plantloopequip was doing
371 448686 : Tank.callerLoopNum = calledFromLocation.loopNum;
372 :
373 448686 : if (this->myOneTimeInitFlag) {
374 23 : if (Tank.myOneTimeInitFlag) {
375 23 : Tank.setupOutputVars(state);
376 23 : Tank.myOneTimeInitFlag = false;
377 : }
378 23 : this->myOneTimeInitFlag = false;
379 : }
380 :
381 448686 : if (this->MyOneTimeFlagHP) {
382 23 : this->MyOneTimeFlagHP = false;
383 : } else {
384 448663 : if (this->MyTwoTimeFlagHP) {
385 23 : Tank.MinePlantStructForInfo(state); // call it again to get control types filled out
386 23 : this->MyTwoTimeFlagHP = false;
387 : }
388 : }
389 448686 : Tank.UseSideLoadRequested = std::abs(CurLoad);
390 808640 : if (Tank.UseSidePlantLoc.loopNum > 0 && Tank.UseSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid &&
391 359954 : !state.dataGlobal->KickOffSimulation) {
392 358574 : Tank.UseCurrentFlowLock = state.dataPlnt->PlantLoop(Tank.UseSidePlantLoc.loopNum).LoopSide(Tank.UseSidePlantLoc.loopSideNum).FlowLock;
393 : } else {
394 90112 : Tank.UseCurrentFlowLock = DataPlant::FlowLock::Locked;
395 : }
396 :
397 448686 : Tank.initialize(state, FirstHVACIteration);
398 :
399 448686 : int InletNodeSav = this->HeatPumpAirInletNode;
400 448686 : int OutletNodeSav = this->HeatPumpAirOutletNode;
401 448686 : int DXINletNodeSav = this->DXCoilAirInletNode;
402 448686 : int IHPFanIndexSav = this->FanNum;
403 448686 : std::string IHPFanNameSave = this->FanName;
404 448686 : HVAC::FanPlace IHPFanplaceSav = this->fanPlace;
405 :
406 448686 : if (this->bIsIHP) // pass the tank indexes to the IHP object
407 : {
408 10046 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankType = this->HPWHType;
409 10046 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankName = this->Name;
410 10046 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).WHtankID = this->WaterHeaterTankNum;
411 10046 : IntegratedHeatPump::IHPOperationMode IHPMode = IntegratedHeatPump::GetCurWorkMode(state, this->DXCoilNum);
412 :
413 10046 : if ((IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) ||
414 7643 : (IntegratedHeatPump::IHPOperationMode::SpaceClgDedicatedWaterHtg == IHPMode) ||
415 7643 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) ||
416 : (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOn == IHPMode)) { // default is to specify the air nodes for SCWH mode
417 2403 : bool bDWHCoilReading = false;
418 2403 : this->HeatPumpAirInletNode =
419 2403 : VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state,
420 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
421 2403 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
422 : bDWHCoilReading);
423 2403 : this->HeatPumpAirOutletNode =
424 2403 : VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state,
425 : "COIL:WATERHEATING:AIRTOWATERHEATPUMP:VARIABLESPEED",
426 2403 : state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).DWHCoilName,
427 : bDWHCoilReading);
428 2403 : this->DXCoilAirInletNode = this->HeatPumpAirInletNode;
429 2403 : } else // default is to input outdoor fan to the HPWH
430 : {
431 7643 : this->FanNum = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanID;
432 7643 : this->FanName = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).IDFanName;
433 7643 : this->fanPlace = state.dataIntegratedHP->IntegratedHeatPumps(this->DXCoilNum).fanPlace;
434 : }
435 : }
436 :
437 448686 : Tank.CalcHeatPumpWaterHeater(state, FirstHVACIteration);
438 448686 : Tank.UpdateWaterThermalTank(state);
439 448686 : Tank.ReportWaterThermalTank(state);
440 :
441 448686 : this->HeatPumpAirInletNode = InletNodeSav;
442 448686 : this->HeatPumpAirOutletNode = OutletNodeSav;
443 448686 : this->DXCoilAirInletNode = DXINletNodeSav;
444 448686 : this->FanNum = IHPFanIndexSav;
445 448686 : this->FanName = IHPFanNameSave;
446 448686 : this->fanPlace = IHPFanplaceSav;
447 : // reset caller loop num to 0 to mimic what plantloopequip was doing
448 448686 : Tank.callerLoopNum = 0;
449 448686 : }
450 0 : void HeatPumpWaterHeaterData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
451 : {
452 0 : }
453 :
454 1397102 : void SimulateWaterHeaterStandAlone(EnergyPlusData &state, int const WaterHeaterNum, bool const FirstHVACIteration)
455 : {
456 :
457 : // SUBROUTINE INFORMATION:
458 : // AUTHOR Peter Graham Ellis
459 : // DATE WRITTEN January 2004
460 : // MODIFIED July 2005, FSEC - added HPWHs and desuperheater water heating coils
461 : // RE-ENGINEERED na
462 :
463 : // PURPOSE OF THIS SUBROUTINE:
464 : // This subroutine acts an interface to SimWaterHeater for stand-alone water heaters with no plant connections,
465 : // HPWHs not defined as zone equipment with no plant connections, and stand-alone water heaters with
466 : // desuperheater heating coils with no plant connections.
467 :
468 : // METHODOLOGY EMPLOYED:
469 : // The necessary control flags and dummy variables are set and passed into SimWaterHeater. This subroutine is
470 : // called from NonZoneEquipmentManager.
471 :
472 : Real64 MyLoad;
473 :
474 1397102 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
475 7 : GetWaterThermalTankInput(state);
476 7 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
477 : }
478 :
479 1397102 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterHeaterNum);
480 :
481 : // Only simulate stand-alone water heaters here. Plant connected water heaters are called by the PlantLoopEquipments.
482 1397102 : if (Tank.StandAlone) {
483 289134 : bool localRunFlag = true;
484 289134 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
485 289134 : Tank.simulate(state, A, FirstHVACIteration, MyLoad, localRunFlag);
486 :
487 : // HPWHs with inlet air from a zone and not connected to a plant loop are simulated through a CALL from ZoneEquipmentManager.
488 : // HPWHs that are plant connected are always simulated through a CALL from PlantLoopEquipments directly to SimWaterThermalTank.
489 :
490 : // NOTE: HPWHs with inlet air from a zone AND plant connected are not stand alone and are simulated in PlantLoopEquipments
491 1107968 : } else if (Tank.HeatPumpNum > 0) {
492 : // Only HPWHs with inlet air from outdoors or scheduled HPWHs (not connected to a plant loop) are simulated here.
493 :
494 141406 : auto &HPWaterHtr = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum);
495 :
496 141406 : if (HPWaterHtr.StandAlone &&
497 82280 : (HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWaterHtr.InletAirConfiguration == WTTAmbientTemp::Schedule)) {
498 43652 : bool LocalRunFlag = true;
499 43652 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
500 43652 : HPWaterHtr.simulate(state, A, FirstHVACIteration, MyLoad, LocalRunFlag);
501 : }
502 :
503 : // Only simulate stand-alone water heaters with desuperheater water heating coils here. Plant connected water heaters
504 : // with desuperheater water heating coils are called by PlantLoopEquipments.
505 966562 : } else if (Tank.DesuperheaterNum > 0) {
506 29484 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).StandAlone) {
507 29484 : bool localRunFlag = true;
508 29484 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
509 29484 : Tank.simulate(state, A, FirstHVACIteration, MyLoad, localRunFlag);
510 : }
511 : }
512 1397102 : }
513 :
514 132563 : void SimHeatPumpWaterHeater(EnergyPlusData &state,
515 : std::string_view CompName,
516 : bool const FirstHVACIteration,
517 : Real64 &SensLoadMet, // sensible load met by this equipment and sent to zone, W
518 : Real64 &LatLoadMet, // net latent load met and sent to zone (kg/s), dehumid = negative
519 : int &CompIndex)
520 : {
521 : // SUBROUTINE INFORMATION:
522 : // AUTHOR Richard Raustad
523 : // DATE WRITTEN April 2005
524 : // MODIFIED Don Shirey, Aug 2009 (LatLoadMet)
525 : // RE-ENGINEERED na
526 :
527 : // PURPOSE OF THIS SUBROUTINE:
528 : // This subroutine acts as an interface to SimWaterHeater.
529 : // HPWHs defined as zone equipment and not connected to a plant loop are called here by ZoneEquipmentManager
530 :
531 : // METHODOLOGY EMPLOYED:
532 : // The necessary control flags and dummy variables are set and passed into SimWaterHeater.
533 :
534 132563 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
535 0 : GetWaterThermalTankInput(state);
536 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
537 : }
538 :
539 : // Find the correct Heat Pump Water Heater
540 : int HeatPumpNum;
541 132563 : if (CompIndex == 0) {
542 10 : HeatPumpNum = Util::FindItemInList(CompName, state.dataWaterThermalTanks->HPWaterHeater);
543 10 : if (HeatPumpNum == 0) {
544 0 : ShowFatalError(state, format("SimHeatPumpWaterHeater: Unit not found={}", CompName));
545 : }
546 10 : CompIndex = HeatPumpNum;
547 : } else {
548 132553 : HeatPumpNum = CompIndex;
549 132553 : if (HeatPumpNum > state.dataWaterThermalTanks->numHeatPumpWaterHeater || HeatPumpNum < 1) {
550 0 : ShowFatalError(state,
551 0 : format("SimHeatPumpWaterHeater: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
552 : HeatPumpNum,
553 0 : state.dataWaterThermalTanks->numHeatPumpWaterHeater,
554 : CompName));
555 : }
556 : }
557 :
558 : // Only simulate HPWHs specified as zone equipment and not connected to a plant loop.
559 : // HPWHs not defined as zone equipment with no plant connections are simulated in NonZoneEquipmentManager.
560 : // Plant connected HPWHs are called by PlantLoopEquipments (but only those on supply side ).
561 : // HPWH will not be included in sizing calculations, fan is initialized only during BeginEnvrnFlag (FALSE during sizing)
562 : // (fan will be turned off during Standard Ratings procedure yielding incorrect results)
563 132563 : if (state.dataGlobal->DoingSizing) return;
564 :
565 : // For HPWHs, StandAlone means not connected to a plant loop (use nodes are not used, source nodes are connected to a HPWH)
566 132553 : if (state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).StandAlone) {
567 40058 : bool LocalRunFlag = true;
568 : Real64 MyLoad;
569 :
570 40058 : PlantLocation A(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
571 40058 : state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).simulate(state, A, FirstHVACIteration, MyLoad, LocalRunFlag);
572 :
573 40058 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
574 40058 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
575 : } else {
576 : // HPWH is plant connected and will get simulated when called from plant SimWaterThermalTank, but need to update loads met here
577 92495 : SensLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterSensibleCapacity;
578 92495 : LatLoadMet = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpNum).HPWaterHeaterLatentCapacity;
579 : }
580 : }
581 :
582 2804678 : void CalcWaterThermalTankZoneGains(EnergyPlusData &state)
583 : {
584 :
585 : // SUBROUTINE INFORMATION:
586 : // AUTHOR Peter Graham Ellis
587 : // DATE WRITTEN March 2005
588 : // MODIFIED B. Griffith November 2011, new internal gains structure
589 : // RE-ENGINEERED na
590 :
591 : // PURPOSE OF THIS SUBROUTINE:
592 : // Calculates the zone internal gains due to water heater skin losses during sizing.
593 : // initializes gains to zone at begin environment.
594 :
595 : // METHODOLOGY EMPLOYED:
596 : // Sums the tank losses from all of the water heaters in the zone to add as a gain to the zone.
597 : // Now used to determine tank losses during sizing. Internal gains are summed in a centralized way now
598 :
599 2804678 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) {
600 :
601 2285644 : if (!state.dataGlobal->DoingSizing) {
602 1688154 : return;
603 : } else {
604 597490 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
605 422 : GetWaterThermalTankInput(state);
606 422 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
607 : }
608 597490 : if (state.dataWaterThermalTanks->numWaterThermalTank == 0) return;
609 : }
610 : }
611 :
612 519146 : if (state.dataGlobal->BeginEnvrnFlag && state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag) {
613 3317 : for (auto &e : state.dataWaterThermalTanks->WaterThermalTank) {
614 1989 : e.AmbientZoneGain = 0.0;
615 1989 : e.FuelEnergy = 0.0;
616 1989 : e.OffCycParaFuelEnergy = 0.0;
617 1989 : e.OnCycParaFuelEnergy = 0.0;
618 : }
619 1328 : state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = false;
620 : }
621 :
622 519146 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataWaterThermalTanks->calcWaterThermalTankZoneGainsMyEnvrnFlag = true;
623 :
624 1226483 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
625 707337 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
626 707337 : if (Tank.AmbientTempZone == 0) continue;
627 237555 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(Tank.AmbientTempZone);
628 237555 : if (state.dataGlobal->DoingSizing) {
629 : // Initialize tank temperature to setpoint
630 : // (use HPWH or Desuperheater heating coil set point if applicable)
631 : int SchIndex;
632 107106 : if (Tank.HeatPumpNum > 0) {
633 14844 : SchIndex = state.dataWaterThermalTanks->HPWaterHeater(Tank.HeatPumpNum).SetPointTempSchedule;
634 92262 : } else if (Tank.DesuperheaterNum > 0) {
635 1350 : SchIndex = state.dataWaterThermalTanks->WaterHeaterDesuperheater(Tank.DesuperheaterNum).SetPointTempSchedule;
636 : } else {
637 90912 : SchIndex = Tank.SetPointTempSchedule;
638 : }
639 :
640 : Real64 TankTemp;
641 107106 : Real64 QLossToZone = 0.0;
642 107106 : if (SchIndex > 0) {
643 107106 : TankTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
644 : } else {
645 0 : TankTemp = 20.0;
646 : }
647 107106 : switch (Tank.WaterThermalTankType) {
648 90918 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
649 90918 : QLossToZone = max(Tank.OnCycLossCoeff * Tank.OnCycLossFracToZone, Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone) *
650 90918 : (TankTemp - thisZoneHB.MAT);
651 90918 : break;
652 : }
653 8094 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
654 8094 : QLossToZone = max(Tank.Node(1).OnCycLossCoeff * Tank.SkinLossFracToZone, Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone) *
655 8094 : (TankTemp - thisZoneHB.MAT);
656 8094 : break;
657 : }
658 4050 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
659 4050 : QLossToZone = Tank.OffCycLossCoeff * Tank.OffCycLossFracToZone * (TankTemp - thisZoneHB.MAT);
660 4050 : break;
661 : }
662 4044 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
663 4044 : QLossToZone = Tank.Node(1).OffCycLossCoeff * Tank.SkinLossFracToZone * (TankTemp - thisZoneHB.MAT);
664 4044 : break;
665 : }
666 0 : default:
667 0 : break;
668 : }
669 107106 : Tank.AmbientZoneGain = QLossToZone;
670 : }
671 : }
672 : }
673 :
674 6 : bool getDesuperHtrInput(EnergyPlusData &state)
675 : {
676 6 : bool ErrorsFound = false;
677 : static constexpr std::string_view RoutineName = "getDesuperHtrInput";
678 : // Make local copies of IPShortCut because other getinputs might overwrite the ones in state <-- need to fix this idiom
679 6 : std::string cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
680 6 : Array1D<std::string> cAlphaArgs = state.dataIPShortCut->cAlphaArgs;
681 6 : Array1D<Real64> rNumericArgs = state.dataIPShortCut->rNumericArgs;
682 6 : Array1D<bool> lNumericFieldBlanks = state.dataIPShortCut->lNumericFieldBlanks;
683 6 : Array1D<bool> lAlphaFieldBlanks = state.dataIPShortCut->lAlphaFieldBlanks;
684 6 : Array1D<std::string> cAlphaFieldNames = state.dataIPShortCut->cAlphaFieldNames;
685 6 : Array1D<std::string> cNumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
686 :
687 6 : cCurrentModuleObject = cCoilDesuperheater;
688 12 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
689 : int NumAlphas;
690 : int NumNums;
691 : int IOStat;
692 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
693 : cCurrentModuleObject,
694 : DesuperheaterNum,
695 : cAlphaArgs,
696 : NumAlphas,
697 : rNumericArgs,
698 : NumNums,
699 : IOStat,
700 : lNumericFieldBlanks,
701 : lAlphaFieldBlanks,
702 : cAlphaFieldNames,
703 : cNumericFieldNames);
704 6 : Util::IsNameEmpty(state, cAlphaArgs(1), cCurrentModuleObject, ErrorsFound);
705 :
706 : // ErrorsFound will be set to True if problem was found, left untouched otherwise
707 6 : GlobalNames::VerifyUniqueCoilName(state, cCurrentModuleObject, cAlphaArgs(1), ErrorsFound, cCurrentModuleObject + " Name");
708 :
709 6 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
710 :
711 6 : DesupHtr.Name = cAlphaArgs(1);
712 6 : DesupHtr.Type = cCurrentModuleObject;
713 :
714 : // convert availability schedule name to pointer
715 6 : if (!lAlphaFieldBlanks(2)) {
716 6 : DesupHtr.AvailSchedPtr = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(2));
717 6 : if (DesupHtr.AvailSchedPtr == 0) {
718 0 : ShowSevereError(state, format("Invalid, {} = {}", cAlphaFieldNames(2), cAlphaArgs(2)));
719 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, cAlphaArgs(1)));
720 0 : ErrorsFound = true;
721 : }
722 : } else {
723 0 : DesupHtr.AvailSchedPtr = ScheduleManager::ScheduleAlwaysOn;
724 : }
725 :
726 : // convert schedule name to pointer
727 6 : DesupHtr.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(3));
728 6 : if (DesupHtr.SetPointTempSchedule == 0) {
729 0 : ShowSevereError(state, format("Invalid, {} = {}", cAlphaFieldNames(3), cAlphaArgs(3)));
730 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, cAlphaArgs(1)));
731 0 : ErrorsFound = true;
732 : }
733 :
734 6 : DesupHtr.DeadBandTempDiff = rNumericArgs(1);
735 6 : if (DesupHtr.DeadBandTempDiff <= 0.0 || DesupHtr.DeadBandTempDiff > 20.0) {
736 0 : ShowSevereError(state,
737 0 : format("{} = {}: {} must be > 0 and <= 20. {} = {:.1T}",
738 : cCurrentModuleObject,
739 0 : DesupHtr.Name,
740 : cNumericFieldNames(1),
741 : cNumericFieldNames(1),
742 : rNumericArgs(1)));
743 0 : ErrorsFound = true;
744 : }
745 :
746 : // Error limits on heat reclaim efficiency applied after source type identified
747 :
748 6 : DesupHtr.RatedInletWaterTemp = rNumericArgs(3);
749 6 : DesupHtr.RatedOutdoorAirTemp = rNumericArgs(4);
750 6 : DesupHtr.MaxInletWaterTemp = rNumericArgs(5);
751 :
752 6 : if (!lAlphaFieldBlanks(4)) {
753 6 : DesupHtr.HEffFTemp = Curve::GetCurveIndex(state, cAlphaArgs(4));
754 6 : if (DesupHtr.HEffFTemp == 0) {
755 0 : ShowSevereError(state,
756 0 : format("{} = {}: {} not found = {}", cCurrentModuleObject, DesupHtr.Name, cAlphaFieldNames(4), cAlphaArgs(4)));
757 0 : ErrorsFound = true;
758 : } else {
759 6 : ErrorsFound |= Curve::CheckCurveDims(state,
760 : DesupHtr.HEffFTemp, // Curve index
761 : {2}, // Valid dimensions
762 : RoutineName, // Routine name
763 : cCurrentModuleObject, // Object Type
764 : DesupHtr.Name, // Object Name
765 6 : cAlphaFieldNames(4)); // Field Name
766 6 : if (!ErrorsFound) {
767 6 : if (DesupHtr.HEffFTemp > 0) {
768 6 : Real64 HEffFTemp = min(
769 6 : 1.0, max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, DesupHtr.RatedInletWaterTemp, DesupHtr.RatedOutdoorAirTemp)));
770 6 : if (std::abs(HEffFTemp - 1.0) > 0.05) {
771 0 : ShowWarningError(state, format("{}, \"{}\":", cCurrentModuleObject, DesupHtr.Name));
772 0 : ShowContinueError(state, format("The {} should be normalized ", cAlphaFieldNames(4)));
773 0 : ShowContinueError(state, format(" to 1.0 at the rating point. Curve output at the rating point = {:.3T}", HEffFTemp));
774 0 : ShowContinueError(state, " The simulation continues using the user-specified curve.");
775 : }
776 : }
777 : }
778 : }
779 : }
780 :
781 6 : DesupHtr.WaterInletNode = NodeInputManager::GetOnlySingleNode(state,
782 6 : cAlphaArgs(5),
783 : ErrorsFound,
784 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
785 6 : cAlphaArgs(1),
786 : DataLoopNode::NodeFluidType::Water,
787 : DataLoopNode::ConnectionType::Inlet,
788 : NodeInputManager::CompFluidStream::Primary,
789 : DataLoopNode::ObjectIsParent);
790 :
791 6 : DesupHtr.WaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
792 6 : cAlphaArgs(6),
793 : ErrorsFound,
794 : DataLoopNode::ConnectionObjectType::CoilWaterHeatingDesuperheater,
795 6 : cAlphaArgs(1),
796 : DataLoopNode::NodeFluidType::Water,
797 : DataLoopNode::ConnectionType::Outlet,
798 : NodeInputManager::CompFluidStream::Primary,
799 : DataLoopNode::ObjectIsParent);
800 :
801 6 : DesupHtr.InletNodeName1 = cAlphaArgs(5);
802 6 : DesupHtr.OutletNodeName1 = cAlphaArgs(6);
803 :
804 6 : DesupHtr.TankType = cAlphaArgs(7);
805 :
806 6 : if (!Util::SameString(DesupHtr.TankType, cMixedWHModuleObj) && !Util::SameString(DesupHtr.TankType, cStratifiedWHModuleObj)) {
807 :
808 0 : ShowSevereError(state, format("{} = {}:", cCurrentModuleObject, state.dataWaterThermalTanks->HPWaterHeater(DesuperheaterNum).Name));
809 0 : ShowContinueError(state, format("Desuperheater can only be used with {} or {}.", cMixedWHModuleObj, cStratifiedWHModuleObj));
810 0 : ErrorsFound = true;
811 : }
812 :
813 6 : DesupHtr.TankName = cAlphaArgs(8);
814 :
815 : // Set up comp set for water side nodes (reverse inlet/outlet for water heater)
816 6 : BranchNodeConnections::SetUpCompSets(state, DesupHtr.Type, DesupHtr.Name, DesupHtr.TankType, DesupHtr.TankName, cAlphaArgs(6), cAlphaArgs(5));
817 :
818 6 : std::string const heatSourceObjType = cAlphaArgs(9);
819 :
820 11 : if ((Util::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
821 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
822 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
823 1 : if (lNumericFieldBlanks(2)) {
824 0 : DesupHtr.HeatReclaimRecoveryEff = 0.8;
825 : } else {
826 1 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
827 1 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.9) {
828 0 : ShowSevereError(state,
829 0 : format("{} = {}: {} must be > 0.0 and <= 0.9, Efficiency = {:.3T}",
830 : cCurrentModuleObject,
831 0 : DesupHtr.Name,
832 : cNumericFieldNames(2),
833 0 : DesupHtr.HeatReclaimRecoveryEff));
834 0 : ErrorsFound = true;
835 : }
836 : } // Blank Num(2)
837 : } else { // max is 0.3 for all other sources
838 5 : if (lNumericFieldBlanks(2)) {
839 0 : DesupHtr.HeatReclaimRecoveryEff = 0.25;
840 : } else {
841 5 : DesupHtr.HeatReclaimRecoveryEff = rNumericArgs(2);
842 5 : if (DesupHtr.HeatReclaimRecoveryEff <= 0.0 || DesupHtr.HeatReclaimRecoveryEff > 0.3) {
843 0 : ShowSevereError(state,
844 0 : format("{} = {}: {} must be > 0.0 and <= 0.3, {} = {:.3T}",
845 : cCurrentModuleObject,
846 0 : DesupHtr.Name,
847 : cNumericFieldNames(2),
848 : cNumericFieldNames(2),
849 0 : DesupHtr.HeatReclaimRecoveryEff));
850 0 : ErrorsFound = true;
851 : }
852 : } // Blank Num(2)
853 : } // setting limits on heat recovery efficiency
854 :
855 : // Find the Refrigeration equipment index associated with the desuperheater heating coil.
856 6 : bool errFlag = false;
857 6 : DesupHtr.HeatingSourceType = heatSourceObjType;
858 6 : DesupHtr.HeatingSourceName = cAlphaArgs(10);
859 6 : if (Util::SameString(heatSourceObjType, "Refrigeration:CompressorRack")) {
860 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CompressorRackRefrigeratedCase;
861 0 : for (int RackNum = 1; RackNum <= state.dataRefrigCase->NumRefrigeratedRacks; ++RackNum) {
862 0 : if (!Util::SameString(state.dataHeatBal->HeatReclaimRefrigeratedRack(RackNum).Name, cAlphaArgs(10))) continue;
863 0 : DesupHtr.ReclaimHeatingSourceIndexNum = RackNum;
864 0 : if (allocated(state.dataHeatBal->HeatReclaimRefrigeratedRack)) {
865 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
866 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(DesupHtr.ReclaimHeatingSourceIndexNum);
867 0 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
868 0 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
869 0 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
870 0 : num = 0.0;
871 : }
872 0 : DesupHtr.ValidSourceType = true;
873 0 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
874 0 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
875 0 : ShowSevereError(
876 : state,
877 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
878 : cCurrentModuleObject,
879 0 : DesupHtr.Name,
880 0 : DesupHtr.HeatingSourceName));
881 0 : ErrorsFound = true;
882 : }
883 : }
884 0 : break;
885 : }
886 11 : } else if ((Util::SameString(heatSourceObjType, "Refrigeration:Condenser:AirCooled")) ||
887 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:EvaporativeCooled")) ||
888 11 : (Util::SameString(heatSourceObjType, "Refrigeration:Condenser:WaterCooled"))) {
889 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CondenserRefrigeration;
890 2 : for (int CondNum = 1; CondNum <= state.dataRefrigCase->NumRefrigCondensers; ++CondNum) {
891 2 : if (!Util::SameString(state.dataHeatBal->HeatReclaimRefrigCondenser(CondNum).Name, cAlphaArgs(10))) continue;
892 1 : DesupHtr.ReclaimHeatingSourceIndexNum = CondNum;
893 1 : if (allocated(state.dataHeatBal->HeatReclaimRefrigCondenser)) {
894 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
895 1 : state.dataHeatBal->HeatReclaimRefrigCondenser(DesupHtr.ReclaimHeatingSourceIndexNum);
896 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
897 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
898 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
899 1 : num = 0.0;
900 : }
901 1 : DesupHtr.ValidSourceType = true;
902 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
903 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.9) {
904 0 : ShowSevereError(
905 : state,
906 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.9",
907 : cCurrentModuleObject,
908 0 : DesupHtr.Name,
909 0 : DesupHtr.HeatingSourceName));
910 0 : ErrorsFound = true;
911 : }
912 : }
913 1 : break;
914 : }
915 8 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed") ||
916 6 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoSpeed") ||
917 11 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:MultiSpeed") ||
918 8 : Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
919 :
920 2 : if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:SingleSpeed")) {
921 2 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXCooling;
922 0 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:TwoStageWithHumidityControlMode")) {
923 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiMode;
924 : } else {
925 0 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXMultiSpeed;
926 : }
927 2 : DXCoils::GetDXCoilIndex(state, DesupHtr.HeatingSourceName, DesupHtr.ReclaimHeatingSourceIndexNum, errFlag, cCurrentModuleObject);
928 2 : if (allocated(state.dataHeatBal->HeatReclaimDXCoil)) {
929 2 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimDXCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
930 2 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
931 2 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
932 4 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
933 2 : num = 0.0;
934 : }
935 2 : DesupHtr.ValidSourceType = true;
936 2 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
937 2 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
938 0 : ShowSevereError(state,
939 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
940 : cCurrentModuleObject,
941 0 : DesupHtr.Name,
942 0 : DesupHtr.HeatingSourceName));
943 0 : ErrorsFound = true;
944 : }
945 : }
946 3 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX:VariableSpeed")) {
947 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::DXVariableCooling;
948 1 : DesupHtr.ReclaimHeatingSourceIndexNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(state, heatSourceObjType, cAlphaArgs(10), errFlag);
949 1 : if (allocated(state.dataHeatBal->HeatReclaimVS_DXCoil)) {
950 1 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim = state.dataHeatBal->HeatReclaimVS_DXCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
951 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
952 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
953 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
954 1 : num = 0.0;
955 : }
956 1 : DesupHtr.ValidSourceType = true;
957 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
958 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
959 0 : ShowSevereError(state,
960 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
961 : cCurrentModuleObject,
962 0 : DesupHtr.Name,
963 0 : DesupHtr.HeatingSourceName));
964 0 : ErrorsFound = true;
965 : }
966 : }
967 2 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:WaterToAirHeatPump:EquationFit")) {
968 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::AirWaterHeatPumpEQ;
969 1 : DesupHtr.ReclaimHeatingSourceIndexNum = WaterToAirHeatPumpSimple::GetCoilIndex(state, heatSourceObjType, cAlphaArgs(10), errFlag);
970 1 : if (allocated(state.dataHeatBal->HeatReclaimSimple_WAHPCoil)) {
971 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
972 1 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(DesupHtr.ReclaimHeatingSourceIndexNum);
973 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
974 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
975 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
976 1 : num = 0.0;
977 : }
978 1 : DesupHtr.ValidSourceType = true;
979 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
980 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
981 0 : ShowSevereError(state,
982 0 : format("{} = {}: sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
983 : cCurrentModuleObject,
984 0 : DesupHtr.Name,
985 0 : DesupHtr.HeatingSourceName));
986 0 : ErrorsFound = true;
987 : }
988 : }
989 1 : } else if (Util::SameString(heatSourceObjType, "Coil:Cooling:DX")) {
990 1 : DesupHtr.ReclaimHeatingSource = ReclaimHeatObjectType::CoilCoolingDX;
991 1 : DesupHtr.ReclaimHeatingSourceIndexNum = CoilCoolingDX::factory(state, cAlphaArgs(10));
992 1 : if (DesupHtr.ReclaimHeatingSourceIndexNum < 0) {
993 0 : ShowSevereError(
994 : state,
995 0 : format("{}={}, could not find desuperheater coil {}={}", cCurrentModuleObject, DesupHtr.Name, cAlphaArgs(9), cAlphaArgs(10)));
996 0 : ErrorsFound = true;
997 : } else {
998 : DataHeatBalance::HeatReclaimDataBase &HeatReclaim =
999 1 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat;
1000 1 : if (!allocated(HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)) {
1001 1 : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
1002 2 : for (auto &num : HeatReclaim.WaterHeatingDesuperheaterReclaimedHeat)
1003 1 : num = 0.0;
1004 : }
1005 1 : DesupHtr.ValidSourceType = true;
1006 1 : HeatReclaim.ReclaimEfficiencyTotal += DesupHtr.HeatReclaimRecoveryEff;
1007 1 : if (HeatReclaim.ReclaimEfficiencyTotal > 0.3) {
1008 0 : ShowSevereError(
1009 : state,
1010 0 : format("{}, \"{}\" sum of heat reclaim recovery efficiencies from the same source coil: \"{}\" cannot be over 0.3",
1011 : cCurrentModuleObject,
1012 0 : DesupHtr.Name,
1013 0 : DesupHtr.HeatingSourceName));
1014 0 : ErrorsFound = true;
1015 : }
1016 : }
1017 : } else {
1018 0 : ShowSevereError(state, format("{} = {}:", cCurrentModuleObject, DesupHtr.Name));
1019 0 : ShowContinueError(state, " desuperheater can only be used with Coil:Cooling:DX:SingleSpeed, ");
1020 0 : ShowContinueError(state,
1021 : " Coil:Cooling:DX:TwoSpeed, Coil:Cooling:DX:MultiSpeed, Coil:Cooling:DX:TwoStageWithHumidityControlMode, "
1022 : "Coil:Cooling:DX:VariableSpeed, "
1023 : "Coil:Cooling:WaterToAirHeatPump:EquationFit, Refrigeration:CompressorRack,");
1024 0 : ShowContinueError(state, " Refrigeration:Condenser:AirCooled ,Refrigeration:Condenser:EvaporativeCooled, ");
1025 0 : ShowContinueError(state, " or Refrigeration:Condenser:WaterCooled.");
1026 0 : ShowContinueError(state, format(" Invalid desuperheater heat source object: {} \"{}\"", heatSourceObjType, cAlphaArgs(10)));
1027 0 : ErrorsFound = true;
1028 : }
1029 6 : if (errFlag) {
1030 0 : ShowContinueError(state, format("...occurs in {}={}", cCurrentModuleObject, DesupHtr.Name));
1031 0 : ErrorsFound = true;
1032 : }
1033 :
1034 6 : if (DesupHtr.ReclaimHeatingSourceIndexNum == 0 && DesupHtr.ReclaimHeatingSource != ReclaimHeatObjectType::CoilCoolingDX) {
1035 0 : ShowSevereError(state,
1036 0 : format("{}, \"{}\" desuperheater heat source object not found: {} \"{}\"",
1037 : cCurrentModuleObject,
1038 0 : DesupHtr.Name,
1039 : heatSourceObjType,
1040 : cAlphaArgs(10)));
1041 0 : ErrorsFound = true;
1042 : }
1043 :
1044 6 : DesupHtr.OperatingWaterFlowRate = rNumericArgs(6);
1045 6 : if (DesupHtr.OperatingWaterFlowRate <= 0.0) {
1046 0 : ShowSevereError(state,
1047 0 : format("{} = {}: {} must be greater than 0. {} = {:.6T}",
1048 : cCurrentModuleObject,
1049 0 : DesupHtr.Name,
1050 : cNumericFieldNames(6),
1051 : cNumericFieldNames(6),
1052 : rNumericArgs(6)));
1053 0 : ErrorsFound = true;
1054 : }
1055 :
1056 6 : DesupHtr.PumpElecPower = rNumericArgs(7);
1057 6 : if (DesupHtr.PumpElecPower < 0.0) {
1058 0 : ShowSevereError(state,
1059 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1060 : cCurrentModuleObject,
1061 0 : DesupHtr.Name,
1062 : cNumericFieldNames(7),
1063 : cNumericFieldNames(7),
1064 : rNumericArgs(7)));
1065 0 : ErrorsFound = true;
1066 : }
1067 :
1068 6 : if ((DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate) > 7.9264e6) {
1069 0 : ShowWarningError(state,
1070 0 : format("{} = {}: {} to {} ratio > 7.9264E6. {} to {} = {:.3T}",
1071 : cCurrentModuleObject,
1072 0 : DesupHtr.Name,
1073 : cNumericFieldNames(7),
1074 : cNumericFieldNames(6),
1075 : cNumericFieldNames(7),
1076 : cNumericFieldNames(6),
1077 0 : (DesupHtr.PumpElecPower / DesupHtr.OperatingWaterFlowRate)));
1078 0 : ShowContinueError(state, format(" Suggest reducing {} or increasing {}.", cNumericFieldNames(7), cNumericFieldNames(6)));
1079 0 : ShowContinueError(state, " The simulation will continue using the user defined values.");
1080 : }
1081 :
1082 6 : DesupHtr.PumpFracToWater = rNumericArgs(8);
1083 6 : if (DesupHtr.PumpFracToWater < 0.0 || DesupHtr.PumpFracToWater > 1.0) {
1084 0 : ShowSevereError(state,
1085 0 : format("{} = {}: {} must be >= 0 or <= 1. {} = {:.3T}",
1086 : cCurrentModuleObject,
1087 0 : DesupHtr.Name,
1088 : cNumericFieldNames(8),
1089 : cNumericFieldNames(8),
1090 : rNumericArgs(8)));
1091 0 : ErrorsFound = true;
1092 : }
1093 :
1094 6 : DesupHtr.OnCycParaLoad = rNumericArgs(9);
1095 6 : if (DesupHtr.OnCycParaLoad < 0.0) {
1096 0 : ShowSevereError(state,
1097 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1098 : cCurrentModuleObject,
1099 0 : DesupHtr.Name,
1100 : cNumericFieldNames(9),
1101 : cNumericFieldNames(9),
1102 : rNumericArgs(9)));
1103 0 : ErrorsFound = true;
1104 : }
1105 :
1106 6 : DesupHtr.OffCycParaLoad = rNumericArgs(10);
1107 6 : if (DesupHtr.OffCycParaLoad < 0.0) {
1108 0 : ShowSevereError(state,
1109 0 : format("{} = {}: {} must be >= 0. {} = {:.2T}",
1110 : cCurrentModuleObject,
1111 0 : DesupHtr.Name,
1112 : cNumericFieldNames(10),
1113 : cNumericFieldNames(10),
1114 : rNumericArgs(10)));
1115 0 : ErrorsFound = true;
1116 : }
1117 6 : }
1118 :
1119 6 : if (ErrorsFound) {
1120 0 : ShowFatalError(state, format("Errors found in getting {} input. Preceding condition causes termination.", cCurrentModuleObject));
1121 : }
1122 :
1123 6 : return ErrorsFound;
1124 :
1125 6 : } // namespace WaterThermalTanks
1126 :
1127 9 : bool getHPWaterHeaterInput(EnergyPlusData &state)
1128 : {
1129 :
1130 : static constexpr std::string_view routineName = "getHPWaterHeaterInput";
1131 9 : bool ErrorsFound = false;
1132 :
1133 9 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
1134 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
1135 : int nAlphaOffset; // the difference of array location between alpha items between pumped and wrapped condensers
1136 : int nNumericOffset; // the difference of array location between numeric items between pumped and wrapped condensers
1137 : int nNumPossibleNumericArgs; // the number of possible numeric arguments in the idd
1138 : int nNumPossibleAlphaArgs; // the number of possible numeric arguments in the idd
1139 :
1140 : // For looking up in IDF/epJSON, you need the index that corresponds to the actual object type (Pumped or Wrapped)
1141 : int HPWaterHeaterNumOfSpecificType;
1142 :
1143 32 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
1144 :
1145 : // Create reference to current HPWH object in array.
1146 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
1147 :
1148 : // Initialize the offsets to zero
1149 23 : nAlphaOffset = 0;
1150 23 : nNumericOffset = 0;
1151 :
1152 : DataLoopNode::ConnectionObjectType objType;
1153 :
1154 23 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
1155 : // Pumped Condenser
1156 20 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
1157 20 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpPumpedCondenser;
1158 20 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped;
1159 20 : nNumPossibleAlphaArgs = 29;
1160 20 : nNumPossibleNumericArgs = 9;
1161 : // Actual index of Pumped type
1162 20 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum;
1163 : } else {
1164 : // Wrapped Condenser
1165 3 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
1166 3 : objType = DataLoopNode::ConnectionObjectType::WaterHeaterHeatPumpWrappedCondenser;
1167 3 : HPWH.HPWHType = DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped;
1168 3 : nNumPossibleAlphaArgs = 27;
1169 3 : nNumPossibleNumericArgs = 10;
1170 : // Actual index of Wrapped type
1171 3 : HPWaterHeaterNumOfSpecificType = HPWaterHeaterNum - NumPumpedCondenser;
1172 : }
1173 :
1174 : int NumAlphas;
1175 : int NumNums;
1176 : int IOStat;
1177 46 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1178 23 : state.dataIPShortCut->cCurrentModuleObject,
1179 : HPWaterHeaterNumOfSpecificType,
1180 23 : state.dataIPShortCut->cAlphaArgs,
1181 : NumAlphas,
1182 23 : state.dataIPShortCut->rNumericArgs,
1183 : NumNums,
1184 : IOStat,
1185 23 : state.dataIPShortCut->lNumericFieldBlanks,
1186 23 : state.dataIPShortCut->lAlphaFieldBlanks,
1187 23 : state.dataIPShortCut->cAlphaFieldNames,
1188 23 : state.dataIPShortCut->cNumericFieldNames);
1189 :
1190 23 : ErrorObjectHeader eoh{routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
1191 :
1192 : // Copy those lists into C++ std::maps // Why, no really why? This is really dumb
1193 23 : std::map<int, std::string> hpwhAlpha;
1194 23 : std::map<int, Real64> hpwhNumeric;
1195 23 : std::map<int, bool> hpwhAlphaBlank;
1196 23 : std::map<int, bool> hpwhNumericBlank;
1197 23 : std::map<int, std::string> hpwhAlphaFieldNames;
1198 23 : std::map<int, std::string> hpwhNumericFieldNames;
1199 204 : for (int i = 1; i <= NumNums; ++i) {
1200 181 : hpwhNumeric[i] = state.dataIPShortCut->rNumericArgs(i);
1201 181 : hpwhNumericBlank[i] = state.dataIPShortCut->lNumericFieldBlanks(i);
1202 181 : hpwhNumericFieldNames[i] = state.dataIPShortCut->cNumericFieldNames(i);
1203 : }
1204 55 : for (int i = NumNums + 1; i <= nNumPossibleNumericArgs; ++i) {
1205 32 : hpwhNumericBlank[i] = true;
1206 : }
1207 642 : for (int i = 1; i <= NumAlphas; ++i) {
1208 619 : hpwhAlpha[i] = state.dataIPShortCut->cAlphaArgs(i);
1209 619 : hpwhAlphaBlank[i] = state.dataIPShortCut->lAlphaFieldBlanks(i);
1210 619 : hpwhAlphaFieldNames[i] = state.dataIPShortCut->cAlphaFieldNames(i);
1211 : }
1212 65 : for (int i = NumAlphas + 1; i <= nNumPossibleAlphaArgs; ++i) {
1213 42 : hpwhAlphaBlank[i] = true;
1214 : }
1215 23 : Util::IsNameEmpty(state, hpwhAlpha[1], state.dataIPShortCut->cCurrentModuleObject, ErrorsFound);
1216 :
1217 : // Name and type
1218 23 : HPWH.Name = hpwhAlpha[1];
1219 23 : HPWH.Type = state.dataIPShortCut->cCurrentModuleObject;
1220 :
1221 : // Availability Schedule
1222 : // convert schedule name to pointer
1223 23 : if (!hpwhAlphaBlank[2]) {
1224 23 : HPWH.AvailSchedPtr = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[2]);
1225 23 : if (HPWH.AvailSchedPtr == 0) {
1226 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1227 0 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[2], hpwhAlpha[2]));
1228 0 : ErrorsFound = true;
1229 : }
1230 : } else {
1231 0 : HPWH.AvailSchedPtr = ScheduleManager::ScheduleAlwaysOn;
1232 : }
1233 :
1234 : // Compressor Setpoint Temperature Schedule
1235 : // convert schedule name to pointer
1236 23 : if (!hpwhAlphaBlank[3]) {
1237 23 : HPWH.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[3]);
1238 23 : if (HPWH.SetPointTempSchedule == 0) {
1239 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1240 0 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[3], hpwhAlpha[3]));
1241 0 : ErrorsFound = true;
1242 : }
1243 : } else {
1244 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1245 0 : ShowContinueError(state, format("required {} is blank.", hpwhAlphaFieldNames[3]));
1246 0 : ErrorsFound = true;
1247 : }
1248 :
1249 : // Dead Band Temperature Difference
1250 23 : HPWH.DeadBandTempDiff = hpwhNumeric[1 + nNumericOffset];
1251 23 : if (HPWH.DeadBandTempDiff <= 0.0 || HPWH.DeadBandTempDiff > 20.0) {
1252 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1253 0 : ShowContinueError(state,
1254 0 : format("{}{}",
1255 0 : hpwhNumericFieldNames[1 + nNumericOffset],
1256 0 : format(" difference must be > 0 and <= 20. Dead band = {:.1T}", hpwhNumeric[1 + nNumericOffset])));
1257 0 : ErrorsFound = true;
1258 : }
1259 :
1260 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1261 :
1262 : // Condenser Inlet/Outlet Nodes
1263 20 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1264 20 : hpwhAlpha[4],
1265 : ErrorsFound,
1266 : objType,
1267 20 : HPWH.Name,
1268 : DataLoopNode::NodeFluidType::Water,
1269 : DataLoopNode::ConnectionType::Inlet,
1270 : NodeInputManager::CompFluidStream::Secondary,
1271 : DataLoopNode::ObjectIsParent);
1272 20 : HPWH.InletNodeName1 = hpwhAlpha[4];
1273 20 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1274 20 : hpwhAlpha[5],
1275 : ErrorsFound,
1276 : objType,
1277 20 : HPWH.Name,
1278 : DataLoopNode::NodeFluidType::Water,
1279 : DataLoopNode::ConnectionType::Outlet,
1280 : NodeInputManager::CompFluidStream::Secondary,
1281 : DataLoopNode::ObjectIsParent);
1282 20 : HPWH.OutletNodeName1 = hpwhAlpha[5];
1283 :
1284 : // Condenser Water Flow Rate
1285 20 : HPWH.OperatingWaterFlowRate = hpwhNumeric[2];
1286 20 : if (HPWH.OperatingWaterFlowRate <= 0.0 && hpwhNumeric[2] != Constant::AutoCalculate) {
1287 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1288 0 : ShowContinueError(state,
1289 0 : format("{} must be greater than 0. Condenser water flow rate = {:.6T}", hpwhNumericFieldNames[2], hpwhNumeric[2]));
1290 0 : ErrorsFound = true;
1291 : }
1292 :
1293 3 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1294 :
1295 : // Wrapped Condenser Location
1296 3 : HPWH.WrappedCondenserBottomLocation = hpwhNumeric[2 + nNumericOffset];
1297 3 : HPWH.WrappedCondenserTopLocation = hpwhNumeric[3 + nNumericOffset];
1298 :
1299 3 : if (HPWH.WrappedCondenserBottomLocation < 0.0) {
1300 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1301 0 : ShowContinueError(state,
1302 0 : format("{} must be greater than 0. Condenser bottom location = {:.6T}",
1303 0 : hpwhNumericFieldNames[2],
1304 0 : HPWH.WrappedCondenserBottomLocation));
1305 0 : ErrorsFound = true;
1306 : }
1307 :
1308 3 : if (HPWH.WrappedCondenserBottomLocation >= HPWH.WrappedCondenserTopLocation) {
1309 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1310 0 : ShowContinueError(state,
1311 0 : format("{} ({:.6T}) must be greater than {} ({:.6T}).",
1312 0 : HPWH.WrappedCondenserTopLocation,
1313 0 : hpwhNumericFieldNames[2],
1314 0 : hpwhNumericFieldNames[3],
1315 0 : HPWH.WrappedCondenserBottomLocation));
1316 0 : ErrorsFound = true;
1317 : }
1318 :
1319 : // Reset the offset
1320 3 : nAlphaOffset = -2;
1321 3 : nNumericOffset = 1;
1322 :
1323 : } else {
1324 0 : assert(0);
1325 : }
1326 :
1327 : // Evaporator Air Flow Rate
1328 23 : HPWH.OperatingAirFlowRate = hpwhNumeric[3 + nNumericOffset];
1329 23 : if (HPWH.OperatingAirFlowRate <= 0.0 && hpwhNumeric[3 + nNumericOffset] != Constant::AutoCalculate) {
1330 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1331 0 : ShowContinueError(state,
1332 0 : format("{}{}",
1333 0 : hpwhNumericFieldNames[3 + nNumericOffset],
1334 0 : format(" must be greater than 0. Evaporator air flow rate = {:.6T}", hpwhNumeric[3 + nNumericOffset])));
1335 0 : ErrorsFound = true;
1336 : }
1337 :
1338 : // Inlet Air Configuration
1339 23 : HPWH.InletAirConfiguration = static_cast<WTTAmbientTemp>(getEnumValue(HPWHAmbientTempNamesUC, Util::makeUPPER(hpwhAlpha[6 + nAlphaOffset])));
1340 23 : switch (HPWH.InletAirConfiguration) {
1341 3 : case WTTAmbientTemp::Schedule: {
1342 :
1343 : // Inlet Air Temperature Schedule
1344 3 : if (!hpwhAlphaBlank[11 + nAlphaOffset]) {
1345 3 : HPWH.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[11 + nAlphaOffset]);
1346 3 : if (HPWH.AmbientTempSchedule == 0) {
1347 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1348 0 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[11 + nAlphaOffset], hpwhAlpha[11 + nAlphaOffset]));
1349 0 : ErrorsFound = true;
1350 : }
1351 : } else {
1352 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1353 0 : ShowContinueError(state, format("required {} is blank.", hpwhAlphaFieldNames[11 + nAlphaOffset]));
1354 0 : ErrorsFound = true;
1355 : }
1356 :
1357 : // Inlet Air Humidity Schedule
1358 3 : if (!hpwhAlphaBlank[12 + nAlphaOffset]) {
1359 3 : HPWH.AmbientRHSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[12 + nAlphaOffset]);
1360 3 : if (HPWH.AmbientRHSchedule == 0) {
1361 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1362 0 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[12 + nAlphaOffset], hpwhAlpha[12 + nAlphaOffset]));
1363 0 : ErrorsFound = true;
1364 : } else {
1365 3 : if (!ScheduleManager::CheckScheduleValueMinMax(state, HPWH.AmbientRHSchedule, ">=", 0.0, "<=", 1.0)) {
1366 0 : ShowSevereError(state, format("{}=\"{}\", invalid values", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1367 0 : ShowContinueError(state,
1368 0 : format("{}=\"{}\", schedule values must be (>=0., <=1.)",
1369 0 : hpwhAlphaFieldNames[12 + nAlphaOffset],
1370 0 : hpwhAlpha[12 + nAlphaOffset]));
1371 0 : ErrorsFound = true;
1372 : }
1373 : }
1374 : } else {
1375 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1376 0 : ShowContinueError(state, format("required {} is blank.", hpwhAlphaFieldNames[12 + nAlphaOffset]));
1377 0 : ErrorsFound = true;
1378 : }
1379 :
1380 3 : break;
1381 : }
1382 10 : case WTTAmbientTemp::ZoneAndOA:
1383 : case WTTAmbientTemp::TempZone: {
1384 :
1385 : // Inlet Air Zone
1386 10 : if (!hpwhAlphaBlank[13 + nAlphaOffset]) {
1387 10 : HPWH.AmbientTempZone = Util::FindItemInList(hpwhAlpha[13 + nAlphaOffset], state.dataHeatBal->Zone);
1388 10 : if (HPWH.AmbientTempZone == 0) {
1389 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1390 0 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[13 + nAlphaOffset], hpwhAlpha[13 + nAlphaOffset]));
1391 0 : ErrorsFound = true;
1392 : }
1393 : } else {
1394 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1395 0 : ShowContinueError(state, format("required {} is blank.", hpwhAlphaFieldNames[13 + nAlphaOffset]));
1396 0 : ErrorsFound = true;
1397 : }
1398 10 : break;
1399 : }
1400 10 : default:
1401 : case WTTAmbientTemp::OutsideAir:
1402 10 : break;
1403 : }
1404 :
1405 : // Read air inlet nodes after mixer/splitter nodes have been read in (state.dataIPShortCut->cAlphaArgs 7-10),
1406 : // Node_ConnectionType differs for inlet node if mixer/splitter node exists
1407 :
1408 : // Tank Name
1409 : // We will verify this exists and is the right kind of tank later when the tanks are all loaded.
1410 23 : HPWH.TankName = hpwhAlpha[15 + nAlphaOffset];
1411 23 : HPWH.TankType = hpwhAlpha[14 + nAlphaOffset];
1412 :
1413 : // Use Side Inlet/Outlet
1414 : // Get the water heater tank use side inlet node names for HPWHs connected to a plant loop
1415 : // Save the name of the node for use with set up comp sets
1416 23 : HPWH.InletNodeName2 = hpwhAlpha[16 + nAlphaOffset];
1417 23 : HPWH.OutletNodeName2 = hpwhAlpha[17 + nAlphaOffset];
1418 :
1419 23 : if (!hpwhAlphaBlank[16 + nAlphaOffset] && !hpwhAlphaBlank[17 + nAlphaOffset]) {
1420 10 : HPWH.WHUseInletNode = NodeInputManager::GetOnlySingleNode(state,
1421 10 : HPWH.InletNodeName2,
1422 : ErrorsFound,
1423 : objType,
1424 10 : HPWH.Name,
1425 : DataLoopNode::NodeFluidType::Water,
1426 : DataLoopNode::ConnectionType::Inlet,
1427 : NodeInputManager::CompFluidStream::Primary,
1428 : DataLoopNode::ObjectIsParent);
1429 20 : HPWH.WHUseOutletNode = NodeInputManager::GetOnlySingleNode(state,
1430 10 : HPWH.OutletNodeName2,
1431 : ErrorsFound,
1432 : objType,
1433 10 : HPWH.Name,
1434 : DataLoopNode::NodeFluidType::Water,
1435 : DataLoopNode::ConnectionType::Outlet,
1436 : NodeInputManager::CompFluidStream::Primary,
1437 : DataLoopNode::ObjectIsParent);
1438 : }
1439 :
1440 : // DX Coil
1441 : // get Coil:DX:HeatPumpWaterHeater object
1442 23 : HPWH.DXCoilName = hpwhAlpha[19 + nAlphaOffset];
1443 23 : HPWH.DXCoilType = hpwhAlpha[18 + nAlphaOffset];
1444 :
1445 : // check that the DX Coil exists
1446 23 : bool DXCoilErrFlag = false;
1447 23 : bool bIsVScoil = false;
1448 23 : DXCoils::GetDXCoilIndex(state, HPWH.DXCoilName, HPWH.DXCoilNum, DXCoilErrFlag, state.dataIPShortCut->cCurrentModuleObject, true);
1449 23 : if (DXCoilErrFlag) {
1450 : // This could be a variable speed heat pump water heater
1451 7 : bool bVSCoilErrFlag = false;
1452 :
1453 7 : bool checkIHPFirst = IntegratedHeatPump::IHPInModel(state);
1454 7 : if (checkIHPFirst) {
1455 1 : HPWH.DXCoilNum =
1456 1 : IntegratedHeatPump::GetCoilIndexIHP(state, "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE", HPWH.DXCoilName, bVSCoilErrFlag);
1457 :
1458 1 : if (!bVSCoilErrFlag) {
1459 1 : HPWH.bIsIHP = true;
1460 : }
1461 : }
1462 :
1463 7 : if (bVSCoilErrFlag || !checkIHPFirst) {
1464 6 : bVSCoilErrFlag = false;
1465 6 : HPWH.DXCoilNum = VariableSpeedCoils::GetCoilIndexVariableSpeed(
1466 6 : state, "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed", HPWH.DXCoilName, bVSCoilErrFlag);
1467 :
1468 6 : if (bVSCoilErrFlag) {
1469 0 : ShowContinueError(state, format("...occurs in {} ={}", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1470 0 : ShowContinueError(state, format("...could not find either DXCoils::DXCoil or Variable Speed Coil {}", HPWH.DXCoilName));
1471 0 : ErrorsFound = true;
1472 : }
1473 : }
1474 :
1475 7 : bIsVScoil = true;
1476 7 : HPWH.DXCoilTypeNum = 0;
1477 7 : if (HPWH.bIsIHP) {
1478 1 : HPWH.DXCoilType = "COILSYSTEM:INTEGRATEDHEATPUMP:AIRSOURCE";
1479 : } else {
1480 6 : HPWH.DXCoilType = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum).VarSpeedCoilType;
1481 : }
1482 : } else {
1483 : // this is a single speed coil
1484 16 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1485 16 : if (!Util::SameString(HPWH.DXCoilType, Coil.DXCoilType)) {
1486 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1487 0 : ShowContinueError(state, format("specifies the coil {}=\"{}\".", HPWH.DXCoilType, HPWH.DXCoilName));
1488 0 : ShowContinueError(state, format("However, {} is a coil of type {}.", HPWH.DXCoilName, Coil.DXCoilType));
1489 0 : ErrorsFound = true;
1490 : }
1491 16 : HPWH.DXCoilTypeNum = Coil.DXCoilType_Num;
1492 : }
1493 :
1494 : // Make sure that the coil and tank are compatible.
1495 23 : if (bIsVScoil) {
1496 7 : if (HPWH.HPWHType != DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1497 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1498 0 : ShowContinueError(state,
1499 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed can only be used with a pumped condenser heat pump "
1500 : "water heater.");
1501 0 : ErrorsFound = true;
1502 : }
1503 : } else {
1504 16 : if (!((HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterPumped &&
1505 13 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) ||
1506 3 : (HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterWrapped &&
1507 3 : HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped))) {
1508 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1509 0 : std::string ExpectedCoilType;
1510 0 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
1511 0 : ExpectedCoilType = HVAC::cAllCoilTypes(HVAC::CoilDX_HeatPumpWaterHeaterPumped);
1512 0 : } else if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
1513 0 : ExpectedCoilType = HVAC::cAllCoilTypes(HVAC::CoilDX_HeatPumpWaterHeaterWrapped);
1514 : } else {
1515 0 : assert(0);
1516 : }
1517 0 : ShowContinueError(state, format("can only be used with {}", ExpectedCoilType));
1518 0 : ErrorsFound = true;
1519 0 : }
1520 : }
1521 :
1522 : // Dummy condenser Inlet/Outlet Nodes for wrapped tanks
1523 23 : if (HPWH.DXCoilTypeNum == HVAC::CoilDX_HeatPumpWaterHeaterWrapped) {
1524 3 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
1525 :
1526 3 : HPWH.InletNodeName1 = "DUMMY CONDENSER INLET " + Coil.Name;
1527 3 : HPWH.CondWaterInletNode = NodeInputManager::GetOnlySingleNode(state,
1528 3 : HPWH.InletNodeName1,
1529 : ErrorsFound,
1530 : objType,
1531 3 : HPWH.Name,
1532 : DataLoopNode::NodeFluidType::Water,
1533 : DataLoopNode::ConnectionType::Inlet,
1534 : NodeInputManager::CompFluidStream::Secondary,
1535 : DataLoopNode::ObjectIsParent);
1536 3 : HPWH.OutletNodeName1 = "DUMMY CONDENSER OUTLET " + Coil.Name;
1537 6 : HPWH.CondWaterOutletNode = NodeInputManager::GetOnlySingleNode(state,
1538 3 : HPWH.OutletNodeName1,
1539 : ErrorsFound,
1540 : objType,
1541 3 : HPWH.Name,
1542 : DataLoopNode::NodeFluidType::Water,
1543 : DataLoopNode::ConnectionType::Outlet,
1544 : NodeInputManager::CompFluidStream::Secondary,
1545 : DataLoopNode::ObjectIsParent);
1546 : }
1547 :
1548 : // Minimum Inlet Air Temperature for Compressor Operation
1549 23 : HPWH.MinAirTempForHPOperation = hpwhNumeric[4 + nNumericOffset];
1550 :
1551 : // Maximum Inlet Air Temperature for Compressor Operation
1552 23 : HPWH.MaxAirTempForHPOperation = hpwhNumeric[5 + nNumericOffset];
1553 23 : if (HPWH.MaxAirTempForHPOperation <= HPWH.MinAirTempForHPOperation) {
1554 0 : ShowWarningError(state,
1555 0 : format("{}=\"{}\": maximum inlet air temperature for heat pump compressor operation",
1556 0 : state.dataIPShortCut->cCurrentModuleObject,
1557 0 : HPWH.Name));
1558 0 : ShowContinueError(state, "must be greater than the minimum inlet air temperature for heat pump compressor operation.");
1559 0 : ShowContinueError(state, format("...Minimum inlet air temperature = {:.1T}", HPWH.MinAirTempForHPOperation));
1560 0 : ShowContinueError(state, format("...Maximum inlet air temperature = {:.1T}", HPWH.MaxAirTempForHPOperation));
1561 : }
1562 :
1563 : // Compressor Location
1564 23 : HPWH.CrankcaseTempIndicator =
1565 23 : static_cast<CrankcaseHeaterControlTemp>(getEnumValue(CrankcaseHeaterControlTempNamesUC, Util::makeUPPER(hpwhAlpha[20 + nAlphaOffset])));
1566 23 : switch (HPWH.CrankcaseTempIndicator) {
1567 3 : case CrankcaseHeaterControlTemp::Schedule: {
1568 3 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1569 : // Compressor Ambient Temperature Schedule
1570 3 : HPWH.CrankcaseTempSchedule = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[21 + nAlphaOffset]);
1571 3 : if (HPWH.CrankcaseTempSchedule == 0) {
1572 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1573 0 : ShowContinueError(state, format("{}=\"{}\".", hpwhAlphaFieldNames[21 + nAlphaOffset], hpwhAlpha[21 + nAlphaOffset]));
1574 0 : ErrorsFound = true;
1575 : }
1576 : } else {
1577 0 : ShowSevereError(state, format("{}=\"{}\", ", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1578 0 : ShowContinueError(state, format("required {} is blank.", hpwhAlphaFieldNames[21 + nAlphaOffset]));
1579 0 : ErrorsFound = true;
1580 : }
1581 :
1582 3 : break;
1583 : }
1584 10 : case CrankcaseHeaterControlTemp::Zone: {
1585 10 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1586 0 : ShowSevereError(state,
1587 0 : format("{}=\"{}\": Inlet Air Configuration must be Zone Air Only or Zone And",
1588 0 : state.dataIPShortCut->cCurrentModuleObject,
1589 0 : HPWH.Name));
1590 0 : ShowContinueError(state, " Outdoor Air when compressor location equals ZONE.");
1591 0 : ErrorsFound = true;
1592 : }
1593 :
1594 10 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1595 0 : ShowWarningError(state,
1596 0 : format("{}=\"{}\" {} was provided but will not be used based on compressor location input=\"{}\".",
1597 0 : state.dataIPShortCut->cCurrentModuleObject,
1598 0 : HPWH.Name,
1599 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1600 0 : hpwhAlpha[20 + nAlphaOffset]));
1601 : }
1602 10 : break;
1603 : }
1604 10 : case CrankcaseHeaterControlTemp::Outdoors: {
1605 10 : if (!hpwhAlphaBlank[21 + nAlphaOffset]) {
1606 0 : ShowWarningError(state,
1607 0 : format("{}=\"{}\" {} was provided but will not be used based on {}=\"{}\".",
1608 0 : state.dataIPShortCut->cCurrentModuleObject,
1609 0 : HPWH.Name,
1610 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1611 0 : hpwhAlphaFieldNames[21 + nAlphaOffset],
1612 0 : hpwhAlpha[20 + nAlphaOffset]));
1613 : }
1614 10 : break;
1615 : }
1616 0 : default:
1617 0 : break;
1618 : }
1619 :
1620 : // Fan Name
1621 23 : HPWH.FanName = hpwhAlpha[23 + nAlphaOffset];
1622 :
1623 23 : Real64 FanVolFlow = 0.0;
1624 23 : bool errFlag(false);
1625 :
1626 23 : HPWH.fanType = static_cast<HVAC::FanType>(getEnumValue(HVAC::fanTypeNamesUC, hpwhAlpha[22 + nAlphaOffset]));
1627 :
1628 23 : if ((HPWH.FanNum = Fans::GetFanIndex(state, HPWH.FanName)) == 0) {
1629 0 : ShowSevereItemNotFound(state, eoh, hpwhAlphaFieldNames[23 + nAlphaOffset], HPWH.FanName);
1630 0 : ErrorsFound = true;
1631 : } else {
1632 23 : assert(HPWH.fanType == state.dataFans->fans(HPWH.FanNum)->type);
1633 23 : FanVolFlow = state.dataFans->fans(HPWH.FanNum)->maxAirFlowRate;
1634 : }
1635 : // issue #5630, set fan info in coils.
1636 23 : if (bIsVScoil) {
1637 7 : VariableSpeedCoils::setVarSpeedHPWHFanType(state, HPWH.DXCoilNum, HPWH.fanType);
1638 7 : VariableSpeedCoils::setVarSpeedHPWHFanIndex(state, HPWH.DXCoilNum, HPWH.FanNum);
1639 : } else {
1640 : // LOL
1641 16 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanName);
1642 16 : DXCoils::SetDXCoolingCoilData(state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.FanNum);
1643 16 : DXCoils::SetDXCoolingCoilData(
1644 16 : state, HPWH.DXCoilNum, errFlag, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, HPWH.fanType);
1645 : }
1646 :
1647 23 : if (errFlag) {
1648 0 : ErrorsFound = true;
1649 23 : } else if (HPWH.fanType != HVAC::FanType::OnOff && HPWH.fanType != HVAC::FanType::SystemModel) {
1650 0 : ShowSevereError(state, format("{}=\"{}\": illegal fan type specified.", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1651 0 : ShowContinueError(
1652 : state,
1653 0 : format(" The fan object ({}) type must be Fan:SystemModel or Fan:OnOff when used with a heat pump water heater.", HPWH.FanName));
1654 0 : ErrorsFound = true;
1655 23 : } else if (HPWH.fanType != HVAC::FanType::OnOff && HPWH.fanType != HVAC::FanType::SystemModel) {
1656 0 : ShowSevereError(state, format("{}=\"{}\": illegal fan type specified.", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1657 0 : ShowContinueError(state, format(" The {} must specify that the fan object", state.dataIPShortCut->cCurrentModuleObject));
1658 0 : ShowContinueError(state,
1659 : " is of type FanSystemModel or Fan:OnOff in addition to the fan actually being of that type and defined elsewhere.");
1660 : }
1661 :
1662 23 : if (FanVolFlow != DataSizing::AutoSize && !errFlag) {
1663 21 : if (FanVolFlow < HPWH.OperatingAirFlowRate) {
1664 0 : ShowSevereError(state,
1665 0 : format("{} - air flow rate = {:.7T} in fan object {} is less than the HPWHs evaporator air flow rate.",
1666 0 : state.dataIPShortCut->cCurrentModuleObject,
1667 : FanVolFlow,
1668 0 : HPWH.FanName));
1669 0 : ShowContinueError(state, " The fan flow rate must be >= to the HPWHs evaporator volumetric air flow rate.");
1670 0 : ShowContinueError(state, format(" Occurs in unit = {}", HPWH.Name));
1671 0 : ErrorsFound = true;
1672 : }
1673 : }
1674 :
1675 : // Fan Placement
1676 23 : HPWH.fanPlace = static_cast<HVAC::FanPlace>(getEnumValue(HVAC::fanPlaceNamesUC, hpwhAlpha[24 + nAlphaOffset]));
1677 23 : if (HPWH.fanPlace == HVAC::FanPlace::Invalid) {
1678 0 : ShowSevereInvalidKey(state, eoh, hpwhAlphaFieldNames[24 + nAlphaOffset], hpwhAlpha[24 + nAlphaOffset]);
1679 0 : ErrorsFound = true;
1680 : }
1681 :
1682 23 : if (HPWH.DXCoilNum > 0 && !bIsVScoil) {
1683 : // get HPWH capacity, air inlet node, and PLF curve info from DX coil object
1684 16 : HPWH.Capacity = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2;
1685 16 : HPWH.DXCoilAirInletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
1686 16 : HPWH.DXCoilPLFFPLR = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).PLFFPLR(1);
1687 : // check the range of condenser pump power to be <= 5 gpm/ton
1688 16 : if (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower / state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2 >
1689 : 0.1422) {
1690 0 : ShowWarningError(
1691 : state,
1692 0 : format("{}= {}{}",
1693 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).DXCoilType,
1694 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).Name,
1695 0 : format(": Rated condenser pump power per watt of rated heating capacity has exceeded the recommended maximum of 0.1422 "
1696 : "W/W (41.67 watt/MBH). Condenser pump power per watt = {:.4T}",
1697 0 : (state.dataDXCoils->DXCoil(HPWH.DXCoilNum).HPWHCondPumpElecNomPower /
1698 0 : state.dataDXCoils->DXCoil(HPWH.DXCoilNum).RatedTotCap2))));
1699 : }
1700 7 : } else if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
1701 :
1702 7 : if (HPWH.bIsIHP) {
1703 1 : HPWH.Capacity =
1704 1 : GetDWHCoilCapacityIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1705 1 : HPWH.DXCoilAirInletNode = IntegratedHeatPump::GetCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1706 1 : HPWH.DXCoilPLFFPLR =
1707 1 : GetIHPDWHCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, IntegratedHeatPump::IHPOperationMode::SCWHMatchWH, DXCoilErrFlag);
1708 : } else {
1709 6 : HPWH.Capacity = VariableSpeedCoils::GetCoilCapacityVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1710 6 : HPWH.DXCoilAirInletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1711 6 : HPWH.DXCoilPLFFPLR = VariableSpeedCoils::GetVSCoilPLFFPLR(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
1712 : }
1713 : // check the range of condenser pump power to be <= 5 gpm/ton, will be checked in the coil object
1714 : }
1715 :
1716 23 : if (HPWH.OperatingWaterFlowRate == Constant::AutoCalculate) {
1717 8 : HPWH.OperatingWaterFlowRate = 0.00000004487 * HPWH.Capacity;
1718 8 : HPWH.WaterFlowRateAutoSized = true;
1719 : }
1720 :
1721 23 : if (HPWH.OperatingAirFlowRate == Constant::AutoCalculate) {
1722 10 : HPWH.OperatingAirFlowRate = 0.00005035 * HPWH.Capacity;
1723 10 : HPWH.AirFlowRateAutoSized = true;
1724 : }
1725 :
1726 : // On Cycle Parasitic Electric Load
1727 23 : HPWH.OnCycParaLoad = hpwhNumeric[6 + nNumericOffset];
1728 23 : if (HPWH.OnCycParaLoad < 0.0) {
1729 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1730 0 : ShowContinueError(state,
1731 0 : format("{} must be >= 0. {}{}",
1732 0 : hpwhNumericFieldNames[6 + nNumericOffset],
1733 0 : hpwhNumericFieldNames[6 + nNumericOffset],
1734 0 : format(" = {:.2T}", hpwhNumeric[6 + nNumericOffset])));
1735 0 : ErrorsFound = true;
1736 : }
1737 :
1738 : // Off Cycle Parasitic Electric Load
1739 23 : HPWH.OffCycParaLoad = hpwhNumeric[7 + nNumericOffset];
1740 23 : if (HPWH.OffCycParaLoad < 0.0) {
1741 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1742 0 : ShowContinueError(state,
1743 0 : format("{} must be >= 0. {}{}",
1744 0 : hpwhNumericFieldNames[7 + nNumericOffset],
1745 0 : hpwhNumericFieldNames[2 + nNumericOffset],
1746 0 : format(" = {:.2T}", hpwhNumeric[7 + nNumericOffset])));
1747 0 : ErrorsFound = true;
1748 : }
1749 :
1750 : // Parasitic Heat Rejection Location
1751 23 : if (Util::SameString(hpwhAlpha[25 + nAlphaOffset], "Zone")) {
1752 8 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::TempZone;
1753 8 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir || HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1754 0 : ShowSevereError(state, format("{}=\"{}\",", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1755 0 : ShowContinueError(state, format("{} must be ZoneAirOnly or ZoneAndOutdoorAir", hpwhAlphaFieldNames[25 + nAlphaOffset]));
1756 0 : ShowContinueError(state, " when parasitic heat rejection location equals Zone.");
1757 0 : ErrorsFound = true;
1758 : }
1759 15 : } else if (Util::SameString(hpwhAlpha[25 + nAlphaOffset], "Outdoors")) {
1760 15 : HPWH.ParasiticTempIndicator = WTTAmbientTemp::OutsideAir;
1761 : } else {
1762 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1763 0 : ShowContinueError(state, " parasitic heat rejection location must be either Zone or Outdoors.");
1764 0 : ErrorsFound = true;
1765 : }
1766 :
1767 : // Inlet Air Mixer Node
1768 : // get mixer/splitter nodes only when Inlet Air Configuration is ZoneAndOutdoorAir
1769 23 : if (!hpwhAlphaBlank[26 + nAlphaOffset]) {
1770 : // For the inlet air mixer node, NodeConnectionType is outlet from the HPWH inlet air node
1771 3 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1772 3 : HPWH.InletAirMixerNode = NodeInputManager::GetOnlySingleNode(state,
1773 3 : hpwhAlpha[26 + nAlphaOffset],
1774 : ErrorsFound,
1775 : objType,
1776 6 : HPWH.Name + "-INLET AIR MIXER",
1777 : DataLoopNode::NodeFluidType::Air,
1778 : DataLoopNode::ConnectionType::Outlet,
1779 : NodeInputManager::CompFluidStream::Primary,
1780 : DataLoopNode::ObjectIsNotParent);
1781 : } else {
1782 0 : ShowWarningError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1783 0 : ShowContinueError(state,
1784 : "Inlet air mixer node name specified but only required when Inlet Air Configuration is selected as "
1785 : "Zone and OutdoorAir. Node name disregarded and simulation continues.");
1786 : }
1787 20 : } else if (hpwhAlphaBlank[26 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1788 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1789 0 : ShowContinueError(state, "Inlet air mixer node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1790 0 : ErrorsFound = true;
1791 : }
1792 :
1793 : // Outlet Air Splitter Node
1794 23 : if (!hpwhAlphaBlank[27 + nAlphaOffset]) {
1795 : // For the outlet air splitter node, NodeConnectionType is inlet to the HPWH outlet air node
1796 3 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1797 3 : HPWH.OutletAirSplitterNode = NodeInputManager::GetOnlySingleNode(state,
1798 3 : hpwhAlpha[27 + nAlphaOffset],
1799 : ErrorsFound,
1800 : objType,
1801 6 : HPWH.Name + "-OUTLET AIR SPLITTER",
1802 : DataLoopNode::NodeFluidType::Air,
1803 : DataLoopNode::ConnectionType::Inlet,
1804 : NodeInputManager::CompFluidStream::Primary,
1805 : DataLoopNode::ObjectIsNotParent);
1806 : } else {
1807 0 : ShowWarningError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1808 0 : ShowContinueError(state,
1809 : "Outlet air splitter node name specified but only required when Inlet Air Configuration is selected as "
1810 : "ZoneAndOutdoorAir. Node name disregarded and simulation continues.");
1811 : }
1812 20 : } else if (hpwhAlphaBlank[27 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1813 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1814 0 : ShowContinueError(state, "Outlet air splitter node name required when Inlet Air Configuration is selected as ZoneAndOutdoorAir.");
1815 0 : ErrorsFound = true;
1816 : }
1817 :
1818 : // get node data for HPWH
1819 23 : if (HPWH.InletAirMixerNode != 0) {
1820 : // when mixer/splitter nodes are used the HPWH's inlet/outlet node are set up as DataLoopNode::ObjectIsNotParent
1821 :
1822 3 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1823 3 : hpwhAlpha[7 + nAlphaOffset],
1824 : ErrorsFound,
1825 : objType,
1826 6 : HPWH.Name + "-INLET AIR MIXER",
1827 : DataLoopNode::NodeFluidType::Air,
1828 : DataLoopNode::ConnectionType::Inlet,
1829 : NodeInputManager::CompFluidStream::Primary,
1830 : DataLoopNode::ObjectIsNotParent);
1831 :
1832 3 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1833 3 : hpwhAlpha[8 + nAlphaOffset],
1834 : ErrorsFound,
1835 : objType,
1836 6 : HPWH.Name + "-OUTLET AIR SPLITTER",
1837 : DataLoopNode::NodeFluidType::Air,
1838 : DataLoopNode::ConnectionType::Outlet,
1839 : NodeInputManager::CompFluidStream::Primary,
1840 : DataLoopNode::ObjectIsNotParent);
1841 :
1842 3 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1843 3 : hpwhAlpha[9 + nAlphaOffset],
1844 : ErrorsFound,
1845 : objType,
1846 3 : HPWH.Name,
1847 : DataLoopNode::NodeFluidType::Air,
1848 : DataLoopNode::ConnectionType::OutsideAirReference,
1849 : NodeInputManager::CompFluidStream::Primary,
1850 : DataLoopNode::ObjectIsParent);
1851 3 : if (!hpwhAlpha[9 + nAlphaOffset].empty()) {
1852 : bool Okay;
1853 3 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, HPWH.OutsideAirNode, Okay);
1854 3 : if (!Okay) {
1855 0 : ShowWarningError(state,
1856 0 : format("{}=\"{}\": Adding outdoor air node={}",
1857 0 : state.dataIPShortCut->cCurrentModuleObject,
1858 0 : HPWH.Name,
1859 0 : hpwhAlpha[9 + nAlphaOffset]));
1860 : }
1861 : }
1862 :
1863 3 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1864 3 : hpwhAlpha[10 + nAlphaOffset],
1865 : ErrorsFound,
1866 : objType,
1867 3 : HPWH.Name,
1868 : DataLoopNode::NodeFluidType::Air,
1869 : DataLoopNode::ConnectionType::ReliefAir,
1870 : NodeInputManager::CompFluidStream::Primary,
1871 : DataLoopNode::ObjectIsParent);
1872 :
1873 : } else {
1874 : // when mixer/splitter nodes are NOT used the HPWH's inlet/outlet nodes are set up as DataLoopNode::ObjectIsParent
1875 20 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule) {
1876 : // for scheduled HPWH's the inlet node is not on any branch or parent object, make it an outlet node
1877 : // to avoid node connection errors
1878 3 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1879 3 : hpwhAlpha[7 + nAlphaOffset],
1880 : ErrorsFound,
1881 : objType,
1882 3 : HPWH.Name,
1883 : DataLoopNode::NodeFluidType::Air,
1884 : DataLoopNode::ConnectionType::Outlet,
1885 : NodeInputManager::CompFluidStream::Primary,
1886 : DataLoopNode::ObjectIsParent);
1887 :
1888 3 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1889 3 : hpwhAlpha[8 + nAlphaOffset],
1890 : ErrorsFound,
1891 : objType,
1892 3 : HPWH.Name,
1893 : DataLoopNode::NodeFluidType::Air,
1894 : DataLoopNode::ConnectionType::Outlet,
1895 : NodeInputManager::CompFluidStream::Primary,
1896 : DataLoopNode::ObjectIsParent);
1897 :
1898 : } else { // HPWH is connected to a zone with no mixer/splitter nodes
1899 17 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1900 7 : HPWH.HeatPumpAirInletNode = NodeInputManager::GetOnlySingleNode(state,
1901 7 : hpwhAlpha[7 + nAlphaOffset],
1902 : ErrorsFound,
1903 : objType,
1904 7 : HPWH.Name,
1905 : DataLoopNode::NodeFluidType::Air,
1906 : DataLoopNode::ConnectionType::Inlet,
1907 : NodeInputManager::CompFluidStream::Primary,
1908 : DataLoopNode::ObjectIsParent);
1909 :
1910 7 : HPWH.HeatPumpAirOutletNode = NodeInputManager::GetOnlySingleNode(state,
1911 7 : hpwhAlpha[8 + nAlphaOffset],
1912 : ErrorsFound,
1913 : objType,
1914 7 : HPWH.Name,
1915 : DataLoopNode::NodeFluidType::Air,
1916 : DataLoopNode::ConnectionType::Outlet,
1917 : NodeInputManager::CompFluidStream::Primary,
1918 : DataLoopNode::ObjectIsParent);
1919 : } else { // HPWH is located outdoors
1920 10 : HPWH.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
1921 10 : hpwhAlpha[9 + nAlphaOffset],
1922 : ErrorsFound,
1923 : objType,
1924 10 : HPWH.Name,
1925 : DataLoopNode::NodeFluidType::Air,
1926 : DataLoopNode::ConnectionType::OutsideAirReference,
1927 : NodeInputManager::CompFluidStream::Primary,
1928 : DataLoopNode::ObjectIsParent);
1929 10 : if (!hpwhAlphaBlank[9 + nAlphaOffset]) {
1930 : bool Okay;
1931 10 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, HPWH.OutsideAirNode, Okay);
1932 10 : if (!Okay) {
1933 0 : ShowWarningError(state,
1934 0 : format("{}=\"{}\": Adding outdoor air node ={}",
1935 0 : state.dataIPShortCut->cCurrentModuleObject,
1936 0 : HPWH.Name,
1937 0 : hpwhAlpha[9 + nAlphaOffset]));
1938 : }
1939 : }
1940 :
1941 10 : HPWH.ExhaustAirNode = NodeInputManager::GetOnlySingleNode(state,
1942 10 : hpwhAlpha[10 + nAlphaOffset],
1943 : ErrorsFound,
1944 : objType,
1945 10 : HPWH.Name,
1946 : DataLoopNode::NodeFluidType::Air,
1947 : DataLoopNode::ConnectionType::ReliefAir,
1948 : NodeInputManager::CompFluidStream::Primary,
1949 : DataLoopNode::ObjectIsParent);
1950 : }
1951 : }
1952 : }
1953 : // check that required node names are present
1954 23 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::Schedule || HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone) {
1955 10 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1956 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1957 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1958 0 : ShowContinueError(
1959 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[7 + nAlphaOffset], hpwhAlphaFieldNames[8 + nAlphaOffset]));
1960 0 : ErrorsFound = true;
1961 : }
1962 13 : } else if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
1963 10 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1964 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1965 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1966 0 : ShowContinueError(
1967 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[9 + nAlphaOffset], hpwhAlphaFieldNames[10 + nAlphaOffset]));
1968 0 : ErrorsFound = true;
1969 : }
1970 3 : } else if (HPWH.InletAirMixerNode > 0 && HPWH.OutletAirSplitterNode > 0 && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
1971 3 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0 || HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1972 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
1973 0 : ShowContinueError(state, format("When {}=\"{}\".", hpwhAlphaFieldNames[6 + nAlphaOffset], hpwhAlpha[6 + nAlphaOffset]));
1974 0 : if (HPWH.HeatPumpAirInletNode == 0 || HPWH.HeatPumpAirOutletNode == 0) {
1975 0 : ShowContinueError(
1976 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[7 + nAlphaOffset], hpwhAlphaFieldNames[8 + nAlphaOffset]));
1977 : }
1978 0 : if (HPWH.OutsideAirNode == 0 || HPWH.ExhaustAirNode == 0) {
1979 0 : ShowContinueError(
1980 0 : state, format("{} and {} must be specified.", hpwhAlphaFieldNames[9 + nAlphaOffset], hpwhAlphaFieldNames[10 + nAlphaOffset]));
1981 : }
1982 0 : ErrorsFound = true;
1983 : }
1984 : }
1985 :
1986 : // check that the HPWH inlet and outlet nodes are in the same zone (ZoneHVAC:EquipmentConnections) when
1987 : // Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
1988 23 : if ((HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) &&
1989 10 : HPWH.AmbientTempZone > 0) {
1990 10 : if (allocated(state.dataZoneEquip->ZoneEquipConfig)) {
1991 10 : bool FoundInletNode = false;
1992 10 : bool FoundOutletNode = false;
1993 10 : int ZoneNum = HPWH.AmbientTempZone;
1994 10 : if (ZoneNum <= state.dataGlobal->NumOfZones) {
1995 37 : for (int SupAirIn = 1; SupAirIn <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumInletNodes; ++SupAirIn) {
1996 27 : if (HPWH.HeatPumpAirOutletNode != state.dataZoneEquip->ZoneEquipConfig(ZoneNum).InletNode(SupAirIn)) continue;
1997 10 : FoundOutletNode = true;
1998 : }
1999 27 : for (int ExhAirOut = 1; ExhAirOut <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumExhaustNodes; ++ExhAirOut) {
2000 17 : if (HPWH.HeatPumpAirInletNode != state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ExhaustNode(ExhAirOut)) continue;
2001 10 : FoundInletNode = true;
2002 : }
2003 10 : if (!FoundInletNode) {
2004 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2005 0 : ShowContinueError(state,
2006 0 : format("The HPWH's air inlet node name = {} was not properly specified ", hpwhAlpha[7 + nAlphaOffset]));
2007 0 : ShowContinueError(
2008 : state,
2009 0 : format("as an exhaust air node for zone = {} in a ZoneHVAC:EquipmentConnections object.", hpwhAlpha[13 + nAlphaOffset]));
2010 0 : ErrorsFound = true;
2011 : }
2012 10 : if (!FoundOutletNode) {
2013 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2014 0 : ShowContinueError(state,
2015 0 : format("The HPWH's air outlet node name = {} was not properly specified ", hpwhAlpha[8 + nAlphaOffset]));
2016 0 : ShowContinueError(
2017 : state,
2018 0 : format("as an inlet air node for zone = {} in a ZoneHVAC:EquipmentConnections object.", hpwhAlpha[13 + nAlphaOffset]));
2019 0 : ErrorsFound = true;
2020 : }
2021 : }
2022 : } else {
2023 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2024 0 : ShowContinueError(state,
2025 : "Heat pump water heater air inlet node name and air outlet node name must be listed in a "
2026 : "ZoneHVAC:EquipmentConnections object when Inlet Air Configuration is equal to ZoneAirOnly or "
2027 : "ZoneAndOutdoorAir.");
2028 0 : ErrorsFound = true;
2029 : }
2030 : }
2031 :
2032 : // only get the inlet air mixer schedule if the inlet air configuration is zone and outdoor air
2033 23 : if (!hpwhAlphaBlank[28 + nAlphaOffset] && HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
2034 3 : HPWH.InletAirMixerSchPtr = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[28 + nAlphaOffset]);
2035 3 : if (HPWH.InletAirMixerSchPtr == 0) {
2036 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2037 0 : ShowContinueError(state, format("{}=\"{}\",", hpwhAlphaFieldNames[28 + nAlphaOffset], hpwhAlpha[28 + nAlphaOffset]));
2038 0 : ErrorsFound = true;
2039 : } else {
2040 3 : bool ValidScheduleValue = ScheduleManager::CheckScheduleValueMinMax(state, HPWH.InletAirMixerSchPtr, ">=", 0.0, "<=", 1.0);
2041 3 : if (!ValidScheduleValue) {
2042 0 : ShowSevereError(state, format("{}=\"{}\", not found", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2043 0 : ShowContinueError(state,
2044 0 : format("{} values out of range of 0 to 1, Schedule=\"{}\".",
2045 0 : hpwhAlphaFieldNames[28 + nAlphaOffset],
2046 0 : hpwhAlpha[28 + nAlphaOffset]));
2047 0 : ErrorsFound = true;
2048 : }
2049 : // set outlet air splitter schedule index equal to inlet air mixer schedule index
2050 : // (place holder for when zone pressurization/depressurization is allowed and different schedules can be used)
2051 3 : HPWH.OutletAirSplitterSchPtr = ScheduleManager::GetScheduleIndex(state, hpwhAlpha[28 + nAlphaOffset]);
2052 : }
2053 : }
2054 :
2055 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2056 23 : if (HPWH.fanPlace == HVAC::FanPlace::DrawThru) {
2057 11 : if (HPWH.OutletAirSplitterNode != 0) {
2058 3 : HPWH.FanOutletNode = HPWH.OutletAirSplitterNode;
2059 : } else {
2060 8 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2061 1 : HPWH.FanOutletNode = HPWH.ExhaustAirNode;
2062 : } else {
2063 7 : HPWH.FanOutletNode = HPWH.HeatPumpAirOutletNode;
2064 : }
2065 : }
2066 12 : } else if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2067 : // set fan outlet node variable for use in setting Node(FanOutletNode)%MassFlowRateMax for fan object
2068 12 : if (bIsVScoil) {
2069 5 : if (HPWH.bIsIHP) {
2070 1 : HPWH.FanOutletNode = IntegratedHeatPump::GetDWHCoilInletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2071 : } else {
2072 4 : HPWH.FanOutletNode = VariableSpeedCoils::GetCoilInletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2073 : }
2074 : } else {
2075 7 : HPWH.FanOutletNode = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirInNode;
2076 : }
2077 : }
2078 :
2079 : // check that fan outlet node is indeed correct
2080 23 : int FanOutletNodeNum = state.dataFans->fans(HPWH.FanNum)->outletNodeNum;
2081 :
2082 23 : if (FanOutletNodeNum != HPWH.FanOutletNode) {
2083 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2084 0 : ShowContinueError(state, "Heat pump water heater fan outlet node name does not match next connected component.");
2085 0 : if (FanOutletNodeNum != 0) {
2086 0 : ShowContinueError(state, format("Fan outlet node name = {}", state.dataLoopNodes->NodeID(FanOutletNodeNum)));
2087 : }
2088 0 : if (HPWH.FanOutletNode != 0) {
2089 0 : ShowContinueError(state, format("Expected fan outlet node name = {}", state.dataLoopNodes->NodeID(HPWH.FanOutletNode)));
2090 : }
2091 0 : ErrorsFound = true;
2092 : }
2093 23 : int FanInletNodeNum = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
2094 :
2095 23 : int HPWHFanInletNodeNum(0);
2096 23 : if (HPWH.InletAirMixerNode != 0) {
2097 3 : HPWHFanInletNodeNum = HPWH.InletAirMixerNode;
2098 : } else {
2099 20 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2100 10 : HPWHFanInletNodeNum = HPWH.OutsideAirNode;
2101 : } else {
2102 10 : HPWHFanInletNodeNum = HPWH.HeatPumpAirInletNode;
2103 : }
2104 : }
2105 23 : if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2106 12 : if (FanInletNodeNum != HPWHFanInletNodeNum) {
2107 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2108 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2109 0 : if (FanOutletNodeNum != 0) {
2110 0 : ShowContinueError(state, format("Fan inlet node name = {}", state.dataLoopNodes->NodeID(FanInletNodeNum)));
2111 : }
2112 0 : if (HPWH.FanOutletNode != 0) {
2113 0 : ShowContinueError(state, format("Expected fan inlet node name = {}", state.dataLoopNodes->NodeID(HPWHFanInletNodeNum)));
2114 : }
2115 0 : ErrorsFound = true;
2116 : }
2117 : }
2118 :
2119 23 : int DXCoilAirOutletNodeNum(0);
2120 23 : if ((HPWH.DXCoilNum > 0) && (bIsVScoil)) {
2121 7 : if (HPWH.bIsIHP) {
2122 1 : DXCoilAirOutletNodeNum = IntegratedHeatPump::GetDWHCoilOutletNodeIHP(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2123 : } else {
2124 6 : DXCoilAirOutletNodeNum = VariableSpeedCoils::GetCoilOutletNodeVariableSpeed(state, HPWH.DXCoilType, HPWH.DXCoilName, DXCoilErrFlag);
2125 : }
2126 :
2127 16 : } else if (HPWH.DXCoilNum > 0) {
2128 16 : DXCoilAirOutletNodeNum = state.dataDXCoils->DXCoil(HPWH.DXCoilNum).AirOutNode;
2129 : }
2130 23 : if (HPWH.fanPlace == HVAC::FanPlace::DrawThru) {
2131 11 : if (FanInletNodeNum != DXCoilAirOutletNodeNum) {
2132 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2133 0 : ShowContinueError(state, "Heat pump water heater fan inlet node name does not match previous connected component.");
2134 0 : if (FanInletNodeNum != 0) {
2135 0 : ShowContinueError(state, format("Fan inlet node name = {}", state.dataLoopNodes->NodeID(FanInletNodeNum)));
2136 : }
2137 0 : if (DXCoilAirOutletNodeNum != 0) {
2138 0 : ShowContinueError(state, format("Expected fan inlet node name = {}", state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum)));
2139 : }
2140 0 : ErrorsFound = true;
2141 : }
2142 12 : } else if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2143 12 : int HPWHCoilOutletNodeNum(0);
2144 12 : if (HPWH.OutletAirSplitterNode != 0) {
2145 0 : HPWHCoilOutletNodeNum = HPWH.OutletAirSplitterNode;
2146 : } else {
2147 12 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::OutsideAir) {
2148 9 : HPWHCoilOutletNodeNum = HPWH.ExhaustAirNode;
2149 : } else {
2150 3 : HPWHCoilOutletNodeNum = HPWH.HeatPumpAirOutletNode;
2151 : }
2152 : }
2153 12 : if (DXCoilAirOutletNodeNum != HPWHCoilOutletNodeNum) {
2154 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2155 0 : ShowContinueError(state, "Heat pump water heater coil outlet node name does not match next connected component.");
2156 0 : if (DXCoilAirOutletNodeNum != 0) {
2157 0 : ShowContinueError(state, format("Coil outlet node name = {}", state.dataLoopNodes->NodeID(DXCoilAirOutletNodeNum)));
2158 : }
2159 0 : if (HPWHCoilOutletNodeNum != 0) {
2160 0 : ShowContinueError(state, format("Expected coil outlet node name = {}", state.dataLoopNodes->NodeID(HPWHCoilOutletNodeNum)));
2161 : }
2162 0 : ErrorsFound = true;
2163 : }
2164 : }
2165 :
2166 : // set the max mass flow rate for outdoor fans
2167 23 : if (HPWH.FanOutletNode > 0)
2168 23 : state.dataLoopNodes->Node(HPWH.FanOutletNode).MassFlowRateMax =
2169 46 : HPWH.OperatingAirFlowRate * Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, 20.0, 0.0);
2170 :
2171 23 : if (HPWH.fanPlace == HVAC::FanPlace::BlowThru) {
2172 12 : if (HPWH.InletAirMixerNode > 0) {
2173 0 : HPWH.FanInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2174 0 : HPWH.FanOutletNode_str = "UNDEFINED";
2175 : } else {
2176 12 : if (HPWH.OutsideAirNode == 0) {
2177 3 : HPWH.FanInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2178 3 : HPWH.FanOutletNode_str = "UNDEFINED";
2179 : } else {
2180 9 : HPWH.FanInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2181 9 : HPWH.FanOutletNode_str = "UNDEFINED";
2182 : }
2183 : }
2184 12 : if (HPWH.OutletAirSplitterNode > 0) {
2185 0 : HPWH.CoilInletNode_str = "UNDEFINED";
2186 0 : HPWH.CoilOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2187 : } else {
2188 12 : if (HPWH.OutsideAirNode == 0) {
2189 3 : HPWH.CoilInletNode_str = "UNDEFINED";
2190 3 : HPWH.CoilOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2191 : } else {
2192 9 : HPWH.CoilInletNode_str = "UNDEFINED";
2193 9 : HPWH.CoilOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2194 : }
2195 : }
2196 : } else {
2197 11 : if (HPWH.InletAirMixerNode > 0) {
2198 3 : HPWH.CoilInletNode_str = hpwhAlpha[26 + nAlphaOffset];
2199 3 : HPWH.CoilOutletNode_str = "UNDEFINED";
2200 : } else {
2201 8 : if (HPWH.OutsideAirNode == 0) {
2202 7 : HPWH.CoilInletNode_str = hpwhAlpha[7 + nAlphaOffset];
2203 7 : HPWH.CoilOutletNode_str = "UNDEFINED";
2204 : } else {
2205 1 : HPWH.CoilInletNode_str = hpwhAlpha[9 + nAlphaOffset];
2206 1 : HPWH.CoilOutletNode_str = "UNDEFINED";
2207 : }
2208 : }
2209 11 : if (HPWH.OutletAirSplitterNode > 0) {
2210 3 : HPWH.FanInletNode_str = "UNDEFINED";
2211 3 : HPWH.FanOutletNode_str = hpwhAlpha[27 + nAlphaOffset];
2212 : } else {
2213 8 : if (HPWH.OutsideAirNode == 0) {
2214 7 : HPWH.FanInletNode_str = "UNDEFINED";
2215 7 : HPWH.FanOutletNode_str = hpwhAlpha[8 + nAlphaOffset];
2216 : } else {
2217 1 : HPWH.FanInletNode_str = "UNDEFINED";
2218 1 : HPWH.FanOutletNode_str = hpwhAlpha[10 + nAlphaOffset];
2219 : }
2220 : }
2221 : }
2222 :
2223 : // set up comp set for air side nodes (can be blow thru or draw thru, may or may not have damper nodes)
2224 23 : if (HPWH.bIsIHP) {
2225 2 : BranchNodeConnections::SetUpCompSets(
2226 2 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName + " Outdoor Coil", HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2227 : } else {
2228 22 : BranchNodeConnections::SetUpCompSets(
2229 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.CoilInletNode_str, HPWH.CoilOutletNode_str);
2230 : }
2231 :
2232 46 : BranchNodeConnections::SetUpCompSets(
2233 23 : state, HPWH.Type, HPWH.Name, HVAC::fanTypeNames[(int)HPWH.fanType], HPWH.FanName, HPWH.FanInletNode_str, HPWH.FanOutletNode_str);
2234 :
2235 : // Control Logic Flag
2236 23 : std::string CtrlLogicFlag = hpwhAlphaBlank[29 + nAlphaOffset] ? "SIMULTANEOUS" : hpwhAlpha[29 + nAlphaOffset];
2237 23 : if (Util::SameString(CtrlLogicFlag, "SIMULTANEOUS")) {
2238 20 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = true;
2239 3 : } else if (Util::SameString(CtrlLogicFlag, "MUTUALLYEXCLUSIVE")) {
2240 3 : HPWH.AllowHeatingElementAndHeatPumpToRunAtSameTime = false;
2241 : } else {
2242 0 : ShowSevereError(state, format("{}=\"{}\":", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
2243 0 : ShowContinueError(state, format("{} is not a valid value for field Tank Element Control Logic.", CtrlLogicFlag));
2244 0 : ErrorsFound = true;
2245 : }
2246 :
2247 : // Control Sensor 1 Location In Stratified Tank
2248 23 : if (!hpwhNumericBlank[8 + nNumericOffset]) {
2249 11 : HPWH.ControlSensor1Height = hpwhNumeric[8 + nNumericOffset];
2250 : } else {
2251 : // use heater1 location, which we don't know right now
2252 12 : HPWH.ControlSensor1Height = -1.0;
2253 : }
2254 :
2255 : // Control Sensor 1 Weight
2256 23 : HPWH.ControlSensor1Weight = hpwhNumericBlank[9 + nNumericOffset] ? 1.0 : hpwhNumeric[9 + nNumericOffset];
2257 :
2258 : // Control Sensor 2 Location In Stratified Tank
2259 23 : if (!hpwhNumericBlank[10 + nNumericOffset]) {
2260 23 : HPWH.ControlSensor2Height = hpwhNumeric[10 + nNumericOffset];
2261 : } else {
2262 0 : HPWH.ControlSensor2Height = -1.0;
2263 : }
2264 :
2265 : // Control Sensor 2 Weight
2266 23 : HPWH.ControlSensor2Weight = 1.0 - HPWH.ControlSensor1Weight;
2267 23 : }
2268 :
2269 9 : return ErrorsFound;
2270 : }
2271 :
2272 123 : bool getWaterHeaterMixedInputs(EnergyPlusData &state)
2273 : {
2274 123 : bool ErrorsFound = false;
2275 123 : state.dataIPShortCut->cCurrentModuleObject = cMixedWHModuleObj;
2276 : static constexpr std::string_view RoutineName = "getWaterHeaterMixedInputs";
2277 :
2278 299 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed; ++WaterThermalTankNum) {
2279 : int NumAlphas;
2280 : int NumNums;
2281 : int IOStat;
2282 352 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2283 176 : state.dataIPShortCut->cCurrentModuleObject,
2284 : WaterThermalTankNum,
2285 176 : state.dataIPShortCut->cAlphaArgs,
2286 : NumAlphas,
2287 176 : state.dataIPShortCut->rNumericArgs,
2288 : NumNums,
2289 : IOStat,
2290 176 : state.dataIPShortCut->lNumericFieldBlanks,
2291 176 : state.dataIPShortCut->lAlphaFieldBlanks,
2292 176 : state.dataIPShortCut->cAlphaFieldNames,
2293 176 : state.dataIPShortCut->cNumericFieldNames);
2294 176 : GlobalNames::VerifyUniqueInterObjectName(state,
2295 176 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2296 176 : state.dataIPShortCut->cAlphaArgs(1),
2297 176 : state.dataIPShortCut->cCurrentModuleObject,
2298 176 : state.dataIPShortCut->cAlphaFieldNames(1),
2299 : ErrorsFound);
2300 :
2301 176 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2302 :
2303 176 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2304 176 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2305 176 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterMixed;
2306 176 : Tank.FluidIndex = Tank.waterIndex;
2307 :
2308 : // default to always on
2309 176 : Tank.SourceSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
2310 176 : Tank.UseSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
2311 :
2312 : // A user field will be added in a later release
2313 176 : Tank.EndUseSubcategoryName = "Water Heater";
2314 :
2315 176 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2316 176 : if (Tank.Volume == DataSizing::AutoSize) {
2317 2 : Tank.VolumeWasAutoSized = true;
2318 : }
2319 176 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
2320 : // Set volume to a really small number to simulate a tankless/instantaneous water heater
2321 0 : Tank.Volume = 0.000001; // = 1 cm3
2322 : }
2323 :
2324 176 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(2));
2325 176 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
2326 0 : ShowSevereError(
2327 : state,
2328 0 : format("{}{}=\"{}\", missing data.", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
2329 0 : ShowContinueError(state, format("blank field, missing {} is required", state.dataIPShortCut->cAlphaFieldNames(2)));
2330 0 : ErrorsFound = true;
2331 176 : } else if (Tank.SetPointTempSchedule == 0) {
2332 0 : ShowSevereError(state,
2333 0 : format("{} = {}: {} not found = {}",
2334 0 : state.dataIPShortCut->cCurrentModuleObject,
2335 0 : state.dataIPShortCut->cAlphaArgs(1),
2336 0 : state.dataIPShortCut->cAlphaFieldNames(2),
2337 0 : state.dataIPShortCut->cAlphaArgs(2)));
2338 0 : ErrorsFound = true;
2339 : }
2340 :
2341 176 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
2342 170 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
2343 : } else {
2344 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2345 6 : Tank.DeadBandDeltaTemp = 0.5;
2346 : }
2347 :
2348 176 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2349 176 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
2350 : } else {
2351 : // Default to very large number
2352 : // BG comment why a large number here why not boilng point of water?
2353 0 : Tank.TankTempLimit = 100.0; // 1.0E9
2354 : }
2355 :
2356 176 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
2357 176 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2358 1 : Tank.MaxCapacityWasAutoSized = true;
2359 : }
2360 :
2361 176 : if ((state.dataIPShortCut->rNumericArgs(5) > Tank.MaxCapacity) && (!Tank.MaxCapacityWasAutoSized)) {
2362 0 : ShowSevereError(state,
2363 0 : format("{} = {}: Heater Minimum Capacity cannot be greater than Heater Maximum Capacity",
2364 0 : state.dataIPShortCut->cCurrentModuleObject,
2365 0 : state.dataIPShortCut->cAlphaArgs(1)));
2366 0 : ErrorsFound = true;
2367 : } else {
2368 176 : Tank.MinCapacity = state.dataIPShortCut->rNumericArgs(5);
2369 : }
2370 :
2371 : // Validate Heater Control Type
2372 176 : Tank.ControlType =
2373 176 : static_cast<HeaterControlMode>(getEnumValue(HeaterControlModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
2374 176 : switch (Tank.ControlType) {
2375 171 : case HeaterControlMode::Cycle: {
2376 171 : Tank.MinCapacity = Tank.MaxCapacity;
2377 171 : break;
2378 : }
2379 5 : case HeaterControlMode::Modulate: {
2380 :
2381 : // CASE ('MODULATE WITH OVERHEAT') ! Not yet implemented
2382 :
2383 : // CASE ('MODULATE WITH UNDERHEAT') ! Not yet implemented
2384 :
2385 5 : break;
2386 : }
2387 0 : default: {
2388 0 : ShowSevereError(state,
2389 0 : format("{} = {}: Invalid Control Type entered={}",
2390 0 : state.dataIPShortCut->cCurrentModuleObject,
2391 0 : state.dataIPShortCut->cAlphaArgs(1),
2392 0 : state.dataIPShortCut->cAlphaArgs(3)));
2393 0 : ErrorsFound = true;
2394 0 : break;
2395 : }
2396 : }
2397 :
2398 176 : Tank.VolFlowRateMin = state.dataIPShortCut->rNumericArgs(6);
2399 176 : Tank.VolFlowRateMin = max(0.0, Tank.VolFlowRateMin);
2400 176 : Tank.IgnitionDelay = state.dataIPShortCut->rNumericArgs(7); // Not yet implemented
2401 :
2402 : // Validate Heater Fuel Type
2403 176 : Tank.FuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(4)));
2404 176 : switch (Tank.FuelType) {
2405 0 : case Constant::eFuel::Invalid: {
2406 0 : ShowSevereError(state,
2407 0 : format("{} = {}: Invalid Heater Fuel Type entered={}",
2408 0 : state.dataIPShortCut->cCurrentModuleObject,
2409 0 : state.dataIPShortCut->cAlphaArgs(1),
2410 0 : state.dataIPShortCut->cAlphaArgs(4)));
2411 : // Set to Electric to avoid errors when setting up output variables
2412 0 : Tank.FuelType = Constant::eFuel::Electricity;
2413 0 : ErrorsFound = true;
2414 0 : break;
2415 : }
2416 176 : default:
2417 176 : break;
2418 : }
2419 :
2420 176 : if (state.dataIPShortCut->rNumericArgs(8) > 0.0) {
2421 176 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(8);
2422 176 : if (state.dataIPShortCut->rNumericArgs(8) > 1.0) {
2423 0 : ShowWarningError(state,
2424 0 : fmt::format("{} = {}: {}={} should not typically be greater than 1.",
2425 0 : state.dataIPShortCut->cCurrentModuleObject,
2426 0 : state.dataIPShortCut->cAlphaArgs(1),
2427 0 : state.dataIPShortCut->cNumericFieldNames(8),
2428 0 : state.dataIPShortCut->rNumericArgs(8)));
2429 : }
2430 : } else {
2431 0 : ShowSevereError(state,
2432 0 : format("{} = {}: Heater Thermal Efficiency must be greater than zero",
2433 0 : state.dataIPShortCut->cCurrentModuleObject,
2434 0 : state.dataIPShortCut->cAlphaArgs(1)));
2435 0 : ErrorsFound = true;
2436 : }
2437 :
2438 176 : if (!state.dataIPShortCut->cAlphaArgs(5).empty()) {
2439 0 : Tank.PLFCurve = Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(5));
2440 0 : if (Tank.PLFCurve == 0) {
2441 0 : ShowSevereError(state,
2442 0 : format("{} = {}: Part Load Factor curve not found = {}",
2443 0 : state.dataIPShortCut->cCurrentModuleObject,
2444 0 : state.dataIPShortCut->cAlphaArgs(1),
2445 0 : state.dataIPShortCut->cAlphaArgs(5)));
2446 0 : ErrorsFound = true;
2447 : } else {
2448 : bool IsValid;
2449 0 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::ValidatePLFCurve(state, Tank.PLFCurve, IsValid);
2450 :
2451 0 : if (!IsValid) {
2452 0 : ShowSevereError(
2453 : state,
2454 0 : format("{} = {}: Part Load Factor curve failed to evaluate to greater than zero for all numbers in the domain of 0 to 1",
2455 0 : state.dataIPShortCut->cCurrentModuleObject,
2456 0 : state.dataIPShortCut->cAlphaArgs(1)));
2457 0 : ErrorsFound = true;
2458 : }
2459 :
2460 0 : ErrorsFound |= Curve::CheckCurveDims(state,
2461 : Tank.PLFCurve, // Curve index
2462 : {1}, // Valid dimensions
2463 : RoutineName, // Routine name
2464 0 : state.dataIPShortCut->cCurrentModuleObject, // Object Type
2465 : Tank.Name, // Object Name
2466 0 : state.dataIPShortCut->cAlphaFieldNames(5)); // Field Name
2467 : }
2468 : }
2469 :
2470 176 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(9);
2471 :
2472 : // Validate Off-Cycle Parasitic Fuel Type
2473 176 : Tank.OffCycParaFuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(6)));
2474 176 : switch (Tank.OffCycParaFuelType) {
2475 16 : case Constant::eFuel::Invalid:
2476 16 : if (state.dataIPShortCut->cAlphaArgs(6).empty()) { // If blank, default to Fuel Type for heater
2477 16 : Tank.OffCycParaFuelType = Tank.FuelType;
2478 : } else { // could have been an unsupported value
2479 0 : ShowSevereError(state,
2480 0 : format("{} = {}: Invalid Off-Cycle Parasitic Fuel Type entered={}",
2481 0 : state.dataIPShortCut->cCurrentModuleObject,
2482 0 : state.dataIPShortCut->cAlphaArgs(1),
2483 0 : state.dataIPShortCut->cAlphaArgs(6)));
2484 : // Set to Electric to avoid errors when setting up output variables
2485 0 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
2486 0 : ErrorsFound = true;
2487 : }
2488 16 : break;
2489 160 : default:
2490 160 : break;
2491 : }
2492 :
2493 176 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(10);
2494 :
2495 176 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(11);
2496 :
2497 : // Validate On-Cycle Parasitic Fuel Type
2498 176 : Tank.OnCycParaFuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(7)));
2499 176 : switch (Tank.OnCycParaFuelType) {
2500 56 : case Constant::eFuel::Invalid:
2501 56 : if (state.dataIPShortCut->cAlphaArgs(7).empty()) { // If blank, default to Fuel Type for heater
2502 56 : Tank.OnCycParaFuelType = Tank.FuelType;
2503 : } else { // could have been an unsupported value
2504 0 : ShowSevereError(state,
2505 0 : format("{} = {}: Invalid On-Cycle Parasitic Fuel Type entered={}",
2506 0 : state.dataIPShortCut->cCurrentModuleObject,
2507 0 : state.dataIPShortCut->cAlphaArgs(1),
2508 0 : state.dataIPShortCut->cAlphaArgs(7)));
2509 : // Set to Electric to avoid errors when setting up output variables
2510 0 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
2511 0 : ErrorsFound = true;
2512 : }
2513 56 : break;
2514 120 : default:
2515 120 : break;
2516 : }
2517 :
2518 176 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(12);
2519 :
2520 176 : Tank.AmbientTempIndicator =
2521 176 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(8))));
2522 176 : switch (Tank.AmbientTempIndicator) {
2523 99 : case WTTAmbientTemp::Schedule: {
2524 99 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(9));
2525 99 : if (Tank.AmbientTempSchedule == 0) {
2526 0 : ShowSevereError(state,
2527 0 : format("{} = {}: Ambient Temperature Schedule not found = {}",
2528 0 : state.dataIPShortCut->cCurrentModuleObject,
2529 0 : state.dataIPShortCut->cAlphaArgs(1),
2530 0 : state.dataIPShortCut->cAlphaArgs(9)));
2531 0 : ErrorsFound = true;
2532 : }
2533 :
2534 99 : break;
2535 : }
2536 70 : case WTTAmbientTemp::TempZone: {
2537 70 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(10), state.dataHeatBal->Zone);
2538 70 : if (Tank.AmbientTempZone == 0) {
2539 0 : ShowSevereError(state,
2540 0 : format("{} = {}: Ambient Temperature Zone not found = {}",
2541 0 : state.dataIPShortCut->cCurrentModuleObject,
2542 0 : state.dataIPShortCut->cAlphaArgs(1),
2543 0 : state.dataIPShortCut->cAlphaArgs(10)));
2544 0 : ErrorsFound = true;
2545 : }
2546 :
2547 70 : break;
2548 : }
2549 7 : case WTTAmbientTemp::OutsideAir: {
2550 7 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
2551 7 : state.dataIPShortCut->cAlphaArgs(11),
2552 : ErrorsFound,
2553 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2554 7 : state.dataIPShortCut->cAlphaArgs(1),
2555 : DataLoopNode::NodeFluidType::Air,
2556 : DataLoopNode::ConnectionType::OutsideAirReference,
2557 : NodeInputManager::CompFluidStream::Primary,
2558 : DataLoopNode::ObjectIsNotParent);
2559 7 : if (!state.dataIPShortCut->cAlphaArgs(11).empty()) {
2560 7 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
2561 0 : ShowSevereError(state,
2562 0 : format("{} = {}: Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node",
2563 0 : state.dataIPShortCut->cCurrentModuleObject,
2564 0 : state.dataIPShortCut->cAlphaArgs(1)));
2565 0 : ShowContinueError(state, format("...Referenced Node Name={}", state.dataIPShortCut->cAlphaArgs(11)));
2566 0 : ErrorsFound = true;
2567 : }
2568 : } else {
2569 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
2570 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
2571 0 : ErrorsFound = true;
2572 : }
2573 :
2574 7 : break;
2575 : }
2576 0 : default: {
2577 0 : ShowSevereError(state,
2578 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
2579 0 : state.dataIPShortCut->cCurrentModuleObject,
2580 0 : state.dataIPShortCut->cAlphaArgs(1),
2581 0 : state.dataIPShortCut->cAlphaArgs(8)));
2582 0 : ShowContinueError(state, " Valid entries are SCHEDULE, ZONE, and OUTDOORS.");
2583 0 : ErrorsFound = true;
2584 0 : break;
2585 : }
2586 : }
2587 :
2588 176 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(13);
2589 176 : Tank.OffCycLossFracToZone = state.dataIPShortCut->rNumericArgs(14);
2590 :
2591 176 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(15);
2592 176 : Tank.OnCycLossFracToZone = state.dataIPShortCut->rNumericArgs(16);
2593 176 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, Tank.FluidIndex, RoutineName);
2594 176 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(17) * rho;
2595 :
2596 176 : if ((state.dataIPShortCut->cAlphaArgs(14).empty()) && (state.dataIPShortCut->cAlphaArgs(15).empty())) {
2597 61 : if (!state.dataIPShortCut->cAlphaArgs(12).empty()) {
2598 61 : Tank.FlowRateSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(12));
2599 61 : if (Tank.FlowRateSchedule == 0) {
2600 0 : ShowSevereError(state,
2601 0 : format("{} = {}: Flow Rate Schedule not found = {}",
2602 0 : state.dataIPShortCut->cCurrentModuleObject,
2603 0 : state.dataIPShortCut->cAlphaArgs(1),
2604 0 : state.dataIPShortCut->cAlphaArgs(12)));
2605 0 : ErrorsFound = true;
2606 : }
2607 : }
2608 : }
2609 :
2610 176 : if (!state.dataIPShortCut->cAlphaArgs(13).empty()) {
2611 9 : Tank.UseInletTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(13));
2612 9 : if (Tank.UseInletTempSchedule == 0) {
2613 0 : ShowSevereError(state,
2614 0 : format("{} = {}: Cold Water Supply Temperature Schedule not found = {}",
2615 0 : state.dataIPShortCut->cCurrentModuleObject,
2616 0 : state.dataIPShortCut->cAlphaArgs(1),
2617 0 : state.dataIPShortCut->cAlphaArgs(13)));
2618 0 : ErrorsFound = true;
2619 : }
2620 : }
2621 :
2622 176 : if (NumNums > 17) {
2623 131 : if ((state.dataIPShortCut->rNumericArgs(18) > 1) || (state.dataIPShortCut->rNumericArgs(18) < 0)) {
2624 0 : ShowSevereError(state,
2625 0 : format("{} = {}: Use Side Effectiveness is out of bounds (0 to 1)",
2626 0 : state.dataIPShortCut->cCurrentModuleObject,
2627 0 : state.dataIPShortCut->cAlphaArgs(1)));
2628 0 : ErrorsFound = true;
2629 : }
2630 131 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(18);
2631 : } else {
2632 45 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
2633 : }
2634 :
2635 176 : if (NumNums > 18) {
2636 131 : if ((state.dataIPShortCut->rNumericArgs(19) > 1) || (state.dataIPShortCut->rNumericArgs(19) <= 0)) {
2637 0 : ShowSevereError(state,
2638 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
2639 0 : state.dataIPShortCut->cCurrentModuleObject,
2640 0 : state.dataIPShortCut->cAlphaArgs(1)));
2641 0 : ErrorsFound = true;
2642 : }
2643 131 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(19);
2644 : } else {
2645 45 : Tank.SourceEffectiveness = 1.0;
2646 : }
2647 :
2648 : // If no plant nodes are connected, simulate in stand-alone mode.
2649 298 : if (state.dataIPShortCut->cAlphaArgs(14).empty() && state.dataIPShortCut->cAlphaArgs(15).empty() &&
2650 298 : state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty()) {
2651 47 : Tank.StandAlone = true;
2652 : }
2653 :
2654 176 : if (!state.dataIPShortCut->lNumericFieldBlanks(20)) {
2655 115 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(20);
2656 115 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
2657 109 : Tank.UseDesignVolFlowRateWasAutoSized = true;
2658 : }
2659 : } else {
2660 61 : Tank.UseDesignVolFlowRate = 0.0;
2661 : }
2662 176 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2663 :
2664 176 : if (!state.dataIPShortCut->lNumericFieldBlanks(21)) {
2665 108 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(21);
2666 108 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
2667 98 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
2668 : }
2669 : } else {
2670 68 : Tank.SourceDesignVolFlowRate = 0.0;
2671 : }
2672 176 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
2673 :
2674 176 : if (!state.dataIPShortCut->lNumericFieldBlanks(22)) {
2675 94 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(22);
2676 : } else {
2677 82 : Tank.SizingRecoveryTime = 1.5;
2678 : }
2679 :
2680 176 : if ((!state.dataIPShortCut->cAlphaArgs(14).empty()) || (!state.dataIPShortCut->cAlphaArgs(15).empty())) {
2681 115 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
2682 115 : state.dataIPShortCut->cAlphaArgs(14),
2683 : ErrorsFound,
2684 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2685 115 : state.dataIPShortCut->cAlphaArgs(1),
2686 : DataLoopNode::NodeFluidType::Water,
2687 : DataLoopNode::ConnectionType::Inlet,
2688 : NodeInputManager::CompFluidStream::Primary,
2689 : DataLoopNode::ObjectIsNotParent);
2690 115 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(14);
2691 115 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
2692 115 : state.dataIPShortCut->cAlphaArgs(15),
2693 : ErrorsFound,
2694 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2695 115 : state.dataIPShortCut->cAlphaArgs(1),
2696 : DataLoopNode::NodeFluidType::Water,
2697 : DataLoopNode::ConnectionType::Outlet,
2698 : NodeInputManager::CompFluidStream::Primary,
2699 : DataLoopNode::ObjectIsNotParent);
2700 115 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(15);
2701 :
2702 115 : if (state.dataIPShortCut->rNumericArgs(17) > 0) {
2703 0 : ShowWarningError(state,
2704 0 : format("{} = {}: Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used",
2705 0 : state.dataIPShortCut->cCurrentModuleObject,
2706 0 : state.dataIPShortCut->cAlphaArgs(1)));
2707 : }
2708 :
2709 115 : if (Tank.FlowRateSchedule > 0) {
2710 0 : ShowWarningError(state,
2711 0 : format("{} = {}: Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used",
2712 0 : state.dataIPShortCut->cCurrentModuleObject,
2713 0 : state.dataIPShortCut->cAlphaArgs(1)));
2714 : }
2715 :
2716 115 : if (Tank.UseInletTempSchedule > 0) {
2717 0 : ShowWarningError(state,
2718 0 : format("{} = {}: Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used",
2719 0 : state.dataIPShortCut->cCurrentModuleObject,
2720 0 : state.dataIPShortCut->cAlphaArgs(1)));
2721 : }
2722 : }
2723 :
2724 176 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
2725 33 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
2726 33 : state.dataIPShortCut->cAlphaArgs(16),
2727 : ErrorsFound,
2728 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2729 33 : state.dataIPShortCut->cAlphaArgs(1),
2730 : DataLoopNode::NodeFluidType::Water,
2731 : DataLoopNode::ConnectionType::Inlet,
2732 : NodeInputManager::CompFluidStream::Secondary,
2733 : DataLoopNode::ObjectIsNotParent);
2734 33 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(16);
2735 33 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
2736 33 : state.dataIPShortCut->cAlphaArgs(17),
2737 : ErrorsFound,
2738 : DataLoopNode::ConnectionObjectType::WaterHeaterMixed,
2739 33 : state.dataIPShortCut->cAlphaArgs(1),
2740 : DataLoopNode::NodeFluidType::Water,
2741 : DataLoopNode::ConnectionType::Outlet,
2742 : NodeInputManager::CompFluidStream::Secondary,
2743 : DataLoopNode::ObjectIsNotParent);
2744 33 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(17);
2745 : }
2746 :
2747 176 : if (!state.dataIPShortCut->lAlphaFieldBlanks(18)) {
2748 1 : Tank.SourceSideControlMode =
2749 1 : static_cast<SourceSideControl>(getEnumValue(SourceSideControlNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(18))));
2750 1 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
2751 0 : ShowSevereError(state,
2752 0 : format("{} = {}: Invalid Control Mode entered={}",
2753 0 : state.dataIPShortCut->cCurrentModuleObject,
2754 0 : state.dataIPShortCut->cAlphaArgs(1),
2755 0 : state.dataIPShortCut->cAlphaArgs(18)));
2756 0 : ErrorsFound = true;
2757 : }
2758 : } else {
2759 175 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
2760 : }
2761 :
2762 176 : if (!state.dataIPShortCut->lAlphaFieldBlanks(19)) {
2763 0 : Tank.SourceSideAltSetpointSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(19));
2764 0 : if (Tank.SourceSideAltSetpointSchedNum == 0) {
2765 0 : ShowSevereError(state,
2766 0 : format("{} = {}: {} not found = {}",
2767 0 : state.dataIPShortCut->cCurrentModuleObject,
2768 0 : state.dataIPShortCut->cAlphaArgs(1),
2769 0 : state.dataIPShortCut->cAlphaFieldNames(19),
2770 0 : state.dataIPShortCut->cAlphaArgs(19)));
2771 0 : ErrorsFound = true;
2772 : }
2773 : }
2774 176 : if (NumAlphas > 19) {
2775 1 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(20);
2776 : }
2777 :
2778 : } // WaterThermalTankNum
2779 :
2780 123 : return ErrorsFound;
2781 : }
2782 :
2783 10 : bool getWaterHeaterStratifiedInput(EnergyPlusData &state)
2784 : {
2785 10 : bool ErrorsFound = false;
2786 : static constexpr std::string_view RoutineName = "getWaterHeaterStratifiedInput";
2787 :
2788 10 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedWHModuleObj; //'WaterHeater:Stratified'
2789 :
2790 26 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + 1;
2791 26 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified;
2792 : ++WaterThermalTankNum) {
2793 : int NumAlphas;
2794 : int NumNums;
2795 : int IOStat;
2796 32 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
2797 16 : state.dataIPShortCut->cCurrentModuleObject,
2798 16 : WaterThermalTankNum - state.dataWaterThermalTanks->numWaterHeaterMixed,
2799 16 : state.dataIPShortCut->cAlphaArgs,
2800 : NumAlphas,
2801 16 : state.dataIPShortCut->rNumericArgs,
2802 : NumNums,
2803 : IOStat,
2804 16 : state.dataIPShortCut->lNumericFieldBlanks,
2805 16 : state.dataIPShortCut->lAlphaFieldBlanks,
2806 16 : state.dataIPShortCut->cAlphaFieldNames,
2807 16 : state.dataIPShortCut->cNumericFieldNames);
2808 16 : GlobalNames::VerifyUniqueInterObjectName(state,
2809 16 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
2810 16 : state.dataIPShortCut->cAlphaArgs(1),
2811 16 : state.dataIPShortCut->cCurrentModuleObject,
2812 16 : state.dataIPShortCut->cAlphaFieldNames(1),
2813 : ErrorsFound);
2814 :
2815 16 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
2816 :
2817 16 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
2818 16 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
2819 16 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::WtrHeaterStratified;
2820 16 : Tank.FluidIndex = Tank.waterIndex;
2821 :
2822 : // default to always on
2823 16 : Tank.SourceSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
2824 16 : Tank.UseSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
2825 :
2826 16 : Tank.EndUseSubcategoryName = state.dataIPShortCut->cAlphaArgs(2);
2827 :
2828 16 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
2829 16 : if (Tank.Volume == DataSizing::AutoSize) {
2830 0 : Tank.VolumeWasAutoSized = true;
2831 : }
2832 16 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, Tank.FluidIndex, RoutineName);
2833 16 : Tank.Mass = Tank.Volume * rho;
2834 16 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
2835 16 : if (Tank.Height == DataSizing::AutoSize) {
2836 0 : Tank.HeightWasAutoSized = true;
2837 : }
2838 :
2839 16 : Tank.Shape = static_cast<TankShape>(getEnumValue(TankShapeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
2840 16 : switch (Tank.Shape) {
2841 16 : case TankShape::HorizCylinder:
2842 : case TankShape::VertCylinder: {
2843 16 : break;
2844 : }
2845 0 : case TankShape::Other: {
2846 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
2847 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
2848 : } else {
2849 0 : ShowSevereError(state,
2850 0 : format("{} = {}: Tank Perimeter must be greater than zero for Tank Shape=OTHER",
2851 0 : state.dataIPShortCut->cCurrentModuleObject,
2852 0 : state.dataIPShortCut->cAlphaArgs(1)));
2853 0 : ErrorsFound = true;
2854 : }
2855 :
2856 0 : break;
2857 : }
2858 0 : default: {
2859 0 : ShowSevereError(state,
2860 0 : format("{} = {}: Invalid Tank Shape entered={}",
2861 0 : state.dataIPShortCut->cCurrentModuleObject,
2862 0 : state.dataIPShortCut->cAlphaArgs(1),
2863 0 : state.dataIPShortCut->cAlphaArgs(3)));
2864 0 : Tank.Shape = TankShape::VertCylinder;
2865 0 : ErrorsFound = true;
2866 0 : break;
2867 : }
2868 : }
2869 :
2870 16 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
2871 16 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(4);
2872 : } else {
2873 : // Default to very large number
2874 0 : Tank.TankTempLimit = 1.0e9;
2875 : }
2876 :
2877 : // Validate Heater Priority Control
2878 16 : Tank.StratifiedControlMode =
2879 16 : static_cast<PriorityControlMode>(getEnumValue(PriorityControlModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(4))));
2880 16 : if (Tank.StratifiedControlMode == PriorityControlMode::Invalid) {
2881 0 : ShowSevereError(state,
2882 0 : format("{} = {}: Invalid Heater Priority Control entered={}",
2883 0 : state.dataIPShortCut->cCurrentModuleObject,
2884 0 : state.dataIPShortCut->cAlphaArgs(1),
2885 0 : state.dataIPShortCut->cAlphaArgs(4)));
2886 0 : ErrorsFound = true;
2887 : }
2888 :
2889 16 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(5));
2890 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(5)) {
2891 0 : ShowSevereError(
2892 : state,
2893 0 : format("{}{}=\"{}\", missing data.", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
2894 0 : ShowContinueError(state, format("blank field, missing {} is required", state.dataIPShortCut->cAlphaFieldNames(5)));
2895 0 : ErrorsFound = true;
2896 16 : } else if (Tank.SetPointTempSchedule == 0) {
2897 0 : ShowSevereError(state,
2898 0 : format("{} = {}: {} not found = {}",
2899 0 : state.dataIPShortCut->cCurrentModuleObject,
2900 0 : state.dataIPShortCut->cAlphaArgs(1),
2901 0 : state.dataIPShortCut->cAlphaFieldNames(5),
2902 0 : state.dataIPShortCut->cAlphaArgs(5)));
2903 0 : ErrorsFound = true;
2904 : }
2905 :
2906 16 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2907 16 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(5);
2908 : } else {
2909 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2910 0 : Tank.DeadBandDeltaTemp = 0.0001;
2911 : }
2912 :
2913 16 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(6);
2914 16 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
2915 2 : Tank.MaxCapacityWasAutoSized = true;
2916 : }
2917 :
2918 16 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(7);
2919 :
2920 : // adjust tank height used in these calculations for testing heater height
2921 : Real64 tankHeightForTesting;
2922 16 : if (Tank.Shape == TankShape::HorizCylinder) {
2923 0 : tankHeightForTesting = 2.0 * sqrt((Tank.Volume / Tank.Height) / Constant::Pi);
2924 : } else {
2925 16 : tankHeightForTesting = Tank.Height;
2926 : }
2927 :
2928 : // Test if Heater height is within range
2929 16 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight1 > tankHeightForTesting)) {
2930 0 : ShowSevereError(state,
2931 0 : format("{} = {}: Heater 1 is located higher than overall tank height.",
2932 0 : state.dataIPShortCut->cCurrentModuleObject,
2933 0 : state.dataIPShortCut->cAlphaArgs(1)));
2934 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2935 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(7), state.dataIPShortCut->rNumericArgs(7)));
2936 0 : ErrorsFound = true;
2937 : }
2938 :
2939 16 : Tank.SetPointTempSchedule2 = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(6));
2940 16 : if (state.dataIPShortCut->lAlphaFieldBlanks(6)) {
2941 0 : ShowSevereError(
2942 : state,
2943 0 : format("{}{}=\"{}\", missing data.", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
2944 0 : ShowContinueError(state, format("blank field, missing {} is required", state.dataIPShortCut->cAlphaFieldNames(6)));
2945 0 : ErrorsFound = true;
2946 16 : } else if (Tank.SetPointTempSchedule2 == 0) {
2947 0 : ShowSevereError(state,
2948 0 : format("{} = {}: {} not found = {}",
2949 0 : state.dataIPShortCut->cCurrentModuleObject,
2950 0 : state.dataIPShortCut->cAlphaArgs(1),
2951 0 : state.dataIPShortCut->cAlphaFieldNames(6),
2952 0 : state.dataIPShortCut->cAlphaArgs(6)));
2953 0 : ErrorsFound = true;
2954 : }
2955 :
2956 16 : if (state.dataIPShortCut->rNumericArgs(5) > 0.0) {
2957 16 : Tank.DeadBandDeltaTemp2 = state.dataIPShortCut->rNumericArgs(8);
2958 : } else {
2959 : // Default to very small number (however it can't be TINY or it will break the algorithm)
2960 0 : Tank.DeadBandDeltaTemp2 = 0.0001;
2961 : }
2962 :
2963 16 : Tank.MaxCapacity2 = state.dataIPShortCut->rNumericArgs(9);
2964 16 : Tank.HeaterHeight2 = state.dataIPShortCut->rNumericArgs(10);
2965 :
2966 : // Test if Heater height is within range
2967 16 : if ((!Tank.HeightWasAutoSized) && (Tank.HeaterHeight2 > tankHeightForTesting)) {
2968 0 : ShowSevereError(state,
2969 0 : format("{} = {}: Heater 2 is located higher than overall tank height.",
2970 0 : state.dataIPShortCut->cCurrentModuleObject,
2971 0 : state.dataIPShortCut->cAlphaArgs(1)));
2972 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
2973 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
2974 0 : ErrorsFound = true;
2975 : }
2976 :
2977 : // Validate Heater Fuel Type
2978 16 : Tank.FuelType = static_cast<Constant::eFuel>(
2979 16 : getEnumValue(Constant::eFuelNamesUC,
2980 16 : state.dataIPShortCut->cAlphaArgs(
2981 : 7))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
2982 16 : if (Tank.FuelType == Constant::eFuel::Invalid) {
2983 0 : ShowSevereError(state,
2984 0 : format("{} = {}: Invalid Heater Fuel Type entered={}",
2985 0 : state.dataIPShortCut->cCurrentModuleObject,
2986 0 : state.dataIPShortCut->cAlphaArgs(1),
2987 0 : state.dataIPShortCut->cAlphaArgs(7)));
2988 : // Set to Electric to avoid errors when setting up output variables
2989 0 : Tank.FuelType = Constant::eFuel::Electricity;
2990 0 : ErrorsFound = true;
2991 : }
2992 :
2993 16 : if (state.dataIPShortCut->rNumericArgs(11) > 0.0) {
2994 16 : Tank.Efficiency = state.dataIPShortCut->rNumericArgs(11);
2995 16 : if (state.dataIPShortCut->rNumericArgs(11) > 1.0) {
2996 0 : ShowWarningError(state,
2997 0 : fmt::format("{} = {}: {}={} should not typically be greater than 1.",
2998 0 : state.dataIPShortCut->cCurrentModuleObject,
2999 0 : state.dataIPShortCut->cAlphaArgs(1),
3000 0 : state.dataIPShortCut->cNumericFieldNames(11),
3001 0 : state.dataIPShortCut->rNumericArgs(11)));
3002 : }
3003 : } else {
3004 0 : ShowSevereError(state,
3005 0 : format("{} = {}: Heater Thermal Efficiency must be greater than zero",
3006 0 : state.dataIPShortCut->cCurrentModuleObject,
3007 0 : state.dataIPShortCut->cAlphaArgs(1)));
3008 0 : ErrorsFound = true;
3009 : }
3010 :
3011 16 : Tank.OffCycParaLoad = state.dataIPShortCut->rNumericArgs(12);
3012 :
3013 : // Validate Off-Cycle Parasitic Fuel Type
3014 16 : Tank.OffCycParaFuelType = static_cast<Constant::eFuel>(
3015 16 : getEnumValue(Constant::eFuelNamesUC,
3016 16 : state.dataIPShortCut->cAlphaArgs(
3017 : 8))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported
3018 16 : if (Tank.OffCycParaFuelType == Constant::eFuel::Invalid) {
3019 0 : if (state.dataIPShortCut->cAlphaArgs(8).empty()) {
3020 0 : Tank.OffCycParaFuelType = Tank.FuelType;
3021 : } else {
3022 0 : ShowSevereError(state,
3023 0 : format("{} = {}: Invalid Off-Cycle Parasitic Fuel Type entered={}",
3024 0 : state.dataIPShortCut->cCurrentModuleObject,
3025 0 : state.dataIPShortCut->cAlphaArgs(1),
3026 0 : state.dataIPShortCut->cAlphaArgs(8)));
3027 : // Set to Electric to avoid errors when setting up output variables
3028 0 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
3029 0 : ErrorsFound = true;
3030 : }
3031 : }
3032 :
3033 16 : Tank.OffCycParaFracToTank = state.dataIPShortCut->rNumericArgs(13);
3034 16 : Tank.OffCycParaHeight = state.dataIPShortCut->rNumericArgs(14);
3035 :
3036 16 : Tank.OnCycParaLoad = state.dataIPShortCut->rNumericArgs(15);
3037 :
3038 : // Validate On-Cycle Parasitic Fuel Type
3039 16 : Tank.OnCycParaFuelType = static_cast<Constant::eFuel>(
3040 16 : getEnumValue(Constant::eFuelNamesUC,
3041 16 : state.dataIPShortCut->cAlphaArgs(
3042 : 9))); // returns all kinds of fuels including district heat and cool + steam, returns unassigned if unsupported/empty
3043 16 : if (Tank.OnCycParaFuelType == Constant::eFuel::Invalid) {
3044 0 : if (state.dataIPShortCut->cAlphaArgs(9).empty()) {
3045 0 : Tank.OnCycParaFuelType = Tank.FuelType;
3046 : } else {
3047 0 : ShowSevereError(state,
3048 0 : format("{} = {}: Invalid On-Cycle Parasitic Fuel Type entered={}",
3049 0 : state.dataIPShortCut->cCurrentModuleObject,
3050 0 : state.dataIPShortCut->cAlphaArgs(1),
3051 0 : state.dataIPShortCut->cAlphaArgs(9)));
3052 : // Set to Electric to avoid errors when setting up output variables
3053 0 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3054 0 : ErrorsFound = true;
3055 : }
3056 : }
3057 :
3058 16 : Tank.OnCycParaFracToTank = state.dataIPShortCut->rNumericArgs(16);
3059 16 : Tank.OnCycParaHeight = state.dataIPShortCut->rNumericArgs(17);
3060 :
3061 16 : Tank.AmbientTempIndicator =
3062 16 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(10))));
3063 16 : switch (Tank.AmbientTempIndicator) {
3064 :
3065 8 : case WTTAmbientTemp::Schedule: {
3066 8 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(11));
3067 8 : if (Tank.AmbientTempSchedule == 0) {
3068 0 : ShowSevereError(state,
3069 0 : format("{} = {}: Ambient Temperature Schedule not found = {}",
3070 0 : state.dataIPShortCut->cCurrentModuleObject,
3071 0 : state.dataIPShortCut->cAlphaArgs(1),
3072 0 : state.dataIPShortCut->cAlphaArgs(11)));
3073 0 : ErrorsFound = true;
3074 : }
3075 :
3076 8 : break;
3077 : }
3078 6 : case WTTAmbientTemp::TempZone: {
3079 6 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(12), state.dataHeatBal->Zone);
3080 6 : if (Tank.AmbientTempZone == 0) {
3081 0 : ShowSevereError(state,
3082 0 : format("{} = {}: Ambient Temperature Zone not found = {}",
3083 0 : state.dataIPShortCut->cCurrentModuleObject,
3084 0 : state.dataIPShortCut->cAlphaArgs(1),
3085 0 : state.dataIPShortCut->cAlphaArgs(12)));
3086 0 : ErrorsFound = true;
3087 : }
3088 :
3089 6 : break;
3090 : }
3091 2 : case WTTAmbientTemp::OutsideAir: {
3092 2 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3093 2 : state.dataIPShortCut->cAlphaArgs(13),
3094 : ErrorsFound,
3095 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3096 2 : state.dataIPShortCut->cAlphaArgs(1),
3097 : DataLoopNode::NodeFluidType::Air,
3098 : DataLoopNode::ConnectionType::Inlet,
3099 : NodeInputManager::CompFluidStream::Primary,
3100 : DataLoopNode::ObjectIsNotParent);
3101 2 : if (!state.dataIPShortCut->cAlphaArgs(13).empty()) {
3102 2 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3103 0 : ShowSevereError(state,
3104 0 : format("{} = {}: Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node",
3105 0 : state.dataIPShortCut->cCurrentModuleObject,
3106 0 : state.dataIPShortCut->cAlphaArgs(1)));
3107 0 : ShowContinueError(state, format("...Referenced Node Name={}", state.dataIPShortCut->cAlphaArgs(13)));
3108 0 : ErrorsFound = true;
3109 : }
3110 : } else {
3111 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3112 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3113 0 : ErrorsFound = true;
3114 : }
3115 :
3116 2 : break;
3117 : }
3118 0 : default: {
3119 0 : ShowSevereError(state,
3120 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3121 0 : state.dataIPShortCut->cCurrentModuleObject,
3122 0 : state.dataIPShortCut->cAlphaArgs(1),
3123 0 : state.dataIPShortCut->cAlphaArgs(10)));
3124 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3125 0 : ErrorsFound = true;
3126 0 : break;
3127 : }
3128 : }
3129 :
3130 16 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(18);
3131 16 : Tank.SkinLossFracToZone = state.dataIPShortCut->rNumericArgs(19);
3132 16 : Tank.OffCycFlueLossCoeff = state.dataIPShortCut->rNumericArgs(20);
3133 16 : Tank.OffCycFlueLossFracToZone = state.dataIPShortCut->rNumericArgs(21);
3134 :
3135 : // this is temporary until we know fluid type
3136 16 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, Tank.FluidIndex, RoutineName);
3137 16 : Tank.MassFlowRateMax = state.dataIPShortCut->rNumericArgs(22) * rho;
3138 :
3139 16 : if ((state.dataIPShortCut->cAlphaArgs(16).empty()) && (state.dataIPShortCut->cAlphaArgs(17).empty())) {
3140 7 : if (!state.dataIPShortCut->cAlphaArgs(14).empty()) {
3141 7 : Tank.FlowRateSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(14));
3142 7 : if (Tank.FlowRateSchedule == 0) {
3143 0 : ShowSevereError(state,
3144 0 : format("{} = {}: Flow Rate Schedule not found = {}",
3145 0 : state.dataIPShortCut->cCurrentModuleObject,
3146 0 : state.dataIPShortCut->cAlphaArgs(1),
3147 0 : state.dataIPShortCut->cAlphaArgs(14)));
3148 0 : ErrorsFound = true;
3149 : }
3150 : }
3151 : }
3152 :
3153 16 : if (!state.dataIPShortCut->cAlphaArgs(15).empty()) {
3154 0 : Tank.UseInletTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(15));
3155 0 : if (Tank.UseInletTempSchedule == 0) {
3156 0 : ShowSevereError(state,
3157 0 : format("{} = {}: Cold Water Supply Temperature Schedule not found = {}",
3158 0 : state.dataIPShortCut->cCurrentModuleObject,
3159 0 : state.dataIPShortCut->cAlphaArgs(1),
3160 0 : state.dataIPShortCut->cAlphaArgs(15)));
3161 0 : ErrorsFound = true;
3162 : }
3163 : }
3164 :
3165 16 : if (NumNums > 22) {
3166 16 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(23);
3167 : } else {
3168 0 : Tank.UseEffectiveness = 1.0; // Default for stand-alone mode
3169 : }
3170 :
3171 16 : if (NumNums > 23) {
3172 16 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(24);
3173 : } else {
3174 : // Defaults to bottom of tank
3175 0 : Tank.UseInletHeight = 0.0;
3176 : }
3177 16 : if ((!Tank.HeightWasAutoSized) && (Tank.UseInletHeight > Tank.Height)) {
3178 0 : ShowSevereError(state,
3179 0 : format("{} = {}: Use inlet is located higher than overall tank height.",
3180 0 : state.dataIPShortCut->cCurrentModuleObject,
3181 0 : state.dataIPShortCut->cAlphaArgs(1)));
3182 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3183 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(24), state.dataIPShortCut->rNumericArgs(24)));
3184 0 : ErrorsFound = true;
3185 : }
3186 :
3187 16 : if ((NumNums > 24) && (state.dataIPShortCut->rNumericArgs(25) != Constant::AutoCalculate)) {
3188 1 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(25);
3189 : } else {
3190 : // Defaults to top of tank
3191 15 : Tank.UseOutletHeight = Tank.Height;
3192 : }
3193 16 : if (Tank.UseOutletHeight == DataSizing::AutoSize) {
3194 0 : Tank.UseOutletHeightWasAutoSized = true;
3195 : }
3196 16 : if ((!Tank.HeightWasAutoSized) && (Tank.UseOutletHeight > Tank.Height)) {
3197 0 : ShowSevereError(state,
3198 0 : format("{} = {}: Use outlet is located higher than overall tank height.",
3199 0 : state.dataIPShortCut->cCurrentModuleObject,
3200 0 : state.dataIPShortCut->cAlphaArgs(1)));
3201 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3202 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(25), state.dataIPShortCut->rNumericArgs(25)));
3203 0 : ErrorsFound = true;
3204 : }
3205 :
3206 16 : if (NumNums > 25) {
3207 16 : if ((state.dataIPShortCut->rNumericArgs(26) > 1) || (state.dataIPShortCut->rNumericArgs(26) <= 0)) {
3208 0 : ShowSevereError(state,
3209 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3210 0 : state.dataIPShortCut->cCurrentModuleObject,
3211 0 : state.dataIPShortCut->cAlphaArgs(1)));
3212 0 : ErrorsFound = true;
3213 : }
3214 16 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(26);
3215 : } else {
3216 0 : Tank.SourceEffectiveness = 1.0;
3217 : }
3218 :
3219 16 : if ((NumNums > 26) && (state.dataIPShortCut->rNumericArgs(27) != Constant::AutoCalculate)) {
3220 11 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(27);
3221 : } else {
3222 : // Defaults to top of tank
3223 5 : Tank.SourceInletHeight = Tank.Height;
3224 : }
3225 16 : if (Tank.SourceInletHeight == DataSizing::AutoSize) {
3226 0 : Tank.SourceInletHeightWasAutoSized = true;
3227 : }
3228 16 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceInletHeight > Tank.Height)) {
3229 0 : ShowSevereError(state,
3230 0 : format("{} = {}: Source inlet is located higher than overall tank height.",
3231 0 : state.dataIPShortCut->cCurrentModuleObject,
3232 0 : state.dataIPShortCut->cAlphaArgs(1)));
3233 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3234 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(27), state.dataIPShortCut->rNumericArgs(27)));
3235 0 : ErrorsFound = true;
3236 : }
3237 :
3238 16 : if ((NumNums > 27) && (state.dataIPShortCut->rNumericArgs(28) != Constant::AutoCalculate)) {
3239 16 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(28);
3240 : } else {
3241 : // Defaults to bottom of tank
3242 0 : Tank.SourceOutletHeight = 0.0;
3243 : }
3244 16 : if ((!Tank.HeightWasAutoSized) && (Tank.SourceOutletHeight > Tank.Height)) {
3245 0 : ShowSevereError(state,
3246 0 : format("{} = {}: Source outlet is located higher than overall tank height.",
3247 0 : state.dataIPShortCut->cCurrentModuleObject,
3248 0 : state.dataIPShortCut->cAlphaArgs(1)));
3249 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3250 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(28), state.dataIPShortCut->rNumericArgs(28)));
3251 0 : ErrorsFound = true;
3252 : }
3253 :
3254 : // If no plant nodes are connected, simulate in stand-alone mode.
3255 30 : if (state.dataIPShortCut->cAlphaArgs(16).empty() && state.dataIPShortCut->cAlphaArgs(17).empty() &&
3256 30 : state.dataIPShortCut->cAlphaArgs(18).empty() && state.dataIPShortCut->cAlphaArgs(19).empty())
3257 2 : Tank.StandAlone = true;
3258 :
3259 16 : if (!state.dataIPShortCut->lNumericFieldBlanks(29)) {
3260 9 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(29);
3261 9 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3262 9 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3263 : }
3264 : } else {
3265 7 : Tank.UseDesignVolFlowRate = 0.0;
3266 : }
3267 :
3268 16 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3269 :
3270 16 : if (!state.dataIPShortCut->lNumericFieldBlanks(30)) {
3271 6 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(30);
3272 6 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3273 4 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3274 : }
3275 : } else {
3276 10 : Tank.SourceDesignVolFlowRate = 0.0;
3277 : }
3278 :
3279 16 : if (NumNums > 30) {
3280 16 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(31);
3281 : } else {
3282 0 : Tank.SizingRecoveryTime = 1.5;
3283 : }
3284 :
3285 16 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3286 :
3287 16 : if ((!state.dataIPShortCut->cAlphaArgs(16).empty()) || (!state.dataIPShortCut->cAlphaArgs(17).empty())) {
3288 9 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3289 9 : state.dataIPShortCut->cAlphaArgs(16),
3290 : ErrorsFound,
3291 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3292 9 : state.dataIPShortCut->cAlphaArgs(1),
3293 : DataLoopNode::NodeFluidType::Water,
3294 : DataLoopNode::ConnectionType::Inlet,
3295 : NodeInputManager::CompFluidStream::Primary,
3296 : DataLoopNode::ObjectIsNotParent);
3297 9 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(16);
3298 9 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3299 9 : state.dataIPShortCut->cAlphaArgs(17),
3300 : ErrorsFound,
3301 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3302 9 : state.dataIPShortCut->cAlphaArgs(1),
3303 : DataLoopNode::NodeFluidType::Water,
3304 : DataLoopNode::ConnectionType::Outlet,
3305 : NodeInputManager::CompFluidStream::Primary,
3306 : DataLoopNode::ObjectIsNotParent);
3307 9 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(17);
3308 :
3309 9 : if (state.dataIPShortCut->rNumericArgs(22) > 0) {
3310 0 : ShowWarningError(state,
3311 0 : format("{} = {}: Use side nodes are specified; Peak Volumetric Use Flow Rate will not be used",
3312 0 : state.dataIPShortCut->cCurrentModuleObject,
3313 0 : state.dataIPShortCut->cAlphaArgs(1)));
3314 : }
3315 :
3316 9 : if (Tank.FlowRateSchedule > 0) {
3317 0 : ShowWarningError(state,
3318 0 : format("{} = {}: Use side nodes are specified; Use Flow Rate Fraction Schedule will not be used",
3319 0 : state.dataIPShortCut->cCurrentModuleObject,
3320 0 : state.dataIPShortCut->cAlphaArgs(1)));
3321 : }
3322 :
3323 9 : if (Tank.UseInletTempSchedule > 0) {
3324 0 : ShowWarningError(state,
3325 0 : format("{} = {}: Use side nodes are specified; Cold Water Supply Temperature Schedule will not be used",
3326 0 : state.dataIPShortCut->cCurrentModuleObject,
3327 0 : state.dataIPShortCut->cAlphaArgs(1)));
3328 : }
3329 : }
3330 :
3331 16 : if ((!state.dataIPShortCut->cAlphaArgs(18).empty()) || (!state.dataIPShortCut->cAlphaArgs(19).empty())) {
3332 10 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3333 10 : state.dataIPShortCut->cAlphaArgs(18),
3334 : ErrorsFound,
3335 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3336 10 : state.dataIPShortCut->cAlphaArgs(1),
3337 : DataLoopNode::NodeFluidType::Water,
3338 : DataLoopNode::ConnectionType::Inlet,
3339 : NodeInputManager::CompFluidStream::Secondary,
3340 : DataLoopNode::ObjectIsNotParent);
3341 10 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(18);
3342 10 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3343 10 : state.dataIPShortCut->cAlphaArgs(19),
3344 : ErrorsFound,
3345 : DataLoopNode::ConnectionObjectType::WaterHeaterStratified,
3346 10 : state.dataIPShortCut->cAlphaArgs(1),
3347 : DataLoopNode::NodeFluidType::Water,
3348 : DataLoopNode::ConnectionType::Outlet,
3349 : NodeInputManager::CompFluidStream::Secondary,
3350 : DataLoopNode::ObjectIsNotParent);
3351 10 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(19);
3352 : }
3353 :
3354 : // Validate inlet mode
3355 16 : Tank.InletMode =
3356 16 : static_cast<InletPositionMode>(getEnumValue(InletPositionModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(20))));
3357 :
3358 16 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(32);
3359 16 : int specifiedNodes = 0;
3360 16 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(33);
3361 :
3362 16 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
3363 16 : Tank.AdditionalLossCoeff = 0.0;
3364 44 : for (int NodeNum = 1; NodeNum <= 12; ++NodeNum) {
3365 44 : int index = 33 + NodeNum;
3366 44 : if (NumNums >= index) {
3367 28 : if (NodeNum <= Tank.Nodes) {
3368 28 : ++specifiedNodes;
3369 28 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(index);
3370 0 : } else if (!state.dataIPShortCut->lNumericFieldBlanks(index) && (state.dataIPShortCut->rNumericArgs(index) != 0)) {
3371 : // If either blank, or zero (default), then do not warn
3372 0 : ++specifiedNodes;
3373 : }
3374 : } else {
3375 16 : break;
3376 : }
3377 : }
3378 :
3379 16 : if (specifiedNodes > Tank.Nodes) {
3380 0 : ShowWarningError(
3381 : state,
3382 0 : format("{} = {}: More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used",
3383 0 : state.dataIPShortCut->cCurrentModuleObject,
3384 0 : state.dataIPShortCut->cAlphaArgs(1)));
3385 : }
3386 :
3387 16 : Tank.SetupStratifiedNodes(state);
3388 :
3389 16 : if (!state.dataIPShortCut->lAlphaFieldBlanks(21)) {
3390 0 : Tank.SourceSideControlMode =
3391 0 : static_cast<SourceSideControl>(getEnumValue(SourceSideControlNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(21))));
3392 0 : if (Tank.SourceSideControlMode == SourceSideControl::Invalid) {
3393 0 : ShowSevereError(state,
3394 0 : format("{} = {}: Invalid Control Mode entered={}",
3395 0 : state.dataIPShortCut->cCurrentModuleObject,
3396 0 : state.dataIPShortCut->cAlphaArgs(1),
3397 0 : state.dataIPShortCut->cAlphaArgs(21)));
3398 0 : ErrorsFound = true;
3399 : }
3400 : } else {
3401 16 : Tank.SourceSideControlMode = SourceSideControl::IndirectHeatPrimarySetpoint;
3402 : }
3403 :
3404 16 : if (!state.dataIPShortCut->lAlphaFieldBlanks(22)) {
3405 0 : Tank.SourceSideAltSetpointSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(22));
3406 0 : if (Tank.SourceSideAltSetpointSchedNum == 0) {
3407 0 : ShowSevereError(state,
3408 0 : format("{} = {}: {} not found = {}",
3409 0 : state.dataIPShortCut->cCurrentModuleObject,
3410 0 : state.dataIPShortCut->cAlphaArgs(1),
3411 0 : state.dataIPShortCut->cAlphaFieldNames(22),
3412 0 : state.dataIPShortCut->cAlphaArgs(22)));
3413 0 : ErrorsFound = true;
3414 : }
3415 : }
3416 : }
3417 :
3418 10 : return ErrorsFound;
3419 : }
3420 :
3421 4 : bool getWaterTankMixedInput(EnergyPlusData &state)
3422 : {
3423 4 : bool ErrorsFound = false;
3424 :
3425 4 : state.dataIPShortCut->cCurrentModuleObject = cMixedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Mixed'
3426 8 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified + 1;
3427 8 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3428 8 : state.dataWaterThermalTanks->numChilledWaterMixed;
3429 : ++WaterThermalTankNum) {
3430 : int NumAlphas;
3431 : int NumNums;
3432 : int IOStat;
3433 8 : state.dataInputProcessing->inputProcessor->getObjectItem(
3434 : state,
3435 4 : state.dataIPShortCut->cCurrentModuleObject,
3436 4 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified),
3437 4 : state.dataIPShortCut->cAlphaArgs,
3438 : NumAlphas,
3439 4 : state.dataIPShortCut->rNumericArgs,
3440 : NumNums,
3441 : IOStat,
3442 4 : state.dataIPShortCut->lNumericFieldBlanks,
3443 4 : state.dataIPShortCut->lAlphaFieldBlanks,
3444 4 : state.dataIPShortCut->cAlphaFieldNames,
3445 4 : state.dataIPShortCut->cNumericFieldNames);
3446 4 : GlobalNames::VerifyUniqueInterObjectName(state,
3447 4 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3448 4 : state.dataIPShortCut->cAlphaArgs(1),
3449 4 : state.dataIPShortCut->cCurrentModuleObject,
3450 4 : state.dataIPShortCut->cAlphaFieldNames(1),
3451 : ErrorsFound);
3452 :
3453 4 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3454 :
3455 4 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3456 4 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3457 4 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankMixed;
3458 4 : Tank.FluidIndex = Tank.waterIndex;
3459 4 : Tank.IsChilledWaterTank = true;
3460 4 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3461 :
3462 4 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3463 4 : if (Tank.Volume == DataSizing::AutoSize) {
3464 0 : Tank.VolumeWasAutoSized = true;
3465 : }
3466 :
3467 4 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
3468 : // Set volume to a really small number to continue simulation
3469 0 : Tank.Volume = 0.000001; // = 1 cm3
3470 : }
3471 :
3472 4 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(2));
3473 4 : if (Tank.SetPointTempSchedule == 0) {
3474 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
3475 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3476 :
3477 0 : ErrorsFound = true;
3478 : }
3479 :
3480 4 : if (state.dataIPShortCut->rNumericArgs(2) > 0.0001) {
3481 4 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(2);
3482 : } else {
3483 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3484 0 : Tank.DeadBandDeltaTemp = 0.5;
3485 : }
3486 :
3487 4 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3488 4 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(3);
3489 : } else {
3490 : // default to just above freezing
3491 0 : Tank.TankTempLimit = 1.0;
3492 : }
3493 :
3494 4 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(4);
3495 4 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3496 0 : Tank.MaxCapacityWasAutoSized = true;
3497 : }
3498 :
3499 4 : Tank.MinCapacity = 0.0;
3500 4 : Tank.ControlType = HeaterControlMode::Cycle;
3501 :
3502 4 : Tank.MassFlowRateMin = 0.0;
3503 4 : Tank.IgnitionDelay = 0.0;
3504 4 : Tank.FuelType = Constant::eFuel::Electricity;
3505 4 : Tank.Efficiency = 1.0;
3506 4 : Tank.PLFCurve = 0;
3507 4 : Tank.OffCycParaLoad = 0.0;
3508 4 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
3509 4 : Tank.OffCycParaFracToTank = 0.0;
3510 4 : Tank.OnCycParaLoad = 0.0;
3511 4 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3512 4 : Tank.OnCycParaFracToTank = 0.0;
3513 :
3514 4 : Tank.AmbientTempIndicator =
3515 4 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(3))));
3516 4 : switch (Tank.AmbientTempIndicator) {
3517 :
3518 0 : case WTTAmbientTemp::Schedule: {
3519 0 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(4));
3520 0 : if (Tank.AmbientTempSchedule == 0) {
3521 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4)));
3522 0 : ShowContinueError(state,
3523 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3524 0 : ShowContinueError(state, "Schedule was not found.");
3525 0 : ErrorsFound = true;
3526 : }
3527 :
3528 0 : break;
3529 : }
3530 3 : case WTTAmbientTemp::TempZone: {
3531 3 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(5), state.dataHeatBal->Zone);
3532 3 : if (Tank.AmbientTempZone == 0) {
3533 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
3534 0 : ShowContinueError(state,
3535 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3536 0 : ShowContinueError(state, "Zone was not found.");
3537 0 : ErrorsFound = true;
3538 : }
3539 :
3540 3 : break;
3541 : }
3542 1 : case WTTAmbientTemp::OutsideAir: {
3543 1 : Tank.AmbientTempOutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
3544 1 : state.dataIPShortCut->cAlphaArgs(6),
3545 : ErrorsFound,
3546 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3547 1 : state.dataIPShortCut->cAlphaArgs(1),
3548 : DataLoopNode::NodeFluidType::Air,
3549 : DataLoopNode::ConnectionType::OutsideAirReference,
3550 : NodeInputManager::CompFluidStream::Primary,
3551 : DataLoopNode::ObjectIsNotParent);
3552 1 : if (!state.dataIPShortCut->lAlphaFieldBlanks(6)) {
3553 1 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3554 0 : ShowSevereError(state,
3555 0 : format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)));
3556 0 : ShowContinueError(state,
3557 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3558 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3559 0 : ErrorsFound = true;
3560 : }
3561 : } else {
3562 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3563 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3564 0 : ErrorsFound = true;
3565 : }
3566 :
3567 1 : break;
3568 : }
3569 0 : default: {
3570 0 : ShowSevereError(state,
3571 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3572 0 : state.dataIPShortCut->cCurrentModuleObject,
3573 0 : state.dataIPShortCut->cAlphaArgs(1),
3574 0 : state.dataIPShortCut->cAlphaArgs(3)));
3575 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3576 0 : ErrorsFound = true;
3577 0 : break;
3578 : }
3579 : }
3580 :
3581 4 : Tank.OffCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3582 4 : Tank.OffCycLossFracToZone = 1.0;
3583 :
3584 4 : Tank.OnCycLossCoeff = state.dataIPShortCut->rNumericArgs(5);
3585 4 : Tank.OnCycLossFracToZone = 1.0;
3586 :
3587 4 : Tank.MassFlowRateMax = 0.0;
3588 4 : Tank.FlowRateSchedule = 0;
3589 4 : Tank.UseInletTempSchedule = 0;
3590 :
3591 : // default to always on
3592 4 : Tank.SourceSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
3593 4 : Tank.UseSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
3594 :
3595 4 : if ((state.dataIPShortCut->rNumericArgs(6) > 1) || (state.dataIPShortCut->rNumericArgs(6) < 0)) {
3596 0 : ShowSevereError(state,
3597 0 : format("{} = {}: Use Side Effectiveness is out of bounds (0 to 1)",
3598 0 : state.dataIPShortCut->cCurrentModuleObject,
3599 0 : state.dataIPShortCut->cAlphaArgs(1)));
3600 0 : ErrorsFound = true;
3601 : }
3602 4 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(6);
3603 :
3604 4 : if ((state.dataIPShortCut->rNumericArgs(8) > 1) || (state.dataIPShortCut->rNumericArgs(8) <= 0)) {
3605 0 : ShowSevereError(state,
3606 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3607 0 : state.dataIPShortCut->cCurrentModuleObject,
3608 0 : state.dataIPShortCut->cAlphaArgs(1)));
3609 0 : ErrorsFound = true;
3610 : }
3611 4 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(8);
3612 :
3613 4 : if (state.dataIPShortCut->lNumericFieldBlanks(7)) {
3614 0 : Tank.UseDesignVolFlowRate = 0.0;
3615 : } else {
3616 4 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(7);
3617 4 : if (Tank.UseDesignVolFlowRate) {
3618 4 : Tank.UseDesignVolFlowRateWasAutoSized = true;
3619 : }
3620 : }
3621 :
3622 4 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3623 :
3624 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(9)) {
3625 0 : Tank.UseSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
3626 : } else {
3627 4 : Tank.UseSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(9));
3628 4 : if (Tank.UseSideAvailSchedNum == 0) {
3629 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(9), state.dataIPShortCut->cAlphaArgs(9)));
3630 0 : ShowContinueError(state,
3631 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3632 0 : ShowContinueError(state, "Schedule was not found.");
3633 0 : ErrorsFound = true;
3634 : }
3635 : }
3636 :
3637 4 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
3638 :
3639 4 : if (state.dataIPShortCut->lNumericFieldBlanks(9)) {
3640 1 : Tank.SourceDesignVolFlowRate = 0.0;
3641 : } else {
3642 3 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(9);
3643 3 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
3644 3 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
3645 : }
3646 : }
3647 :
3648 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(12)) {
3649 1 : Tank.SourceSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
3650 : } else {
3651 3 : Tank.SourceSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(12));
3652 3 : if (Tank.SourceSideAvailSchedNum == 0) {
3653 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(12), state.dataIPShortCut->cAlphaArgs(12)));
3654 0 : ShowContinueError(state,
3655 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3656 0 : ShowContinueError(state, "Schedule was not found.");
3657 0 : ErrorsFound = true;
3658 : }
3659 : }
3660 4 : if (state.dataIPShortCut->lNumericFieldBlanks(10)) {
3661 1 : Tank.SizingRecoveryTime = 4.0;
3662 : } else {
3663 3 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(10);
3664 : }
3665 :
3666 4 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(7)) || (!state.dataIPShortCut->lAlphaFieldBlanks(8))) {
3667 4 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
3668 4 : state.dataIPShortCut->cAlphaArgs(7),
3669 : ErrorsFound,
3670 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3671 4 : state.dataIPShortCut->cAlphaArgs(1),
3672 : DataLoopNode::NodeFluidType::Water,
3673 : DataLoopNode::ConnectionType::Inlet,
3674 : NodeInputManager::CompFluidStream::Primary,
3675 : DataLoopNode::ObjectIsNotParent);
3676 4 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(7);
3677 4 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
3678 4 : state.dataIPShortCut->cAlphaArgs(8),
3679 : ErrorsFound,
3680 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3681 4 : state.dataIPShortCut->cAlphaArgs(1),
3682 : DataLoopNode::NodeFluidType::Water,
3683 : DataLoopNode::ConnectionType::Outlet,
3684 : NodeInputManager::CompFluidStream::Primary,
3685 : DataLoopNode::ObjectIsNotParent);
3686 4 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
3687 : }
3688 :
3689 4 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(10)) || (!state.dataIPShortCut->lAlphaFieldBlanks(11))) {
3690 3 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
3691 3 : state.dataIPShortCut->cAlphaArgs(10),
3692 : ErrorsFound,
3693 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3694 3 : state.dataIPShortCut->cAlphaArgs(1),
3695 : DataLoopNode::NodeFluidType::Water,
3696 : DataLoopNode::ConnectionType::Inlet,
3697 : NodeInputManager::CompFluidStream::Secondary,
3698 : DataLoopNode::ObjectIsNotParent);
3699 3 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(10);
3700 3 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
3701 3 : state.dataIPShortCut->cAlphaArgs(11),
3702 : ErrorsFound,
3703 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterMixed,
3704 3 : state.dataIPShortCut->cAlphaArgs(1),
3705 : DataLoopNode::NodeFluidType::Water,
3706 : DataLoopNode::ConnectionType::Outlet,
3707 : NodeInputManager::CompFluidStream::Secondary,
3708 : DataLoopNode::ObjectIsNotParent);
3709 3 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
3710 : }
3711 :
3712 4 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
3713 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
3714 : }
3715 :
3716 : } // WaterThermalTankNum
3717 :
3718 4 : return ErrorsFound;
3719 : }
3720 :
3721 2 : bool getWaterTankStratifiedInput(EnergyPlusData &state)
3722 : {
3723 2 : bool ErrorsFound = false;
3724 : static constexpr std::string_view RoutineName = "getWaterTankStratifiedInput";
3725 :
3726 2 : state.dataIPShortCut->cCurrentModuleObject = cStratifiedCWTankModuleObj; // 'ThermalStorage:ChilledWater:Stratified'
3727 :
3728 4 : for (int WaterThermalTankNum = state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3729 2 : state.dataWaterThermalTanks->numChilledWaterMixed + 1;
3730 4 : WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
3731 4 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
3732 : ++WaterThermalTankNum) {
3733 : int NumNums;
3734 : int NumAlphas;
3735 : int IOStat;
3736 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3737 2 : state.dataIPShortCut->cCurrentModuleObject,
3738 2 : WaterThermalTankNum - (state.dataWaterThermalTanks->numWaterHeaterMixed +
3739 2 : state.dataWaterThermalTanks->numWaterHeaterStratified +
3740 2 : state.dataWaterThermalTanks->numChilledWaterMixed),
3741 2 : state.dataIPShortCut->cAlphaArgs,
3742 : NumAlphas,
3743 2 : state.dataIPShortCut->rNumericArgs,
3744 : NumNums,
3745 : IOStat,
3746 2 : state.dataIPShortCut->lNumericFieldBlanks,
3747 2 : state.dataIPShortCut->lAlphaFieldBlanks,
3748 2 : state.dataIPShortCut->cAlphaFieldNames,
3749 2 : state.dataIPShortCut->cNumericFieldNames);
3750 2 : GlobalNames::VerifyUniqueInterObjectName(state,
3751 2 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames,
3752 2 : state.dataIPShortCut->cAlphaArgs(1),
3753 2 : state.dataIPShortCut->cCurrentModuleObject,
3754 2 : state.dataIPShortCut->cAlphaFieldNames(1),
3755 : ErrorsFound);
3756 :
3757 2 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum);
3758 :
3759 2 : Tank.Name = state.dataIPShortCut->cAlphaArgs(1);
3760 2 : Tank.Type = state.dataIPShortCut->cCurrentModuleObject;
3761 2 : Tank.WaterThermalTankType = DataPlant::PlantEquipmentType::ChilledWaterTankStratified;
3762 2 : Tank.FluidIndex = Tank.waterIndex;
3763 2 : Tank.IsChilledWaterTank = true;
3764 2 : Tank.EndUseSubcategoryName = "Chilled Water Storage";
3765 :
3766 2 : Tank.Volume = state.dataIPShortCut->rNumericArgs(1);
3767 2 : if (Tank.Volume == DataSizing::AutoSize) {
3768 0 : Tank.VolumeWasAutoSized = true;
3769 : }
3770 2 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, Tank.FluidIndex, RoutineName);
3771 2 : Tank.Mass = Tank.Volume * rho;
3772 2 : Tank.Height = state.dataIPShortCut->rNumericArgs(2);
3773 2 : if (Tank.Height == DataSizing::AutoSize) {
3774 0 : Tank.HeightWasAutoSized = true;
3775 : }
3776 :
3777 2 : Tank.Shape = static_cast<TankShape>(getEnumValue(TankShapeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(2))));
3778 2 : switch (Tank.Shape) {
3779 2 : case TankShape::HorizCylinder:
3780 : case TankShape::VertCylinder: {
3781 2 : break;
3782 : }
3783 0 : case TankShape::Other: {
3784 0 : if (state.dataIPShortCut->rNumericArgs(3) > 0.0) {
3785 0 : Tank.Perimeter = state.dataIPShortCut->rNumericArgs(3);
3786 : } else {
3787 0 : ShowSevereError(state,
3788 0 : format("{} = {}: Tank Perimeter must be greater than zero for Tank Shape=OTHER",
3789 0 : state.dataIPShortCut->cCurrentModuleObject,
3790 0 : state.dataIPShortCut->cAlphaArgs(1)));
3791 0 : ErrorsFound = true;
3792 : }
3793 0 : break;
3794 : }
3795 0 : default: {
3796 0 : ShowSevereError(state,
3797 0 : format("{} = {}: Invalid Tank Shape entered={}",
3798 0 : state.dataIPShortCut->cCurrentModuleObject,
3799 0 : state.dataIPShortCut->cAlphaArgs(1),
3800 0 : state.dataIPShortCut->cAlphaArgs(2)));
3801 0 : Tank.Shape = TankShape::VertCylinder;
3802 0 : ErrorsFound = true;
3803 0 : break;
3804 : }
3805 : }
3806 :
3807 2 : if (state.dataIPShortCut->rNumericArgs(6) > 0.0) {
3808 2 : Tank.TankTempLimit = state.dataIPShortCut->rNumericArgs(6);
3809 : } else {
3810 : // default to just above freezing
3811 0 : Tank.TankTempLimit = 1.0;
3812 : }
3813 :
3814 2 : Tank.SetPointTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(3));
3815 2 : if (Tank.SetPointTempSchedule == 0) {
3816 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
3817 0 : ShowContinueError(state, format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3818 0 : ShowContinueError(state, "Schedule was not found.");
3819 0 : ErrorsFound = true;
3820 : }
3821 :
3822 2 : if (state.dataIPShortCut->rNumericArgs(4) > 0.0) {
3823 2 : Tank.DeadBandDeltaTemp = state.dataIPShortCut->rNumericArgs(4);
3824 : } else {
3825 : // Default to very small number (however it can't be TINY or it will break the algorithm)
3826 0 : Tank.DeadBandDeltaTemp = 0.0001;
3827 : }
3828 :
3829 2 : Tank.HeaterHeight1 = state.dataIPShortCut->rNumericArgs(5);
3830 2 : Tank.MaxCapacity = state.dataIPShortCut->rNumericArgs(7);
3831 2 : if (Tank.MaxCapacity == DataSizing::AutoSize) {
3832 0 : Tank.MaxCapacityWasAutoSized = true;
3833 : }
3834 :
3835 2 : Tank.Efficiency = 1.0;
3836 2 : Tank.SetPointTempSchedule2 = 0;
3837 2 : Tank.MaxCapacity2 = 0.0;
3838 2 : Tank.HeaterHeight2 = 0.0;
3839 2 : Tank.FuelType = Constant::eFuel::Electricity;
3840 :
3841 2 : Tank.OffCycParaLoad = 0.0;
3842 2 : Tank.OffCycParaFuelType = Constant::eFuel::Electricity;
3843 2 : Tank.OffCycParaFracToTank = 0.0;
3844 2 : Tank.OffCycParaHeight = 0.0;
3845 2 : Tank.OnCycParaLoad = 0.0;
3846 2 : Tank.OnCycParaFuelType = Constant::eFuel::Electricity;
3847 2 : Tank.OnCycParaFracToTank = 0.0;
3848 2 : Tank.OnCycParaHeight = 0.0;
3849 :
3850 2 : Tank.AmbientTempIndicator =
3851 2 : static_cast<WTTAmbientTemp>(getEnumValue(TankAmbientTempNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(4))));
3852 2 : switch (Tank.AmbientTempIndicator) {
3853 :
3854 0 : case WTTAmbientTemp::Schedule: {
3855 0 : Tank.AmbientTempSchedule = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(5));
3856 0 : if (Tank.AmbientTempSchedule == 0) {
3857 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
3858 0 : ShowContinueError(state,
3859 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3860 0 : ShowContinueError(state, "Schedule was not found.");
3861 0 : ErrorsFound = true;
3862 : }
3863 :
3864 0 : break;
3865 : }
3866 2 : case WTTAmbientTemp::TempZone: {
3867 2 : Tank.AmbientTempZone = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(6), state.dataHeatBal->Zone);
3868 2 : if (Tank.AmbientTempZone == 0) {
3869 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)));
3870 0 : ShowContinueError(state,
3871 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3872 0 : ShowContinueError(state, "Zone was not found.");
3873 0 : ErrorsFound = true;
3874 : }
3875 2 : Tank.OffCycLossFracToZone = 1.0;
3876 :
3877 2 : break;
3878 : }
3879 0 : case WTTAmbientTemp::OutsideAir: {
3880 0 : Tank.AmbientTempOutsideAirNode =
3881 0 : NodeInputManager::GetOnlySingleNode(state,
3882 0 : state.dataIPShortCut->cAlphaArgs(7),
3883 : ErrorsFound,
3884 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
3885 0 : state.dataIPShortCut->cAlphaArgs(1),
3886 : DataLoopNode::NodeFluidType::Air,
3887 : DataLoopNode::ConnectionType::Inlet,
3888 : NodeInputManager::CompFluidStream::Primary,
3889 : DataLoopNode::ObjectIsNotParent);
3890 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(7)) {
3891 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, Tank.AmbientTempOutsideAirNode)) {
3892 0 : ShowSevereError(state,
3893 0 : format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(7), state.dataIPShortCut->cAlphaArgs(7)));
3894 0 : ShowContinueError(state,
3895 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3896 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
3897 0 : ErrorsFound = true;
3898 : }
3899 : } else {
3900 0 : ShowSevereError(state, format("{} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
3901 0 : ShowContinueError(state, "An Ambient Outdoor Air Node name must be used when the Ambient Temperature Indicator is Outdoors.");
3902 0 : ErrorsFound = true;
3903 : }
3904 :
3905 0 : break;
3906 : }
3907 0 : default: {
3908 0 : ShowSevereError(state,
3909 0 : format("{} = {}: Invalid Ambient Temperature Indicator entered={}",
3910 0 : state.dataIPShortCut->cCurrentModuleObject,
3911 0 : state.dataIPShortCut->cAlphaArgs(1),
3912 0 : state.dataIPShortCut->cAlphaArgs(4)));
3913 0 : ShowContinueError(state, " Valid entries are Schedule, Zone, and Outdoors.");
3914 0 : ErrorsFound = true;
3915 0 : break;
3916 : }
3917 : }
3918 :
3919 2 : Tank.SkinLossCoeff = state.dataIPShortCut->rNumericArgs(8);
3920 2 : Tank.SkinLossFracToZone = 1.0;
3921 2 : Tank.OffCycFlueLossCoeff = 0.0;
3922 2 : Tank.OffCycFlueLossFracToZone = 0.0;
3923 :
3924 2 : Tank.MassFlowRateMax = 0.0;
3925 2 : Tank.FlowRateSchedule = 0;
3926 2 : Tank.UseInletTempSchedule = 0;
3927 2 : Tank.UseEffectiveness = state.dataIPShortCut->rNumericArgs(9);
3928 2 : Tank.UseInletHeight = state.dataIPShortCut->rNumericArgs(10);
3929 :
3930 : // default to always on
3931 2 : Tank.SourceSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
3932 2 : Tank.UseSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
3933 :
3934 2 : if (state.dataIPShortCut->rNumericArgs(10) == Constant::AutoCalculate) {
3935 0 : Tank.UseInletHeight = Tank.Height; // top of tank
3936 : }
3937 2 : if (Tank.UseInletHeight > Tank.Height) {
3938 0 : ShowSevereError(state,
3939 0 : format("{} = {}: Use inlet is located higher than overall tank height.",
3940 0 : state.dataIPShortCut->cCurrentModuleObject,
3941 0 : state.dataIPShortCut->cAlphaArgs(1)));
3942 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3943 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(10), state.dataIPShortCut->rNumericArgs(10)));
3944 0 : ErrorsFound = true;
3945 : }
3946 :
3947 2 : Tank.UseOutletHeight = state.dataIPShortCut->rNumericArgs(11);
3948 2 : if (Tank.UseOutletHeight > Tank.Height) {
3949 0 : ShowSevereError(state,
3950 0 : format("{} = {}: Use outlet is located higher than overall tank height.",
3951 0 : state.dataIPShortCut->cCurrentModuleObject,
3952 0 : state.dataIPShortCut->cAlphaArgs(1)));
3953 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3954 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(11), state.dataIPShortCut->rNumericArgs(11)));
3955 0 : ErrorsFound = true;
3956 : }
3957 :
3958 2 : if ((state.dataIPShortCut->rNumericArgs(13) > 1) || (state.dataIPShortCut->rNumericArgs(13) <= 0)) {
3959 0 : ShowSevereError(state,
3960 0 : format("{} = {}: Source Side Effectiveness is out of bounds (>0 to 1)",
3961 0 : state.dataIPShortCut->cCurrentModuleObject,
3962 0 : state.dataIPShortCut->cAlphaArgs(1)));
3963 0 : ErrorsFound = true;
3964 : }
3965 2 : Tank.SourceEffectiveness = state.dataIPShortCut->rNumericArgs(13);
3966 :
3967 2 : Tank.SourceInletHeight = state.dataIPShortCut->rNumericArgs(14);
3968 2 : if (Tank.SourceInletHeight > Tank.Height) {
3969 0 : ShowSevereError(state,
3970 0 : format("{} = {}: Source inlet is located higher than overall tank height.",
3971 0 : state.dataIPShortCut->cCurrentModuleObject,
3972 0 : state.dataIPShortCut->cAlphaArgs(1)));
3973 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3974 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(14), state.dataIPShortCut->rNumericArgs(14)));
3975 0 : ErrorsFound = true;
3976 : }
3977 :
3978 2 : Tank.SourceOutletHeight = state.dataIPShortCut->rNumericArgs(15);
3979 2 : if (state.dataIPShortCut->rNumericArgs(15) == Constant::AutoCalculate) {
3980 0 : Tank.SourceOutletHeight = Tank.Height; // top of tank
3981 : }
3982 2 : if (Tank.SourceOutletHeight > Tank.Height) {
3983 0 : ShowSevereError(state,
3984 0 : format("{} = {}: Source outlet is located higher than overall tank height.",
3985 0 : state.dataIPShortCut->cCurrentModuleObject,
3986 0 : state.dataIPShortCut->cAlphaArgs(1)));
3987 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
3988 0 : ShowContinueError(state, format("{} = {:.4R}", state.dataIPShortCut->cNumericFieldNames(15), state.dataIPShortCut->rNumericArgs(15)));
3989 0 : ErrorsFound = true;
3990 : }
3991 :
3992 2 : Tank.StandAlone = false;
3993 :
3994 2 : if (state.dataIPShortCut->lNumericFieldBlanks(12)) {
3995 0 : Tank.UseDesignVolFlowRate = 0.0;
3996 : } else {
3997 2 : Tank.UseDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(12);
3998 2 : if (Tank.UseDesignVolFlowRate == DataSizing::AutoSize) {
3999 2 : Tank.UseDesignVolFlowRateWasAutoSized = true;
4000 : }
4001 : }
4002 :
4003 2 : Tank.UseSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
4004 :
4005 2 : if (state.dataIPShortCut->lNumericFieldBlanks(16)) {
4006 0 : Tank.SourceDesignVolFlowRate = 0.0;
4007 : } else {
4008 2 : Tank.SourceDesignVolFlowRate = state.dataIPShortCut->rNumericArgs(16);
4009 2 : if (Tank.SourceDesignVolFlowRate == DataSizing::AutoSize) {
4010 0 : Tank.SourceDesignVolFlowRateWasAutoSized = true;
4011 : }
4012 : }
4013 :
4014 2 : Tank.SizingRecoveryTime = state.dataIPShortCut->rNumericArgs(17);
4015 :
4016 2 : Tank.SrcSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
4017 :
4018 2 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(8)) || (!state.dataIPShortCut->lAlphaFieldBlanks(9))) {
4019 2 : Tank.UseInletNode = NodeInputManager::GetOnlySingleNode(state,
4020 2 : state.dataIPShortCut->cAlphaArgs(8),
4021 : ErrorsFound,
4022 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
4023 2 : state.dataIPShortCut->cAlphaArgs(1),
4024 : DataLoopNode::NodeFluidType::Water,
4025 : DataLoopNode::ConnectionType::Inlet,
4026 : NodeInputManager::CompFluidStream::Primary,
4027 : DataLoopNode::ObjectIsNotParent);
4028 2 : Tank.InletNodeName1 = state.dataIPShortCut->cAlphaArgs(8);
4029 2 : Tank.UseOutletNode = NodeInputManager::GetOnlySingleNode(state,
4030 2 : state.dataIPShortCut->cAlphaArgs(9),
4031 : ErrorsFound,
4032 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
4033 2 : state.dataIPShortCut->cAlphaArgs(1),
4034 : DataLoopNode::NodeFluidType::Water,
4035 : DataLoopNode::ConnectionType::Outlet,
4036 : NodeInputManager::CompFluidStream::Primary,
4037 : DataLoopNode::ObjectIsNotParent);
4038 2 : Tank.OutletNodeName1 = state.dataIPShortCut->cAlphaArgs(9);
4039 : }
4040 :
4041 2 : if ((!state.dataIPShortCut->lAlphaFieldBlanks(11)) || (!state.dataIPShortCut->lAlphaFieldBlanks(12))) {
4042 1 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
4043 1 : state.dataIPShortCut->cAlphaArgs(11),
4044 : ErrorsFound,
4045 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
4046 1 : state.dataIPShortCut->cAlphaArgs(1),
4047 : DataLoopNode::NodeFluidType::Water,
4048 : DataLoopNode::ConnectionType::Inlet,
4049 : NodeInputManager::CompFluidStream::Secondary,
4050 : DataLoopNode::ObjectIsNotParent);
4051 1 : Tank.InletNodeName2 = state.dataIPShortCut->cAlphaArgs(11);
4052 1 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
4053 1 : state.dataIPShortCut->cAlphaArgs(12),
4054 : ErrorsFound,
4055 : DataLoopNode::ConnectionObjectType::ThermalStorageChilledWaterStratified,
4056 1 : state.dataIPShortCut->cAlphaArgs(1),
4057 : DataLoopNode::NodeFluidType::Water,
4058 : DataLoopNode::ConnectionType::Outlet,
4059 : NodeInputManager::CompFluidStream::Secondary,
4060 : DataLoopNode::ObjectIsNotParent);
4061 1 : Tank.OutletNodeName2 = state.dataIPShortCut->cAlphaArgs(12);
4062 : }
4063 :
4064 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(10)) {
4065 0 : Tank.UseSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
4066 : } else {
4067 2 : Tank.UseSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(10));
4068 2 : if (Tank.UseSideAvailSchedNum == 0) {
4069 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(10), state.dataIPShortCut->cAlphaArgs(10)));
4070 0 : ShowContinueError(state,
4071 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
4072 0 : ShowContinueError(state, "Schedule was not found.");
4073 0 : ErrorsFound = true;
4074 : }
4075 : }
4076 :
4077 2 : if (Tank.UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand && Tank.SourceInletNode != 0) {
4078 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, Tank.SourceInletNode, Tank.SourceDesignVolFlowRate);
4079 : }
4080 :
4081 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(13)) {
4082 0 : Tank.SourceSideAvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
4083 : } else {
4084 2 : Tank.SourceSideAvailSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(13));
4085 2 : if (Tank.SourceSideAvailSchedNum == 0) {
4086 0 : ShowSevereError(state, format("Invalid, {} = {}", state.dataIPShortCut->cAlphaFieldNames(13), state.dataIPShortCut->cAlphaArgs(13)));
4087 0 : ShowContinueError(state,
4088 0 : format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
4089 0 : ShowContinueError(state, "Schedule was not found.");
4090 0 : ErrorsFound = true;
4091 : }
4092 : }
4093 :
4094 : // Validate inlet mode
4095 2 : Tank.InletMode =
4096 2 : static_cast<InletPositionMode>(getEnumValue(InletPositionModeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(14))));
4097 :
4098 2 : Tank.Nodes = state.dataIPShortCut->rNumericArgs(18);
4099 2 : Tank.AdditionalCond = state.dataIPShortCut->rNumericArgs(19);
4100 :
4101 2 : Tank.AdditionalLossCoeff.allocate(Tank.Nodes);
4102 2 : Tank.AdditionalLossCoeff = 0.0;
4103 2 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4104 2 : if (NumNums > 19 + NodeNum) {
4105 0 : Tank.AdditionalLossCoeff(NodeNum) = state.dataIPShortCut->rNumericArgs(19 + NodeNum);
4106 : } else {
4107 2 : break;
4108 : }
4109 : }
4110 :
4111 2 : if (NumNums > 19 + Tank.Nodes) {
4112 0 : ShowWarningError(
4113 : state,
4114 0 : format("{} = {}: More Additional Loss Coefficients were entered than the number of nodes; extra coefficients will not be used",
4115 0 : state.dataIPShortCut->cCurrentModuleObject,
4116 0 : state.dataIPShortCut->cAlphaArgs(1)));
4117 : }
4118 :
4119 2 : Tank.SetupStratifiedNodes(state);
4120 : }
4121 :
4122 2 : return ErrorsFound;
4123 : }
4124 :
4125 442 : bool GetWaterThermalTankInput(EnergyPlusData &state)
4126 : {
4127 :
4128 : // SUBROUTINE INFORMATION:
4129 : // AUTHOR Dan Fisher and Brandon Anderson
4130 : // DATE WRITTEN May 2000
4131 : // MODIFIED R. Raustad, June 2005, added HPWH and desuperheater water heating coils
4132 : // B. Griffith, Oct. 2007 extensions for indirect water heaters
4133 : // B. Griffith, Feb. 2008 extensions for autosizing water heaters
4134 : // BG Mar 2009. Trap for bad heater height input for stratified water heater CR7718
4135 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
4136 :
4137 : // PURPOSE OF THIS SUBROUTINE:
4138 : // Gets the water heater, HPWH, and/or desuperheater heating coil input from the input file.
4139 :
4140 442 : bool ErrorsFound = false;
4141 :
4142 : // Make sure refrigeration input is gotten before this input
4143 442 : RefrigeratedCase::CheckRefrigerationInput(state);
4144 :
4145 442 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
4146 442 : state.dataWaterThermalTanks->numWaterHeaterMixed = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedWHModuleObj);
4147 884 : state.dataWaterThermalTanks->numWaterHeaterStratified =
4148 442 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedWHModuleObj);
4149 884 : state.dataWaterThermalTanks->numChilledWaterMixed =
4150 442 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cMixedCWTankModuleObj);
4151 884 : state.dataWaterThermalTanks->numChilledWaterStratified =
4152 442 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cStratifiedCWTankModuleObj);
4153 884 : state.dataWaterThermalTanks->numWaterThermalTank =
4154 442 : state.dataWaterThermalTanks->numWaterHeaterMixed + state.dataWaterThermalTanks->numWaterHeaterStratified +
4155 442 : state.dataWaterThermalTanks->numChilledWaterMixed + state.dataWaterThermalTanks->numChilledWaterStratified;
4156 884 : state.dataWaterThermalTanks->numHeatPumpWaterHeater =
4157 442 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHPumpedCondenser) +
4158 442 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cHPWHWrappedCondenser);
4159 884 : state.dataWaterThermalTanks->numWaterHeaterDesuperheater =
4160 442 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCoilDesuperheater);
4161 :
4162 442 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4163 : static constexpr std::string_view Format_720(
4164 : "! <Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery Efficiency, "
4165 : "Standard Rated Energy Factor\n");
4166 : static constexpr std::string_view Format_721(
4167 : "! <Heat Pump Water Heater Information>,Type,Name,Volume {{m3}},Maximum Capacity {{W}},Standard Rated Recovery "
4168 : "Efficiency,Standard Rated Energy Factor,DX Coil Total Cooling Rate {{W}}\n");
4169 : static constexpr std::string_view Format_722(
4170 : "! <Water Heater Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},Maximum Capacity "
4171 : "{{W}},Off-Cycle UA {{W/K}},On-Cycle UA {{W/K}},Number Of Inlets,Number Of Outlets\n");
4172 : static constexpr std::string_view Format_725(
4173 : "! <Chilled Water Tank Information>,Type,Name,Volume {{m3}},Use Side Design Flow Rate {{m3/s}}, "
4174 : "Source Side Design Flow Rate {{m3/s}}\n");
4175 : static constexpr std::string_view Format_726(
4176 : "! <Chilled Water Tank Stratified Node Information>,Node Number,Height {{m}},Volume {{m3}},UA {{W/K}},Number Of "
4177 : "Inlets,Number Of Outlets\n");
4178 :
4179 : // Write water heater header for EIO
4180 132 : if ((state.dataWaterThermalTanks->numWaterHeaterMixed > 0) || (state.dataWaterThermalTanks->numWaterHeaterStratified > 0))
4181 132 : print(state.files.eio, Format_720);
4182 132 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) print(state.files.eio, Format_721);
4183 132 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) print(state.files.eio, Format_722);
4184 132 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) print(state.files.eio, Format_725);
4185 132 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) print(state.files.eio, Format_726);
4186 : }
4187 :
4188 442 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4189 132 : state.dataWaterThermalTanks->WaterThermalTank.allocate(state.dataWaterThermalTanks->numWaterThermalTank);
4190 132 : state.dataWaterThermalTanks->UniqueWaterThermalTankNames.reserve(static_cast<unsigned>(state.dataWaterThermalTanks->numWaterThermalTank));
4191 : }
4192 442 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4193 9 : state.dataWaterThermalTanks->HPWaterHeater.allocate(state.dataWaterThermalTanks->numHeatPumpWaterHeater);
4194 : }
4195 :
4196 442 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4197 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater.allocate(state.dataWaterThermalTanks->numWaterHeaterDesuperheater);
4198 : }
4199 :
4200 : // ======= Get Coil:WaterHeating:Desuperheater ======================================================================
4201 442 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4202 6 : ErrorsFound |= getDesuperHtrInput(state);
4203 : }
4204 :
4205 : // ======= Get HEAT PUMP:WATER HEATER ===============================================================================
4206 442 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4207 9 : ErrorsFound |= getHPWaterHeaterInput(state);
4208 : }
4209 :
4210 : // ======= Get WATER HEATER:MIXED ===================================================================================
4211 442 : if (state.dataWaterThermalTanks->numWaterHeaterMixed > 0) {
4212 123 : ErrorsFound |= getWaterHeaterMixedInputs(state);
4213 : }
4214 :
4215 : // ======= Get WATER HEATER:STRATIFIED ==============================================================================
4216 442 : if (state.dataWaterThermalTanks->numWaterHeaterStratified > 0) {
4217 10 : ErrorsFound |= getWaterHeaterStratifiedInput(state);
4218 : }
4219 :
4220 : // ======= Get Chilled Water :MIXED ===================================================================================
4221 442 : if (state.dataWaterThermalTanks->numChilledWaterMixed > 0) {
4222 4 : ErrorsFound |= getWaterTankMixedInput(state);
4223 : }
4224 :
4225 : // ======= Get 'ThermalStorage:ChilledWater:Stratified' =======================================================
4226 442 : if (state.dataWaterThermalTanks->numChilledWaterStratified > 0) {
4227 2 : ErrorsFound |= getWaterTankStratifiedInput(state);
4228 : }
4229 :
4230 : // Loop through all desuperheating coils and then search all water heaters for the tank connected to the desuperheating coil
4231 442 : if (state.dataWaterThermalTanks->numWaterHeaterDesuperheater > 0) {
4232 6 : state.dataIPShortCut->cCurrentModuleObject = cCoilDesuperheater;
4233 12 : for (int DesuperheaterNum = 1; DesuperheaterNum <= state.dataWaterThermalTanks->numWaterHeaterDesuperheater; ++DesuperheaterNum) {
4234 6 : auto &DesuperHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum);
4235 12 : for (int WtrHtrNum = 1; WtrHtrNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WtrHtrNum) {
4236 6 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(WtrHtrNum);
4237 6 : if (!Util::SameString(DesuperHtr.TankName, Tank.Name) || !Util::SameString(DesuperHtr.TankType, Tank.Type)) continue;
4238 6 : Tank.DesuperheaterNum = DesuperheaterNum;
4239 6 : DesuperHtr.WaterHeaterTankNum = WtrHtrNum;
4240 6 : DesuperHtr.TankTypeNum = Tank.WaterThermalTankType;
4241 6 : DesuperHtr.BackupElementCapacity = Tank.MaxCapacity;
4242 6 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) DesuperHtr.StandAlone = true;
4243 :
4244 : // verify Desuperheater/tank source node connections
4245 6 : if (DesuperHtr.WaterInletNode != Tank.SourceOutletNode) {
4246 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4247 0 : ShowContinueError(state, "Desuperheater inlet node name does not match thermal tank source outlet node name.");
4248 0 : ShowContinueError(state,
4249 0 : format("Desuperheater water inlet and outlet node names = {} and {}",
4250 0 : DesuperHtr.InletNodeName1,
4251 0 : DesuperHtr.OutletNodeName1));
4252 0 : ShowContinueError(state,
4253 0 : format("Thermal tank source side inlet and outlet node names = {} and {}",
4254 0 : Tank.InletNodeName2,
4255 0 : Tank.OutletNodeName2));
4256 0 : ErrorsFound = true;
4257 : }
4258 :
4259 6 : if (DesuperHtr.WaterOutletNode != Tank.SourceInletNode) {
4260 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4261 0 : ShowContinueError(state, "Desuperheater water outlet node name does not match thermal tank source inlet node name.");
4262 0 : ShowContinueError(state,
4263 0 : format("Desuperheater water inlet and outlet node names = {} and {}",
4264 0 : DesuperHtr.InletNodeName1,
4265 0 : DesuperHtr.OutletNodeName1));
4266 0 : ShowContinueError(state,
4267 0 : format("Thermal tank source side inlet and outlet node names = {} and {}",
4268 0 : Tank.InletNodeName2,
4269 0 : Tank.OutletNodeName2));
4270 0 : ErrorsFound = true;
4271 : }
4272 : }
4273 :
4274 6 : if (DesuperHtr.WaterHeaterTankNum == 0) {
4275 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, DesuperHtr.Name));
4276 0 : ShowContinueError(state, format(" Water heater tank = {} not found.", DesuperHtr.TankName));
4277 0 : ErrorsFound = true;
4278 : }
4279 : }
4280 : }
4281 :
4282 : // Loop through HPWH's and then search all water heaters for the tank connected to the HPWH
4283 442 : if (state.dataWaterThermalTanks->numHeatPumpWaterHeater > 0) {
4284 :
4285 9 : int const NumPumpedCondenser = state.dataInputProcessing->inputProcessor->getNumObjectsFound(
4286 : state, cHPWHPumpedCondenser); // number of WaterHeater:HeatPump:PumpedCondenser objects
4287 32 : for (int HPWaterHeaterNum = 1; HPWaterHeaterNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPWaterHeaterNum) {
4288 :
4289 : // Create reference to current HPWH object in array.
4290 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPWaterHeaterNum);
4291 23 : if (HPWaterHeaterNum <= NumPumpedCondenser) {
4292 : // Pumped Condenser
4293 20 : state.dataIPShortCut->cCurrentModuleObject = cHPWHPumpedCondenser;
4294 : } else {
4295 : // Wrapped Condenser
4296 3 : state.dataIPShortCut->cCurrentModuleObject = cHPWHWrappedCondenser;
4297 : }
4298 :
4299 : // find the tank associated with the heat pump water heater and change its %TYPE to HEAT PUMP:WATER HEATER
4300 143 : for (int CheckWaterHeaterNum = 1; CheckWaterHeaterNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++CheckWaterHeaterNum) {
4301 :
4302 120 : auto &Tank = state.dataWaterThermalTanks->WaterThermalTank(CheckWaterHeaterNum);
4303 :
4304 120 : if (!(Util::SameString(HPWH.TankName, Tank.Name) && Util::SameString(HPWH.TankType, Tank.Type))) continue;
4305 :
4306 : // save backup element and on/off-cycle parasitic properties for use during standard rating procedure
4307 23 : HPWH.BackupElementCapacity = Tank.MaxCapacity;
4308 23 : HPWH.BackupElementEfficiency = Tank.Efficiency;
4309 23 : HPWH.WHOnCycParaLoad = Tank.OnCycParaLoad;
4310 23 : HPWH.WHOffCycParaLoad = Tank.OffCycParaLoad;
4311 23 : HPWH.WHOnCycParaFracToTank = Tank.OnCycParaFracToTank;
4312 23 : HPWH.WHOffCycParaFracToTank = Tank.OffCycParaFracToTank;
4313 23 : HPWH.WHPLFCurve = Tank.PLFCurve;
4314 :
4315 23 : if (((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4316 12 : (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) ||
4317 11 : (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified)) {
4318 23 : HPWH.TankType = Tank.Type;
4319 23 : HPWH.HPWHTankType = Tank.WaterThermalTankType;
4320 : } else {
4321 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4322 0 : ShowContinueError(state, format("Invalid water heater tank type = {}", Tank.Type));
4323 0 : ErrorsFound = true;
4324 : }
4325 :
4326 : // Set up comp set for condenser water side nodes (reverse inlet/outlet for water heater)
4327 23 : if (HPWH.bIsIHP) {
4328 2 : BranchNodeConnections::SetUpCompSets(state,
4329 : HPWH.Type,
4330 : HPWH.Name,
4331 : HPWH.DXCoilType,
4332 2 : HPWH.DXCoilName + " Water Coil",
4333 : HPWH.InletNodeName1,
4334 : HPWH.OutletNodeName1,
4335 : "HPWH To Coil");
4336 : } else {
4337 22 : BranchNodeConnections::SetUpCompSets(
4338 : state, HPWH.Type, HPWH.Name, HPWH.DXCoilType, HPWH.DXCoilName, HPWH.InletNodeName1, HPWH.OutletNodeName1, "HPWH To Coil");
4339 : }
4340 23 : BranchNodeConnections::SetUpCompSets(
4341 : state, HPWH.Type, HPWH.Name, HPWH.TankType, HPWH.TankName, HPWH.OutletNodeName1, HPWH.InletNodeName1, "HPWH To Tank");
4342 :
4343 : // If WaterHeaterMixed: do not allow modulating control for HPWH's (i.e. modulating control usually used for tankless WH's)
4344 23 : if ((Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) &&
4345 12 : (Tank.ControlType == HeaterControlMode::Modulate)) {
4346 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4347 0 : ShowContinueError(state, format("Heater Control Type for {} = {} must be CYCLE.", Tank.Type, Tank.Name));
4348 0 : ErrorsFound = true;
4349 : }
4350 :
4351 23 : Tank.HeatPumpNum = HPWaterHeaterNum;
4352 23 : HPWH.WaterHeaterTankNum = CheckWaterHeaterNum;
4353 23 : HPWH.FoundTank = true;
4354 :
4355 23 : if (Tank.DesuperheaterNum > 0) {
4356 0 : ShowSevereError(
4357 : state,
4358 0 : format("{} = {}and Coil:WaterHeating:Desuperheater = {}: cannot be connected to the same water heater tank = {}",
4359 0 : state.dataIPShortCut->cCurrentModuleObject,
4360 0 : HPWH.Name,
4361 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(CheckWaterHeaterNum).Name,
4362 0 : Tank.Name));
4363 : }
4364 :
4365 : // check that water heater source side effectiveness is greater than 0
4366 23 : if (Tank.SourceEffectiveness <= 0.0) {
4367 0 : ShowSevereError(state,
4368 0 : format("{} = {}: Invalid source side effectiveness for heat pump water heater = {:.3T}",
4369 0 : state.dataIPShortCut->cCurrentModuleObject,
4370 0 : HPWH.Name,
4371 0 : Tank.SourceEffectiveness));
4372 0 : ShowContinueError(state, " water heater source effectiveness will default to 1.0 and simulation continues.");
4373 0 : Tank.SourceEffectiveness = 1.0;
4374 : }
4375 :
4376 : // Set up the source side nodes for wrapped condensers
4377 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4378 3 : if (Tank.SourceInletNode > 0 || Tank.SourceOutletNode > 0) {
4379 0 : ShowSevereError(state, format("{} = {} has a source inlet or outlet node specified,", Tank.Type, Tank.Name));
4380 0 : ShowContinueError(
4381 0 : state, format("but it is attached to {} = {}, which doesn't permit source side connections.", HPWH.Type, HPWH.Name));
4382 0 : ShowContinueError(state, "Please leave the source side inlet and outlet fields blank.");
4383 0 : ErrorsFound = true;
4384 : } else {
4385 :
4386 : DataLoopNode::ConnectionObjectType objType = static_cast<DataLoopNode::ConnectionObjectType>(
4387 3 : getEnumValue(BranchNodeConnections::ConnectionObjectTypeNamesUC, Util::makeUPPER(Tank.Type)));
4388 :
4389 3 : Tank.SourceInletNode = NodeInputManager::GetOnlySingleNode(state,
4390 3 : HPWH.OutletNodeName1,
4391 : ErrorsFound,
4392 : objType,
4393 3 : Tank.Name,
4394 : DataLoopNode::NodeFluidType::Water,
4395 : DataLoopNode::ConnectionType::Inlet,
4396 : NodeInputManager::CompFluidStream::Secondary,
4397 : DataLoopNode::ObjectIsNotParent);
4398 3 : Tank.InletNodeName2 = HPWH.OutletNodeName1;
4399 3 : Tank.SourceOutletNode = NodeInputManager::GetOnlySingleNode(state,
4400 3 : HPWH.InletNodeName1,
4401 : ErrorsFound,
4402 : objType,
4403 3 : Tank.Name,
4404 : DataLoopNode::NodeFluidType::Water,
4405 : DataLoopNode::ConnectionType::Outlet,
4406 : NodeInputManager::CompFluidStream::Secondary,
4407 : DataLoopNode::ObjectIsNotParent);
4408 3 : Tank.OutletNodeName2 = HPWH.InletNodeName1;
4409 : }
4410 :
4411 : // Mark the tank as not stand alone because it is connected now.
4412 3 : Tank.StandAlone = false;
4413 : }
4414 :
4415 : // Set HPWH structure variable StandAlone to TRUE if use nodes are not connected
4416 23 : if (Tank.UseInletNode == 0 && Tank.UseOutletNode == 0) HPWH.StandAlone = true;
4417 :
4418 23 : if (HPWH.WHUseInletNode != Tank.UseInletNode || HPWH.WHUseOutletNode != Tank.UseOutletNode) {
4419 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4420 0 : ShowContinueError(state,
4421 0 : format("Heat pump water heater tank use side inlet and outlet node names must match the use side inlet and "
4422 : "outlet node names for water heater tank = {}: {}",
4423 0 : HPWH.TankType,
4424 0 : HPWH.TankName));
4425 0 : ShowContinueError(state,
4426 0 : format("Heat pump water heater use side inlet and outlet node names = {} and {}",
4427 0 : HPWH.InletNodeName2,
4428 0 : HPWH.OutletNodeName2));
4429 0 : ShowContinueError(state,
4430 0 : format("Water heater tank use side inlet and outlet node names = {} and {}",
4431 0 : Tank.InletNodeName1,
4432 0 : Tank.OutletNodeName1));
4433 0 : ErrorsFound = true;
4434 : } else {
4435 23 : if (!HPWH.StandAlone) {
4436 10 : BranchNodeConnections::TestCompSet(state, HPWH.Type, HPWH.Name, Tank.InletNodeName1, Tank.OutletNodeName1, "Water Nodes");
4437 : }
4438 : }
4439 :
4440 : // verify HP/tank source node connections
4441 23 : if (HPWH.CondWaterInletNode != Tank.SourceOutletNode) {
4442 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4443 0 : ShowContinueError(state,
4444 : "Heat Pump condenser water inlet node name does not match water heater tank source outlet node name.");
4445 0 : ShowContinueError(
4446 : state,
4447 0 : format("Heat pump condenser water inlet and outlet node names = {} and {}", HPWH.InletNodeName1, HPWH.OutletNodeName1));
4448 0 : ShowContinueError(state,
4449 0 : format("Water heater tank source side inlet and outlet node names = {} and {}",
4450 0 : Tank.InletNodeName2,
4451 0 : Tank.OutletNodeName2));
4452 0 : ErrorsFound = true;
4453 : }
4454 :
4455 23 : if (HPWH.CondWaterOutletNode != Tank.SourceInletNode) {
4456 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4457 0 : ShowContinueError(state,
4458 : "Heat Pump condenser water outlet node name does not match water heater tank source inlet node name.");
4459 0 : ShowContinueError(
4460 : state,
4461 0 : format("Heat pump condenser water inlet and outlet node names = {} and {}", HPWH.InletNodeName1, HPWH.OutletNodeName1));
4462 0 : ShowContinueError(state,
4463 0 : format("Water heater tank source side inlet and outlet node names = {} and {}",
4464 0 : Tank.InletNodeName2,
4465 0 : Tank.OutletNodeName2));
4466 0 : ErrorsFound = true;
4467 : }
4468 :
4469 : // verify wrapped condenser location
4470 23 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4471 : // make sure the top of the condenser is not above the tank height.
4472 3 : if (HPWH.WrappedCondenserTopLocation > Tank.Height) {
4473 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4474 0 : ShowContinueError(state, "The height of the top of the wrapped condenser is greater than the height of the tank.");
4475 0 : ErrorsFound = true;
4476 : }
4477 : }
4478 :
4479 : // Verify tank name is in a zone equipment list if HPWH Inlet Air Configuration is Zone Air Only or Zone and Outdoor Air
4480 23 : if (HPWH.InletAirConfiguration == WTTAmbientTemp::TempZone || HPWH.InletAirConfiguration == WTTAmbientTemp::ZoneAndOA) {
4481 10 : if (allocated(state.dataZoneEquip->ZoneEquipConfig) && allocated(state.dataZoneEquip->ZoneEquipList)) {
4482 10 : bool FoundTankInList = false;
4483 10 : bool TankNotLowestPriority = false;
4484 10 : int ZoneEquipConfigNum = HPWH.AmbientTempZone;
4485 10 : int ZoneEquipListNum = state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigNum).EquipListIndex;
4486 10 : int TankCoolingPriority = 0;
4487 10 : int TankHeatingPriority = 0;
4488 10 : auto const &zoneEquipList = state.dataZoneEquip->ZoneEquipList(ZoneEquipListNum);
4489 19 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= zoneEquipList.NumOfEquipTypes; ++EquipmentTypeNum) {
4490 19 : if (zoneEquipList.EquipName(EquipmentTypeNum) != HPWH.Name) continue;
4491 10 : FoundTankInList = true;
4492 10 : TankCoolingPriority = zoneEquipList.CoolingPriority(EquipmentTypeNum);
4493 10 : TankHeatingPriority = zoneEquipList.HeatingPriority(EquipmentTypeNum);
4494 10 : break;
4495 : } // EquipmentTypeNum
4496 10 : if (!FoundTankInList) {
4497 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4498 0 : ShowContinueError(state,
4499 : "Heat pump water heater type and name must be listed in the correct "
4500 : "ZoneHVAC:EquipmentList object when Inlet Air Configuration is equal to "
4501 : "ZoneAirOnly or ZoneAndOutdoorAir.");
4502 0 : ErrorsFound = true;
4503 : }
4504 : // check that tank has lower priority than all other non-HPWH objects in Zone
4505 : // Equipment List
4506 39 : for (int EquipmentTypeNum = 1; EquipmentTypeNum <= zoneEquipList.NumOfEquipTypes; ++EquipmentTypeNum) {
4507 29 : if (Util::SameString(zoneEquipList.EquipTypeName(EquipmentTypeNum), state.dataIPShortCut->cCurrentModuleObject))
4508 14 : continue;
4509 30 : if (TankCoolingPriority > zoneEquipList.CoolingPriority(EquipmentTypeNum) ||
4510 15 : TankHeatingPriority > zoneEquipList.HeatingPriority(EquipmentTypeNum)) {
4511 0 : TankNotLowestPriority = true;
4512 : }
4513 : } // EquipmentTypeNum
4514 10 : if (TankNotLowestPriority && FoundTankInList) {
4515 0 : ShowWarningError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4516 0 : ShowContinueError(state,
4517 : "Heat pump water heaters should be simulated first, before other space "
4518 : "conditioning equipment.");
4519 0 : ShowContinueError(state,
4520 : "Poor temperature control may result if the Heating/Cooling sequence number is "
4521 : "not 1 in the ZoneHVAC:EquipmentList.");
4522 : }
4523 : } else {
4524 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4525 0 : ShowContinueError(state,
4526 : "ZoneHVAC:EquipmentList and ZoneHVAC:EquipmentConnections objects are required when Inlet Air "
4527 : "Configuration is either ZoneAirOnly or ZoneAndOutdoorAir.");
4528 0 : ErrorsFound = true;
4529 : } // ALLOCATED
4530 : } // InletAirConfiguration
4531 :
4532 23 : if (Tank.WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
4533 :
4534 : // Nodal heat distribution fraction for stratified tank wrapped condensers
4535 11 : if (HPWH.HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
4536 3 : if (Tank.Shape == TankShape::HorizCylinder) {
4537 0 : ShowWarningError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4538 0 : ShowContinueError(state, "A wrapped condenser HPWH model should not be used with a horizontal stratified tank.");
4539 0 : ShowContinueError(
4540 : state, "Ignoring condenser location and distributing heat evenly throughout the tank. Simulation continues.");
4541 0 : Real64 const SameFrac = 1.0 / Tank.Nodes;
4542 0 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4543 0 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac = SameFrac;
4544 : }
4545 : } else {
4546 3 : Real64 H0 = Tank.Height; // height of top of node
4547 : Real64 H; // height of bottom of node
4548 3 : Real64 SumFrac(0.0);
4549 : // Get the fraction of each stratified node that is wrapped by the condenser
4550 17 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4551 14 : StratifiedNodeData &CurNode = Tank.Node(NodeNum);
4552 14 : if (NodeNum == Tank.Nodes) {
4553 3 : H = 0.0;
4554 : } else {
4555 11 : H = H0 - CurNode.Height;
4556 : }
4557 14 : if (H < HPWH.WrappedCondenserBottomLocation && H0 > HPWH.WrappedCondenserBottomLocation) {
4558 : // The bottom of the condenser starts partway through this node.
4559 3 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0 - (HPWH.WrappedCondenserBottomLocation - H) / CurNode.Height;
4560 11 : } else if (H >= HPWH.WrappedCondenserBottomLocation && H <= HPWH.WrappedCondenserTopLocation) {
4561 6 : if (H0 > HPWH.WrappedCondenserTopLocation) {
4562 : // the top of the condenser ends partway through this node.
4563 1 : CurNode.HPWHWrappedCondenserHeatingFrac = (HPWH.WrappedCondenserTopLocation - H) / CurNode.Height;
4564 : } else {
4565 : // the entire node is wrapped by the condenser
4566 5 : CurNode.HPWHWrappedCondenserHeatingFrac = 1.0;
4567 : }
4568 : } else {
4569 5 : CurNode.HPWHWrappedCondenserHeatingFrac = 0.0;
4570 : }
4571 14 : SumFrac += CurNode.HPWHWrappedCondenserHeatingFrac;
4572 14 : H0 = H;
4573 : }
4574 : // Normalize the fractions so they sum to 1.
4575 17 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4576 14 : Tank.Node(NodeNum).HPWHWrappedCondenserHeatingFrac /= SumFrac;
4577 : }
4578 : }
4579 : }
4580 :
4581 : // Stratified Tank HPWH control sensor node locations
4582 11 : if (HPWH.ControlSensor1Height < 0.0) {
4583 : // default to heater 1
4584 0 : HPWH.ControlSensor1Height = Tank.HeaterHeight1;
4585 : }
4586 11 : if (HPWH.ControlSensor2Height < 0.0) {
4587 : // default to heater 2
4588 0 : HPWH.ControlSensor2Height = Tank.HeaterHeight2;
4589 : }
4590 :
4591 : // Get the vertical tank height depending on the type of tank
4592 : Real64 TankHeight;
4593 11 : if (Tank.Shape == TankShape::VertCylinder || Tank.Shape == TankShape::Other) {
4594 11 : TankHeight = Tank.Height;
4595 : } else {
4596 0 : assert(Tank.Shape == TankShape::HorizCylinder);
4597 : // For horizontal cylinders, the tank "height" is actually the length.
4598 : // We need to calculate the height.
4599 0 : Real64 EndArea = Tank.Volume / Tank.Height;
4600 0 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
4601 0 : TankHeight = 2.0 * Radius; // actual vertical height
4602 : }
4603 :
4604 : // Make sure the control sensor locations are in the tank
4605 11 : if (HPWH.ControlSensor1Height < 0.0 || HPWH.ControlSensor1Height > TankHeight) {
4606 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4607 0 : ShowContinueError(state, "Control Sensor 1 is located outside the tank.");
4608 0 : ErrorsFound = true;
4609 : }
4610 11 : if (HPWH.ControlSensor2Height < 0.0 || HPWH.ControlSensor2Height > TankHeight) {
4611 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4612 0 : ShowContinueError(state, "Control Sensor 2 is located outside the tank.");
4613 0 : ErrorsFound = true;
4614 : }
4615 :
4616 : // Assign the control sensors to the appropriate nodes
4617 11 : Real64 H0 = TankHeight;
4618 : Real64 H;
4619 97 : for (int NodeNum = 1; NodeNum <= Tank.Nodes; ++NodeNum) {
4620 86 : StratifiedNodeData const &TankNode = Tank.Node(NodeNum);
4621 86 : if (NodeNum == Tank.Nodes) {
4622 11 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
4623 : } else {
4624 75 : H = H0 - TankNode.Height;
4625 : }
4626 :
4627 : // Control Sensor 1 Node
4628 86 : if (HPWH.ControlSensor1Height <= H0 && HPWH.ControlSensor1Height > H) {
4629 11 : HPWH.ControlSensor1Node = NodeNum;
4630 : }
4631 :
4632 : // Control Sensor 2 Node
4633 86 : if (HPWH.ControlSensor2Height <= H0 && HPWH.ControlSensor2Height > H) {
4634 11 : HPWH.ControlSensor2Node = NodeNum;
4635 : }
4636 :
4637 86 : H0 = H;
4638 : }
4639 : }
4640 :
4641 : } // DO CheckWaterHeaterNum = 1, NumWaterHeater
4642 :
4643 23 : if (!HPWH.FoundTank) {
4644 0 : ShowSevereError(state, format("{} = {}:", state.dataIPShortCut->cCurrentModuleObject, HPWH.Name));
4645 0 : ShowContinueError(state, format("Water heater tank object not found = {}, {}", HPWH.TankType, HPWH.TankName));
4646 0 : ErrorsFound = true;
4647 : }
4648 :
4649 : } // DO HPWaterHeaterNum = 1, NumHeatPumpWaterHeater
4650 : }
4651 :
4652 : // Get water heater sizing input.
4653 442 : state.dataIPShortCut->cCurrentModuleObject = "WaterHeater:Sizing";
4654 884 : state.dataWaterThermalTanks->numWaterHeaterSizing =
4655 442 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
4656 :
4657 442 : if (state.dataWaterThermalTanks->numWaterHeaterSizing > 0) {
4658 :
4659 10 : for (int WHsizingNum = 1; WHsizingNum <= state.dataWaterThermalTanks->numWaterHeaterSizing; ++WHsizingNum) {
4660 : int NumAlphas;
4661 : int NumNums;
4662 : int IOStat;
4663 10 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4664 5 : state.dataIPShortCut->cCurrentModuleObject,
4665 : WHsizingNum,
4666 5 : state.dataIPShortCut->cAlphaArgs,
4667 : NumAlphas,
4668 5 : state.dataIPShortCut->rNumericArgs,
4669 : NumNums,
4670 : IOStat);
4671 :
4672 : // find which water heater this object is for
4673 5 : int WaterThermalTankNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(1), state.dataWaterThermalTanks->WaterThermalTank);
4674 5 : if (WaterThermalTankNum == 0) {
4675 : // did not match name throw warning.
4676 0 : ShowSevereError(state,
4677 0 : format("{} object name: {} does not match any of the water heaters defined in the file",
4678 0 : state.dataIPShortCut->cCurrentModuleObject,
4679 0 : state.dataIPShortCut->cAlphaArgs(1)));
4680 0 : ErrorsFound = true;
4681 0 : continue;
4682 : } else { // we have a match
4683 : // store the sizing data in "sizing" nested derived type for the correct water heater
4684 :
4685 5 : if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PeakDraw")) {
4686 2 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PeakDraw;
4687 3 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "ResidentialHUD-FHAMinimum")) {
4688 3 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::ResidentialMin;
4689 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerPerson")) {
4690 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerPerson;
4691 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerFloorArea")) {
4692 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerFloorArea;
4693 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerUnit")) {
4694 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerUnit;
4695 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "PerSolarCollectorArea")) {
4696 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode = SizingMode::PerSolarColArea;
4697 : } else {
4698 : // wrong design mode entered, throw error
4699 0 : ShowSevereError(state,
4700 0 : format("{} object named: {} contains an incorrect Design Mode of: {}",
4701 0 : state.dataIPShortCut->cCurrentModuleObject,
4702 0 : state.dataIPShortCut->cAlphaArgs(1),
4703 0 : state.dataIPShortCut->cAlphaArgs(2)));
4704 0 : ErrorsFound = true;
4705 : }
4706 :
4707 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime = state.dataIPShortCut->rNumericArgs(1);
4708 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryTime = state.dataIPShortCut->rNumericArgs(2);
4709 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow =
4710 5 : state.dataIPShortCut->rNumericArgs(3);
4711 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms =
4712 5 : int(state.dataIPShortCut->rNumericArgs(4));
4713 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBathrooms =
4714 5 : int(state.dataIPShortCut->rNumericArgs(5));
4715 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson =
4716 5 : state.dataIPShortCut->rNumericArgs(6);
4717 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson =
4718 5 : state.dataIPShortCut->rNumericArgs(7);
4719 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea =
4720 5 : state.dataIPShortCut->rNumericArgs(8);
4721 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea =
4722 5 : state.dataIPShortCut->rNumericArgs(9);
4723 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits = state.dataIPShortCut->rNumericArgs(10);
4724 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit =
4725 5 : state.dataIPShortCut->rNumericArgs(11);
4726 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit =
4727 5 : state.dataIPShortCut->rNumericArgs(12);
4728 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea =
4729 5 : state.dataIPShortCut->rNumericArgs(13);
4730 5 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.HeightAspectRatio =
4731 5 : state.dataIPShortCut->rNumericArgs(14);
4732 :
4733 5 : switch (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode) {
4734 :
4735 0 : case SizingMode::Invalid: {
4736 : // do nothing, error thrown if design mode not found
4737 0 : break;
4738 : }
4739 2 : case SizingMode::PeakDraw: { // need to have entered a reasonable value for TankDrawTime
4740 2 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime <= 0.0) {
4741 0 : ShowSevereError(state,
4742 0 : format("{}, named {}, design mode set to Peak Draw but needs a positive value for tank draw time",
4743 0 : state.dataIPShortCut->cCurrentModuleObject,
4744 0 : state.dataIPShortCut->cAlphaArgs(1)));
4745 0 : ErrorsFound = true;
4746 : }
4747 : // constrain crazy sizes by limiting to 10 years or 8760*10
4748 2 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankDrawTime > 87600.0) {
4749 0 : ShowWarningError(state,
4750 0 : format("{}, named {}, has input with an unreasonably large Tank Draw Time, more than 10 years",
4751 0 : state.dataIPShortCut->cCurrentModuleObject,
4752 0 : state.dataIPShortCut->cAlphaArgs(1)));
4753 0 : ErrorsFound = true;
4754 : }
4755 : // if both volume and demand side flow connections are autosized, must be a good NominalVolForSizingDemandSideFlow
4756 2 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseSidePlantLoc.loopSideNum ==
4757 2 : DataPlant::LoopSideLocation::Demand) &&
4758 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseDesignVolFlowRateWasAutoSized)) {
4759 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4760 0 : ShowWarningError(state,
4761 0 : format("{}, named {} needs a value for Nominal Tank Volume for Autosizing Plant Connections",
4762 0 : state.dataIPShortCut->cCurrentModuleObject,
4763 0 : state.dataIPShortCut->cAlphaArgs(1)));
4764 0 : ErrorsFound = true;
4765 : }
4766 : }
4767 2 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SrcSidePlantLoc.loopSideNum ==
4768 2 : DataPlant::LoopSideLocation::Demand) &&
4769 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceDesignVolFlowRateWasAutoSized)) {
4770 0 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NominalVolForSizingDemandSideFlow <= 0.0) {
4771 0 : ShowWarningError(state,
4772 0 : format("{}, named {} needs a value for Nominal Tank Volume for Autosizing Plant Connections",
4773 0 : state.dataIPShortCut->cCurrentModuleObject,
4774 0 : state.dataIPShortCut->cAlphaArgs(1)));
4775 0 : ErrorsFound = true;
4776 : }
4777 : }
4778 :
4779 2 : break;
4780 : }
4781 3 : case SizingMode::ResidentialMin: {
4782 : // it would have to have at least on bedroom and any more than 10 is crazy for this mode
4783 3 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms < 1) {
4784 0 : ShowSevereError(state,
4785 0 : format("{}, named {}, mode needs at least one bedroom",
4786 0 : state.dataIPShortCut->cCurrentModuleObject,
4787 0 : state.dataIPShortCut->cAlphaArgs(1)));
4788 0 : ErrorsFound = true;
4789 : }
4790 3 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfBedrooms > 10) {
4791 0 : ShowWarningError(state,
4792 0 : format("{}, named {}, probably has too many bedrooms for the selected design mode",
4793 0 : state.dataIPShortCut->cCurrentModuleObject,
4794 0 : state.dataIPShortCut->cAlphaArgs(1)));
4795 : }
4796 :
4797 3 : break;
4798 : }
4799 0 : case SizingMode::PerPerson: {
4800 :
4801 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4802 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerPerson <= 0.0)) {
4803 0 : ShowSevereError(state,
4804 0 : format("{}, named {}, PerPerson mode needs positive value input for storage capacity per person",
4805 0 : state.dataIPShortCut->cCurrentModuleObject,
4806 0 : state.dataIPShortCut->cAlphaArgs(1)));
4807 0 : ErrorsFound = true;
4808 : }
4809 :
4810 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4811 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerPerson <= 0.0)) {
4812 0 : ShowSevereError(state,
4813 0 : format("{}, named {}, PerPerson mode needs positive value input for recovery capacity per person",
4814 0 : state.dataIPShortCut->cCurrentModuleObject,
4815 0 : state.dataIPShortCut->cAlphaArgs(1)));
4816 0 : ErrorsFound = true;
4817 : }
4818 :
4819 0 : break;
4820 : }
4821 0 : case SizingMode::PerFloorArea: {
4822 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4823 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerArea <= 0.0)) {
4824 0 : ShowSevereError(state,
4825 0 : format("{}, named {}, PerArea mode needs positive value input for storage capacity per floor area",
4826 0 : state.dataIPShortCut->cCurrentModuleObject,
4827 0 : state.dataIPShortCut->cAlphaArgs(1)));
4828 0 : ErrorsFound = true;
4829 : }
4830 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4831 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerArea <= 0.0)) {
4832 0 : ShowSevereError(state,
4833 0 : format("{}, named {}, PerArea mode needs positive value input for recovery capacity per floor area",
4834 0 : state.dataIPShortCut->cCurrentModuleObject,
4835 0 : state.dataIPShortCut->cAlphaArgs(1)));
4836 0 : ErrorsFound = true;
4837 : }
4838 :
4839 0 : break;
4840 : }
4841 0 : case SizingMode::PerUnit: {
4842 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4843 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerUnit <= 0.0)) {
4844 0 : ShowSevereError(state,
4845 0 : format("{}, named {}, PerUnit mode needs positive value input for storage capacity per unit",
4846 0 : state.dataIPShortCut->cCurrentModuleObject,
4847 0 : state.dataIPShortCut->cAlphaArgs(1)));
4848 0 : ErrorsFound = true;
4849 : }
4850 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4851 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4852 0 : ShowSevereError(state,
4853 0 : format("{}, named {}, PerUnit mode needs positive value input for number of units",
4854 0 : state.dataIPShortCut->cCurrentModuleObject,
4855 0 : state.dataIPShortCut->cAlphaArgs(1)));
4856 0 : ErrorsFound = true;
4857 : }
4858 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4859 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.RecoveryCapacityPerUnit <= 0.0)) {
4860 0 : ShowSevereError(state,
4861 0 : format("{}, named {}, PerUnit mode needs positive value input for recovery capacity per unit",
4862 0 : state.dataIPShortCut->cCurrentModuleObject,
4863 0 : state.dataIPShortCut->cAlphaArgs(1)));
4864 0 : ErrorsFound = true;
4865 : }
4866 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4867 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.NumberOfUnits <= 0.0)) {
4868 0 : ShowSevereError(state,
4869 0 : format("{}, named {}, PerUnit mode needs positive value input for number of units",
4870 0 : state.dataIPShortCut->cCurrentModuleObject,
4871 0 : state.dataIPShortCut->cAlphaArgs(1)));
4872 0 : ErrorsFound = true;
4873 : }
4874 0 : break;
4875 : }
4876 0 : case SizingMode::PerSolarColArea: {
4877 0 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4878 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.TankCapacityPerCollectorArea <= 0.0)) {
4879 0 : ShowSevereError(
4880 : state,
4881 0 : format("{}, named {}, PerSolarCollectorArea mode needs positive value input for storage capacity per collector area",
4882 0 : state.dataIPShortCut->cCurrentModuleObject,
4883 0 : state.dataIPShortCut->cAlphaArgs(1)));
4884 0 : ErrorsFound = true;
4885 : }
4886 0 : break;
4887 : }
4888 0 : default:
4889 0 : break;
4890 : }
4891 :
4892 : } // found water heater num okay
4893 : } // loop over sizing objects
4894 :
4895 : } // any water heater sizing objects
4896 :
4897 : // now check that if water heater fields were autosized, that there was also a sizing object for that water heater
4898 442 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4899 330 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4900 :
4901 200 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).VolumeWasAutoSized) &&
4902 2 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4903 0 : ShowWarningError(
4904 : state,
4905 0 : format("Water heater named {}has tank volume set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4906 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4907 0 : ErrorsFound = true;
4908 : }
4909 201 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).MaxCapacityWasAutoSized) &&
4910 3 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4911 0 : ShowWarningError(
4912 : state,
4913 0 : format("Water heater named {}has heater capacity set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4914 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4915 0 : ErrorsFound = true;
4916 : }
4917 198 : if ((state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeightWasAutoSized) &&
4918 0 : (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Sizing.DesignMode == SizingMode::Invalid)) {
4919 0 : ShowWarningError(
4920 : state,
4921 0 : format("Water heater named {}has tank height set to AUTOSIZE but it is missing associated WaterHeater:Sizing object",
4922 0 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name));
4923 0 : ErrorsFound = true;
4924 : }
4925 : }
4926 : }
4927 :
4928 : // now do calls to TestCompSet for tanks, depending on nodes and heat pump water heater
4929 442 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4930 330 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4931 328 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseInletNode > 0 &&
4932 130 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).UseOutletNode > 0) {
4933 130 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).HeatPumpNum > 0) {
4934 : // do nothing, Use nodes are tested for HeatPump:WaterHeater not tank
4935 : } else {
4936 240 : BranchNodeConnections::TestCompSet(state,
4937 120 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4938 120 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4939 120 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName1,
4940 120 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName1,
4941 : "Use Side Water Nodes");
4942 : }
4943 : }
4944 248 : if (state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceInletNode > 0 &&
4945 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).SourceOutletNode > 0) {
4946 :
4947 100 : BranchNodeConnections::TestCompSet(state,
4948 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Type,
4949 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).Name,
4950 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).InletNodeName2,
4951 50 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).OutletNodeName2,
4952 : "Source Side Water Nodes");
4953 : }
4954 : }
4955 : }
4956 :
4957 442 : if (state.dataWaterThermalTanks->numWaterThermalTank > 0) {
4958 330 : for (int WaterThermalTankNum = 1; WaterThermalTankNum <= state.dataWaterThermalTanks->numWaterThermalTank; ++WaterThermalTankNum) {
4959 :
4960 198 : state.dataWaterThermalTanks->WaterThermalTank(WaterThermalTankNum).setupZoneInternalGains(state);
4961 :
4962 : } // WaterThermalTankNum
4963 : }
4964 : } // get input flag
4965 :
4966 442 : return ErrorsFound;
4967 : }
4968 :
4969 197 : void WaterThermalTankData::setupOutputVars(EnergyPlusData &state)
4970 : {
4971 197 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) ||
4972 193 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
4973 6 : this->setupChilledWaterTankOutputVars(state);
4974 : } else {
4975 : // moving setupWaterHeaterOutputVars to here causes big table diffs...
4976 191 : this->setupWaterHeaterOutputVars(state);
4977 : }
4978 : // moving setupZoneInternalGains to here causes math and table diffs...
4979 : // this->setupZoneInternalGains();
4980 197 : }
4981 :
4982 6 : void WaterThermalTankData::setupChilledWaterTankOutputVars(EnergyPlusData &state)
4983 : {
4984 :
4985 : // CurrentModuleObject='ThermalStorage:ChilledWater:Mixed/ThermalStorage:ChilledWater:Stratified'
4986 12 : SetupOutputVariable(state,
4987 : "Chilled Water Thermal Storage Tank Temperature",
4988 : Constant::Units::C,
4989 6 : this->TankTempAvg,
4990 : OutputProcessor::TimeStepType::System,
4991 : OutputProcessor::StoreType::Average,
4992 6 : this->Name);
4993 :
4994 12 : SetupOutputVariable(state,
4995 : "Chilled Water Thermal Storage Final Tank Temperature",
4996 : Constant::Units::C,
4997 6 : this->TankTemp,
4998 : OutputProcessor::TimeStepType::System,
4999 : OutputProcessor::StoreType::Average,
5000 6 : this->Name);
5001 :
5002 12 : SetupOutputVariable(state,
5003 : "Chilled Water Thermal Storage Tank Heat Gain Rate",
5004 : Constant::Units::W,
5005 6 : this->LossRate,
5006 : OutputProcessor::TimeStepType::System,
5007 : OutputProcessor::StoreType::Average,
5008 6 : this->Name);
5009 12 : SetupOutputVariable(state,
5010 : "Chilled Water Thermal Storage Tank Heat Gain Energy",
5011 : Constant::Units::J,
5012 6 : this->LossEnergy,
5013 : OutputProcessor::TimeStepType::System,
5014 : OutputProcessor::StoreType::Sum,
5015 6 : this->Name);
5016 :
5017 12 : SetupOutputVariable(state,
5018 : "Chilled Water Thermal Storage Use Side Mass Flow Rate",
5019 : Constant::Units::kg_s,
5020 6 : this->UseMassFlowRate,
5021 : OutputProcessor::TimeStepType::System,
5022 : OutputProcessor::StoreType::Average,
5023 6 : this->Name);
5024 :
5025 12 : SetupOutputVariable(state,
5026 : "Chilled Water Thermal Storage Use Side Inlet Temperature",
5027 : Constant::Units::C,
5028 6 : this->UseInletTemp,
5029 : OutputProcessor::TimeStepType::System,
5030 : OutputProcessor::StoreType::Average,
5031 6 : this->Name);
5032 :
5033 12 : SetupOutputVariable(state,
5034 : "Chilled Water Thermal Storage Use Side Outlet Temperature",
5035 : Constant::Units::C,
5036 6 : this->UseOutletTemp,
5037 : OutputProcessor::TimeStepType::System,
5038 : OutputProcessor::StoreType::Average,
5039 6 : this->Name);
5040 :
5041 12 : SetupOutputVariable(state,
5042 : "Chilled Water Thermal Storage Use Side Heat Transfer Rate",
5043 : Constant::Units::W,
5044 6 : this->UseRate,
5045 : OutputProcessor::TimeStepType::System,
5046 : OutputProcessor::StoreType::Average,
5047 6 : this->Name);
5048 12 : SetupOutputVariable(state,
5049 : "Chilled Water Thermal Storage Use Side Heat Transfer Energy",
5050 : Constant::Units::J,
5051 6 : this->UseEnergy,
5052 : OutputProcessor::TimeStepType::System,
5053 : OutputProcessor::StoreType::Sum,
5054 6 : this->Name);
5055 :
5056 12 : SetupOutputVariable(state,
5057 : "Chilled Water Thermal Storage Source Side Mass Flow Rate",
5058 : Constant::Units::kg_s,
5059 6 : this->SourceMassFlowRate,
5060 : OutputProcessor::TimeStepType::System,
5061 : OutputProcessor::StoreType::Average,
5062 6 : this->Name);
5063 :
5064 12 : SetupOutputVariable(state,
5065 : "Chilled Water Thermal Storage Source Side Inlet Temperature",
5066 : Constant::Units::C,
5067 6 : this->SourceInletTemp,
5068 : OutputProcessor::TimeStepType::System,
5069 : OutputProcessor::StoreType::Average,
5070 6 : this->Name);
5071 :
5072 12 : SetupOutputVariable(state,
5073 : "Chilled Water Thermal Storage Source Side Outlet Temperature",
5074 : Constant::Units::C,
5075 6 : this->SourceOutletTemp,
5076 : OutputProcessor::TimeStepType::System,
5077 : OutputProcessor::StoreType::Average,
5078 6 : this->Name);
5079 :
5080 12 : SetupOutputVariable(state,
5081 : "Chilled Water Thermal Storage Source Side Heat Transfer Rate",
5082 : Constant::Units::W,
5083 6 : this->SourceRate,
5084 : OutputProcessor::TimeStepType::System,
5085 : OutputProcessor::StoreType::Average,
5086 6 : this->Name);
5087 12 : SetupOutputVariable(state,
5088 : "Chilled Water Thermal Storage Source Side Heat Transfer Energy",
5089 : Constant::Units::J,
5090 6 : this->SourceEnergy,
5091 : OutputProcessor::TimeStepType::System,
5092 : OutputProcessor::StoreType::Sum,
5093 6 : this->Name);
5094 :
5095 6 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
5096 :
5097 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5098 36 : SetupOutputVariable(state,
5099 24 : format("Chilled Water Thermal Storage Temperature Node {}", NodeNum),
5100 : Constant::Units::C,
5101 12 : this->Node(NodeNum).TempAvg,
5102 : OutputProcessor::TimeStepType::System,
5103 : OutputProcessor::StoreType::Average,
5104 12 : this->Name);
5105 : }
5106 :
5107 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5108 36 : SetupOutputVariable(state,
5109 24 : format("Chilled Water Thermal Storage Final Temperature Node {}", NodeNum),
5110 : Constant::Units::C,
5111 12 : this->Node(NodeNum).Temp,
5112 : OutputProcessor::TimeStepType::System,
5113 : OutputProcessor::StoreType::Average,
5114 12 : this->Name);
5115 : }
5116 : }
5117 :
5118 6 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
5119 :
5120 14 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5121 : static constexpr std::string_view Format_724("Chilled Water Tank Stratified Node Information,{},{:.4T},{:.4T},{:.4T},{},{}\n");
5122 :
5123 12 : print(state.files.eio,
5124 : Format_724,
5125 : NodeNum,
5126 12 : this->Node(NodeNum).Height,
5127 12 : this->Node(NodeNum).Volume,
5128 12 : this->Node(NodeNum).OffCycLossCoeff,
5129 12 : this->Node(NodeNum).Inlets,
5130 12 : this->Node(NodeNum).Outlets);
5131 : }
5132 : }
5133 6 : }
5134 :
5135 198 : void WaterThermalTankData::setupZoneInternalGains(EnergyPlusData &state)
5136 : {
5137 : // set up internal gains if tank is in a thermal zone
5138 198 : if (this->AmbientTempZone > 0) {
5139 81 : switch (this->WaterThermalTankType) {
5140 70 : case DataPlant::PlantEquipmentType::WtrHeaterMixed: {
5141 70 : SetupZoneInternalGain(state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterMixed, &this->AmbientZoneGain);
5142 70 : break;
5143 : }
5144 6 : case DataPlant::PlantEquipmentType::WtrHeaterStratified: {
5145 6 : SetupZoneInternalGain(
5146 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::WaterHeaterStratified, &this->AmbientZoneGain);
5147 6 : break;
5148 : }
5149 3 : case DataPlant::PlantEquipmentType::ChilledWaterTankMixed: {
5150 3 : SetupZoneInternalGain(
5151 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterMixed, &this->AmbientZoneGain);
5152 3 : break;
5153 : }
5154 2 : case DataPlant::PlantEquipmentType::ChilledWaterTankStratified: {
5155 2 : SetupZoneInternalGain(
5156 : state, this->AmbientTempZone, this->Name, DataHeatBalance::IntGainType::ThermalStorageChilledWaterStratified, &this->AmbientZoneGain);
5157 2 : break;
5158 : }
5159 0 : default:
5160 0 : break;
5161 : }
5162 : }
5163 198 : }
5164 :
5165 191 : void WaterThermalTankData::setupWaterHeaterOutputVars(EnergyPlusData &state)
5166 : {
5167 :
5168 : // Setup report variables for WaterHeater:Mixed
5169 : // CurrentModuleObject='WaterHeater:Mixed'
5170 382 : SetupOutputVariable(state,
5171 : "Water Heater Tank Temperature",
5172 : Constant::Units::C,
5173 191 : this->TankTempAvg,
5174 : OutputProcessor::TimeStepType::System,
5175 : OutputProcessor::StoreType::Average,
5176 191 : this->Name);
5177 :
5178 382 : SetupOutputVariable(state,
5179 : "Water Heater Final Tank Temperature",
5180 : Constant::Units::C,
5181 191 : this->TankTemp,
5182 : OutputProcessor::TimeStepType::System,
5183 : OutputProcessor::StoreType::Average,
5184 191 : this->Name);
5185 :
5186 382 : SetupOutputVariable(state,
5187 : "Water Heater Heat Loss Rate",
5188 : Constant::Units::W,
5189 191 : this->LossRate,
5190 : OutputProcessor::TimeStepType::System,
5191 : OutputProcessor::StoreType::Average,
5192 191 : this->Name);
5193 382 : SetupOutputVariable(state,
5194 : "Water Heater Heat Loss Energy",
5195 : Constant::Units::J,
5196 191 : this->LossEnergy,
5197 : OutputProcessor::TimeStepType::System,
5198 : OutputProcessor::StoreType::Sum,
5199 191 : this->Name);
5200 :
5201 382 : SetupOutputVariable(state,
5202 : "Water Heater Use Side Mass Flow Rate",
5203 : Constant::Units::kg_s,
5204 191 : this->UseMassFlowRate,
5205 : OutputProcessor::TimeStepType::System,
5206 : OutputProcessor::StoreType::Average,
5207 191 : this->Name);
5208 :
5209 382 : SetupOutputVariable(state,
5210 : "Water Heater Use Side Inlet Temperature",
5211 : Constant::Units::C,
5212 191 : this->UseInletTemp,
5213 : OutputProcessor::TimeStepType::System,
5214 : OutputProcessor::StoreType::Average,
5215 191 : this->Name);
5216 :
5217 382 : SetupOutputVariable(state,
5218 : "Water Heater Use Side Outlet Temperature",
5219 : Constant::Units::C,
5220 191 : this->UseOutletTemp,
5221 : OutputProcessor::TimeStepType::System,
5222 : OutputProcessor::StoreType::Average,
5223 191 : this->Name);
5224 :
5225 382 : SetupOutputVariable(state,
5226 : "Water Heater Use Side Heat Transfer Rate",
5227 : Constant::Units::W,
5228 191 : this->UseRate,
5229 : OutputProcessor::TimeStepType::System,
5230 : OutputProcessor::StoreType::Average,
5231 191 : this->Name);
5232 382 : SetupOutputVariable(state,
5233 : "Water Heater Use Side Heat Transfer Energy",
5234 : Constant::Units::J,
5235 191 : this->UseEnergy,
5236 : OutputProcessor::TimeStepType::System,
5237 : OutputProcessor::StoreType::Sum,
5238 191 : this->Name);
5239 :
5240 382 : SetupOutputVariable(state,
5241 : "Water Heater Source Side Mass Flow Rate",
5242 : Constant::Units::kg_s,
5243 191 : this->SourceMassFlowRate,
5244 : OutputProcessor::TimeStepType::System,
5245 : OutputProcessor::StoreType::Average,
5246 191 : this->Name);
5247 :
5248 382 : SetupOutputVariable(state,
5249 : "Water Heater Source Side Inlet Temperature",
5250 : Constant::Units::C,
5251 191 : this->SourceInletTemp,
5252 : OutputProcessor::TimeStepType::System,
5253 : OutputProcessor::StoreType::Average,
5254 191 : this->Name);
5255 :
5256 382 : SetupOutputVariable(state,
5257 : "Water Heater Source Side Outlet Temperature",
5258 : Constant::Units::C,
5259 191 : this->SourceOutletTemp,
5260 : OutputProcessor::TimeStepType::System,
5261 : OutputProcessor::StoreType::Average,
5262 191 : this->Name);
5263 :
5264 382 : SetupOutputVariable(state,
5265 : "Water Heater Source Side Heat Transfer Rate",
5266 : Constant::Units::W,
5267 191 : this->SourceRate,
5268 : OutputProcessor::TimeStepType::System,
5269 : OutputProcessor::StoreType::Average,
5270 191 : this->Name);
5271 382 : SetupOutputVariable(state,
5272 : "Water Heater Source Side Heat Transfer Energy",
5273 : Constant::Units::J,
5274 191 : this->SourceEnergy,
5275 : OutputProcessor::TimeStepType::System,
5276 : OutputProcessor::StoreType::Sum,
5277 191 : this->Name,
5278 : Constant::eResource::PlantLoopHeatingDemand,
5279 : OutputProcessor::Group::Plant,
5280 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5281 : this->EndUseSubcategoryName);
5282 :
5283 382 : SetupOutputVariable(state,
5284 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Rate",
5285 : Constant::Units::W,
5286 191 : this->OffCycParaRateToTank,
5287 : OutputProcessor::TimeStepType::System,
5288 : OutputProcessor::StoreType::Average,
5289 191 : this->Name);
5290 382 : SetupOutputVariable(state,
5291 : "Water Heater Off Cycle Parasitic Tank Heat Transfer Energy",
5292 : Constant::Units::J,
5293 191 : this->OffCycParaEnergyToTank,
5294 : OutputProcessor::TimeStepType::System,
5295 : OutputProcessor::StoreType::Sum,
5296 191 : this->Name);
5297 :
5298 382 : SetupOutputVariable(state,
5299 : "Water Heater On Cycle Parasitic Tank Heat Transfer Rate",
5300 : Constant::Units::W,
5301 191 : this->OnCycParaRateToTank,
5302 : OutputProcessor::TimeStepType::System,
5303 : OutputProcessor::StoreType::Average,
5304 191 : this->Name);
5305 382 : SetupOutputVariable(state,
5306 : "Water Heater On Cycle Parasitic Tank Heat Transfer Energy",
5307 : Constant::Units::J,
5308 191 : this->OnCycParaEnergyToTank,
5309 : OutputProcessor::TimeStepType::System,
5310 : OutputProcessor::StoreType::Sum,
5311 191 : this->Name);
5312 :
5313 382 : SetupOutputVariable(state,
5314 : "Water Heater Total Demand Heat Transfer Rate",
5315 : Constant::Units::W,
5316 191 : this->TotalDemandRate,
5317 : OutputProcessor::TimeStepType::System,
5318 : OutputProcessor::StoreType::Average,
5319 191 : this->Name);
5320 382 : SetupOutputVariable(state,
5321 : "Water Heater Total Demand Heat Transfer Energy",
5322 : Constant::Units::J,
5323 191 : this->TotalDemandEnergy,
5324 : OutputProcessor::TimeStepType::System,
5325 : OutputProcessor::StoreType::Sum,
5326 191 : this->Name);
5327 :
5328 382 : SetupOutputVariable(state,
5329 : "Water Heater Heating Rate",
5330 : Constant::Units::W,
5331 191 : this->HeaterRate,
5332 : OutputProcessor::TimeStepType::System,
5333 : OutputProcessor::StoreType::Average,
5334 191 : this->Name);
5335 382 : SetupOutputVariable(state,
5336 : "Water Heater Heating Energy",
5337 : Constant::Units::J,
5338 191 : this->HeaterEnergy,
5339 : OutputProcessor::TimeStepType::System,
5340 : OutputProcessor::StoreType::Sum,
5341 191 : this->Name);
5342 :
5343 382 : SetupOutputVariable(state,
5344 : "Water Heater Unmet Demand Heat Transfer Rate",
5345 : Constant::Units::W,
5346 191 : this->UnmetRate,
5347 : OutputProcessor::TimeStepType::System,
5348 : OutputProcessor::StoreType::Average,
5349 191 : this->Name);
5350 382 : SetupOutputVariable(state,
5351 : "Water Heater Unmet Demand Heat Transfer Energy",
5352 : Constant::Units::J,
5353 191 : this->UnmetEnergy,
5354 : OutputProcessor::TimeStepType::System,
5355 : OutputProcessor::StoreType::Sum,
5356 191 : this->Name);
5357 :
5358 382 : SetupOutputVariable(state,
5359 : "Water Heater Venting Heat Transfer Rate",
5360 : Constant::Units::W,
5361 191 : this->VentRate,
5362 : OutputProcessor::TimeStepType::System,
5363 : OutputProcessor::StoreType::Average,
5364 191 : this->Name);
5365 382 : SetupOutputVariable(state,
5366 : "Water Heater Venting Heat Transfer Energy",
5367 : Constant::Units::J,
5368 191 : this->VentEnergy,
5369 : OutputProcessor::TimeStepType::System,
5370 : OutputProcessor::StoreType::Sum,
5371 191 : this->Name);
5372 :
5373 382 : SetupOutputVariable(state,
5374 : "Water Heater Net Heat Transfer Rate",
5375 : Constant::Units::W,
5376 191 : this->NetHeatTransferRate,
5377 : OutputProcessor::TimeStepType::System,
5378 : OutputProcessor::StoreType::Average,
5379 191 : this->Name);
5380 382 : SetupOutputVariable(state,
5381 : "Water Heater Net Heat Transfer Energy",
5382 : Constant::Units::J,
5383 191 : this->NetHeatTransferEnergy,
5384 : OutputProcessor::TimeStepType::System,
5385 : OutputProcessor::StoreType::Sum,
5386 191 : this->Name);
5387 :
5388 191 : SetupOutputVariable(state,
5389 : "Water Heater Cycle On Count",
5390 : Constant::Units::None,
5391 191 : this->CycleOnCount,
5392 : OutputProcessor::TimeStepType::System,
5393 : OutputProcessor::StoreType::Sum,
5394 191 : this->Name);
5395 382 : SetupOutputVariable(state,
5396 : "Water Heater Runtime Fraction",
5397 : Constant::Units::None,
5398 191 : this->RuntimeFraction,
5399 : OutputProcessor::TimeStepType::System,
5400 : OutputProcessor::StoreType::Average,
5401 191 : this->Name);
5402 382 : SetupOutputVariable(state,
5403 : "Water Heater Part Load Ratio",
5404 : Constant::Units::None,
5405 191 : this->PartLoadRatio,
5406 : OutputProcessor::TimeStepType::System,
5407 : OutputProcessor::StoreType::Average,
5408 191 : this->Name);
5409 :
5410 573 : SetupOutputVariable(state,
5411 382 : format("Water Heater {} Rate", Constant::eFuelNames[static_cast<int>(this->FuelType)]),
5412 : Constant::Units::W,
5413 191 : this->FuelRate,
5414 : OutputProcessor::TimeStepType::System,
5415 : OutputProcessor::StoreType::Average,
5416 191 : this->Name);
5417 573 : SetupOutputVariable(state,
5418 382 : format("Water Heater {} Energy", Constant::eFuelNames[static_cast<int>(this->FuelType)]),
5419 : Constant::Units::J,
5420 191 : this->FuelEnergy,
5421 : OutputProcessor::TimeStepType::System,
5422 : OutputProcessor::StoreType::Sum,
5423 191 : this->Name,
5424 191 : Constant::eFuel2eResource[(int)this->FuelType],
5425 : OutputProcessor::Group::Plant,
5426 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5427 : this->EndUseSubcategoryName);
5428 :
5429 573 : SetupOutputVariable(state,
5430 382 : format("Water Heater Off Cycle Parasitic {} Rate", Constant::eFuelNames[static_cast<int>(this->OffCycParaFuelType)]),
5431 : Constant::Units::W,
5432 191 : this->OffCycParaFuelRate,
5433 : OutputProcessor::TimeStepType::System,
5434 : OutputProcessor::StoreType::Average,
5435 191 : this->Name);
5436 573 : SetupOutputVariable(state,
5437 382 : format("Water Heater Off Cycle Parasitic {} Energy", Constant::eFuelNames[static_cast<int>(this->OffCycParaFuelType)]),
5438 : Constant::Units::J,
5439 191 : this->OffCycParaFuelEnergy,
5440 : OutputProcessor::TimeStepType::System,
5441 : OutputProcessor::StoreType::Sum,
5442 191 : this->Name,
5443 191 : Constant::eFuel2eResource[(int)this->OffCycParaFuelType],
5444 : OutputProcessor::Group::Plant,
5445 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5446 : this->EndUseSubcategoryName);
5447 :
5448 573 : SetupOutputVariable(state,
5449 382 : format("Water Heater On Cycle Parasitic {} Rate", Constant::eFuelNames[static_cast<int>(this->OnCycParaFuelType)]),
5450 : Constant::Units::W,
5451 191 : this->OnCycParaFuelRate,
5452 : OutputProcessor::TimeStepType::System,
5453 : OutputProcessor::StoreType::Average,
5454 191 : this->Name);
5455 573 : SetupOutputVariable(state,
5456 382 : format("Water Heater On Cycle Parasitic {} Energy", Constant::eFuelNames[static_cast<int>(this->OnCycParaFuelType)]),
5457 : Constant::Units::J,
5458 191 : this->OnCycParaFuelEnergy,
5459 : OutputProcessor::TimeStepType::System,
5460 : OutputProcessor::StoreType::Sum,
5461 191 : this->Name,
5462 191 : Constant::eFuel2eResource[(int)this->OnCycParaFuelType],
5463 : OutputProcessor::Group::Plant,
5464 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5465 : this->EndUseSubcategoryName);
5466 :
5467 382 : SetupOutputVariable(state,
5468 : "Water Heater Water Volume Flow Rate",
5469 : Constant::Units::m3_s,
5470 191 : this->VolFlowRate,
5471 : OutputProcessor::TimeStepType::System,
5472 : OutputProcessor::StoreType::Average,
5473 191 : this->Name);
5474 382 : SetupOutputVariable(state,
5475 : "Water Heater Water Volume",
5476 : Constant::Units::m3,
5477 191 : this->VolumeConsumed,
5478 : OutputProcessor::TimeStepType::System,
5479 : OutputProcessor::StoreType::Sum,
5480 191 : this->Name,
5481 : Constant::eResource::Water,
5482 : OutputProcessor::Group::Plant,
5483 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5484 : this->EndUseSubcategoryName);
5485 382 : SetupOutputVariable(state,
5486 : "Water Heater Mains Water Volume",
5487 : Constant::Units::m3,
5488 191 : this->VolumeConsumed,
5489 : OutputProcessor::TimeStepType::System,
5490 : OutputProcessor::StoreType::Sum,
5491 191 : this->Name,
5492 : Constant::eResource::MainsWater,
5493 : OutputProcessor::Group::Plant,
5494 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5495 : this->EndUseSubcategoryName);
5496 :
5497 191 : if (this->HeatPumpNum > 0) {
5498 : // CurrentModuleObject='WaterHeater:HeatPump:PumpedCondenser'
5499 23 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
5500 46 : SetupOutputVariable(state,
5501 : "Water Heater Compressor Part Load Ratio",
5502 : Constant::Units::None,
5503 23 : HPWH.HeatingPLR,
5504 : OutputProcessor::TimeStepType::System,
5505 : OutputProcessor::StoreType::Average,
5506 23 : HPWH.Name);
5507 46 : SetupOutputVariable(state,
5508 : "Water Heater Off Cycle Ancillary Electricity Rate",
5509 : Constant::Units::W,
5510 23 : HPWH.OffCycParaFuelRate,
5511 : OutputProcessor::TimeStepType::System,
5512 : OutputProcessor::StoreType::Average,
5513 23 : HPWH.Name);
5514 46 : SetupOutputVariable(state,
5515 : "Water Heater Off Cycle Ancillary Electricity Energy",
5516 : Constant::Units::J,
5517 23 : HPWH.OffCycParaFuelEnergy,
5518 : OutputProcessor::TimeStepType::System,
5519 : OutputProcessor::StoreType::Sum,
5520 23 : HPWH.Name,
5521 : Constant::eResource::Electricity,
5522 : OutputProcessor::Group::Plant,
5523 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5524 : "Water Heater Parasitic");
5525 46 : SetupOutputVariable(state,
5526 : "Water Heater On Cycle Ancillary Electricity Rate",
5527 : Constant::Units::W,
5528 23 : HPWH.OnCycParaFuelRate,
5529 : OutputProcessor::TimeStepType::System,
5530 : OutputProcessor::StoreType::Average,
5531 23 : HPWH.Name);
5532 46 : SetupOutputVariable(state,
5533 : "Water Heater On Cycle Ancillary Electricity Energy",
5534 : Constant::Units::J,
5535 23 : HPWH.OnCycParaFuelEnergy,
5536 : OutputProcessor::TimeStepType::System,
5537 : OutputProcessor::StoreType::Sum,
5538 23 : HPWH.Name,
5539 : Constant::eResource::Electricity,
5540 : OutputProcessor::Group::Plant,
5541 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5542 : "Water Heater Parasitic");
5543 46 : SetupOutputVariable(state,
5544 : "Water Heater Heat Pump Control Tank Temperature",
5545 : Constant::Units::C,
5546 23 : HPWH.ControlTempAvg,
5547 : OutputProcessor::TimeStepType::System,
5548 : OutputProcessor::StoreType::Average,
5549 23 : HPWH.Name);
5550 46 : SetupOutputVariable(state,
5551 : "Water Heater Heat Pump Control Tank Final Temperature",
5552 : Constant::Units::C,
5553 23 : HPWH.ControlTempFinal,
5554 : OutputProcessor::TimeStepType::System,
5555 : OutputProcessor::StoreType::Average,
5556 23 : HPWH.Name);
5557 : }
5558 :
5559 191 : if (this->DesuperheaterNum > 0) {
5560 : // CurrentModuleObject='Coil:WaterHeating:Desuperheater'
5561 12 : SetupOutputVariable(state,
5562 : "Water Heater Part Load Ratio",
5563 : Constant::Units::None,
5564 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR,
5565 : OutputProcessor::TimeStepType::System,
5566 : OutputProcessor::StoreType::Average,
5567 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5568 12 : SetupOutputVariable(state,
5569 : "Water Heater On Cycle Parasitic Electricity Rate",
5570 : Constant::Units::W,
5571 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelRate,
5572 : OutputProcessor::TimeStepType::System,
5573 : OutputProcessor::StoreType::Average,
5574 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5575 12 : SetupOutputVariable(state,
5576 : "Water Heater On Cycle Parasitic Electricity Energy",
5577 : Constant::Units::J,
5578 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OnCycParaFuelEnergy,
5579 : OutputProcessor::TimeStepType::System,
5580 : OutputProcessor::StoreType::Sum,
5581 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5582 : Constant::eResource::Electricity,
5583 : OutputProcessor::Group::Plant,
5584 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5585 : "Water Heater Parasitic");
5586 12 : SetupOutputVariable(state,
5587 : "Water Heater Off Cycle Parasitic Electricity Rate",
5588 : Constant::Units::W,
5589 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelRate,
5590 : OutputProcessor::TimeStepType::System,
5591 : OutputProcessor::StoreType::Average,
5592 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5593 12 : SetupOutputVariable(state,
5594 : "Water Heater Off Cycle Parasitic Electricity Energy",
5595 : Constant::Units::J,
5596 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OffCycParaFuelEnergy,
5597 : OutputProcessor::TimeStepType::System,
5598 : OutputProcessor::StoreType::Sum,
5599 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5600 : Constant::eResource::Electricity,
5601 : OutputProcessor::Group::Plant,
5602 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5603 : "Water Heater Parasitic");
5604 12 : SetupOutputVariable(state,
5605 : "Water Heater Heat Reclaim Efficiency Modifier Multiplier",
5606 : Constant::Units::None,
5607 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HEffFTempOutput,
5608 : OutputProcessor::TimeStepType::System,
5609 : OutputProcessor::StoreType::Average,
5610 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5611 12 : SetupOutputVariable(state,
5612 : "Water Heater Pump Electricity Rate",
5613 : Constant::Units::W,
5614 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpPower,
5615 : OutputProcessor::TimeStepType::System,
5616 : OutputProcessor::StoreType::Average,
5617 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5618 12 : SetupOutputVariable(state,
5619 : "Water Heater Pump Electricity Energy",
5620 : Constant::Units::J,
5621 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).PumpEnergy,
5622 : OutputProcessor::TimeStepType::System,
5623 : OutputProcessor::StoreType::Sum,
5624 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5625 : Constant::eResource::Electricity,
5626 : OutputProcessor::Group::Plant,
5627 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5628 : "Desuperheater Pump");
5629 12 : SetupOutputVariable(state,
5630 : "Water Heater Heating Rate",
5631 : Constant::Units::W,
5632 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate,
5633 : OutputProcessor::TimeStepType::System,
5634 : OutputProcessor::StoreType::Average,
5635 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name);
5636 12 : SetupOutputVariable(state,
5637 : "Water Heater Heating Energy",
5638 : Constant::Units::J,
5639 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterEnergy,
5640 : OutputProcessor::TimeStepType::System,
5641 : OutputProcessor::StoreType::Sum,
5642 6 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Name,
5643 : Constant::eResource::EnergyTransfer,
5644 : OutputProcessor::Group::Plant,
5645 : OutputProcessor::EndUseCat::WaterSystem, // DHW
5646 : "Water Heater");
5647 : }
5648 :
5649 : // Setup report variables for WaterHeater:Stratified
5650 : // CurrentModuleObject='WaterHeater:Stratified'
5651 191 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5652 :
5653 32 : SetupOutputVariable(state,
5654 : "Water Heater Heater 1 Heating Rate",
5655 : Constant::Units::W,
5656 16 : this->HeaterRate1,
5657 : OutputProcessor::TimeStepType::System,
5658 : OutputProcessor::StoreType::Average,
5659 16 : this->Name);
5660 32 : SetupOutputVariable(state,
5661 : "Water Heater Heater 2 Heating Rate",
5662 : Constant::Units::W,
5663 16 : this->HeaterRate2,
5664 : OutputProcessor::TimeStepType::System,
5665 : OutputProcessor::StoreType::Average,
5666 16 : this->Name);
5667 :
5668 32 : SetupOutputVariable(state,
5669 : "Water Heater Heater 1 Heating Energy",
5670 : Constant::Units::J,
5671 16 : this->HeaterEnergy1,
5672 : OutputProcessor::TimeStepType::System,
5673 : OutputProcessor::StoreType::Sum,
5674 16 : this->Name);
5675 32 : SetupOutputVariable(state,
5676 : "Water Heater Heater 2 Heating Energy",
5677 : Constant::Units::J,
5678 16 : this->HeaterEnergy2,
5679 : OutputProcessor::TimeStepType::System,
5680 : OutputProcessor::StoreType::Sum,
5681 16 : this->Name);
5682 :
5683 16 : SetupOutputVariable(state,
5684 : "Water Heater Heater 1 Cycle On Count",
5685 : Constant::Units::None,
5686 16 : this->CycleOnCount1,
5687 : OutputProcessor::TimeStepType::System,
5688 : OutputProcessor::StoreType::Sum,
5689 16 : this->Name);
5690 16 : SetupOutputVariable(state,
5691 : "Water Heater Heater 2 Cycle On Count",
5692 : Constant::Units::None,
5693 16 : this->CycleOnCount2,
5694 : OutputProcessor::TimeStepType::System,
5695 : OutputProcessor::StoreType::Sum,
5696 16 : this->Name);
5697 :
5698 32 : SetupOutputVariable(state,
5699 : "Water Heater Heater 1 Runtime Fraction",
5700 : Constant::Units::None,
5701 16 : this->RuntimeFraction1,
5702 : OutputProcessor::TimeStepType::System,
5703 : OutputProcessor::StoreType::Average,
5704 16 : this->Name);
5705 32 : SetupOutputVariable(state,
5706 : "Water Heater Heater 2 Runtime Fraction",
5707 : Constant::Units::None,
5708 16 : this->RuntimeFraction2,
5709 : OutputProcessor::TimeStepType::System,
5710 : OutputProcessor::StoreType::Average,
5711 16 : this->Name);
5712 :
5713 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5714 384 : SetupOutputVariable(state,
5715 256 : format("Water Heater Temperature Node {}", NodeNum),
5716 : Constant::Units::C,
5717 128 : this->Node(NodeNum).TempAvg,
5718 : OutputProcessor::TimeStepType::System,
5719 : OutputProcessor::StoreType::Average,
5720 128 : this->Name);
5721 : }
5722 :
5723 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5724 384 : SetupOutputVariable(state,
5725 256 : format("Water Heater Final Temperature Node {}", NodeNum),
5726 : Constant::Units::C,
5727 128 : this->Node(NodeNum).Temp,
5728 : OutputProcessor::TimeStepType::System,
5729 : OutputProcessor::StoreType::Average,
5730 128 : this->Name);
5731 : }
5732 : }
5733 :
5734 191 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
5735 :
5736 144 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
5737 : static constexpr std::string_view Format_723("Water Heater Stratified Node Information,{},{:.4T},{:.4T},{:.3T},{:.4T},{:.4T},{},{}\n");
5738 128 : print(state.files.eio,
5739 : Format_723,
5740 : NodeNum,
5741 128 : this->Node(NodeNum).Height,
5742 128 : this->Node(NodeNum).Volume,
5743 128 : this->Node(NodeNum).MaxCapacity,
5744 128 : this->Node(NodeNum).OffCycLossCoeff,
5745 128 : this->Node(NodeNum).OnCycLossCoeff,
5746 128 : this->Node(NodeNum).Inlets,
5747 128 : this->Node(NodeNum).Outlets);
5748 : }
5749 : }
5750 191 : }
5751 :
5752 0 : void WaterThermalTankData::ValidatePLFCurve(EnergyPlusData &state, int const CurveIndex, bool &IsValid)
5753 : {
5754 :
5755 : // SUBROUTINE INFORMATION:
5756 : // AUTHOR Peter Graham Ellis
5757 : // DATE WRITTEN February 2005
5758 : // MODIFIED na
5759 : // RE-ENGINEERED na
5760 :
5761 : // PURPOSE OF THIS SUBROUTINE:
5762 : // Validates the Part Load Factor curve by making sure it can never be less than or equal to zero
5763 : // over the domain of Part Load Ratio inputs from 0 to 1.
5764 :
5765 : // METHODOLOGY EMPLOYED:
5766 : // Currently can only check 0 and 1. Need changes in CurveManager to be able to check minimums and
5767 : // maximums.
5768 :
5769 0 : IsValid = true;
5770 :
5771 : // Check 0 and 1
5772 0 : if (Curve::CurveValue(state, CurveIndex, 0.0) <= 0) IsValid = false;
5773 0 : if (Curve::CurveValue(state, CurveIndex, 1.0) <= 0) IsValid = false;
5774 0 : }
5775 :
5776 18 : void WaterThermalTankData::SetupStratifiedNodes(EnergyPlusData &state)
5777 : {
5778 :
5779 : // SUBROUTINE INFORMATION:
5780 : // AUTHOR Peter Graham Ellis
5781 : // DATE WRITTEN January 2007
5782 : // MODIFIED na
5783 : // RE-ENGINEERED na
5784 :
5785 : // PURPOSE OF THIS SUBROUTINE:
5786 : // Sets up node properties based on the tank shape, i.e., vertical cylinder, horizontal cylinder, or other.
5787 : // Node height, skin area, vertical conduction area, and loss coefficients are calculated and assigned.
5788 : // Heating elements, parasitics, and fluid inlet and outlet flows are assigned according to node height.
5789 :
5790 : // METHODOLOGY EMPLOYED:
5791 : // Tank is divided into nodes of equal mass. For horizontal cylinders, node heights are calculated using
5792 : // the Newton-Raphson iterative method. For vertical cylinders and other shapes, the node heights are calculated
5793 : // using basic geometry.
5794 :
5795 : static constexpr std::string_view RoutineName("GetWaterThermalTankInput");
5796 :
5797 18 : constexpr Real64 Tolerance(1.0e-8); // Tolerance for Newton-Raphson solution
5798 18 : constexpr Real64 FluidCond(0.6); // Conductivity of water (W/m-K)
5799 :
5800 18 : int NumNodes = this->Nodes;
5801 18 : this->Node.allocate(NumNodes);
5802 : Real64 rho;
5803 18 : if ((this->UseSidePlantLoc.loopNum > 0) && allocated(state.dataPlnt->PlantLoop)) {
5804 0 : rho = FluidProperties::GetDensityGlycol(state,
5805 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
5806 : Constant::InitConvTemp,
5807 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
5808 : RoutineName);
5809 : } else {
5810 18 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, this->FluidIndex, RoutineName);
5811 : }
5812 :
5813 18 : Real64 NodeMass = this->Volume * rho / NumNodes;
5814 : Real64 TankHeight;
5815 :
5816 : // Mixing rate set to 50% of the max value for dt = 1.0
5817 18 : this->InversionMixingRate = NodeMass * 0.5 * 1.0;
5818 :
5819 18 : if ((this->Shape == TankShape::VertCylinder) || (this->Shape == TankShape::Other)) {
5820 18 : TankHeight = this->Height;
5821 18 : Real64 EndArea = this->Volume / TankHeight;
5822 18 : Real64 NodeHeight = TankHeight / NumNodes;
5823 18 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * EndArea / NodeHeight;
5824 :
5825 : Real64 Perimeter_loc;
5826 18 : if (this->Shape == TankShape::VertCylinder) {
5827 18 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
5828 18 : Perimeter_loc = 2.0 * Constant::Pi * Radius;
5829 : } else { // TankShapeOther
5830 0 : Perimeter_loc = this->Perimeter;
5831 : }
5832 :
5833 158 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5834 140 : this->Node(NodeNum).Mass = NodeMass;
5835 140 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5836 140 : this->Node(NodeNum).Height = NodeHeight;
5837 140 : this->Node(NodeNum).CondCoeffUp = CondCoeff;
5838 140 : this->Node(NodeNum).CondCoeffDn = CondCoeff;
5839 :
5840 : Real64 SkinArea;
5841 140 : if ((NodeNum == 1) || (NodeNum == NumNodes)) {
5842 34 : SkinArea = Perimeter_loc * NodeHeight + EndArea;
5843 : } else {
5844 106 : SkinArea = Perimeter_loc * NodeHeight;
5845 : }
5846 :
5847 140 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5848 :
5849 140 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5850 :
5851 : } // NodeNum
5852 :
5853 18 : this->Node(1).CondCoeffUp = 0.0;
5854 18 : this->Node(NumNodes).CondCoeffDn = 0.0;
5855 :
5856 18 : } else { // Tank%Shape == TankShapeHorizCylinder
5857 0 : Real64 TankLength = this->Height; // Height is the length in the axial direction
5858 0 : Real64 EndArea = this->Volume / TankLength;
5859 0 : Real64 Radius = std::sqrt(EndArea / Constant::Pi);
5860 0 : TankHeight = 2.0 * Radius; // Actual vertical height
5861 0 : Real64 NodeEndArea = EndArea / NumNodes;
5862 :
5863 0 : Real64 R = Radius;
5864 0 : Real64 H0 = 0.0;
5865 0 : Real64 ChordLength = 0.0;
5866 0 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5867 0 : this->Node(NodeNum).Mass = NodeMass;
5868 0 : this->Node(NodeNum).Volume = this->Volume / NumNodes;
5869 : Real64 H;
5870 0 : if (NodeNum == NumNodes) {
5871 0 : H = TankHeight;
5872 : } else {
5873 : // Use the Newton-Raphson method to solve the nonlinear algebraic equation for node height
5874 0 : H = H0 + TankHeight / NumNodes; // Initial guess
5875 :
5876 : while (true) {
5877 0 : Real64 a = std::sqrt(H);
5878 0 : Real64 b = std::sqrt(2.0 * R - H);
5879 0 : Real64 c = 2.0 * R * R * std::atan(a / b) - (2.0 * R * R - 3.0 * H * R + H * H) * (a / b);
5880 : Real64 c0;
5881 0 : if (H0 > 0.0) {
5882 0 : Real64 a0 = std::sqrt(H0);
5883 0 : Real64 b0 = std::sqrt(2.0 * R - H0);
5884 0 : c0 = 2.0 * R * R * std::atan(a0 / b0) - (2.0 * R * R - 3.0 * H0 * R + H0 * H0) * (a0 / b0);
5885 : } else {
5886 0 : c0 = 0.0;
5887 : }
5888 :
5889 0 : Real64 ApproxEndArea = c - c0; // Area approximated by iteration
5890 0 : Real64 G = ApproxEndArea - NodeEndArea; // G is the function that should converge to zero
5891 :
5892 0 : if (std::abs(G) < Tolerance) {
5893 0 : break; // Converged !!!
5894 : } else {
5895 0 : H -= G / (2.0 * a * b); // Calculate next guess: H = Hprev - G/G'
5896 : }
5897 0 : } // Newton-Raphson
5898 : }
5899 :
5900 0 : this->Node(NodeNum).Height = H - H0;
5901 :
5902 0 : if (NodeNum > 1) {
5903 0 : Real64 CrossArea = 2.0 * ChordLength * TankLength; // Use old ChordLength from previous node
5904 0 : Real64 CondCoeff = (FluidCond + this->AdditionalCond) * CrossArea / (0.5 * (H - H0) + 0.5 * this->Node(NodeNum - 1).Height);
5905 0 : this->Node(NodeNum - 1).CondCoeffUp = CondCoeff; // Set for previous node
5906 0 : this->Node(NodeNum).CondCoeffDn = CondCoeff; // Set for this node
5907 : }
5908 :
5909 0 : ChordLength = std::sqrt(2.0 * R * H - H * H); // Calc new ChordLength to be used with next node
5910 :
5911 0 : Real64 Perimeter_loc = 2.0 * R * (std::acos((R - H) / R) - std::acos((R - H0) / R)); // Segments of circular perimeter
5912 0 : Real64 SkinArea = Perimeter_loc * TankLength + 2.0 * NodeEndArea;
5913 :
5914 0 : this->Node(NodeNum).OnCycLossCoeff = this->SkinLossCoeff * SkinArea + this->AdditionalLossCoeff(NodeNum);
5915 :
5916 0 : this->Node(NodeNum).OffCycLossCoeff = this->Node(NodeNum).OnCycLossCoeff + this->OffCycFlueLossCoeff;
5917 : // Although it doesn't make much sense to have a flue in a horizontal tank, keep it in anyway
5918 :
5919 0 : H0 = H;
5920 : } // NodeNum
5921 :
5922 0 : this->Node(1).CondCoeffUp = 0.0;
5923 0 : this->Node(NumNodes).CondCoeffDn = 0.0;
5924 : }
5925 :
5926 : // Loop through nodes again (from top to bottom this time) and assign heating elements, parasitics, flow inlets/outlets
5927 : // according to their vertical heights in the tank
5928 18 : Real64 H0 = TankHeight;
5929 158 : for (int NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
5930 : Real64 H;
5931 140 : if (NodeNum == NumNodes) {
5932 18 : H = -1.0; // Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
5933 : } else {
5934 122 : H = H0 - this->Node(NodeNum).Height;
5935 : }
5936 :
5937 : // Assign heater elements to the nodes at the specified heights
5938 140 : if ((this->HeaterHeight1 <= H0) && (this->HeaterHeight1 > H)) {
5939 : // sensor node will not get set if user enters 0 for this heater capacity
5940 : // (Tank%MaxCapacity > 0.0d0)) THEN
5941 18 : this->HeaterNode1 = NodeNum;
5942 18 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity;
5943 : }
5944 :
5945 140 : if ((this->HeaterHeight2 <= H0) && (this->HeaterHeight2 > H)) {
5946 : // sensor node will not get set if user enters 0 for this heater capacity
5947 : // .AND. (Tank%MaxCapacity2 > 0.0d0)) THEN
5948 18 : this->HeaterNode2 = NodeNum;
5949 :
5950 18 : if ((NodeNum == this->HeaterNode1) && (this->StratifiedControlMode == PriorityControlMode::Simultaneous)) {
5951 0 : this->Node(NodeNum).MaxCapacity += this->MaxCapacity2;
5952 : } else {
5953 18 : this->Node(NodeNum).MaxCapacity = this->MaxCapacity2;
5954 : }
5955 : }
5956 :
5957 : // Assign parasitic heat gains to the nodes at the specified heights
5958 140 : if ((this->OffCycParaHeight <= H0) && (this->OffCycParaHeight > H)) {
5959 18 : this->Node(NodeNum).OffCycParaLoad = this->OffCycParaFracToTank * this->OffCycParaLoad;
5960 : }
5961 :
5962 140 : if ((this->OnCycParaHeight <= H0) && (this->OnCycParaHeight > H)) {
5963 18 : this->Node(NodeNum).OnCycParaLoad = this->OnCycParaFracToTank * this->OnCycParaLoad;
5964 : }
5965 :
5966 : // Assign inlets and outlets to the nodes at the specified heights
5967 140 : if ((this->UseInletHeight <= H0) && (this->UseInletHeight > H)) {
5968 18 : this->UseInletStratNode = NodeNum;
5969 :
5970 18 : if ((this->UseInletNode > 0) || (this->MassFlowRateMax > 0.0)) ++this->Node(NodeNum).Inlets;
5971 : }
5972 :
5973 140 : if ((this->UseOutletHeight <= H0) && (this->UseOutletHeight > H)) {
5974 18 : this->UseOutletStratNode = NodeNum;
5975 :
5976 18 : if ((this->UseOutletNode > 0) || (this->MassFlowRateMax > 0.0)) ++this->Node(NodeNum).Outlets;
5977 : }
5978 :
5979 140 : if ((this->SourceInletHeight <= H0) && (this->SourceInletHeight > H) && (this->SourceInletNode > 0)) {
5980 :
5981 11 : this->SourceInletStratNode = NodeNum;
5982 11 : ++this->Node(NodeNum).Inlets;
5983 : }
5984 :
5985 140 : if ((this->SourceOutletHeight <= H0) && (this->SourceOutletHeight > H) && (this->SourceOutletNode > 0)) {
5986 :
5987 11 : this->SourceOutletStratNode = NodeNum;
5988 11 : ++this->Node(NodeNum).Outlets;
5989 : }
5990 :
5991 140 : H0 = H;
5992 : } // NodeNum
5993 18 : }
5994 :
5995 6284636 : void WaterThermalTankData::initialize(EnergyPlusData &state, bool const FirstHVACIteration)
5996 : {
5997 :
5998 : // SUBROUTINE INFORMATION:
5999 : // AUTHOR Peter Graham Ellis
6000 : // DATE WRITTEN February 2004
6001 : // MODIFIED FSEC, July 2005
6002 : // Brent Griffith, October 2007 indirect fired water heater
6003 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
6004 : // RE-ENGINEERED na
6005 :
6006 : // PURPOSE OF THIS SUBROUTINE:
6007 : // Initialize the water heater, heat pump water heater, or desuperheater heating coil objects during the simulation.
6008 : // determine flow rates thru use side and source side plant connections (if any)
6009 :
6010 : // METHODOLOGY EMPLOYED:
6011 : // Inlet and outlet nodes are initialized. Scheduled values are retrieved for the current timestep.
6012 :
6013 6284636 : auto &ZoneEqSizing = state.dataSize->ZoneEqSizing;
6014 :
6015 : static constexpr std::string_view RoutineName("InitWaterThermalTank");
6016 : static constexpr std::string_view GetWaterThermalTankInput("GetWaterThermalTankInput");
6017 : static constexpr std::string_view SizeTankForDemand("SizeTankForDemandSide");
6018 :
6019 6284636 : if (this->scanPlantLoopsFlag && allocated(state.dataPlnt->PlantLoop)) {
6020 197 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
6021 119 : bool errFlag = false;
6022 357 : PlantUtilities::ScanPlantLoopsForObject(
6023 238 : state, this->Name, this->WaterThermalTankType, this->UseSidePlantLoc, errFlag, _, _, _, this->UseInletNode, _);
6024 119 : if (errFlag) {
6025 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6026 : }
6027 : }
6028 197 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
6029 : // this is a heat pump water heater, need a separate block because TypeOf_HeatPumpWtrHeater shows up on Branch
6030 : // (input should probably have been the associated tank )
6031 10 : bool errFlag = false;
6032 30 : PlantUtilities::ScanPlantLoopsForObject(state,
6033 10 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
6034 10 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType,
6035 10 : this->UseSidePlantLoc,
6036 : errFlag,
6037 : _,
6038 : _,
6039 : _,
6040 10 : this->UseInletNode,
6041 : _);
6042 10 : if (errFlag) {
6043 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6044 : }
6045 : }
6046 197 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
6047 21 : bool errFlag = false;
6048 63 : PlantUtilities::ScanPlantLoopsForObject(
6049 42 : state, this->Name, this->WaterThermalTankType, this->SrcSidePlantLoc, errFlag, _, _, _, this->SourceInletNode, _);
6050 21 : if (this->UseInletNode > 0) {
6051 21 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->UseSidePlantLoc, this->SrcSidePlantLoc, this->WaterThermalTankType, true);
6052 : }
6053 21 : if (errFlag) {
6054 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6055 : }
6056 : }
6057 197 : this->scanPlantLoopsFlag = false;
6058 : }
6059 :
6060 6284636 : if (this->SetLoopIndexFlag && allocated(state.dataPlnt->PlantLoop)) {
6061 1434 : if ((this->UseInletNode > 0) && (this->HeatPumpNum == 0)) {
6062 1356 : Real64 rho = FluidProperties::GetDensityGlycol(state,
6063 1356 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6064 : Constant::InitConvTemp,
6065 1356 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6066 : GetWaterThermalTankInput);
6067 1356 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6068 1356 : this->Mass = this->Volume * rho;
6069 1356 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
6070 1356 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
6071 0 : ShowSevereError(state,
6072 0 : format("InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = {}", this->Name));
6073 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6074 : }
6075 : }
6076 1434 : if ((this->UseInletNode > 0) && (this->HeatPumpNum > 0)) {
6077 10 : Real64 rho = FluidProperties::GetDensityGlycol(state,
6078 10 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6079 : Constant::InitConvTemp,
6080 10 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6081 : GetWaterThermalTankInput);
6082 10 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6083 10 : this->Mass = this->Volume * rho;
6084 10 : this->UseSidePlantSizNum = state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).PlantSizNum;
6085 10 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
6086 0 : ShowSevereError(state,
6087 0 : format("InitWaterThermalTank: Did not find Sizing:Plant object for use side of plant thermal tank = {}", this->Name));
6088 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6089 : }
6090 : }
6091 1434 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
6092 379 : Real64 rho = FluidProperties::GetDensityGlycol(state,
6093 379 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
6094 : Constant::InitConvTemp,
6095 379 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
6096 : GetWaterThermalTankInput);
6097 379 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6098 379 : this->SourceSidePlantSizNum = state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).PlantSizNum;
6099 379 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0)) {
6100 0 : ShowSevereError(
6101 0 : state, format("InitWaterThermalTank: Did not find Sizing:Plant object for source side of plant thermal tank = {}", this->Name));
6102 0 : ShowFatalError(state, "InitWaterThermalTank: Program terminated due to previous condition(s).");
6103 : }
6104 : }
6105 1434 : if (((this->SourceInletNode > 0) && (this->DesuperheaterNum > 0)) || (this->HeatPumpNum > 0)) {
6106 29 : this->SetLoopIndexFlag = false;
6107 : }
6108 1434 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) this->SetLoopIndexFlag = false;
6109 1434 : if (this->StandAlone) {
6110 49 : this->SizeStandAloneWaterHeater(state);
6111 49 : this->SetLoopIndexFlag = false;
6112 : }
6113 6283202 : } else if (this->SetLoopIndexFlag && !state.dataGlobal->AnyPlantInModel) {
6114 0 : if (this->StandAlone) {
6115 0 : this->SizeStandAloneWaterHeater(state);
6116 : }
6117 0 : this->SetLoopIndexFlag = false;
6118 : }
6119 :
6120 6284636 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag && !this->SetLoopIndexFlag) {
6121 :
6122 49289 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
6123 :
6124 48385 : if (this->ControlType == HeaterControlMode::Cycle) {
6125 47867 : this->MinCapacity = this->MaxCapacity;
6126 : }
6127 :
6128 : // check for sizing issues that model can not support
6129 :
6130 : // if stratified tank model, ensure that nominal change over rate is greater than one minute, avoid numerical problems.
6131 :
6132 48385 : if ((this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) ||
6133 46059 : (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified)) {
6134 3004 : Real64 MaxSideVolFlow = max(this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
6135 :
6136 3004 : if (MaxSideVolFlow > 0.0) { // protect div by zero
6137 2827 : Real64 TankChangeRateScale = this->Volume / MaxSideVolFlow;
6138 2827 : if (TankChangeRateScale < 60.0) { // nominal change over in less than one minute
6139 0 : ShowSevereError(state, "InitWaterThermalTank: Detected problem for stratified tank model. Model cannot be applied.");
6140 0 : ShowContinueError(state, format("Occurs for stratified tank name = {}", this->Name));
6141 0 : ShowContinueError(state, format("Tank volume = {:.4R} [m3]", this->Volume));
6142 0 : ShowContinueError(state, format("Tank use side volume flow rate = {:.4R} [m3/s]", this->UseDesignVolFlowRate));
6143 0 : ShowContinueError(state, format("Tank source side volume flow rate = {:.4R} [m3/s]", this->SourceDesignVolFlowRate));
6144 0 : ShowContinueError(state, format("Nominal tank change over rate = {:.2R} [s]", TankChangeRateScale));
6145 0 : ShowContinueError(
6146 : state, "Change over rate is too fast, increase tank volume, decrease connection flow rates or use mixed tank model");
6147 :
6148 0 : ShowFatalError(state, "InitWaterThermalTank: Simulation halted because of sizing problem in stratified tank model.");
6149 : }
6150 : }
6151 : }
6152 : }
6153 :
6154 : // Clear node initial conditions
6155 49289 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
6156 47796 : state.dataLoopNodes->Node(this->UseInletNode).Temp = 0.0;
6157 47796 : Real64 rho = FluidProperties::GetDensityGlycol(state,
6158 47796 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6159 : Constant::InitConvTemp,
6160 47796 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6161 : GetWaterThermalTankInput);
6162 47796 : this->MassFlowRateMin = this->VolFlowRateMin * rho;
6163 47796 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
6164 47796 : PlantUtilities::InitComponentNodes(state, this->MassFlowRateMin, this->PlantUseMassFlowRateMax, this->UseInletNode, this->UseOutletNode);
6165 47796 : this->UseOutletTemp = 0.0;
6166 47796 : this->UseMassFlowRate = 0.0;
6167 47796 : this->SavedUseOutletTemp = 0.0;
6168 :
6169 47796 : this->Mass = this->Volume * rho;
6170 47796 : this->UseBranchControlType = DataPlant::CompData::getPlantComponent(state, this->UseSidePlantLoc).FlowCtrl;
6171 : }
6172 :
6173 49289 : if ((this->SourceInletNode > 0) && (this->DesuperheaterNum == 0) && (this->HeatPumpNum == 0)) {
6174 14760 : Real64 rho = FluidProperties::GetDensityGlycol(state,
6175 14760 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
6176 : Constant::InitConvTemp,
6177 14760 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
6178 : GetWaterThermalTankInput);
6179 14760 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6180 14760 : PlantUtilities::InitComponentNodes(state, 0.0, this->PlantSourceMassFlowRateMax, this->SourceInletNode, this->SourceOutletNode);
6181 :
6182 14760 : this->SourceOutletTemp = 0.0;
6183 14760 : this->SourceMassFlowRate = 0.0;
6184 14760 : this->SavedSourceOutletTemp = 0.0;
6185 :
6186 14760 : this->SourceBranchControlType = DataPlant::CompData::getPlantComponent(state, this->SrcSidePlantLoc).FlowCtrl;
6187 : }
6188 :
6189 49289 : if ((this->SourceInletNode > 0) && ((this->DesuperheaterNum > 0) || (this->HeatPumpNum > 0))) {
6190 2410 : state.dataLoopNodes->Node(this->SourceInletNode).Temp = 0.0;
6191 2410 : this->SourceOutletTemp = 0.0;
6192 2410 : this->SourceMassFlowRate = 0.0;
6193 2410 : this->SavedSourceOutletTemp = 0.0;
6194 2410 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, this->FluidIndex, SizeTankForDemand);
6195 2410 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
6196 : }
6197 :
6198 : // Initialize tank temperature to setpoint of first hour of warm up period
6199 : // (use HPWH or Desuperheater heating coil set point if applicable)
6200 : int SchIndex;
6201 49289 : if (this->HeatPumpNum > 0) {
6202 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6203 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveMode = TankOperatingMode::Floating;
6204 2278 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SaveWHMode = TankOperatingMode::Floating;
6205 2278 : SchIndex = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTempSchedule;
6206 47011 : } else if (this->DesuperheaterNum > 0) {
6207 132 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6208 132 : SchIndex = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTempSchedule;
6209 : } else {
6210 46879 : SchIndex = this->SetPointTempSchedule;
6211 : }
6212 :
6213 49289 : if (SchIndex > 0) {
6214 49289 : this->TankTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6215 49289 : this->SavedTankTemp = this->TankTemp;
6216 :
6217 49289 : if (this->Nodes > 0) {
6218 27013 : for (auto &e : this->Node) {
6219 23926 : e.Temp = e.SavedTemp = this->TankTemp;
6220 : }
6221 : }
6222 : } else {
6223 0 : this->TankTemp = 20.0;
6224 0 : this->SavedTankTemp = this->TankTemp;
6225 :
6226 0 : if (this->Nodes > 0) {
6227 0 : for (auto &e : this->Node) {
6228 0 : e.Temp = e.SavedTemp = this->TankTemp;
6229 : }
6230 : }
6231 : }
6232 49289 : this->SourceOutletTemp = this->SavedTankTemp;
6233 49289 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6234 49289 : this->UseOutletTemp = this->SavedTankTemp;
6235 49289 : this->SavedUseOutletTemp = this->SavedTankTemp;
6236 49289 : this->TankTempAvg = this->SavedTankTemp;
6237 :
6238 49289 : this->SavedHeaterOn1 = false;
6239 49289 : this->SavedHeaterOn2 = false;
6240 49289 : this->Mode = TankOperatingMode::Floating;
6241 49289 : this->SavedMode = TankOperatingMode::Floating;
6242 49289 : this->FirstRecoveryDone = false;
6243 49289 : this->FirstRecoveryFuel = 0.0;
6244 49289 : this->UnmetEnergy = 0.0;
6245 49289 : this->LossEnergy = 0.0;
6246 49289 : this->FlueLossEnergy = 0.0;
6247 49289 : this->UseEnergy = 0.0;
6248 49289 : this->TotalDemandEnergy = 0.0;
6249 49289 : this->SourceEnergy = 0.0;
6250 49289 : this->HeaterEnergy = 0.0;
6251 49289 : this->HeaterEnergy1 = 0.0;
6252 49289 : this->HeaterEnergy2 = 0.0;
6253 49289 : this->FuelEnergy = 0.0;
6254 49289 : this->FuelEnergy1 = 0.0;
6255 49289 : this->FuelEnergy2 = 0.0;
6256 49289 : this->VentEnergy = 0.0;
6257 49289 : this->OffCycParaFuelEnergy = 0.0;
6258 49289 : this->OffCycParaEnergyToTank = 0.0;
6259 49289 : this->OnCycParaFuelEnergy = 0.0;
6260 49289 : this->OnCycParaEnergyToTank = 0.0;
6261 49289 : this->NetHeatTransferEnergy = 0.0;
6262 : }
6263 :
6264 6284636 : if (!state.dataGlobal->BeginEnvrnFlag) this->MyEnvrnFlag = true;
6265 :
6266 6284636 : if (this->WarmupFlag && (!state.dataGlobal->WarmupFlag)) {
6267 : // reInitialize tank temperature to setpoint of first hour (use HPWH or Desuperheater heating coil set point if applicable)
6268 : // BG's interpretation here is that its better to reset initial condition to setpoint once warm up is over.
6269 : // (otherwise with a dynamic storage model it is difficult for the user to see the initial performance if it isn't periodic.)
6270 : int SchIndex;
6271 437 : if (this->HeatPumpNum > 0) {
6272 46 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Mode = TankOperatingMode::Floating;
6273 46 : SchIndex = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTempSchedule;
6274 391 : } else if (this->DesuperheaterNum > 0) {
6275 12 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).Mode = TankOperatingMode::Floating;
6276 12 : SchIndex = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTempSchedule;
6277 : } else {
6278 379 : SchIndex = this->SetPointTempSchedule;
6279 : }
6280 :
6281 437 : if (SchIndex > 0) {
6282 437 : this->TankTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6283 437 : this->SavedTankTemp = this->TankTemp;
6284 :
6285 437 : if (this->Nodes > 0) {
6286 316 : for (auto &e : this->Node) {
6287 280 : e.Temp = e.SavedTemp = this->TankTemp;
6288 : }
6289 : }
6290 : } else {
6291 0 : this->TankTemp = 20.0;
6292 0 : this->SavedTankTemp = this->TankTemp;
6293 :
6294 0 : if (this->Nodes > 0) {
6295 0 : for (auto &e : this->Node) {
6296 0 : e.Temp = e.SavedTemp = this->TankTemp;
6297 : }
6298 : }
6299 : }
6300 437 : this->SourceOutletTemp = this->SavedTankTemp;
6301 437 : this->SavedSourceOutletTemp = this->SavedTankTemp;
6302 437 : this->UseOutletTemp = this->SavedTankTemp;
6303 437 : this->SavedUseOutletTemp = this->SavedTankTemp;
6304 437 : this->SavedHeaterOn1 = false;
6305 437 : this->SavedHeaterOn2 = false;
6306 437 : this->Mode = TankOperatingMode::Floating;
6307 437 : this->SavedMode = TankOperatingMode::Floating;
6308 437 : this->WarmupFlag = false;
6309 : }
6310 6284636 : if (state.dataGlobal->WarmupFlag) this->WarmupFlag = true;
6311 :
6312 6284636 : if (FirstHVACIteration) {
6313 : // Get all scheduled values
6314 3008026 : int SchIndex = this->SetPointTempSchedule;
6315 3008026 : this->SetPointTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6316 :
6317 3008026 : if (!this->IsChilledWaterTank) {
6318 2723759 : if (this->SetPointTemp > this->TankTempLimit) {
6319 : // Setpoint temperature scheduled higher than maximum tank temperature limit
6320 0 : this->SetPointTemp = this->TankTempLimit - 1.0;
6321 :
6322 0 : if (this->ShowSetPointWarning) {
6323 0 : ShowSevereError(
6324 : state,
6325 0 : format("Water heater = {}: Water heater tank set point temperature is greater than the maximum tank temperature limit.",
6326 0 : this->Name));
6327 0 : ShowContinueErrorTimeStamp(state,
6328 0 : format("Water heater tank set point temperature is reset to Tank Temperature Limit minus 1 C "
6329 : "({:.2T}) and simulation continues.",
6330 0 : this->SetPointTemp));
6331 0 : this->ShowSetPointWarning = false;
6332 : }
6333 : }
6334 : } else {
6335 284267 : if (this->SetPointTemp < this->TankTempLimit) {
6336 : // Setpoint temperature scheduled lower than minimum tank temperature limit
6337 0 : this->SetPointTemp = this->TankTempLimit + 1.0;
6338 :
6339 0 : if (this->ShowSetPointWarning) {
6340 0 : ShowSevereError(
6341 : state,
6342 0 : format("Chilled Water Tank = {}: Water heater tank set point temperature is lower than the minimum tank temperature limit.",
6343 0 : this->Name));
6344 0 : ShowContinueErrorTimeStamp(state,
6345 0 : format("Chilled water tank set point temperature is reset to Tank Temperature Limit plus 1 C "
6346 : "({:.2T}) and simulation continues.",
6347 0 : this->SetPointTemp));
6348 0 : this->ShowSetPointWarning = false;
6349 : }
6350 : }
6351 : }
6352 :
6353 3008026 : SchIndex = this->SetPointTempSchedule2;
6354 3008026 : if (SchIndex > 0) {
6355 158244 : this->SetPointTemp2 = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6356 : }
6357 :
6358 3008026 : switch (this->AmbientTempIndicator) {
6359 2244433 : case WTTAmbientTemp::Schedule: {
6360 2244433 : SchIndex = this->AmbientTempSchedule;
6361 2244433 : this->AmbientTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6362 :
6363 2244433 : break;
6364 : }
6365 611344 : case WTTAmbientTemp::TempZone: {
6366 611344 : this->AmbientTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->AmbientTempZone).MAT;
6367 :
6368 611344 : break;
6369 : }
6370 152249 : case WTTAmbientTemp::OutsideAir: {
6371 152249 : this->AmbientTemp = state.dataLoopNodes->Node(this->AmbientTempOutsideAirNode).Temp;
6372 152249 : break;
6373 : }
6374 0 : default:
6375 0 : break;
6376 : }
6377 :
6378 3008026 : if (this->UseInletNode == 0) { // Stand-alone operation
6379 :
6380 212099 : SchIndex = this->UseInletTempSchedule;
6381 212099 : if (SchIndex > 0) {
6382 80871 : this->UseInletTemp = ScheduleManager::GetCurrentScheduleValue(state, SchIndex);
6383 : } else {
6384 131228 : this->UseInletTemp = state.dataEnvrn->WaterMainsTemp;
6385 : }
6386 :
6387 212099 : SchIndex = this->FlowRateSchedule;
6388 212099 : if (SchIndex > 0) {
6389 212099 : this->UseMassFlowRate = ScheduleManager::GetCurrentScheduleValue(state, SchIndex) * this->MassFlowRateMax;
6390 :
6391 212099 : this->VolFlowRate = this->UseMassFlowRate / Psychrometrics::RhoH2O(Constant::InitConvTemp);
6392 : } else {
6393 0 : this->UseMassFlowRate = this->MassFlowRateMax;
6394 0 : this->VolFlowRate = this->UseMassFlowRate / Psychrometrics::RhoH2O(Constant::InitConvTemp);
6395 : }
6396 : }
6397 :
6398 3008026 : if (this->HeatPumpNum > 0) {
6399 226094 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp =
6400 226094 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTempSchedule);
6401 226094 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp >= this->TankTempLimit) {
6402 : // HP setpoint temperature scheduled equal to or higher than tank temperature limit
6403 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp = this->TankTempLimit - 1.0;
6404 :
6405 0 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning) {
6406 0 : ShowSevereError(state,
6407 0 : format("Heat Pump Water Heater = {}: Heat Pump water heater set point temperature is equal to or greater than "
6408 : "the maximum tank temperature limit.",
6409 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name));
6410 0 : ShowContinueErrorTimeStamp(state,
6411 0 : format("Heat Pump water heater tank set point temperature is reset to Tank Temperature Limit "
6412 : "minus 1 C ({:.2T}) and simulation continues.",
6413 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).SetPointTemp));
6414 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).ShowSetPointWarning = false;
6415 : }
6416 : }
6417 : }
6418 :
6419 3008026 : if (this->DesuperheaterNum > 0) {
6420 14742 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTemp = ScheduleManager::GetCurrentScheduleValue(
6421 14742 : state, state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).SetPointTempSchedule);
6422 : }
6423 :
6424 : } // first HVAC Iteration
6425 :
6426 6284636 : if (this->UseInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6427 : Real64 DeadBandTemp;
6428 5862748 : if (this->IsChilledWaterTank) {
6429 573190 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6430 : } else {
6431 5289558 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6432 : }
6433 :
6434 11725496 : Real64 mdotUse = this->PlantMassFlowRatesFunc(state,
6435 : this->UseInletNode,
6436 : FirstHVACIteration,
6437 : WaterHeaterSide::Use,
6438 : this->UseSidePlantLoc.loopSideNum,
6439 5862748 : this->UseSideSeries,
6440 : this->UseBranchControlType,
6441 : this->SavedUseOutletTemp,
6442 : DeadBandTemp,
6443 5862748 : this->SetPointTemp);
6444 5862748 : PlantUtilities::SetComponentFlowRate(state, mdotUse, this->UseInletNode, this->UseOutletNode, this->UseSidePlantLoc);
6445 :
6446 5862748 : this->UseInletTemp = state.dataLoopNodes->Node(this->UseInletNode).Temp;
6447 5862748 : this->UseMassFlowRate = mdotUse;
6448 : }
6449 :
6450 6284636 : if (this->SourceInletNode > 0 && !this->SetLoopIndexFlag) { // setup mass flows for plant connections
6451 : Real64 DeadBandTemp;
6452 1858772 : if (this->IsChilledWaterTank) {
6453 418496 : DeadBandTemp = this->SetPointTemp + this->DeadBandDeltaTemp;
6454 : } else {
6455 1440276 : DeadBandTemp = this->SetPointTemp - this->DeadBandDeltaTemp;
6456 : }
6457 :
6458 : Real64 sensedTemp;
6459 1858772 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
6460 104404 : int tmpNodeNum = this->HeaterNode1;
6461 104404 : sensedTemp = this->Node(tmpNodeNum).SavedTemp;
6462 : } else {
6463 1754368 : sensedTemp = this->SavedSourceOutletTemp;
6464 : }
6465 :
6466 3717544 : Real64 mdotSource = this->PlantMassFlowRatesFunc(state,
6467 : this->SourceInletNode,
6468 : FirstHVACIteration,
6469 : WaterHeaterSide::Source,
6470 : this->SrcSidePlantLoc.loopSideNum,
6471 1858772 : this->SourceSideSeries,
6472 : this->SourceBranchControlType,
6473 : sensedTemp,
6474 : DeadBandTemp,
6475 1858772 : this->SetPointTemp);
6476 1858772 : if (this->SrcSidePlantLoc.loopNum > 0) {
6477 1380552 : PlantUtilities::SetComponentFlowRate(state, mdotSource, this->SourceInletNode, this->SourceOutletNode, this->SrcSidePlantLoc);
6478 : } else { // not really plant connected (desuperheater or heat pump)
6479 478220 : state.dataLoopNodes->Node(this->SourceInletNode).MassFlowRate = mdotSource;
6480 478220 : state.dataLoopNodes->Node(this->SourceOutletNode).MassFlowRate = mdotSource;
6481 : }
6482 :
6483 1858772 : this->SourceInletTemp = state.dataLoopNodes->Node(this->SourceInletNode).Temp;
6484 1858772 : this->SourceMassFlowRate = mdotSource;
6485 : }
6486 :
6487 : // initialize HPWHs each iteration
6488 6284636 : if (this->HeatPumpNum > 0) {
6489 :
6490 448736 : int HPNum = this->HeatPumpNum;
6491 :
6492 448736 : if (this->MyHPSizeFlag) {
6493 : // autosize info must be calculated in GetWaterThermalTankInputFlag for use in StandardRating procedure
6494 : // (called at end of GetWaterThermalTankInputFlag)
6495 : // report autosizing information here (must be done after GetWaterThermalTankInputFlag is complete)
6496 61 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).WaterFlowRateAutoSized &&
6497 16 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6498 16 : BaseSizer::reportSizerOutput(state,
6499 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6500 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6501 : "Condenser water flow rate [m3/s]",
6502 8 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate);
6503 : }
6504 65 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).AirFlowRateAutoSized &&
6505 20 : (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated)) {
6506 20 : BaseSizer::reportSizerOutput(state,
6507 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Type,
6508 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name,
6509 : "Evaporator air flow rate [m3/s]",
6510 10 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate);
6511 : }
6512 45 : state.dataSize->DataNonZoneNonAirloopValue = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate;
6513 45 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate =
6514 45 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirFlowRate * state.dataEnvrn->StdRhoAir;
6515 45 : if (state.dataSize->CurZoneEqNum > 0) {
6516 12 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirFlow = true;
6517 12 : ZoneEqSizing(state.dataSize->CurZoneEqNum).CoolingAirVolFlow = state.dataSize->DataNonZoneNonAirloopValue;
6518 : }
6519 45 : if (state.dataPlnt->PlantFirstSizesOkayToReport || !state.dataGlobal->AnyPlantInModel || this->AlreadyRated) this->MyHPSizeFlag = false;
6520 : }
6521 :
6522 448736 : int HPAirInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode;
6523 448736 : int HPAirOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirOutletNode;
6524 448736 : int OutdoorAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode;
6525 448736 : int ExhaustAirNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).ExhaustAirNode;
6526 448736 : int HPWaterInletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode;
6527 448736 : int HPWaterOutletNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterOutletNode;
6528 448736 : int InletAirMixerNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode;
6529 448736 : int OutletAirSplitterNode = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutletAirSplitterNode;
6530 :
6531 448736 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).CrankcaseTempIndicator) {
6532 189862 : case CrankcaseHeaterControlTemp::Zone: {
6533 379724 : state.dataHVACGlobal->HPWHCrankcaseDBTemp =
6534 189862 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientTempZone).MAT;
6535 189862 : break;
6536 : }
6537 239560 : case CrankcaseHeaterControlTemp::Outdoors: {
6538 239560 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = state.dataEnvrn->OutDryBulbTemp;
6539 239560 : break;
6540 : }
6541 19314 : case CrankcaseHeaterControlTemp::Schedule: {
6542 38628 : state.dataHVACGlobal->HPWHCrankcaseDBTemp =
6543 19314 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).CrankcaseTempSchedule);
6544 19314 : break;
6545 : }
6546 0 : default:
6547 0 : break;
6548 : }
6549 :
6550 : // initialize HPWH report variables to 0 and set tank inlet node equal to outlet node
6551 448736 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterSensibleCapacity = 0.0;
6552 448736 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWaterHeaterLatentCapacity = 0.0;
6553 448736 : this->SourceMassFlowRate = 0.0;
6554 448736 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatingPLR = 0.0;
6555 448736 : this->SourceInletTemp = this->SourceOutletTemp;
6556 :
6557 : // determine HPWH inlet air conditions based on inlet air configuration (Zone, ZoneAndOA, OutdoorAir, or Schedule)
6558 448736 : Real64 HPInletDryBulbTemp = 0.0;
6559 448736 : Real64 HPInletHumRat = 0.0;
6560 : Real64 HPInletRelHum;
6561 448736 : switch (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirConfiguration) {
6562 169833 : case WTTAmbientTemp::TempZone: {
6563 169833 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6564 169833 : HPInletDryBulbTemp = state.dataLoopNodes->Node(HPAirInletNode).Temp;
6565 169833 : HPInletHumRat = state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6566 169833 : break;
6567 : }
6568 20029 : case WTTAmbientTemp::ZoneAndOA: {
6569 20029 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerSchPtr > 0) {
6570 : // schedule values are checked for boundary of 0 and 1 in GetWaterThermalTankInputFlag
6571 20029 : state.dataWaterThermalTanks->mixerInletAirSchedule =
6572 20029 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerSchPtr);
6573 : } else {
6574 0 : state.dataWaterThermalTanks->mixerInletAirSchedule = 0.0;
6575 : }
6576 20029 : HPInletDryBulbTemp = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).Temp +
6577 20029 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).Temp;
6578 20029 : HPInletHumRat = state.dataWaterThermalTanks->mixerInletAirSchedule * state.dataLoopNodes->Node(OutdoorAirNode).HumRat +
6579 20029 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule) * state.dataLoopNodes->Node(HPAirInletNode).HumRat;
6580 20029 : break;
6581 : }
6582 239560 : case WTTAmbientTemp::OutsideAir: {
6583 239560 : state.dataWaterThermalTanks->mixerInletAirSchedule = 1.0;
6584 239560 : HPInletDryBulbTemp = state.dataLoopNodes->Node(OutdoorAirNode).Temp;
6585 239560 : HPInletHumRat = state.dataLoopNodes->Node(OutdoorAirNode).HumRat;
6586 :
6587 239560 : break;
6588 : }
6589 19314 : case WTTAmbientTemp::Schedule: {
6590 : HPInletDryBulbTemp =
6591 19314 : ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientTempSchedule);
6592 19314 : HPInletRelHum = ScheduleManager::GetCurrentScheduleValue(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).AmbientRHSchedule);
6593 19314 : HPInletHumRat = Psychrometrics::PsyWFnTdbRhPb(state, HPInletDryBulbTemp, HPInletRelHum, state.dataEnvrn->OutBaroPress, RoutineName);
6594 19314 : state.dataLoopNodes->Node(HPAirInletNode).Temp = HPInletDryBulbTemp;
6595 19314 : state.dataLoopNodes->Node(HPAirInletNode).HumRat = HPInletHumRat;
6596 19314 : state.dataLoopNodes->Node(HPAirInletNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6597 19314 : state.dataLoopNodes->Node(HPAirInletNode).Press = state.dataEnvrn->OutBaroPress;
6598 :
6599 19314 : break;
6600 : }
6601 0 : default:
6602 0 : assert(false);
6603 : break;
6604 : }
6605 :
6606 448736 : state.dataWaterThermalTanks->mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
6607 :
6608 : // set up initial conditions on nodes
6609 448736 : if (InletAirMixerNode > 0) {
6610 20029 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate = 0.0;
6611 20029 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6612 20029 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6613 20029 : state.dataLoopNodes->Node(InletAirMixerNode).Temp = HPInletDryBulbTemp;
6614 20029 : state.dataLoopNodes->Node(InletAirMixerNode).HumRat = HPInletHumRat;
6615 20029 : state.dataLoopNodes->Node(InletAirMixerNode).Enthalpy = Psychrometrics::PsyHFnTdbW(HPInletDryBulbTemp, HPInletHumRat);
6616 20029 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6617 20029 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6618 20029 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6619 20029 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6620 : } else {
6621 428707 : if (OutdoorAirNode == 0) {
6622 189147 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = 0.0;
6623 189147 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6624 189147 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6625 189147 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate = 0.0;
6626 : } else {
6627 239560 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate = 0.0;
6628 239560 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
6629 239560 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
6630 239560 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate = 0.0;
6631 : }
6632 : }
6633 :
6634 448736 : if (OutletAirSplitterNode > 0) state.dataLoopNodes->Node(OutletAirSplitterNode).MassFlowRate = 0.0;
6635 : // these are water nodes are not managed by plant. the HP connects
6636 : // directly to the WH without using plant.
6637 448736 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
6638 344781 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
6639 344781 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
6640 : }
6641 :
6642 : // set the max mass flow rate for outdoor fans
6643 448736 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6644 448736 : state.dataWaterThermalTanks->mdotAir;
6645 :
6646 : // Curve objects in DXCoils::CalcHPWHDXCoil will use inlet conditions to HPWH not inlet air conditions to DX Coil
6647 : // HPWHInletDBTemp and HPWHInletWBTemp are DataHVACGlobals to pass info to HPWHDXCoil
6648 448736 : state.dataHVACGlobal->HPWHInletDBTemp = HPInletDryBulbTemp;
6649 897472 : state.dataHVACGlobal->HPWHInletWBTemp =
6650 448736 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, HPInletHumRat, state.dataEnvrn->OutBaroPress);
6651 :
6652 : // initialize flow rates at speed levels for variable-speed HPWH
6653 458782 : if ((state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) &&
6654 10046 : (0 == state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) // use SCWH coil represents
6655 : {
6656 1 : IntegratedHeatPump::SizeIHP(state, state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum); //
6657 : // IntegratedHeatPump::SimIHP(modBlankString, HPWaterHeater(HPNum).DXCoilNum,
6658 : // 0, EMP1, EMP2, EMP3, 0, 0.0, 1, 0.0, 0.0, 0.0, false, 0.0); //conduct the sizing operation in the IHP
6659 1 : int VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6660 1 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6661 :
6662 448735 : } else if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
6663 547453 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") &&
6664 98718 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed == 0)) {
6665 6 : VariableSpeedCoils::SimVariableSpeedCoils(state,
6666 6 : std::string(),
6667 6 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
6668 : HVAC::FanOp::Invalid, // Invalid instead of off?
6669 : HVAC::CompressorOp::Off,
6670 : 0.0,
6671 : 1,
6672 : 0.0,
6673 : 0.0,
6674 : 0.0,
6675 : 0.0); // conduct the sizing operation in the VS WSHP
6676 6 : int VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6677 6 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NumOfSpeeds;
6678 : // below pass the flow rates from the VS coil to the water heater object
6679 : }
6680 :
6681 448736 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed > 0) {
6682 : int VSCoilID;
6683 108764 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP)
6684 10046 : VSCoilID = state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
6685 : else
6686 98718 : VSCoilID = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
6687 :
6688 : // scale air flow rates
6689 108764 : Real64 MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedAirVolFlowRate /
6690 108764 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(
6691 108764 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6692 1196404 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6693 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6694 1087640 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedAirVolFlowRate(Iter) * MulSpeedFlowScale;
6695 : }
6696 :
6697 : // check fan flow rate, should be larger than the max flow rate of the VS coil
6698 108764 : Real64 FanVolFlow = state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->maxAirFlowRate;
6699 :
6700 217528 : if (FanVolFlow < state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6701 108764 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)) { // but this is the not the scaled mas flow
6702 : // if ( FanVolFlow < HPWaterHeater( HPNum ).HPWHAirVolFlowRate( HPWaterHeater( HPNum ).NumofSpeed ) ) {
6703 :
6704 0 : ShowWarningError(state,
6705 0 : format("InitWaterThermalTank: -air flow rate = {:.7T} in fan object is less than the MSHP system air flow rate "
6706 : "when waterheating is required({:.7T}).",
6707 : FanVolFlow,
6708 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6709 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed)));
6710 0 : ShowContinueError(state,
6711 : " The MSHP system flow rate when waterheating is required is reset to the"
6712 : " fan flow rate and the simulation continues.");
6713 0 : ShowContinueError(state, format(" Occurs in {}", state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name));
6714 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed) =
6715 : FanVolFlow;
6716 : // Check flow rates in other speeds and ensure flow rates are not above the max flow rate
6717 0 : for (int Iter = state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed - 1; Iter >= 1; --Iter) {
6718 0 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) >
6719 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1)) {
6720 0 : ShowContinueError(state,
6721 0 : format(" The MSHP system flow rate when waterheating is required is reset to the flow rate at higher "
6722 : "speed and the simulation continues at Speed{}.",
6723 : Iter));
6724 0 : ShowContinueError(state, format(" Occurs in {}", state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name));
6725 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) =
6726 0 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter + 1);
6727 : }
6728 : }
6729 : }
6730 :
6731 1196404 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6732 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSAirSpeedRatio(Iter) =
6733 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) /
6734 2175280 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(
6735 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6736 : }
6737 :
6738 : // scale water flow rates
6739 108764 : MulSpeedFlowScale = state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).RatedWaterVolFlowRate /
6740 108764 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6741 108764 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).NormSpedLevel);
6742 1196404 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6743 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterVolFlowRate(Iter) =
6744 1087640 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) * MulSpeedFlowScale;
6745 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHWaterMassFlowRate(Iter) =
6746 1087640 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterMassFlowRate(Iter) * MulSpeedFlowScale;
6747 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).MSWaterSpeedRatio(Iter) =
6748 1087640 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(Iter) /
6749 2175280 : state.dataVariableSpeedCoils->VarSpeedCoil(VSCoilID).MSRatedWaterVolFlowRate(
6750 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6751 : }
6752 :
6753 108764 : Real64 rhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, HPInletDryBulbTemp, HPInletHumRat);
6754 :
6755 1196404 : for (int Iter = 1; Iter <= state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed; ++Iter) {
6756 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(Iter) =
6757 1087640 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirVolFlowRate(Iter) * rhoAir;
6758 : }
6759 :
6760 : // set the max mass flow rate for outdoor fans
6761 108764 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanOutletNode).MassFlowRateMax =
6762 108764 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHAirMassFlowRate(state.dataWaterThermalTanks->HPWaterHeater(HPNum).NumofSpeed);
6763 : }
6764 :
6765 : } // IF(WaterThermalTank(WaterThermalTankNum)%HeatPumpNum .GT. 0)THEN
6766 :
6767 : // calling CalcStandardRatings early bypasses fan sizing since DataSizing::DataNonZoneNonAirloopValue has not been set yet
6768 6284636 : if (!this->AlreadyRated) {
6769 293 : if (this->IsChilledWaterTank) {
6770 6 : this->AlreadyRated = true;
6771 : } else {
6772 391 : if (!state.dataGlobal->AnyPlantInModel || state.dataPlnt->PlantFirstSizesOkayToReport || this->MaxCapacity > 0.0 ||
6773 104 : this->HeatPumpNum > 0) {
6774 185 : this->CalcStandardRatings(state);
6775 : }
6776 : }
6777 : }
6778 6284636 : }
6779 :
6780 6523006 : void WaterThermalTankData::CalcWaterThermalTankMixed(EnergyPlusData &state) // Water Heater being simulated
6781 : {
6782 :
6783 : // SUBROUTINE INFORMATION:
6784 : // AUTHOR Peter Graham Ellis
6785 : // DATE WRITTEN January 2005
6786 : // MODIFIED na
6787 : // RE-ENGINEERED na
6788 :
6789 : // PURPOSE OF THIS SUBROUTINE:
6790 : // Simulates a well-mixed, single node water heater tank.
6791 :
6792 : // METHODOLOGY EMPLOYED:
6793 : // This model uses analytical calculations based on the differential equation describing the tank energy
6794 : // balance. The model operates in three different modes: heating, floating, and venting. Temperatures and
6795 : // energies change dynamically over the timestep. The final reported tank temperature is the average over
6796 : // the timestep. The final reported heat rates are averages based on the total energy transfer over the
6797 : // timestep.
6798 :
6799 : static constexpr std::string_view RoutineName("CalcWaterThermalTankMixed");
6800 :
6801 : Real64 TimeElapsed_loc =
6802 6523006 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
6803 :
6804 6523006 : if (this->TimeElapsed != TimeElapsed_loc) {
6805 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
6806 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
6807 589816 : this->SavedTankTemp = this->TankTemp;
6808 589816 : this->SavedMode = this->Mode;
6809 :
6810 : // Save outlet temperatures for demand-side flow control
6811 589816 : this->SavedUseOutletTemp = this->UseOutletTemp;
6812 589816 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
6813 :
6814 589816 : this->TimeElapsed = TimeElapsed_loc;
6815 : }
6816 :
6817 6523006 : Real64 TankTemp_loc = this->SavedTankTemp;
6818 6523006 : TankOperatingMode Mode_loc = this->SavedMode;
6819 :
6820 6523006 : Real64 Qmaxcap = this->MaxCapacity;
6821 6523006 : Real64 Qmincap = this->MinCapacity;
6822 6523006 : Real64 Qoffcycfuel = this->OffCycParaLoad;
6823 6523006 : Real64 Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
6824 6523006 : Real64 Qoncycfuel = this->OnCycParaLoad;
6825 6523006 : Real64 Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
6826 :
6827 6523006 : Real64 SetPointTemp_loc = this->SetPointTemp;
6828 6523006 : Real64 DeadBandTemp = this->getDeadBandTemp();
6829 6523006 : Real64 MaxTemp = this->TankTempLimit;
6830 6523006 : Real64 AmbientTemp_loc = this->AmbientTemp;
6831 :
6832 6523006 : Real64 UseInletTemp_loc = this->UseInletTemp;
6833 6523006 : Real64 UseMassFlowRate_loc = this->UseMassFlowRate * this->UseEffectiveness;
6834 6523006 : Real64 SourceInletTemp_loc = this->SourceInletTemp;
6835 6523006 : Real64 SourceMassFlowRate_loc = this->SourceMassFlowRate * this->SourceEffectiveness;
6836 :
6837 : Real64 rho;
6838 6523006 : if (this->UseSidePlantLoc.loopNum > 0) {
6839 5944106 : rho = FluidProperties::GetDensityGlycol(state,
6840 5944106 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6841 : TankTemp_loc,
6842 5944106 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6843 : RoutineName);
6844 : } else {
6845 578900 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, TankTemp_loc, this->waterIndex, RoutineName);
6846 : }
6847 :
6848 6523006 : Real64 TankMass = rho * this->Volume;
6849 :
6850 : Real64 Cp;
6851 6523006 : if (this->UseSidePlantLoc.loopNum > 0) {
6852 5944106 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
6853 5944106 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
6854 : TankTemp_loc,
6855 5944106 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
6856 : RoutineName);
6857 : } else {
6858 578900 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, TankTemp_loc, this->waterIndex, RoutineName);
6859 : }
6860 :
6861 6523006 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
6862 6523006 : Real64 TimeRemaining = SecInTimeStep;
6863 6523006 : int CycleOnCount_loc = 0;
6864 6523006 : int MaxCycles = SecInTimeStep;
6865 6523006 : Real64 Runtime = 0.0;
6866 6523006 : bool SetPointRecovered = false;
6867 :
6868 6523006 : Real64 Tsum = 0.0;
6869 6523006 : Real64 Eloss = 0.0;
6870 6523006 : Real64 Elosszone = 0.0;
6871 6523006 : Real64 Euse = 0.0;
6872 6523006 : Real64 Esource = 0.0;
6873 6523006 : Real64 Eheater = 0.0;
6874 6523006 : Real64 Event = 0.0;
6875 6523006 : Real64 Eneeded = 0.0;
6876 6523006 : Real64 Eunmet = 0.0;
6877 6523006 : Real64 Efuel = 0.0;
6878 6523006 : Real64 Eoncycfuel = 0.0;
6879 6523006 : Real64 Eoffcycfuel = 0.0;
6880 6523006 : Real64 PLR = 0.0;
6881 6523006 : Real64 PLRsum = 0.0;
6882 :
6883 6523006 : Real64 Qheat = 0.0;
6884 6523006 : Real64 Qheater = 0.0;
6885 6523006 : Real64 Qvent = 0.0;
6886 6523006 : Real64 Qneeded = 0.0;
6887 6523006 : Real64 Qunmet = 0.0;
6888 6523006 : Real64 Qfuel = 0.0;
6889 :
6890 : // Calculate the heating rate from the heat pump.
6891 6523006 : Real64 HPWHCondenserDeltaT = 0.0;
6892 :
6893 6523006 : if (this->HeatPumpNum > 0) {
6894 895707 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
6895 895707 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
6896 895707 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
6897 895707 : HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
6898 : }
6899 6523006 : assert(HPWHCondenserDeltaT >= 0);
6900 :
6901 : Real64 Qheatpump;
6902 : Real64 Qsource;
6903 6523006 : EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
6904 : HPWHCondenserDeltaT, SourceInletTemp_loc, Cp, SetPointTemp_loc, SourceMassFlowRate_loc, Qheatpump, Qsource);
6905 :
6906 : // Calculate steady-state use heat rate.
6907 6523006 : Real64 Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - SetPointTemp_loc);
6908 6523006 : Real64 Qloss = 0.0, PLF = 0.0;
6909 :
6910 24276701 : while (TimeRemaining > 0.0) {
6911 :
6912 17753695 : Real64 TimeNeeded = 0.0;
6913 :
6914 17753695 : Real64 NewTankTemp = TankTemp_loc;
6915 17753695 : Real64 LossCoeff_loc = 0.0;
6916 17753695 : Real64 LossFracToZone = 0.0;
6917 :
6918 17753695 : switch (Mode_loc) {
6919 :
6920 7892872 : case TankOperatingMode::Heating: // Heater is on
6921 :
6922 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
6923 7892872 : LossCoeff_loc = this->OnCycLossCoeff;
6924 7892872 : LossFracToZone = this->OnCycLossFracToZone;
6925 7892872 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
6926 7892872 : Qneeded = -Quse - Qsource - Qloss - Qoncycheat;
6927 :
6928 7892872 : if (TankTemp_loc > SetPointTemp_loc) {
6929 : // Heater is not needed after all, possibly due to step change in scheduled SetPointTemp
6930 :
6931 8 : Qheater = 0.0;
6932 8 : Qunmet = 0.0;
6933 8 : Mode_loc = TankOperatingMode::Floating;
6934 8 : continue;
6935 :
6936 7892864 : } else if (TankTemp_loc < SetPointTemp_loc) {
6937 : // Attempt to recover to the setpoint as quickly as possible by using maximum heater capacity
6938 :
6939 : // Qneeded is calculated above
6940 : // Qneeded does not account for the extra energy needed to recover to the setpoint
6941 4181777 : Qheater = Qmaxcap;
6942 4181777 : Qunmet = max(Qneeded - Qheater, 0.0);
6943 4181777 : Qheat = Qoncycheat + Qheater + Qheatpump;
6944 :
6945 : // Calculate time needed to recover to the setpoint at maximum heater capacity
6946 4181777 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
6947 : SetPointTemp_loc,
6948 : AmbientTemp_loc,
6949 : UseInletTemp_loc,
6950 : SourceInletTemp_loc,
6951 : TankMass,
6952 : Cp,
6953 : UseMassFlowRate_loc,
6954 : SourceMassFlowRate_loc,
6955 : LossCoeff_loc,
6956 : Qheat);
6957 :
6958 4181777 : if (TimeNeeded > TimeRemaining) {
6959 : // Heater is at maximum capacity and heats for all of the remaining time
6960 : // Setpoint temperature WILL NOT be recovered
6961 :
6962 522572 : TimeNeeded = TimeRemaining;
6963 :
6964 522572 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
6965 : AmbientTemp_loc,
6966 : UseInletTemp_loc,
6967 : SourceInletTemp_loc,
6968 : TankMass,
6969 : Cp,
6970 : UseMassFlowRate_loc,
6971 : SourceMassFlowRate_loc,
6972 : LossCoeff_loc,
6973 : Qheat,
6974 : TimeNeeded);
6975 :
6976 : } else { // TimeNeeded <= TimeRemaining
6977 : // Heater is at maximum capacity but will not heat for all of the remaining time (at maximum anyway)
6978 : // Setpoint temperature WILL be recovered
6979 :
6980 3659205 : NewTankTemp = SetPointTemp_loc;
6981 :
6982 3659205 : SetPointRecovered = true;
6983 :
6984 : } // TimeNeeded > TimeRemaining
6985 :
6986 : } else { // TankTemp == SetPointTemp
6987 : // Attempt to maintain the setpoint by using the needed heater capacity (modulating, if allowed)
6988 :
6989 3711087 : if (Qneeded <= 0.0) {
6990 : // Heater is not needed
6991 :
6992 9256 : Qneeded = 0.0;
6993 9256 : Qheater = 0.0;
6994 9256 : Qunmet = 0.0;
6995 9256 : Mode_loc = TankOperatingMode::Floating;
6996 9256 : continue;
6997 :
6998 3701831 : } else if (Qneeded < Qmincap) {
6999 : // Heater is required at less than the minimum capacity
7000 : // If cycling, Qmincap = Qmaxcap. Once the setpoint is reached, heater will almost always be shut off here
7001 :
7002 3649286 : if (this->ControlType == HeaterControlMode::Cycle) {
7003 : // Control will cycle on and off based on DeadBandTemp
7004 3649286 : Qheater = 0.0;
7005 3649286 : Qunmet = 0.0;
7006 3649286 : Mode_loc = TankOperatingMode::Floating;
7007 3649286 : continue;
7008 :
7009 0 : } else if (this->ControlType == HeaterControlMode::Modulate) {
7010 : // Control will cycle on and off based on DeadBandTemp until Qneeded > Qmincap again
7011 0 : Qheater = 0.0;
7012 0 : Qunmet = Qneeded;
7013 0 : Mode_loc = TankOperatingMode::Floating;
7014 0 : continue;
7015 :
7016 : // CASE (ControlTypeModulateWithOverheat) ! Not yet implemented
7017 : // Calculate time to reach steady-state temp; check for venting at MaxTemp limit
7018 : // Qheater = Qmincap
7019 :
7020 : // CASE (ControlTypeModulateWithUnderheat) ! Not yet implemented
7021 : // Heater must not come back on until Qneeded >= Qmincap
7022 : // Mode = modfloatMode
7023 : }
7024 :
7025 52545 : } else if (Qneeded <= Qmaxcap) {
7026 : // Heater can exactly meet the needed heat rate (usually by modulating) and heats for all of the remaining time
7027 : // Setpoint temperature WILL be maintained
7028 :
7029 52503 : TimeNeeded = TimeRemaining;
7030 :
7031 52503 : Qheater = Qneeded;
7032 52503 : Qunmet = 0.0;
7033 :
7034 52503 : NewTankTemp = SetPointTemp_loc;
7035 :
7036 : } else { // Qneeded > Qmaxcap
7037 : // Heater is at maximum capacity and heats for all of the remaining time
7038 : // Setpoint temperature WILL NOT be maintained
7039 :
7040 42 : TimeNeeded = TimeRemaining;
7041 :
7042 42 : Qheater = Qmaxcap;
7043 42 : Qunmet = Qneeded - Qheater;
7044 42 : Qheat = Qoncycheat + Qheater + Qheatpump;
7045 :
7046 42 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7047 : AmbientTemp_loc,
7048 : UseInletTemp_loc,
7049 : SourceInletTemp_loc,
7050 : TankMass,
7051 : Cp,
7052 : UseMassFlowRate_loc,
7053 : SourceMassFlowRate_loc,
7054 : LossCoeff_loc,
7055 : Qheat,
7056 : TimeNeeded);
7057 :
7058 : } // Qneeded > Qmaxcap
7059 :
7060 : } // TankTemp > SetPointTemp
7061 :
7062 : // Update summed values
7063 4234322 : Eneeded += Qneeded * TimeNeeded;
7064 4234322 : Eheater += Qheater * TimeNeeded;
7065 4234322 : Eunmet += Qunmet * TimeNeeded;
7066 4234322 : Eoncycfuel += Qoncycfuel * TimeNeeded;
7067 :
7068 4234322 : if (Qmaxcap > 0.0) PLR = Qheater / Qmaxcap;
7069 4234322 : PLF = this->PartLoadFactor(state, PLR);
7070 4234322 : Efuel += Qheater * TimeNeeded / (PLF * this->Efficiency);
7071 :
7072 4234322 : Runtime += TimeNeeded;
7073 4234322 : PLRsum += PLR * TimeNeeded;
7074 :
7075 4234322 : if (!this->FirstRecoveryDone) {
7076 107362 : this->FirstRecoveryFuel += Efuel + Eoffcycfuel + Eoncycfuel;
7077 107362 : if (SetPointRecovered) this->FirstRecoveryDone = true;
7078 : }
7079 4234322 : break;
7080 :
7081 9734343 : case TankOperatingMode::Floating:
7082 : case TankOperatingMode::Cooling: // Heater is off
7083 :
7084 : // Calculate heat rate needed to maintain the setpoint at steady-state conditions
7085 9734343 : LossCoeff_loc = this->OffCycLossCoeff;
7086 9734343 : LossFracToZone = this->OffCycLossFracToZone;
7087 9734343 : Qloss = LossCoeff_loc * (AmbientTemp_loc - SetPointTemp_loc);
7088 9734343 : Qneeded = -Quse - Qsource - Qloss - Qoffcycheat;
7089 :
7090 : // This section really needs to work differently depending on ControlType
7091 : // CYCLE will look at TankTemp, MODULATE will look at Qneeded
7092 :
7093 9734343 : if ((TankTemp_loc < DeadBandTemp) && (!this->IsChilledWaterTank)) {
7094 : // Tank temperature is already below the minimum, possibly due to step change in scheduled SetPointTemp
7095 :
7096 445 : Mode_loc = TankOperatingMode::Heating;
7097 445 : ++CycleOnCount_loc;
7098 445 : continue;
7099 :
7100 9733898 : } else if ((TankTemp_loc >= DeadBandTemp) && (!this->IsChilledWaterTank)) {
7101 :
7102 9336118 : Qheat = Qoffcycheat + Qheatpump;
7103 :
7104 : // Calculate time needed for tank temperature to fall to minimum (setpoint - deadband)
7105 9336118 : TimeNeeded = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTimeNeeded(TankTemp_loc,
7106 : DeadBandTemp,
7107 : AmbientTemp_loc,
7108 : UseInletTemp_loc,
7109 : SourceInletTemp_loc,
7110 : TankMass,
7111 : Cp,
7112 : UseMassFlowRate_loc,
7113 : SourceMassFlowRate_loc,
7114 : LossCoeff_loc,
7115 : Qheat);
7116 :
7117 9336118 : if (TimeNeeded <= TimeRemaining) {
7118 : // Heating will be needed in this DataGlobals::TimeStep
7119 :
7120 3852515 : NewTankTemp = DeadBandTemp;
7121 3852515 : Mode_loc = TankOperatingMode::Heating;
7122 3852515 : ++CycleOnCount_loc;
7123 :
7124 : } else { // TimeNeeded > TimeRemaining
7125 : // Heating will not be needed for all of the remaining time
7126 :
7127 5483603 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7128 : AmbientTemp_loc,
7129 : UseInletTemp_loc,
7130 : SourceInletTemp_loc,
7131 : TankMass,
7132 : Cp,
7133 : UseMassFlowRate_loc,
7134 : SourceMassFlowRate_loc,
7135 : LossCoeff_loc,
7136 : Qheat,
7137 : TimeRemaining);
7138 :
7139 5483603 : if ((NewTankTemp < MaxTemp) || (this->IsChilledWaterTank)) {
7140 : // Neither heating nor venting is needed for all of the remaining time
7141 :
7142 5464756 : TimeNeeded = TimeRemaining;
7143 :
7144 : } else { // NewTankTemp >= MaxTemp
7145 : // Venting will be needed in this DataGlobals::TimeStep
7146 :
7147 : // Calculate time needed for tank temperature to rise to the maximum
7148 18847 : TimeNeeded = CalcTimeNeeded(TankTemp_loc,
7149 : MaxTemp,
7150 : AmbientTemp_loc,
7151 : UseInletTemp_loc,
7152 : SourceInletTemp_loc,
7153 : TankMass,
7154 : Cp,
7155 : UseMassFlowRate_loc,
7156 : SourceMassFlowRate_loc,
7157 : LossCoeff_loc,
7158 : Qheat);
7159 :
7160 : // if limit NewTankTemp >= MaxTemp
7161 18847 : if (TankTemp_loc >= MaxTemp) {
7162 0 : TimeNeeded = TimeRemaining;
7163 : }
7164 18847 : NewTankTemp = MaxTemp;
7165 18847 : Mode_loc = TankOperatingMode::Venting;
7166 :
7167 : } // NewTankTemp >= MaxTemp
7168 :
7169 : } // TimeNeeded <= TimeRemaining
7170 :
7171 397780 : } else if ((TankTemp_loc > DeadBandTemp) && (this->IsChilledWaterTank)) {
7172 78670 : Mode_loc = TankOperatingMode::Cooling;
7173 78670 : Qheat = Qheatpump;
7174 :
7175 78670 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7176 : AmbientTemp_loc,
7177 : UseInletTemp_loc,
7178 : SourceInletTemp_loc,
7179 : TankMass,
7180 : Cp,
7181 : UseMassFlowRate_loc,
7182 : SourceMassFlowRate_loc,
7183 : LossCoeff_loc,
7184 : Qheat,
7185 : TimeRemaining);
7186 78670 : TimeNeeded = TimeRemaining;
7187 319110 : } else if ((TankTemp_loc <= DeadBandTemp) && (this->IsChilledWaterTank)) {
7188 :
7189 319110 : if (TankTemp_loc < SetPointTemp_loc) Mode_loc = TankOperatingMode::Floating;
7190 :
7191 319110 : Qheat = Qheatpump;
7192 :
7193 319110 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7194 : AmbientTemp_loc,
7195 : UseInletTemp_loc,
7196 : SourceInletTemp_loc,
7197 : TankMass,
7198 : Cp,
7199 : UseMassFlowRate_loc,
7200 : SourceMassFlowRate_loc,
7201 : LossCoeff_loc,
7202 : Qheat,
7203 : TimeRemaining);
7204 319110 : TimeNeeded = TimeRemaining;
7205 : } // TankTemp vs DeadBandTemp for heaters and chilled water tanks
7206 :
7207 : // Update summed values
7208 9733898 : Eneeded += Qneeded * TimeNeeded;
7209 9733898 : Eunmet += Qunmet * TimeNeeded; // Qunmet may be propagated thru from the previous iteration
7210 9733898 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7211 9733898 : break;
7212 :
7213 126480 : case TankOperatingMode::Venting: // Excess heat is vented
7214 :
7215 126480 : LossCoeff_loc = this->OffCycLossCoeff;
7216 126480 : LossFracToZone = this->OffCycLossFracToZone;
7217 126480 : Qheat = Qoffcycheat + Qheatpump;
7218 :
7219 126480 : NewTankTemp = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTankTemp(TankTemp_loc,
7220 : AmbientTemp_loc,
7221 : UseInletTemp_loc,
7222 : SourceInletTemp_loc,
7223 : TankMass,
7224 : Cp,
7225 : UseMassFlowRate_loc,
7226 : SourceMassFlowRate_loc,
7227 : LossCoeff_loc,
7228 : Qheat,
7229 : TimeRemaining);
7230 :
7231 126480 : if (NewTankTemp < MaxTemp) {
7232 : // Venting is no longer needed because conditions have changed
7233 :
7234 41127 : Mode_loc = TankOperatingMode::Floating;
7235 41127 : continue;
7236 :
7237 : } else { // NewTankTemp >= MaxTemp
7238 :
7239 85353 : TimeNeeded = TimeRemaining;
7240 :
7241 : // Calculate the steady-state venting rate needed to maintain the tank at maximum temperature
7242 85353 : Real64 Qloss = LossCoeff_loc * (AmbientTemp_loc - MaxTemp);
7243 85353 : Quse = UseMassFlowRate_loc * Cp * (UseInletTemp_loc - MaxTemp);
7244 85353 : Qsource = SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc - MaxTemp);
7245 85353 : Qvent = -Quse - Qsource - Qloss - Qoffcycheat;
7246 :
7247 85353 : NewTankTemp = MaxTemp;
7248 :
7249 : } // NewTankTemp < MaxTemp
7250 :
7251 : // Update summed values
7252 85353 : Event += Qvent * TimeNeeded;
7253 85353 : Eoffcycfuel += Qoffcycfuel * TimeNeeded;
7254 85353 : break;
7255 0 : default:
7256 0 : assert(false); // should never get here
7257 : }
7258 :
7259 14053573 : Real64 deltaTsum = EnergyPlus::WaterThermalTanks::WaterThermalTankData::CalcTempIntegral(TankTemp_loc,
7260 : NewTankTemp,
7261 : AmbientTemp_loc,
7262 : UseInletTemp_loc,
7263 : SourceInletTemp_loc,
7264 : TankMass,
7265 : Cp,
7266 : UseMassFlowRate_loc,
7267 : SourceMassFlowRate_loc,
7268 : LossCoeff_loc,
7269 : Qheat,
7270 : TimeNeeded);
7271 :
7272 : // Update summed values
7273 14053573 : Tsum += deltaTsum;
7274 14053573 : Eloss += LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7275 14053573 : Elosszone += LossFracToZone * LossCoeff_loc * (AmbientTemp_loc * TimeNeeded - deltaTsum);
7276 14053573 : Euse += UseMassFlowRate_loc * Cp * (UseInletTemp_loc * TimeNeeded - deltaTsum);
7277 14053573 : if (this->HeatPumpNum > 0) {
7278 1004358 : Esource += Qheatpump * TimeNeeded;
7279 : } else {
7280 13049215 : Esource += SourceMassFlowRate_loc * Cp * (SourceInletTemp_loc * TimeNeeded - deltaTsum);
7281 : }
7282 :
7283 14053573 : TankTemp_loc = NewTankTemp; // Update tank temperature
7284 :
7285 14053573 : TimeRemaining -= TimeNeeded;
7286 :
7287 14053573 : if (CycleOnCount_loc > MaxCycles) {
7288 :
7289 0 : if (!state.dataGlobal->WarmupFlag) {
7290 0 : if (this->MaxCycleErrorIndex == 0) {
7291 0 : ShowWarningError(state, format("WaterHeater:Mixed = {}: Heater is cycling on and off more than once per second.", this->Name));
7292 0 : ShowContinueError(state, "Try increasing Deadband Temperature Difference or Tank Volume");
7293 0 : ShowContinueErrorTimeStamp(state, "");
7294 : }
7295 0 : ShowRecurringWarningErrorAtEnd(state,
7296 0 : "WaterHeater:Mixed = " + this->Name + " Heater is cycling on and off more than once per second:",
7297 0 : this->MaxCycleErrorIndex);
7298 : }
7299 :
7300 0 : break;
7301 :
7302 : } // CycleOnCount > MaxCycles
7303 :
7304 : } // TimeRemaining > 0.0
7305 :
7306 : // 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
7307 6523006 : Real64 TankTempAvg_loc = Tsum / SecInTimeStep;
7308 6523006 : Qloss = Eloss / SecInTimeStep;
7309 6523006 : Real64 Qlosszone = Elosszone / SecInTimeStep;
7310 6523006 : Quse = Euse / SecInTimeStep;
7311 6523006 : Qsource = Esource / SecInTimeStep;
7312 6523006 : Qheater = Eheater / SecInTimeStep;
7313 6523006 : Qoffcycfuel = Eoffcycfuel / SecInTimeStep;
7314 6523006 : Qoffcycheat = Qoffcycfuel * this->OffCycParaFracToTank;
7315 6523006 : Qoncycfuel = Eoncycfuel / SecInTimeStep;
7316 6523006 : Qoncycheat = Qoncycfuel * this->OnCycParaFracToTank;
7317 6523006 : Qvent = Event / SecInTimeStep;
7318 6523006 : Qneeded = Eneeded / SecInTimeStep;
7319 6523006 : Qunmet = Eunmet / SecInTimeStep;
7320 6523006 : Real64 RTF = Runtime / SecInTimeStep;
7321 6523006 : PLR = PLRsum / SecInTimeStep;
7322 :
7323 6523006 : if (this->ControlType == HeaterControlMode::Cycle) {
7324 : // Recalculate Part Load Factor and fuel energy based on Runtime Fraction, instead of Part Load Ratio
7325 6469218 : PLF = this->PartLoadFactor(state, RTF);
7326 6469218 : Efuel = Eheater / (PLF * this->Efficiency);
7327 : }
7328 :
7329 6523006 : Qfuel = Efuel / SecInTimeStep;
7330 :
7331 6523006 : this->Mode = Mode_loc; // Operating mode for carry-over to next DataGlobals::TimeStep
7332 6523006 : this->TankTemp = TankTemp_loc; // Final tank temperature for carry-over to next DataGlobals::TimeStep
7333 6523006 : this->TankTempAvg = TankTempAvg_loc; // Average tank temperature over the DataGlobals::TimeStep for reporting
7334 :
7335 6523006 : if (!state.dataGlobal->WarmupFlag) {
7336 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
7337 872188 : if (this->TankTemp < 2) {
7338 0 : if (this->FreezingErrorIndex == 0) {
7339 0 : ShowWarningError(state,
7340 0 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
7341 : RoutineName,
7342 0 : this->Type,
7343 0 : this->Name,
7344 0 : this->TankTemp));
7345 0 : ShowContinueErrorTimeStamp(state, "");
7346 : }
7347 0 : ShowRecurringWarningErrorAtEnd(state,
7348 0 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
7349 0 : this->FreezingErrorIndex,
7350 0 : this->TankTemp, // Report Max
7351 0 : this->TankTemp, // Report Min
7352 : _, // Don't report Sum
7353 : "{C}", // Max Unit
7354 : "{C}"); // Min Unit
7355 : }
7356 : }
7357 6523006 : this->UseOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7358 6523006 : this->SourceOutletTemp = TankTempAvg_loc; // Because entire tank is at same temperature
7359 6523006 : if (this->HeatPumpNum > 0) {
7360 895707 : this->SourceInletTemp =
7361 895707 : TankTempAvg_loc + HPWHCondenserDeltaT; // Update the source inlet temperature to the average over the DataGlobals::TimeStep
7362 : }
7363 :
7364 6523006 : this->LossRate = Qloss;
7365 6523006 : this->UseRate = Quse;
7366 6523006 : this->SourceRate = Qsource;
7367 6523006 : this->OffCycParaRateToTank = Qoffcycheat;
7368 6523006 : this->OnCycParaRateToTank = Qoncycheat;
7369 6523006 : this->TotalDemandRate = -Quse - Qsource - Qloss - Qoffcycheat - Qoncycheat;
7370 6523006 : this->HeaterRate = Qheater;
7371 6523006 : this->UnmetRate = Qunmet;
7372 6523006 : this->VentRate = Qvent;
7373 6523006 : this->NetHeatTransferRate = Quse + Qsource + Qloss + Qoffcycheat + Qoncycheat + Qheater + Qvent;
7374 :
7375 6523006 : this->CycleOnCount = CycleOnCount_loc;
7376 6523006 : this->RuntimeFraction = RTF;
7377 6523006 : this->PartLoadRatio = PLR;
7378 :
7379 6523006 : this->FuelRate = Qfuel;
7380 6523006 : this->OffCycParaFuelRate = Qoffcycfuel;
7381 6523006 : this->OnCycParaFuelRate = Qoncycfuel;
7382 :
7383 : // Add water heater skin losses and venting losses to ambient zone, if specified
7384 6523006 : if (this->AmbientTempZone > 0) this->AmbientZoneGain = -Qlosszone - Qvent;
7385 6523006 : }
7386 :
7387 6523006 : void WaterThermalTankData::CalcMixedTankSourceSideHeatTransferRate(
7388 : Real64 HPWHCondenserDeltaT, // input, The temperature difference (C) across the heat pump, zero if
7389 : // there is no heat pump or if the heat pump is off
7390 : Real64 SourceInletTemp, // input, Source inlet temperature (C)
7391 : Real64 Cp, // Specific heat of fluid (J/kg deltaC)
7392 : Real64 SetPointTemp, // input, Mixed tank set point temperature
7393 : Real64 &SourceMassFlowRate, // source mass flow rate (kg/s)
7394 : Real64 &Qheatpump, // heat transfer rate from heat pump
7395 : Real64 &Qsource // steady state heat transfer rate from a constant temperature source side flow
7396 : )
7397 : {
7398 : // Function Information:
7399 : // Author: Noel Merket
7400 : // Date Written: January 2015
7401 : // Modified: na
7402 : // Re-engineered: na
7403 :
7404 : // Purpose of this function:
7405 : // Determines if the source side heat transfer is coming from a heat pump.
7406 : // If so it treats the source side heat transfer as a constant heat source
7407 : // If it is not coming from a heat pump it treats the source side heat transfer
7408 : // as a constant temperature.
7409 :
7410 : // Determine if the source side heating is coming from a heat pump.
7411 6523006 : Qheatpump = SourceMassFlowRate * Cp * HPWHCondenserDeltaT;
7412 6523006 : if (Qheatpump > 0.0) {
7413 580371 : SourceMassFlowRate = 0.0; // Handle this heating as a constant heat source
7414 580371 : Qsource = Qheatpump;
7415 : } else {
7416 5942635 : Qsource = SourceMassFlowRate * Cp * (SourceInletTemp - SetPointTemp);
7417 : }
7418 6523006 : }
7419 :
7420 13536742 : Real64 WaterThermalTankData::CalcTimeNeeded(Real64 const Ti, // Initial tank temperature (C)
7421 : Real64 const Tf, // Final tank temperature (C)
7422 : Real64 const Ta, // Ambient environment temperature (C)
7423 : Real64 const T1, // Temperature of flow 1 (C)
7424 : Real64 const T2, // Temperature of flow 2 (C)
7425 : Real64 const m, // Mass of tank fluid (kg)
7426 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7427 : Real64 const m1, // Mass flow rate 1 (kg/s)
7428 : Real64 const m2, // Mass flow rate 2 (kg/s)
7429 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7430 : Real64 const Q // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7431 : )
7432 : {
7433 :
7434 : // SUBROUTINE INFORMATION:
7435 : // AUTHOR Peter Graham Ellis
7436 : // DATE WRITTEN February 2005
7437 : // MODIFIED na
7438 : // RE-ENGINEERED na
7439 :
7440 : // PURPOSE OF THIS SUBROUTINE:
7441 : // Calculates the time needed for the tank temperature to change from Ti to Tf given heat loss,
7442 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7443 :
7444 : // METHODOLOGY EMPLOYED:
7445 : // Equations are derived by solving the differential equation governing the tank energy balance.
7446 : // Special cases which cause the natural logarithm to blow up are trapped and interpreted as
7447 : // requiring an infinite amount of time because Tf can never be reached under the given conditions.
7448 :
7449 : // Return value
7450 : Real64 CalcTimeNeeded;
7451 :
7452 : // SUBROUTINE PARAMETER DEFINITIONS:
7453 13536742 : Real64 constexpr Infinity(99999999.9); // A time interval much larger than any single DataGlobals::TimeStep (s)
7454 :
7455 : Real64 t; // Time elapsed from Ti to Tf (s)
7456 :
7457 13536742 : if (Tf == Ti) {
7458 : // Already at Tf; no time is needed
7459 0 : t = 0.0;
7460 :
7461 : } else {
7462 : Real64 a; // Intermediate variable
7463 : Real64 b; // Intermediate variable
7464 : Real64 Tm; // Mixed temperature after an infinite amount of time has passed (C)
7465 : Real64 quotient; // Intermediate variable
7466 :
7467 13536742 : if (UA / Cp + m1 + m2 == 0.0) {
7468 :
7469 23269 : if (Q == 0.0) {
7470 : // With no mass flow and no heat flow and Tf<>Ti, then Tf can never be reached
7471 22396 : t = Infinity;
7472 :
7473 : } else {
7474 873 : a = Q / (m * Cp);
7475 :
7476 873 : t = (Tf - Ti) / a;
7477 : }
7478 :
7479 : } else {
7480 13513473 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7481 13513473 : b = -(UA / Cp + m1 + m2) / m;
7482 :
7483 : // Calculate the mixed temperature Tm of the tank after an infinite amount of time has passed
7484 13513473 : Tm = -a / b;
7485 :
7486 13513473 : if (Tm == Ti) {
7487 : // Mixed temperature is the same as Ti; if Tf<>Ti, then Tf can never be reached
7488 1538 : t = Infinity;
7489 :
7490 13511935 : } else if (Tm == Tf) {
7491 : // Tf only approaches Tm; it can never actually get there in finite time (also avoids divide by zero error)
7492 0 : t = Infinity;
7493 :
7494 : } else {
7495 13511935 : quotient = (Tf - Tm) / (Ti - Tm);
7496 :
7497 13511935 : if (quotient <= 0.0) { // Autodesk:Num Changed < to <= to elim poss floating point error in LOG call
7498 : // Tm is in between Ti and Tf; Tf can never be reached
7499 558190 : t = Infinity;
7500 :
7501 : } else {
7502 12953745 : t = std::log(quotient) / b;
7503 : }
7504 : }
7505 : }
7506 :
7507 13536742 : if (t < 0.0) t = Infinity; // If negative time, Tf can never be reached in the future
7508 : }
7509 :
7510 13536742 : CalcTimeNeeded = t;
7511 :
7512 13536742 : return CalcTimeNeeded;
7513 : }
7514 :
7515 6547252 : Real64 WaterThermalTankData::CalcTankTemp(Real64 const Ti, // Initial tank temperature (C)
7516 : Real64 const Ta, // Ambient environment temperature (C)
7517 : Real64 const T1, // Temperature of flow 1 (C)
7518 : Real64 const T2, // Temperature of flow 2 (C)
7519 : Real64 const m, // Mass of tank fluid (kg)
7520 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7521 : Real64 const m1, // Mass flow rate 1 (kg/s)
7522 : Real64 const m2, // Mass flow rate 2 (kg/s)
7523 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7524 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7525 : Real64 const t // Time elapsed from Ti to Tf (s)
7526 : )
7527 : {
7528 :
7529 : // SUBROUTINE INFORMATION:
7530 : // AUTHOR Peter Graham Ellis
7531 : // DATE WRITTEN February 2005
7532 : // MODIFIED na
7533 : // RE-ENGINEERED na
7534 :
7535 : // PURPOSE OF THIS SUBROUTINE:
7536 : // Calculates the final tank temperature Tf after time t has elapsed given heat loss,
7537 : // mass flow rates and temperatures, and net heat transfer due to heater and parasitics.
7538 :
7539 : // METHODOLOGY EMPLOYED:
7540 : // Equations are derived by solving the differential equation governing the tank energy balance.
7541 :
7542 : // Return value
7543 : Real64 CalcTankTemp;
7544 :
7545 : // Locals
7546 : // SUBROUTINE ARGUMENT DEFINITIONS:
7547 :
7548 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7549 : Real64 a; // Intermediate variable
7550 : Real64 b; // Intermediate variable
7551 : Real64 Tf; // Final tank temperature (C)
7552 :
7553 6547252 : if (UA / Cp + m1 + m2 == 0.0) {
7554 23197 : a = Q / (m * Cp);
7555 :
7556 23197 : Tf = a * t + Ti;
7557 :
7558 : } else {
7559 6524055 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7560 6524055 : b = -(UA / Cp + m1 + m2) / m;
7561 :
7562 6524055 : Tf = (a / b + Ti) * std::exp(b * t) - a / b;
7563 : }
7564 :
7565 6547252 : CalcTankTemp = Tf;
7566 :
7567 6547252 : return CalcTankTemp;
7568 : }
7569 :
7570 14070348 : Real64 WaterThermalTankData::CalcTempIntegral(Real64 const Ti, // Initial tank temperature (C)
7571 : Real64 const Tf, // Final tank temperature (C)
7572 : Real64 const Ta, // Ambient environment temperature (C)
7573 : Real64 const T1, // Temperature of flow 1 (C)
7574 : Real64 const T2, // Temperature of flow 2 (C)
7575 : Real64 const m, // Mass of tank fluid (kg)
7576 : Real64 const Cp, // Specific heat of fluid (J/kg deltaC)
7577 : Real64 const m1, // Mass flow rate 1 (kg/s)
7578 : Real64 const m2, // Mass flow rate 2 (kg/s)
7579 : Real64 const UA, // Heat loss coefficient to ambient environment (W/deltaC)
7580 : Real64 const Q, // Net heating rate for non-temp dependent sources, i.e. heater and parasitics (W)
7581 : Real64 const t // Time elapsed from Ti to Tf (s)
7582 : )
7583 : {
7584 :
7585 : // SUBROUTINE INFORMATION:
7586 : // AUTHOR Peter Graham Ellis
7587 : // DATE WRITTEN February 2005
7588 : // MODIFIED na
7589 : // RE-ENGINEERED na
7590 :
7591 : // PURPOSE OF THIS SUBROUTINE:
7592 : // Calculates the integral of the tank temperature from Ti to Tf. The integral is added to a sum which is
7593 : // later divided by the elapsed time to yield the average tank temperature over the DataGlobals::TimeStep.
7594 :
7595 : // METHODOLOGY EMPLOYED:
7596 : // Equations are the mathematical integrals of the governing differential equations.
7597 :
7598 : // Return value
7599 : Real64 CalcTempIntegral;
7600 :
7601 : // Locals
7602 : // SUBROUTINE ARGUMENT DEFINITIONS:
7603 :
7604 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7605 : Real64 a; // Intermediate variable
7606 : Real64 b; // Intermediate variable
7607 : Real64 dTsum; // Integral of tank temperature (C s)
7608 :
7609 14070348 : if (t == 0.0) {
7610 0 : dTsum = 0.0;
7611 :
7612 14070348 : } else if (Tf == Ti) { // Steady-state conditions
7613 179424 : dTsum = Tf * t;
7614 :
7615 13890924 : } else if (UA / Cp + m1 + m2 == 0.0) {
7616 873 : a = Q / (m * Cp);
7617 :
7618 : // Integral of T(t) = a * t + Ti, evaluated from 0 to t
7619 873 : dTsum = 0.5 * a * t * t + Ti * t;
7620 :
7621 : } else {
7622 13890051 : a = (Q / Cp + UA * Ta / Cp + m1 * T1 + m2 * T2) / m;
7623 13890051 : b = -(UA / Cp + m1 + m2) / m;
7624 :
7625 : // Integral of T(t) = (a / b + Ti) * EXP(b * t) - a / b, evaluated from 0 to t
7626 13890051 : dTsum = (a / b + Ti) * (std::exp(b * t) - 1.0) / b - a * t / b;
7627 : }
7628 :
7629 14070348 : CalcTempIntegral = dTsum;
7630 :
7631 14070348 : return CalcTempIntegral;
7632 : }
7633 :
7634 10703540 : Real64 WaterThermalTankData::PartLoadFactor(EnergyPlusData &state, Real64 const PartLoadRatio_loc)
7635 : {
7636 :
7637 : // SUBROUTINE INFORMATION:
7638 : // AUTHOR Peter Graham Ellis
7639 : // DATE WRITTEN January 2005
7640 : // MODIFIED na
7641 : // RE-ENGINEERED na
7642 :
7643 : // PURPOSE OF THIS SUBROUTINE:
7644 : // Calculates the Part Load Factor (PLF) based on a curve correlated to Part Load Ratio, if Heater Control Type
7645 : // is MODULATE, or correlated to Runtime Fraction, if Heater Control Type is CYCLE.
7646 :
7647 10703540 : if (this->PLFCurve > 0) {
7648 1260 : return max(Curve::CurveValue(state, this->PLFCurve, PartLoadRatio_loc), 0.1);
7649 : } else {
7650 10702280 : return 1.0;
7651 : }
7652 : }
7653 :
7654 1164484 : void WaterThermalTankData::CalcWaterThermalTankStratified(EnergyPlusData &state)
7655 : {
7656 : // SUBROUTINE INFORMATION:
7657 : // AUTHOR Noel Merket, originally by Peter Graham Ellis
7658 : // DATE WRITTEN January 2007
7659 : // MODIFIED Nov 2011, BAN; modified the use and source outlet temperature calculation
7660 : // RE-ENGINEERED Noel Merket, November 2018
7661 :
7662 : // PURPOSE OF THIS SUBROUTINE:
7663 : // Simulates a stratified, multi-node water heater tank with up to two heating elements.
7664 :
7665 : // METHODOLOGY EMPLOYED:
7666 : // This model uses a numerical calculation based on an analytical solution of the ODE dT/dt = a*T + b.
7667 : // A heat balance is calculated for each node.
7668 : // Temperatures and energies change dynamically over the system time step.
7669 : // Final node temperatures are reported as final instantaneous values as well as averages over the
7670 : // time step. Heat transfer rates are averages over the time step.
7671 :
7672 : static constexpr std::string_view RoutineName("CalcWaterThermalTankStratified");
7673 1164484 : constexpr Real64 TemperatureConvergenceCriteria = 0.0001;
7674 1164484 : constexpr Real64 SubTimestepMax = 60.0 * 10.0; // seconds
7675 1164484 : constexpr Real64 SubTimestepMin = 10.0; // seconds
7676 : Real64 dt;
7677 :
7678 : // Tank object reference
7679 1164484 : const Real64 &nTankNodes = this->Nodes;
7680 :
7681 : // Fraction of the current hour that has elapsed (h)
7682 : const Real64 TimeElapsed_loc =
7683 1164484 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
7684 :
7685 : // Seconds in one DataGlobals::TimeStep (s)
7686 1164484 : const Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
7687 :
7688 : // Advance tank simulation to the next system DataGlobals::TimeStep, if applicable
7689 1164484 : if (this->TimeElapsed != TimeElapsed_loc) {
7690 : // The simulation has advanced to the next system DataGlobals::TimeStep. Save conditions from the end of the previous system
7691 : // DataGlobals::TimeStep for use as the initial conditions of each iteration that does not advance the system DataGlobals::TimeStep.
7692 413556 : for (auto &e : this->Node)
7693 367540 : e.SavedTemp = e.Temp;
7694 :
7695 46016 : this->SavedHeaterOn1 = this->HeaterOn1;
7696 46016 : this->SavedHeaterOn2 = this->HeaterOn2;
7697 :
7698 : // Save outlet temperatures for demand-side flow control
7699 46016 : this->SavedUseOutletTemp = this->UseOutletTemp;
7700 46016 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
7701 :
7702 46016 : this->TimeElapsed = TimeElapsed_loc;
7703 : }
7704 :
7705 : // Reset node temperatures to what they were at the beginning of the system DataGlobals::TimeStep.
7706 9387118 : for (auto &e : this->Node)
7707 8222634 : e.Temp = e.SavedTemp;
7708 :
7709 1164484 : this->HeaterOn1 = this->SavedHeaterOn1;
7710 1164484 : this->HeaterOn2 = this->SavedHeaterOn2;
7711 :
7712 : // Condenser configuration of heat pump water heater
7713 : const DataPlant::PlantEquipmentType HPWHCondenserConfig =
7714 1164484 : this->HeatPumpNum > 0 ? state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType : DataPlant::PlantEquipmentType::Invalid;
7715 :
7716 : // Heat rate from the heat pump (W)
7717 2964868 : const Real64 Qheatpump = [&, this] { // BLB
7718 1164484 : if (this->HeatPumpNum == 0) return 0.0;
7719 900192 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
7720 : Real64 CoilTotalHeatingEnergyRate;
7721 900192 : if (HPWH.NumofSpeed > 0) {
7722 : // VSHPWH
7723 70625 : VariableSpeedCoils::VariableSpeedCoilData const &Coil = state.dataVariableSpeedCoils->VarSpeedCoil(HPWH.DXCoilNum);
7724 70625 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7725 : } else {
7726 : // Single speed HPWH
7727 829567 : DXCoils::DXCoilData const &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
7728 829567 : CoilTotalHeatingEnergyRate = Coil.TotalHeatingEnergyRate;
7729 : }
7730 900192 : return CoilTotalHeatingEnergyRate * this->SourceEffectiveness;
7731 1164484 : }();
7732 :
7733 : // Minimum tank temperatures
7734 1164484 : const Real64 MinTemp1 = this->SetPointTemp - this->DeadBandDeltaTemp;
7735 1164484 : const Real64 MinTemp2 = this->SetPointTemp2 - this->DeadBandDeltaTemp2;
7736 :
7737 : // Specific Heat of water (J/kg K)
7738 1164484 : const Real64 Cp = [&] {
7739 1164484 : if (this->UseSidePlantLoc.loopNum > 0) {
7740 1093285 : return FluidProperties::GetSpecificHeatGlycol(state,
7741 1093285 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
7742 : this->TankTemp,
7743 1093285 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
7744 1093285 : RoutineName);
7745 : } else {
7746 71199 : return FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, this->TankTemp, this->waterIndex, RoutineName);
7747 : }
7748 1164484 : }();
7749 :
7750 1164484 : Real64 Eloss = 0.0; // Energy change due to ambient losses over the DataGlobals::TimeStep (J)
7751 1164484 : Real64 Euse = 0.0; // Energy change due to use side mass flow over the DataGlobals::TimeStep (J)
7752 1164484 : Real64 Esource = 0.0; // Energy change due to source side mass flow over the DataGlobals::TimeStep (J)
7753 1164484 : Real64 Eheater1 = 0.0; // Energy change due to heater 1 over the DataGlobals::TimeStep (J)
7754 1164484 : Real64 Eheater2 = 0.0; // Energy change due to heater 2 over the DataGlobals::TimeStep (J)
7755 1164484 : Real64 Eunmet = 0.0; // Energy change unmet over the DataGlobals::TimeStep (J)
7756 1164484 : Real64 Event = 0.0; // Energy change due to venting over the DataGlobals::TimeStep (J)
7757 1164484 : int CycleOnCount1_loc = 0; // Number of times heater 1 cycles on in the current time step
7758 1164484 : int CycleOnCount2_loc = 0; // Number of times heater 2 cycles on in the current time step
7759 1164484 : Real64 Runtime = 0.0; // Time that either heater is running (s)
7760 1164484 : Real64 Runtime1 = 0.0; // Time that heater 1 is running (s)
7761 1164484 : Real64 Runtime2 = 0.0; // Time that heater 2 is running (s)
7762 1164484 : bool SetPointRecovered = false; // Flag to indicate when set point is recovered for the first time
7763 : // Added three variables for desuperheater sourceinlet temperature update
7764 : Real64 MdotDesuperheaterWater; // mass flow rate of desuperheater source side water, kg/s
7765 1164484 : Real64 DesuperheaterPLR = 0.0; // Desuperheater part load ratio
7766 1164484 : Real64 DesuperheaterHeaterRate = 0.0; // Desuperheater heater rate (W)
7767 1164484 : Real64 SourceInletTempSum = 0.0; // Sum the source inlet temperature in sub time step to calculate average tempearature
7768 : Real64 Qheater1; // Heating rate of burner or electric heating element 1 (W)
7769 : Real64 Qheater2; // Heating rate of burner or electric heating element 2 (W)
7770 :
7771 1164484 : if (this->InletMode == InletPositionMode::Fixed) CalcNodeMassFlows(InletPositionMode::Fixed);
7772 :
7773 : // Time remaining in the current DataGlobals::TimeStep (s)
7774 1164484 : Real64 TimeRemaining = SecInTimeStep;
7775 :
7776 : // Diff Eq. Coefficients for each node
7777 1164484 : std::vector<Real64> A;
7778 1164484 : A.resize(nTankNodes);
7779 1164484 : std::vector<Real64> B;
7780 1164484 : B.resize(nTankNodes);
7781 :
7782 : // Temperature at the end of the internal DataGlobals::TimeStep
7783 1164484 : std::vector<Real64> Tfinal;
7784 1164484 : Tfinal.resize(nTankNodes);
7785 :
7786 : // Average temperature of each node over the internal DataGlobals::TimeStep
7787 1164484 : std::vector<Real64> Tavg;
7788 1164484 : Tavg.resize(nTankNodes);
7789 :
7790 1164484 : int SubTimestepCount = 0;
7791 :
7792 6325347 : while (TimeRemaining > 0.0) {
7793 :
7794 5160863 : ++SubTimestepCount;
7795 :
7796 5160863 : bool PrevHeaterOn1 = this->HeaterOn1;
7797 5160863 : bool PrevHeaterOn2 = this->HeaterOn2;
7798 :
7799 5160863 : if (this->InletMode == InletPositionMode::Seeking) CalcNodeMassFlows(InletPositionMode::Seeking);
7800 :
7801 : // Heater control logic
7802 5160863 : if (this->IsChilledWaterTank) {
7803 : // Chilled Water Tank, no heating
7804 350994 : Qheater1 = 0.0;
7805 350994 : Qheater2 = 0.0;
7806 : } else {
7807 : // Control the first heater element (master)
7808 4809869 : if (this->MaxCapacity > 0.0) {
7809 2875584 : const Real64 &NodeTemp = this->Node(this->HeaterNode1).Temp;
7810 :
7811 2875584 : if (this->HeaterOn1) {
7812 278506 : if (NodeTemp >= this->SetPointTemp) {
7813 42983 : this->HeaterOn1 = false;
7814 42983 : SetPointRecovered = true;
7815 : }
7816 : } else { // Heater is off
7817 2597078 : if (NodeTemp < MinTemp1) {
7818 84329 : this->HeaterOn1 = true;
7819 84329 : ++CycleOnCount1_loc;
7820 : }
7821 : }
7822 : }
7823 :
7824 4809869 : if (this->HeaterOn1) {
7825 319872 : Qheater1 = this->MaxCapacity;
7826 : } else {
7827 4489997 : Qheater1 = 0.0;
7828 : }
7829 :
7830 : // Control the second heater element (slave)
7831 4809869 : if (this->MaxCapacity2 > 0.0) {
7832 756904 : if ((this->StratifiedControlMode == PriorityControlMode::MasterSlave) && this->HeaterOn1) {
7833 30937 : this->HeaterOn2 = false;
7834 :
7835 : } else {
7836 725967 : const Real64 &NodeTemp = this->Node(this->HeaterNode2).Temp;
7837 :
7838 725967 : if (this->HeaterOn2) {
7839 422778 : if (NodeTemp >= this->SetPointTemp2) {
7840 7028 : this->HeaterOn2 = false;
7841 7028 : SetPointRecovered = true;
7842 : }
7843 : } else { // Heater is off
7844 303189 : if (NodeTemp < MinTemp2) {
7845 58361 : this->HeaterOn2 = true;
7846 58361 : ++CycleOnCount2_loc;
7847 : }
7848 : }
7849 : }
7850 : }
7851 :
7852 4809869 : if (this->HeaterOn2) {
7853 474111 : Qheater2 = this->MaxCapacity2;
7854 : } else {
7855 4335758 : Qheater2 = 0.0;
7856 : }
7857 : }
7858 :
7859 5160863 : if (SubTimestepCount == 1) {
7860 1164484 : dt = SubTimestepMin;
7861 : } else {
7862 :
7863 : // Set the maximum tank temperature change allowed
7864 3996379 : Real64 dT_max = std::numeric_limits<Real64>::max();
7865 3996379 : if (this->HeaterOn1) {
7866 310637 : if (this->Node(this->HeaterNode1).Temp < this->SetPointTemp) {
7867 : // Node temperature is less than setpoint and heater is on
7868 310637 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7869 : } else {
7870 : // Node temperature is greater than or equal to setpoint and heater is on
7871 : // Heater will turn off next time around, calculate assuming that
7872 0 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7873 : }
7874 : } else { // Heater off
7875 3685742 : if (this->Node(this->HeaterNode1).Temp >= MinTemp1) {
7876 : // Node temperature is greater than or equal to cut in temperature and heater is off
7877 2904330 : dT_max = min(dT_max, this->Node(this->HeaterNode1).Temp - MinTemp1);
7878 : } else {
7879 : // Heater will turn on next time around, calculate to setpoint
7880 781412 : dT_max = min(dT_max, this->SetPointTemp - this->Node(this->HeaterNode1).Temp);
7881 : }
7882 : }
7883 3996379 : if (this->HeaterOn2) {
7884 454824 : if (this->Node(this->HeaterNode2).Temp < this->SetPointTemp2) {
7885 : // Node temperature is less than setpoint and heater is on
7886 454824 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7887 : } else {
7888 : // Node temperature is greater than or equal to setpoint and heater is on
7889 : // Heater will turn off next time around, calculate assuming that
7890 0 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7891 : }
7892 : } else { // Heater off
7893 3541555 : if (this->Node(this->HeaterNode2).Temp >= MinTemp2) {
7894 : // Node temperature is greater than or equal to cut in temperature and heater is off
7895 1901952 : dT_max = min(dT_max, this->Node(this->HeaterNode2).Temp - MinTemp2);
7896 : } else {
7897 : // Heater will turn on next time around, calculate to setpoint
7898 1639603 : dT_max = min(dT_max, this->SetPointTemp2 - this->Node(this->HeaterNode2).Temp);
7899 : }
7900 : }
7901 :
7902 : // Make adjustments to A and B to account for heaters being on or off now
7903 3996379 : if (this->HeaterOn1 && !PrevHeaterOn1) {
7904 : // If heater 1 is on now and wasn't before add the heat rate to the B term
7905 83473 : B[this->HeaterNode1 - 1] += Qheater1 / (this->Node(this->HeaterNode1).Mass * Cp);
7906 3912906 : } else if (!this->HeaterOn1 && PrevHeaterOn1) {
7907 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7908 41709 : B[this->HeaterNode1 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode1).Mass * Cp);
7909 : }
7910 3996379 : if (this->HeaterOn2 && !PrevHeaterOn2) {
7911 : // If heater 2 is on now and wasn't before add the heat rate to the B term
7912 57921 : B[this->HeaterNode2 - 1] += Qheater2 / (this->Node(this->HeaterNode2).Mass * Cp);
7913 3938458 : } else if (!this->HeaterOn2 && PrevHeaterOn2) {
7914 : // If heater 1 is off now and was on before, remove the heat rate from the B term
7915 12215 : B[this->HeaterNode2 - 1] -= this->MaxCapacity / (this->Node(this->HeaterNode2).Mass * Cp);
7916 : }
7917 :
7918 3996379 : if ((this->HeaterOn1 || this->HeaterOn2) && !(PrevHeaterOn1 || PrevHeaterOn2)) {
7919 : // Remove off cycle loads
7920 : // Apply on cycle loads
7921 1266063 : for (int i = 0; i < nTankNodes; i++) {
7922 1130608 : auto &node = this->Node[i];
7923 1130608 : Real64 NodeCapacitance = node.Mass * Cp;
7924 1130608 : A[i] += (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7925 1130608 : B[i] += (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7926 : NodeCapacitance;
7927 : }
7928 3996379 : } else if (!(this->HeaterOn1 || this->HeaterOn2) && (PrevHeaterOn1 || PrevHeaterOn2)) {
7929 : // Remove on cycle loads
7930 : // Apply off cycle loads
7931 501951 : for (int i = 0; i < nTankNodes; i++) {
7932 453966 : auto &node = this->Node[i];
7933 453966 : Real64 NodeCapacitance = node.Mass * Cp;
7934 453966 : A[i] -= (node.OffCycLossCoeff - node.OnCycLossCoeff) / NodeCapacitance;
7935 453966 : B[i] -= (-node.OffCycParaLoad + node.OnCycParaLoad + (node.OnCycLossCoeff - node.OffCycLossCoeff) * this->AmbientTemp) /
7936 : NodeCapacitance;
7937 : }
7938 : }
7939 :
7940 : // Set the sub DataGlobals::TimeStep (dt)
7941 3996379 : dt = TimeRemaining;
7942 30133559 : for (int i = 0; i < nTankNodes; ++i) {
7943 26137180 : const Real64 Denominator = fabs(A[i] * Tavg[i] + B[i]);
7944 26137180 : if (Denominator != 0.0) dt = min(dt, dT_max / Denominator);
7945 : }
7946 3996379 : dt = max(min(SubTimestepMin, TimeRemaining), dt);
7947 3996379 : dt = min(SubTimestepMax, dt);
7948 : }
7949 :
7950 : // Make initial guess that average and final temperatures over the DataGlobals::TimeStep are equal to the starting temperatures
7951 39520677 : for (int i = 0; i < nTankNodes; i++) {
7952 34359814 : const auto &NodeTemp = this->Node[i].Temp;
7953 34359814 : Tfinal[i] = NodeTemp;
7954 34359814 : Tavg[i] = NodeTemp;
7955 : }
7956 :
7957 22791631 : for (int ConvergenceCounter = 1; ConvergenceCounter <= 10; ConvergenceCounter++) {
7958 :
7959 22437820 : std::fill(A.begin(), A.end(), 0.0);
7960 22437820 : std::fill(B.begin(), B.end(), 0.0);
7961 :
7962 : // Heater Coefficients
7963 22437820 : B[this->HeaterNode1 - 1] += Qheater1;
7964 22437820 : B[this->HeaterNode2 - 1] += Qheater2;
7965 :
7966 206578402 : for (int i = 0; i < nTankNodes; i++) {
7967 184140582 : const int NodeNum = i + 1;
7968 184140582 : const auto &tank_node = this->Node(NodeNum);
7969 :
7970 : // Parasitic Loads and Losses to Ambient
7971 184140582 : if (this->HeaterOn1 || this->HeaterOn2) {
7972 : // Parasitic Loads
7973 31359840 : B[i] += tank_node.OnCycParaLoad;
7974 : // Losses to Ambient
7975 31359840 : A[i] += -tank_node.OnCycLossCoeff;
7976 31359840 : B[i] += tank_node.OnCycLossCoeff * this->AmbientTemp;
7977 : } else {
7978 : // Parasitic Loads
7979 152780742 : B[i] += tank_node.OffCycParaLoad;
7980 : // Losses to Ambient
7981 152780742 : A[i] += -tank_node.OffCycLossCoeff;
7982 152780742 : B[i] += tank_node.OffCycLossCoeff * this->AmbientTemp;
7983 : }
7984 :
7985 : // Conduction to adjacent nodes
7986 184140582 : A[i] += -(tank_node.CondCoeffDn + tank_node.CondCoeffUp);
7987 184140582 : if (NodeNum > 1) B[i] += tank_node.CondCoeffUp * Tavg[i - 1];
7988 184140582 : if (NodeNum < nTankNodes) B[i] += tank_node.CondCoeffDn * Tavg[i + 1];
7989 :
7990 : // Use side plant connection
7991 184140582 : const Real64 use_e_mdot_cp = tank_node.UseMassFlowRate * Cp;
7992 184140582 : A[i] += -use_e_mdot_cp;
7993 184140582 : B[i] += use_e_mdot_cp * this->UseInletTemp;
7994 :
7995 : // Source side heat transfer rate
7996 184140582 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
7997 : // Pumped Condenser Heat Pump Water Heater
7998 124137456 : if (tank_node.SourceMassFlowRate > 0.0) B[i] += Qheatpump;
7999 : } else {
8000 : // Source side plant connection (constant temperature)
8001 60003126 : const Real64 src_e_mdot_cp = tank_node.SourceMassFlowRate * Cp;
8002 60003126 : A[i] += -src_e_mdot_cp;
8003 60003126 : B[i] += src_e_mdot_cp * this->SourceInletTemp;
8004 : }
8005 :
8006 : // Wrapped condenser heat pump water heater
8007 184140582 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped)) {
8008 27582976 : B[i] += Qheatpump * tank_node.HPWHWrappedCondenserHeatingFrac;
8009 : }
8010 :
8011 : // Internodal flow
8012 184140582 : A[i] += -(tank_node.MassFlowFromUpper + tank_node.MassFlowFromLower) * Cp;
8013 184140582 : if (NodeNum > 1) B[i] += tank_node.MassFlowFromUpper * Cp * Tavg[i - 1];
8014 184140582 : if (NodeNum < nTankNodes) B[i] += tank_node.MassFlowFromLower * Cp * Tavg[i + 1];
8015 :
8016 : // Divide by mass and specific heat
8017 : // m * cp * dT/dt = q_net => dT/dt = a * T + b
8018 184140582 : A[i] /= tank_node.Mass * Cp;
8019 184140582 : B[i] /= tank_node.Mass * Cp;
8020 :
8021 : } // end for each node
8022 :
8023 : // Calculate the average and final temperatures over the interval
8024 22437820 : Real64 TfinalDiff = 0.0;
8025 206578402 : for (int i = 0; i < nTankNodes; ++i) {
8026 184140582 : const Real64 Tstart = this->Node[i].Temp;
8027 184140582 : const Real64 b_a = B[i] / A[i];
8028 184140582 : const Real64 e_a_dt = exp(A[i] * dt);
8029 184140582 : Tavg[i] = (Tstart + b_a) * (e_a_dt - 1.0) / (A[i] * dt) - b_a;
8030 184140582 : const Real64 Tfinal_old = Tfinal[i];
8031 184140582 : Tfinal[i] = (Tstart + b_a) * e_a_dt - b_a;
8032 184140582 : TfinalDiff = max(fabs(Tfinal[i] - Tfinal_old), TfinalDiff);
8033 : }
8034 :
8035 22437820 : if (TfinalDiff < TemperatureConvergenceCriteria) break;
8036 :
8037 17630768 : if (this->DesuperheaterNum > 0) {
8038 47893 : DesuperheaterPLR = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).DesuperheaterPLR;
8039 47893 : DesuperheaterHeaterRate = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).HeaterRate;
8040 47893 : MdotDesuperheaterWater = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).OperatingWaterFlowRate *
8041 47893 : Psychrometrics::RhoH2O(Tavg[this->SourceOutletStratNode - 1]);
8042 47893 : if (DesuperheaterPLR > 0.0 && MdotDesuperheaterWater > 0.0) {
8043 34540 : this->SourceInletTemp =
8044 34540 : Tavg[this->SourceOutletStratNode - 1] + (DesuperheaterHeaterRate / DesuperheaterPLR) / (MdotDesuperheaterWater * Cp);
8045 : } else {
8046 13353 : this->SourceInletTemp = Tavg[this->SourceOutletStratNode - 1];
8047 : }
8048 : }
8049 : } // end temperature convergence loop
8050 :
8051 : // Inversion mixing
8052 : bool HasInversion;
8053 8209118 : do {
8054 8209118 : HasInversion = false;
8055 : // Starting from the top of the tank check if the node below has a temperature inversion.
8056 47554278 : for (int j = 0; j < nTankNodes - 1; ++j) {
8057 42393415 : if (Tfinal[j] < Tfinal[j + 1]) {
8058 :
8059 : // Temperature inversion!
8060 3048255 : HasInversion = true;
8061 :
8062 : // From the node above the inversion, move down calculating a weighted average
8063 : // of node temperatures until the node below the group of mixed nodes isn't hotter
8064 : // or we hit the bottom of the tank.
8065 3048255 : Real64 Tmixed = 0.0;
8066 3048255 : Real64 MassMixed = 0.0;
8067 : int m;
8068 15129678 : for (m = j; m < nTankNodes; ++m) {
8069 15129678 : Tmixed += Tfinal[m] * this->Node[m].Mass;
8070 15129678 : MassMixed += this->Node[m].Mass;
8071 15129678 : if ((m == nTankNodes - 1) || (Tmixed / MassMixed > Tfinal[m + 1])) break;
8072 : }
8073 3048255 : Tmixed /= MassMixed;
8074 :
8075 : // Now we have a range of nodes (j = top, m = bottom) that are mixed
8076 : // and the mixed temperature (Tmixed).
8077 : // Move through the mixed nodes and set the final temperature to the mixed temperature.
8078 : // Also calculate a corrected average temperature for each node.
8079 18177933 : for (int k = j; k <= m; ++k) {
8080 : Real64 FinalFactorMixing;
8081 : Real64 AvgFactorMixing;
8082 15129678 : const Real64 NodeCapacitance = this->Node[k].Mass * Cp;
8083 15129678 : if (A[k] == 0.0) {
8084 0 : FinalFactorMixing = dt / NodeCapacitance;
8085 0 : AvgFactorMixing = FinalFactorMixing / 2.0;
8086 : } else {
8087 15129678 : FinalFactorMixing = (exp(A[k] * dt) - 1.0) / A[k] / NodeCapacitance;
8088 15129678 : AvgFactorMixing = ((exp(A[k] * dt) - 1.0) / A[k] / dt - 1.0) / A[k] / NodeCapacitance;
8089 : }
8090 15129678 : const Real64 Q_AdiabaticMixing = (Tmixed - Tfinal[k]) / FinalFactorMixing;
8091 15129678 : Tfinal[k] = Tmixed;
8092 15129678 : Tavg[k] += Q_AdiabaticMixing * AvgFactorMixing;
8093 : }
8094 :
8095 : // Since we mixed, get out of here and start from the top to check again for mixing.
8096 3048255 : break;
8097 : }
8098 : }
8099 : } while (HasInversion);
8100 :
8101 : // Venting
8102 5160863 : if (!this->IsChilledWaterTank) {
8103 4809869 : if (Tfinal[0] > this->TankTempLimit) {
8104 1058206 : for (int i = 0; i < nTankNodes; ++i) {
8105 914594 : if (Tfinal[i] > this->TankTempLimit) {
8106 366497 : Event += this->Node[i].Mass * Cp * (this->TankTempLimit - Tfinal[i]);
8107 366497 : Tfinal[i] = this->TankTempLimit;
8108 : }
8109 : }
8110 : }
8111 : }
8112 :
8113 : // Increment to next internal time step
8114 5160863 : TimeRemaining -= dt;
8115 5160863 : Real64 Qloss = 0.0;
8116 39520677 : for (int i = 0; i < nTankNodes; ++i) {
8117 34359814 : auto &node = this->Node[i];
8118 34359814 : node.Temp = Tfinal[i];
8119 34359814 : node.TempSum += Tavg[i] * dt;
8120 :
8121 : // Bookkeeping for reporting variables, mostly for Qunmet.
8122 34359814 : Real64 Qloss_node = (this->AmbientTemp - Tavg[i]);
8123 : Real64 Qheat_node;
8124 34359814 : const Real64 Quse_node = node.UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[i]);
8125 34359814 : const Real64 Qsource_node = [&] {
8126 34359814 : if (this->HeatPumpNum > 0) {
8127 27896208 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8128 21368426 : if (node.SourceMassFlowRate > 0.0) {
8129 1580231 : return Qheatpump;
8130 : } else {
8131 19788195 : return 0.0;
8132 : }
8133 : } else {
8134 6527782 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
8135 6527782 : return Qheatpump * node.HPWHWrappedCondenserHeatingFrac;
8136 : }
8137 : } else {
8138 6463606 : return node.SourceMassFlowRate * Cp * (this->SourceInletTemp - Tavg[i]);
8139 : }
8140 34359814 : }();
8141 :
8142 34359814 : if (this->HeaterOn1 || this->HeaterOn2) {
8143 5981750 : Qloss_node *= node.OnCycLossCoeff;
8144 5981750 : Qheat_node = node.OnCycParaLoad * this->OnCycParaFracToTank;
8145 : } else {
8146 28378064 : Qloss_node *= node.OffCycLossCoeff;
8147 28378064 : Qheat_node = node.OffCycParaLoad * this->OffCycParaFracToTank;
8148 : }
8149 34359814 : Qloss += Qloss_node;
8150 34359814 : const Real64 Qneeded_node = max(-Quse_node - Qsource_node - Qloss_node - Qheat_node, 0.0);
8151 34359814 : const Real64 Qunmet_node = max(Qneeded_node - Qheater1 - Qheater2, 0.0);
8152 34359814 : Eunmet += Qunmet_node * dt;
8153 : }
8154 5160863 : SourceInletTempSum += this->SourceInletTemp * dt;
8155 : // More bookkeeping for reporting variables
8156 5160863 : Eloss += Qloss * dt;
8157 5160863 : const Real64 Quse = (this->UseOutletStratNode > 0)
8158 5160863 : ? this->UseEffectiveness * this->UseMassFlowRate * Cp * (this->UseInletTemp - Tavg[this->UseOutletStratNode - 1])
8159 5160863 : : 0.0;
8160 5160863 : Euse += Quse * dt;
8161 5160863 : const Real64 Qsource = [&] {
8162 5160863 : if (this->HeatPumpNum > 0) {
8163 4338294 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8164 2404739 : return Qheatpump;
8165 : } else {
8166 1933555 : assert(HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped);
8167 1933555 : return 0.0;
8168 : }
8169 : } else {
8170 822569 : if (this->SourceOutletStratNode > 0) {
8171 576157 : return this->SourceEffectiveness * this->SourceMassFlowRate * Cp *
8172 576157 : (this->SourceInletTemp - Tavg[this->SourceOutletStratNode - 1]);
8173 : } else {
8174 246412 : return 0.0;
8175 : }
8176 : }
8177 5160863 : }();
8178 5160863 : Esource += Qsource * dt;
8179 5160863 : if (this->HeaterOn1) Runtime1 += dt;
8180 5160863 : if (this->HeaterOn2) Runtime2 += dt;
8181 5160863 : if (this->HeaterOn1 || this->HeaterOn2) Runtime += dt;
8182 5160863 : Eheater1 += Qheater1 * dt;
8183 5160863 : Eheater2 += Qheater2 * dt;
8184 :
8185 : // Calculation for standard ratings
8186 5160863 : if (!this->FirstRecoveryDone) {
8187 : Real64 Qrecovery;
8188 3298474 : if (this->HeaterOn1 || this->HeaterOn2) {
8189 106652 : Qrecovery = (Qheater1 + Qheater1) / this->Efficiency + this->OnCycParaLoad;
8190 : } else {
8191 3191822 : Qrecovery = this->OffCycParaLoad;
8192 : }
8193 3298474 : this->FirstRecoveryFuel += Qrecovery * dt;
8194 3298474 : if (SetPointRecovered) this->FirstRecoveryDone = true;
8195 : }
8196 : } // end while TimeRemaining > 0.0
8197 :
8198 9387118 : for (auto &e : this->Node) {
8199 8222634 : e.TempAvg = e.TempSum / SecInTimeStep;
8200 8222634 : e.TempSum = 0.0;
8201 : }
8202 :
8203 1164484 : this->TankTemp = sum(this->Node, &StratifiedNodeData::Temp) / this->Nodes;
8204 1164484 : this->TankTempAvg = sum(this->Node, &StratifiedNodeData::TempAvg) / this->Nodes;
8205 :
8206 1164484 : if (!state.dataGlobal->WarmupFlag) {
8207 : // Warn for potential freezing when avg of final temp over all nodes is below 2°C (nearing 0°C)
8208 252428 : if (this->TankTemp < 2) {
8209 0 : if (this->FreezingErrorIndex == 0) {
8210 0 : ShowWarningError(state,
8211 0 : format("{}: {} = '{}': Temperature of tank < 2C indicates of possibility of freeze. Tank Temperature = {:.2R} C.",
8212 : RoutineName,
8213 0 : this->Type,
8214 0 : this->Name,
8215 0 : this->TankTemp));
8216 0 : ShowContinueErrorTimeStamp(state, "");
8217 : }
8218 0 : ShowRecurringWarningErrorAtEnd(state,
8219 0 : this->Type + " = '" + this->Name + "': Temperature of tank < 2C indicates of possibility of freeze",
8220 0 : this->FreezingErrorIndex,
8221 0 : this->TankTemp, // Report Max
8222 0 : this->TankTemp, // Report Min
8223 : _, // Don't report Sum
8224 : "{C}", // Max Unit
8225 : "{C}"); // Min Unit
8226 : }
8227 : }
8228 :
8229 1164484 : if (this->UseOutletStratNode > 0) {
8230 1164484 : this->UseOutletTemp = this->Node(this->UseOutletStratNode).TempAvg;
8231 : // Revised use outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8232 1164484 : if (this->UseMassFlowRate > 0.0) {
8233 894247 : this->UseOutletTemp = this->UseInletTemp * (1.0 - this->UseEffectiveness) + this->UseOutletTemp * this->UseEffectiveness;
8234 : }
8235 : }
8236 :
8237 1164484 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) {
8238 : // If we have a wrapped condenser HPWH, set the source outlet to the weighted average of the node
8239 : // temperatures the condenser sees
8240 476567 : Real64 WeightedAverageSourceOutletTemp(0.0);
8241 2912883 : for (int i = 1; i <= this->Nodes; ++i) {
8242 2436316 : WeightedAverageSourceOutletTemp += this->Node(i).TempAvg * this->Node(i).HPWHWrappedCondenserHeatingFrac;
8243 : }
8244 476567 : this->SourceOutletTemp = WeightedAverageSourceOutletTemp;
8245 687917 : } else if (this->SourceOutletStratNode > 0) {
8246 : // otherwise set it to the temperature of the source outlet node
8247 604769 : this->SourceOutletTemp = this->Node(this->SourceOutletStratNode).TempAvg;
8248 : // Output the average inlet temperature for the DataGlobals::TimeStep
8249 604769 : this->SourceInletTemp = SourceInletTempSum / SecInTimeStep;
8250 : }
8251 1164484 : if (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
8252 : // For pumped condensers, set the source inlet and outlets to match the delta T
8253 : // across the water side of the DX coil.
8254 423625 : HeatPumpWaterHeaterData const &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8255 423625 : DataLoopNode::NodeData const &HPWHCondWaterInletNode = state.dataLoopNodes->Node(HeatPump.CondWaterInletNode);
8256 423625 : DataLoopNode::NodeData const &HPWHCondWaterOutletNode = state.dataLoopNodes->Node(HeatPump.CondWaterOutletNode);
8257 423625 : Real64 const HPWHCondenserDeltaT = HPWHCondWaterOutletNode.Temp - HPWHCondWaterInletNode.Temp;
8258 423625 : this->SourceInletTemp = this->SourceOutletTemp + HPWHCondenserDeltaT;
8259 : }
8260 :
8261 : // Revised source outlet temperature to ensure energy balance. Assumes a constant CP. CR8341/CR8570
8262 1164484 : if (this->SourceOutletStratNode > 0) {
8263 604769 : if (this->SourceMassFlowRate > 0.0) {
8264 276498 : this->SourceOutletTemp = this->SourceInletTemp * (1.0 - this->SourceEffectiveness) + this->SourceOutletTemp * this->SourceEffectiveness;
8265 : }
8266 : }
8267 :
8268 1164484 : this->LossRate = Eloss / SecInTimeStep;
8269 1164484 : this->UseRate = Euse / SecInTimeStep;
8270 1164484 : Real64 WrappedCondenserHeatPumpRate = 0.0;
8271 1164484 : if ((this->HeatPumpNum > 0) && (HPWHCondenserConfig == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped)) {
8272 423625 : this->SourceRate = Qheatpump;
8273 : } else {
8274 740859 : this->SourceRate = Esource / SecInTimeStep;
8275 740859 : WrappedCondenserHeatPumpRate = Qheatpump;
8276 : }
8277 :
8278 1164484 : this->OffCycParaFuelRate = this->OffCycParaLoad * (SecInTimeStep - Runtime) / SecInTimeStep;
8279 1164484 : this->OnCycParaFuelRate = this->OnCycParaLoad * Runtime / SecInTimeStep;
8280 1164484 : this->OffCycParaRateToTank = this->OffCycParaFuelRate * this->OffCycParaFracToTank;
8281 1164484 : this->OnCycParaRateToTank = this->OnCycParaFuelRate * this->OnCycParaFracToTank;
8282 1164484 : this->TotalDemandRate =
8283 1164484 : -this->UseRate - this->SourceRate - this->LossRate - this->OffCycParaRateToTank - this->OnCycParaRateToTank - WrappedCondenserHeatPumpRate;
8284 1164484 : this->HeaterRate1 = Eheater1 / SecInTimeStep;
8285 1164484 : this->HeaterRate2 = Eheater2 / SecInTimeStep;
8286 1164484 : this->HeaterRate = this->HeaterRate1 + this->HeaterRate2;
8287 :
8288 1164484 : this->UnmetRate = Eunmet / SecInTimeStep;
8289 1164484 : this->VentRate = Event / SecInTimeStep;
8290 1164484 : this->NetHeatTransferRate = this->UseRate + this->SourceRate + this->LossRate + this->OffCycParaRateToTank + this->OnCycParaRateToTank +
8291 1164484 : this->HeaterRate + this->VentRate + WrappedCondenserHeatPumpRate;
8292 :
8293 1164484 : this->CycleOnCount = CycleOnCount1_loc + CycleOnCount2_loc;
8294 1164484 : this->CycleOnCount1 = CycleOnCount1_loc;
8295 1164484 : this->CycleOnCount2 = CycleOnCount2_loc;
8296 :
8297 1164484 : this->RuntimeFraction = Runtime / SecInTimeStep;
8298 1164484 : this->RuntimeFraction1 = Runtime1 / SecInTimeStep;
8299 1164484 : this->RuntimeFraction2 = Runtime2 / SecInTimeStep;
8300 :
8301 1164484 : this->FuelRate = (Eheater1 + Eheater2) / this->Efficiency / SecInTimeStep;
8302 :
8303 : // Add water heater skin losses and venting losses to ambient zone, if specified
8304 1164484 : if (this->AmbientTempZone > 0) this->AmbientZoneGain = -this->LossRate * this->SkinLossFracToZone - this->VentRate;
8305 1164484 : }
8306 :
8307 1340033 : void WaterThermalTankData::CalcNodeMassFlows(InletPositionMode inletMode)
8308 : {
8309 :
8310 : // SUBROUTINE INFORMATION:
8311 : // AUTHOR Peter Graham Ellis
8312 : // DATE WRITTEN January 2007
8313 : // MODIFIED na
8314 : // RE-ENGINEERED na
8315 :
8316 : // PURPOSE OF THIS SUBROUTINE:
8317 : // Determines mass flow rates between nodes according to the locations of the use- and source-side inlet and outlet
8318 : // nodes.
8319 :
8320 : // METHODOLOGY EMPLOYED:
8321 : // In 'Seeking' mode, nodes are searched between the user-specified inlet and outlet nodes to find the node closest
8322 : // in temperature to the inlet fluid temperature. In 'Fixed' mode, the user-specified nodes are always used.
8323 : // Upward and downward flows are added to each node between an inlet and outlet. Flows in both directions cancel out
8324 : // to leave only the net flow in one direction.
8325 :
8326 1340033 : int useInletStratNod = this->UseInletStratNode;
8327 1340033 : int useOutletStratNode = this->UseOutletStratNode;
8328 1340033 : int sourceInletStratNode = this->SourceInletStratNode;
8329 1340033 : int sourceOutletStratNode = this->SourceOutletStratNode;
8330 :
8331 1340033 : Real64 useMassFlowRate = this->UseMassFlowRate * this->UseEffectiveness;
8332 1340033 : Real64 sourceMassFlowRate = this->SourceMassFlowRate * this->SourceEffectiveness;
8333 :
8334 10615961 : for (auto &e : this->Node) {
8335 9275928 : e.UseMassFlowRate = 0.0;
8336 9275928 : e.SourceMassFlowRate = 0.0;
8337 9275928 : e.MassFlowFromUpper = 0.0;
8338 9275928 : e.MassFlowFromLower = 0.0;
8339 9275928 : e.MassFlowToUpper = 0.0;
8340 9275928 : e.MassFlowToLower = 0.0;
8341 : }
8342 :
8343 1340033 : if (inletMode == InletPositionMode::Seeking) {
8344 : // 'Seek' the node with the temperature closest to the inlet temperature
8345 : // Start at the user-specified inlet node and search to the user-specified outlet node
8346 : int Step;
8347 350994 : if (useMassFlowRate > 0.0) {
8348 164192 : if (useInletStratNod > useOutletStratNode) {
8349 0 : Step = -1;
8350 : } else {
8351 164192 : Step = 1;
8352 : }
8353 164192 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8354 164192 : int const NodeNum_stop(floop_end(useInletStratNod, useOutletStratNode, Step));
8355 574740 : for (int NodeNum = useInletStratNod; NodeNum != NodeNum_stop; NodeNum += Step) {
8356 527781 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->UseInletTemp);
8357 527781 : if (DeltaTemp < MinDeltaTemp) {
8358 345284 : MinDeltaTemp = DeltaTemp;
8359 345284 : useInletStratNod = NodeNum;
8360 182497 : } else if (DeltaTemp > MinDeltaTemp) {
8361 117233 : break;
8362 : }
8363 : }
8364 : }
8365 :
8366 350994 : if (sourceMassFlowRate > 0.0) {
8367 44186 : if (sourceInletStratNode > sourceOutletStratNode) {
8368 44186 : Step = -1;
8369 : } else {
8370 0 : Step = 1;
8371 : }
8372 44186 : Real64 MinDeltaTemp = 1.0e6; // Some big number
8373 44186 : int const NodeNum_stop(floop_end(sourceInletStratNode, sourceOutletStratNode, Step));
8374 89120 : for (int NodeNum = sourceInletStratNode; NodeNum != NodeNum_stop; NodeNum += Step) {
8375 89120 : Real64 DeltaTemp = std::abs(this->Node(NodeNum).Temp - this->SourceInletTemp);
8376 89120 : if (DeltaTemp < MinDeltaTemp) {
8377 44186 : MinDeltaTemp = DeltaTemp;
8378 44186 : sourceInletStratNode = NodeNum;
8379 44934 : } else if (DeltaTemp > MinDeltaTemp) {
8380 44186 : break;
8381 : }
8382 : }
8383 : }
8384 : }
8385 :
8386 1340033 : if (useInletStratNod > 0) this->Node(useInletStratNod).UseMassFlowRate = useMassFlowRate;
8387 1340033 : if (sourceInletStratNode > 0) this->Node(sourceInletStratNode).SourceMassFlowRate = sourceMassFlowRate;
8388 :
8389 1340033 : if (useMassFlowRate > 0.0) {
8390 976346 : if (useOutletStratNode > useInletStratNod) {
8391 : // Use-side flow is down
8392 776068 : for (int NodeNum = useInletStratNod; NodeNum <= useOutletStratNode - 1; ++NodeNum) {
8393 639868 : this->Node(NodeNum).MassFlowToLower += useMassFlowRate;
8394 : }
8395 776068 : for (int NodeNum = useInletStratNod + 1; NodeNum <= useOutletStratNode; ++NodeNum) {
8396 639868 : this->Node(NodeNum).MassFlowFromUpper += useMassFlowRate;
8397 : }
8398 :
8399 840146 : } else if (useOutletStratNode < useInletStratNod) {
8400 : // Use-side flow is up
8401 5127842 : for (int NodeNum = useOutletStratNode; NodeNum <= useInletStratNod - 1; ++NodeNum) {
8402 4613112 : this->Node(NodeNum).MassFlowFromLower += useMassFlowRate;
8403 : }
8404 5127842 : for (int NodeNum = useOutletStratNode + 1; NodeNum <= useInletStratNod; ++NodeNum) {
8405 4613112 : this->Node(NodeNum).MassFlowToUpper += useMassFlowRate;
8406 : }
8407 :
8408 : } else {
8409 : // Use-side flow is across the node; no flow to other nodes
8410 : }
8411 : }
8412 :
8413 1340033 : if (sourceMassFlowRate > 0.0) {
8414 298591 : if (sourceOutletStratNode > sourceInletStratNode) {
8415 : // Source-side flow is down
8416 672770 : for (int NodeNum = sourceInletStratNode; NodeNum <= sourceOutletStratNode - 1; ++NodeNum) {
8417 574385 : this->Node(NodeNum).MassFlowToLower += sourceMassFlowRate;
8418 : }
8419 672770 : for (int NodeNum = sourceInletStratNode + 1; NodeNum <= sourceOutletStratNode; ++NodeNum) {
8420 574385 : this->Node(NodeNum).MassFlowFromUpper += sourceMassFlowRate;
8421 : }
8422 :
8423 200206 : } else if (sourceOutletStratNode < sourceInletStratNode) {
8424 : // Source-side flow is up
8425 1121921 : for (int NodeNum = sourceOutletStratNode; NodeNum <= sourceInletStratNode - 1; ++NodeNum) {
8426 921715 : this->Node(NodeNum).MassFlowFromLower += sourceMassFlowRate;
8427 : }
8428 1121921 : for (int NodeNum = sourceOutletStratNode + 1; NodeNum <= sourceInletStratNode; ++NodeNum) {
8429 921715 : this->Node(NodeNum).MassFlowToUpper += sourceMassFlowRate;
8430 : }
8431 :
8432 : } else {
8433 : // Source-side flow is across the node; no flow to other nodes
8434 : }
8435 : }
8436 :
8437 : // Cancel out any up and down flows
8438 10615961 : for (int NodeNum = 1; NodeNum <= this->Nodes; ++NodeNum) {
8439 9275928 : this->Node(NodeNum).MassFlowFromUpper = max((this->Node(NodeNum).MassFlowFromUpper - this->Node(NodeNum).MassFlowToUpper), 0.0);
8440 9275928 : this->Node(NodeNum).MassFlowFromLower = max((this->Node(NodeNum).MassFlowFromLower - this->Node(NodeNum).MassFlowToLower), 0.0);
8441 : }
8442 1340033 : }
8443 :
8444 29484 : void WaterThermalTankData::CalcDesuperheaterWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8445 : {
8446 :
8447 : // SUBROUTINE INFORMATION:
8448 : // AUTHOR Richard Raustad
8449 : // DATE WRITTEN July 2005
8450 : // MODIFIED na
8451 : // RE-ENGINEERED na
8452 :
8453 : // PURPOSE OF THIS SUBROUTINE:
8454 : // Simulates a refrigerant desuperheater to heat water
8455 :
8456 : // METHODOLOGY EMPLOYED:
8457 : // This model uses the rated heat reclaim recovery efficiency, recovery efficiency modifier curve,
8458 : // set point temperature, and dead band temperature difference to simulate the desuperheater coil
8459 : // and sets up inputs to the tank model associated with the desuperheater coil
8460 :
8461 29484 : int constexpr MaxIte(500); // Maximum number of iterations for RegulaFalsi
8462 :
8463 29484 : auto &DesupHtr = state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum);
8464 :
8465 29484 : int WaterInletNode = DesupHtr.WaterInletNode;
8466 29484 : int WaterOutletNode = DesupHtr.WaterOutletNode;
8467 :
8468 : // store first iteration tank temperature and desuperheater mode of operation
8469 29484 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && DesupHtr.FirstTimeThroughFlag) {
8470 : // Save conditions from end of previous system timestep
8471 : // Every iteration that does not advance time should reset to these values
8472 13796 : this->SavedTankTemp = this->TankTemp;
8473 13796 : this->SavedSourceOutletTemp = this->SourceOutletTemp;
8474 13796 : DesupHtr.SaveMode = DesupHtr.Mode;
8475 13796 : DesupHtr.FirstTimeThroughFlag = false;
8476 : }
8477 :
8478 15688 : else if (!FirstHVACIteration) {
8479 14742 : DesupHtr.FirstTimeThroughFlag = true;
8480 : }
8481 :
8482 : // initialize variables before invoking any RETURN statement
8483 29484 : this->SourceMassFlowRate = 0.0;
8484 : // reset tank inlet temp from previous time step
8485 29484 : this->SourceInletTemp = this->SavedSourceOutletTemp;
8486 29484 : DesupHtr.DesuperheaterPLR = 0.0;
8487 :
8488 29484 : state.dataLoopNodes->Node(WaterInletNode).MassFlowRate = 0.0;
8489 29484 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = 0.0;
8490 29484 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SavedSourceOutletTemp;
8491 :
8492 29484 : DesupHtr.DesuperheaterPLR = 0.0;
8493 29484 : DesupHtr.OnCycParaFuelRate = 0.0;
8494 29484 : DesupHtr.OnCycParaFuelEnergy = 0.0;
8495 29484 : DesupHtr.OffCycParaFuelRate = 0.0;
8496 29484 : DesupHtr.OffCycParaFuelEnergy = 0.0;
8497 29484 : DesupHtr.HEffFTempOutput = 0.0;
8498 29484 : DesupHtr.HeaterRate = 0.0;
8499 29484 : DesupHtr.HeaterEnergy = 0.0;
8500 29484 : DesupHtr.PumpPower = 0.0;
8501 29484 : DesupHtr.PumpEnergy = 0.0;
8502 :
8503 : // simulate only the water heater tank if the desuperheater coil is scheduled off
8504 29484 : Real64 AvailSchedule = ScheduleManager::GetCurrentScheduleValue(state, DesupHtr.AvailSchedPtr);
8505 29484 : if (AvailSchedule == 0.0) {
8506 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8507 0 : this->CalcWaterThermalTank(state);
8508 18199 : return;
8509 : }
8510 :
8511 : // simulate only the water heater tank if the lowest temperature available from the desuperheater coil
8512 : // is less than water inlet temperature if the reclaim source is a refrigeration condenser
8513 29484 : if (DesupHtr.ValidSourceType) {
8514 29484 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8515 29484 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8516 4602 : if (state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailTemperature <= this->SourceInletTemp) {
8517 24 : DesupHtr.Mode = TankOperatingMode::Floating;
8518 24 : this->CalcWaterThermalTank(state);
8519 72 : ShowRecurringWarningErrorAtEnd(state,
8520 48 : "WaterHeating:Desuperheater " + DesupHtr.Name +
8521 : " - Waste heat source temperature was too low to be useful.",
8522 24 : DesupHtr.InsuffTemperatureWarn);
8523 24 : return;
8524 : } // Temp too low
8525 : } // desuperheater source is condenser_refrigeration
8526 : } // validsourcetype
8527 :
8528 29460 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad;
8529 29460 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8530 :
8531 : // check that water heater tank cut-in temp is greater than desuperheater cut-in temp
8532 29460 : Real64 desupHtrSetPointTemp = DesupHtr.SetPointTemp;
8533 29460 : Real64 DeadBandTempDiff = DesupHtr.DeadBandTempDiff;
8534 29460 : if ((desupHtrSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
8535 4 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
8536 0 : Real64 MinTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8537 0 : ++DesupHtr.SetPointError;
8538 0 : if (DesupHtr.SetPointError < 5) {
8539 0 : ShowWarningError(state,
8540 0 : format("{} \"{}\": Water heater tank set point temperature is greater than or equal to the cut-in temperature of "
8541 : "the desuperheater. Desuperheater will be disabled.",
8542 0 : DesupHtr.Type,
8543 0 : DesupHtr.Name));
8544 0 : ShowContinueErrorTimeStamp(state, format(" ...Desuperheater cut-in temperature = {:.2R}", MinTemp));
8545 : } else {
8546 0 : ShowRecurringWarningErrorAtEnd(state,
8547 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8548 : "\": Water heater tank set point temperature is greater than or equal to the cut-in "
8549 : "temperature of the desuperheater. Desuperheater will be disabled warning continues...",
8550 0 : DesupHtr.SetPointErrIndex1,
8551 : MinTemp,
8552 : MinTemp);
8553 : }
8554 : }
8555 :
8556 : // Simulate tank if desuperheater unavailable for water heating
8557 4 : this->CalcWaterThermalTank(state);
8558 4 : return;
8559 : }
8560 :
8561 29456 : Real64 Effic = DesupHtr.HeatReclaimRecoveryEff;
8562 :
8563 29456 : state.dataLoopNodes->Node(WaterInletNode).Temp = this->SavedSourceOutletTemp;
8564 29456 : DesupHtr.Mode = DesupHtr.SaveMode;
8565 :
8566 : Real64 HEffFTemp;
8567 29456 : if (DesupHtr.HEffFTemp > 0) {
8568 29456 : HEffFTemp = max(0.0, Curve::CurveValue(state, DesupHtr.HEffFTemp, this->SavedTankTemp, state.dataEnvrn->OutDryBulbTemp));
8569 : } else {
8570 0 : HEffFTemp = 1.0;
8571 : }
8572 :
8573 : // set limits on heat recovery efficiency
8574 29456 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8575 4578 : if ((HEffFTemp * Effic) > 0.9) HEffFTemp = 0.9 / Effic;
8576 : } else { // max is 0.3 for all other sources
8577 24878 : if ((HEffFTemp * Effic) > 0.3) HEffFTemp = 0.3 / Effic;
8578 : } // setting limits on heat recovery efficiency
8579 :
8580 : // Access the appropriate structure to find the average heating capacity of the desuperheater heating coil
8581 29456 : Real64 AverageWasteHeat = 0.0;
8582 29456 : if (DesupHtr.ValidSourceType) {
8583 29456 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8584 29456 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8585 : // Refrigeration systems are solved outside the time step iteration, so the
8586 : // appropriate decrement for other waste heat applications is handled differently
8587 0 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).AvailCapacity -
8588 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8589 0 : DesupHtr.DXSysPLR = 1.0;
8590 29456 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8591 4578 : AverageWasteHeat = state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).AvailCapacity -
8592 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8593 4578 : DesupHtr.DXSysPLR = 1.0;
8594 24878 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8595 12978 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8596 12978 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8597 11900 : AverageWasteHeat = state.dataHeatBal->HeatReclaimDXCoil(SourceID).AvailCapacity -
8598 11900 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8599 11900 : DesupHtr.DXSysPLR = state.dataDXCoils->DXCoil(SourceID).PartLoadRatio;
8600 12978 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling) {
8601 3782 : AverageWasteHeat = state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).AvailCapacity -
8602 3782 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8603 3782 : DesupHtr.DXSysPLR = state.dataVariableSpeedCoils->VarSpeedCoil(SourceID).PartLoadRatio;
8604 9196 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8605 3246 : AverageWasteHeat = state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).AvailCapacity -
8606 3246 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).HVACDesuperheaterReclaimedHeatTotal;
8607 3246 : DesupHtr.DXSysPLR = state.dataWaterToAirHeatPumpSimple->SimpleWatertoAirHP(SourceID).PartLoadRatio;
8608 5950 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8609 5950 : AverageWasteHeat =
8610 5950 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.AvailCapacity -
8611 5950 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.HVACDesuperheaterReclaimedHeatTotal;
8612 5950 : DesupHtr.DXSysPLR = state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].partLoadRatioReport;
8613 : }
8614 : } else {
8615 0 : AverageWasteHeat = 0.0;
8616 : }
8617 :
8618 : // simulate only water heater tank if reclaim heating source is off
8619 29456 : if (DesupHtr.DXSysPLR == 0.0) {
8620 18171 : this->CalcWaterThermalTank(state);
8621 18171 : return;
8622 : }
8623 :
8624 : // If the set point is higher than the maximum water temp, reset both the set point and the dead band temperature difference
8625 11285 : if (desupHtrSetPointTemp > DesupHtr.MaxInletWaterTemp) {
8626 11285 : Real64 CutInTemp = desupHtrSetPointTemp - DeadBandTempDiff;
8627 11285 : desupHtrSetPointTemp = DesupHtr.MaxInletWaterTemp;
8628 11285 : DeadBandTempDiff = max(0.0, (desupHtrSetPointTemp - CutInTemp));
8629 : }
8630 :
8631 : Real64 Acc; // Accuracy of result from RegulaFalsi
8632 11285 : if (DesupHtr.TankTypeNum == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8633 951 : Acc = 0.001;
8634 : } else {
8635 10334 : Acc = 0.00001;
8636 : }
8637 :
8638 : // set the water-side mass flow rate
8639 11285 : Real64 CpWater = Psychrometrics::CPHW(state.dataLoopNodes->Node(WaterInletNode).Temp);
8640 11285 : Real64 MdotWater = DesupHtr.OperatingWaterFlowRate * Psychrometrics::RhoH2O(state.dataLoopNodes->Node(WaterInletNode).Temp);
8641 11285 : Real64 QHeatRate = 0.0;
8642 11285 : if (state.dataLoopNodes->Node(WaterInletNode).Temp <= DesupHtr.MaxInletWaterTemp + Acc) {
8643 10099 : QHeatRate = ((AverageWasteHeat * Effic * HEffFTemp) / DesupHtr.DXSysPLR) + (DesupHtr.PumpElecPower * DesupHtr.PumpFracToWater);
8644 : }
8645 :
8646 : // change to tanktypenum using parameters?
8647 11285 : Real64 partLoadRatio = 0.0;
8648 : Real64 NewTankTemp;
8649 : {
8650 11285 : DataPlant::PlantEquipmentType const TankType = DesupHtr.TankTypeNum;
8651 :
8652 11285 : if (TankType == DataPlant::PlantEquipmentType::WtrHeaterMixed || TankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
8653 :
8654 11285 : DesupHtr.SaveWHMode = this->Mode;
8655 11285 : Real64 PreTankAvgTemp = this->TankTempAvg;
8656 11285 : Real64 NewTankAvgTemp = 0.0; // Initialization
8657 11285 : int max_count = 200;
8658 11285 : int count = 0;
8659 11285 : bool firstThrough = true;
8660 11285 : switch (DesupHtr.Mode) {
8661 10391 : case TankOperatingMode::Heating:
8662 : // Calculate until consistency of desuperheater and tank source side energy transfer achieved
8663 30514 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > HVAC::SmallTempDiff || firstThrough) && count < max_count) {
8664 20123 : count++;
8665 20123 : firstThrough = false;
8666 20123 : PreTankAvgTemp = this->TankTempAvg;
8667 20123 : partLoadRatio = DesupHtr.DXSysPLR;
8668 20123 : if (MdotWater > 0.0) {
8669 20123 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8670 : } else {
8671 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8672 : }
8673 :
8674 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8675 20123 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8676 :
8677 : // set the source mass flow rate for the tank
8678 20123 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8679 :
8680 20123 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8681 20123 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8682 20123 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8683 20123 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8684 20123 : this->CalcWaterThermalTank(state);
8685 20123 : Real64 NewTankTemp = this->TankTemp;
8686 :
8687 20123 : if (NewTankTemp > desupHtrSetPointTemp) {
8688 : // Only revert to floating mode if the tank temperature is higher than the cut out temperature
8689 6809 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8690 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8691 : }
8692 : int SolFla;
8693 6809 : std::string IterNum;
8694 54454 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8695 27227 : this->Mode = DesupHtr.SaveWHMode;
8696 27227 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8697 27227 : this->CalcWaterThermalTank(state);
8698 27227 : Real64 NewTankTemp = this->TankTemp;
8699 27227 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8700 27227 : return PLRResidualWaterThermalTank;
8701 6809 : };
8702 6809 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8703 6809 : if (SolFla == -1) {
8704 0 : IterNum = fmt::to_string(MaxIte);
8705 0 : if (!state.dataGlobal->WarmupFlag) {
8706 0 : ++DesupHtr.IterLimitExceededNum1;
8707 0 : if (DesupHtr.IterLimitExceededNum1 == 1) {
8708 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8709 0 : ShowContinueError(state,
8710 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8711 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8712 : IterNum,
8713 : partLoadRatio));
8714 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8715 : } else {
8716 0 : ShowRecurringWarningErrorAtEnd(state,
8717 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8718 : "\": Iteration limit exceeded in heating mode warning continues. "
8719 : "Part-load ratio statistics follow.",
8720 0 : DesupHtr.IterLimitErrIndex1,
8721 : partLoadRatio,
8722 : partLoadRatio);
8723 : }
8724 : }
8725 6809 : } else if (SolFla == -2) {
8726 0 : partLoadRatio =
8727 0 : max(0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8728 0 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8729 0 : this->CalcWaterThermalTank(state);
8730 0 : if (!state.dataGlobal->WarmupFlag) {
8731 0 : ++DesupHtr.RegulaFalsiFailedNum1;
8732 0 : if (DesupHtr.RegulaFalsiFailedNum1 == 1) {
8733 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8734 0 : ShowContinueError(state,
8735 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to 1 "
8736 : "exceeded. Part-load ratio used = {:.3R}",
8737 : partLoadRatio));
8738 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8739 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
8740 : } else {
8741 0 : ShowRecurringWarningErrorAtEnd(state,
8742 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8743 : "\": Part-load ratio calculation failed in heating mode warning "
8744 : "continues. Part-load ratio statistics follow.",
8745 0 : DesupHtr.RegulaFalsiFailedIndex1,
8746 : partLoadRatio,
8747 : partLoadRatio);
8748 : }
8749 : }
8750 : }
8751 6809 : } else {
8752 13314 : partLoadRatio = DesupHtr.DXSysPLR;
8753 : }
8754 20123 : NewTankAvgTemp = this->TankTempAvg;
8755 : }
8756 10391 : break;
8757 894 : case TankOperatingMode::Floating:
8758 894 : if (MdotWater > 0.0) {
8759 894 : state.dataLoopNodes->Node(WaterOutletNode).Temp =
8760 894 : state.dataLoopNodes->Node(WaterInletNode).Temp + QHeatRate / (MdotWater * CpWater);
8761 : } else {
8762 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = state.dataLoopNodes->Node(WaterInletNode).Temp;
8763 : }
8764 : // check tank temperature by setting source inlet mass flow rate to zero
8765 894 : partLoadRatio = 0.0;
8766 :
8767 : // set the full load outlet temperature on the water heater source inlet node (init has already been called)
8768 894 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8769 :
8770 : // check tank temperature by setting source inlet mass flow rate to zero
8771 894 : this->SourceMassFlowRate = 0.0;
8772 :
8773 : // disable the tank heater to find PLR of the HPWH
8774 894 : this->MaxCapacity = 0.0;
8775 894 : this->MinCapacity = 0.0;
8776 894 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8777 894 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8778 894 : this->CalcWaterThermalTank(state);
8779 894 : NewTankTemp = this->TankTemp;
8780 :
8781 894 : if (NewTankTemp <= (desupHtrSetPointTemp - DeadBandTempDiff)) {
8782 44 : this->Mode = DesupHtr.SaveWHMode;
8783 44 : if ((this->SavedTankTemp - NewTankTemp) != 0.0) {
8784 44 : partLoadRatio =
8785 44 : min(DesupHtr.DXSysPLR,
8786 44 : max(0.0, ((desupHtrSetPointTemp - DeadBandTempDiff) - NewTankTemp) / (this->SavedTankTemp - NewTankTemp)));
8787 : } else {
8788 0 : partLoadRatio = DesupHtr.DXSysPLR;
8789 : }
8790 201 : while ((std::abs(PreTankAvgTemp - NewTankAvgTemp) > HVAC::SmallTempDiff || firstThrough) && count < max_count) {
8791 157 : count++;
8792 157 : firstThrough = false;
8793 157 : PreTankAvgTemp = this->TankTempAvg;
8794 157 : DesupHtr.Mode = TankOperatingMode::Heating;
8795 157 : if (MdotWater > 0.0) {
8796 157 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp + QHeatRate / (MdotWater * CpWater);
8797 : } else {
8798 0 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8799 : }
8800 :
8801 : // set the full load outlet temperature on the water heater source inlet node
8802 157 : this->SourceInletTemp = state.dataLoopNodes->Node(WaterOutletNode).Temp;
8803 :
8804 : // set the source mass flow rate for the tank and enable backup heating element
8805 157 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8806 157 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8807 157 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8808 157 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8809 157 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8810 157 : this->CalcWaterThermalTank(state);
8811 157 : NewTankTemp = this->TankTemp;
8812 :
8813 157 : if (NewTankTemp > desupHtrSetPointTemp) {
8814 : // Only revert to floating mode if the tank temperature is higher than the cut-out temperature
8815 0 : if (NewTankTemp > DesupHtr.SetPointTemp) {
8816 0 : DesupHtr.Mode = TankOperatingMode::Floating;
8817 : }
8818 : int SolFla;
8819 0 : std::string IterNum;
8820 0 : auto f = [&state, this, desupHtrSetPointTemp, &DesupHtr, MdotWater](Real64 const HPPartLoadRatio) {
8821 0 : this->Mode = DesupHtr.SaveWHMode;
8822 0 : this->SourceMassFlowRate = MdotWater * HPPartLoadRatio;
8823 0 : this->CalcWaterThermalTank(state);
8824 0 : Real64 NewTankTemp = this->TankTemp;
8825 0 : Real64 PLRResidualWaterThermalTank = desupHtrSetPointTemp - NewTankTemp;
8826 0 : return PLRResidualWaterThermalTank;
8827 0 : };
8828 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, partLoadRatio, f, 0.0, DesupHtr.DXSysPLR);
8829 0 : if (SolFla == -1) {
8830 0 : IterNum = fmt::to_string(MaxIte);
8831 0 : if (!state.dataGlobal->WarmupFlag) {
8832 0 : ++DesupHtr.IterLimitExceededNum2;
8833 0 : if (DesupHtr.IterLimitExceededNum2 == 1) {
8834 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8835 0 : ShowContinueError(state,
8836 0 : format("Iteration limit exceeded calculating desuperheater unit part-load ratio, "
8837 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
8838 : IterNum,
8839 : partLoadRatio));
8840 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8841 : } else {
8842 0 : ShowRecurringWarningErrorAtEnd(state,
8843 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8844 : "\": Iteration limit exceeded in float mode warning continues. "
8845 : "Part-load ratio statistics follow.",
8846 0 : DesupHtr.IterLimitErrIndex2,
8847 : partLoadRatio,
8848 : partLoadRatio);
8849 : }
8850 : }
8851 0 : } else if (SolFla == -2) {
8852 0 : partLoadRatio = max(
8853 0 : 0.0, min(DesupHtr.DXSysPLR, (desupHtrSetPointTemp - this->SavedTankTemp) / (NewTankTemp - this->SavedTankTemp)));
8854 0 : if (!state.dataGlobal->WarmupFlag) {
8855 0 : ++DesupHtr.RegulaFalsiFailedNum2;
8856 0 : if (DesupHtr.RegulaFalsiFailedNum2 == 1) {
8857 0 : ShowWarningError(state, format("{} \"{}\"", DesupHtr.Type, DesupHtr.Name));
8858 0 : ShowContinueError(state,
8859 0 : format("Desuperheater unit part-load ratio calculation failed: PLR limits of 0 to "
8860 : "1 exceeded. Part-load ratio used = {:.3R}",
8861 : partLoadRatio));
8862 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
8863 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
8864 : } else {
8865 0 : ShowRecurringWarningErrorAtEnd(
8866 : state,
8867 0 : DesupHtr.Type + " \"" + DesupHtr.Name +
8868 : "\": Part-load ratio calculation failed in float mode warning "
8869 : "continues. Part-load ratio statistics follow.",
8870 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).RegulaFalsiFailedIndex2,
8871 : partLoadRatio,
8872 : partLoadRatio);
8873 : }
8874 : }
8875 : }
8876 0 : }
8877 157 : NewTankAvgTemp = this->TankTempAvg;
8878 : }
8879 : } else {
8880 850 : this->MaxCapacity = DesupHtr.BackupElementCapacity;
8881 850 : this->MinCapacity = DesupHtr.BackupElementCapacity;
8882 : }
8883 894 : break;
8884 0 : default:
8885 0 : break;
8886 : }
8887 :
8888 : // should never get here, case is checked in GetWaterThermalTankInput
8889 11285 : } else {
8890 0 : ShowFatalError(state,
8891 0 : format("Coil:WaterHeating:Desuperheater = {}: invalid water heater tank type and name entered = {}, {}",
8892 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).Name,
8893 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankType,
8894 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(DesuperheaterNum).TankName));
8895 : }
8896 : }
8897 :
8898 11285 : if (QHeatRate == 0) partLoadRatio = 0.0;
8899 :
8900 11285 : state.dataLoopNodes->Node(WaterOutletNode).MassFlowRate = MdotWater * partLoadRatio;
8901 11285 : DesupHtr.HEffFTempOutput = HEffFTemp;
8902 11285 : DesupHtr.HeaterRate = QHeatRate * partLoadRatio;
8903 11285 : this->SourceMassFlowRate = MdotWater * partLoadRatio;
8904 :
8905 11285 : if (partLoadRatio == 0) {
8906 1186 : this->SourceInletTemp = this->SourceOutletTemp;
8907 1186 : state.dataLoopNodes->Node(WaterOutletNode).Temp = this->SourceOutletTemp;
8908 1186 : DesupHtr.HEffFTempOutput = 0.0;
8909 1186 : DesupHtr.HeaterRate = 0.0;
8910 : }
8911 :
8912 11285 : DesupHtr.HeaterEnergy = DesupHtr.HeaterRate * state.dataHVACGlobal->TimeStepSysSec;
8913 11285 : DesupHtr.DesuperheaterPLR = partLoadRatio;
8914 11285 : DesupHtr.OnCycParaFuelRate = DesupHtr.OnCycParaLoad * partLoadRatio;
8915 11285 : DesupHtr.OnCycParaFuelEnergy = DesupHtr.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8916 11285 : DesupHtr.OffCycParaFuelRate = DesupHtr.OffCycParaLoad * (1 - partLoadRatio);
8917 11285 : DesupHtr.OffCycParaFuelEnergy = DesupHtr.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
8918 11285 : DesupHtr.PumpPower = DesupHtr.PumpElecPower * (partLoadRatio);
8919 11285 : DesupHtr.PumpEnergy = DesupHtr.PumpPower * state.dataHVACGlobal->TimeStepSysSec;
8920 :
8921 : // Update used waste heat (just in case multiple users of waste heat use same source)
8922 11285 : if (DesupHtr.ValidSourceType) {
8923 11285 : int SourceID = DesupHtr.ReclaimHeatingSourceIndexNum;
8924 11285 : if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CompressorRackRefrigeratedCase) {
8925 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8926 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8927 0 : for (auto &num : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8928 0 : state.dataHeatBal->HeatReclaimRefrigeratedRack(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8929 11285 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CondenserRefrigeration) {
8930 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8931 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8932 9156 : for (auto &num : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8933 4578 : state.dataHeatBal->HeatReclaimRefrigCondenser(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8934 6707 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXCooling ||
8935 4467 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiSpeed ||
8936 4467 : DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXMultiMode) {
8937 2240 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8938 2240 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8939 4480 : for (auto &num : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8940 2240 : state.dataHeatBal->HeatReclaimDXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8941 6707 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::DXVariableCooling) {
8942 2396 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8943 2396 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8944 4792 : for (auto &num : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8945 2396 : state.dataHeatBal->HeatReclaimVS_DXCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8946 2071 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::AirWaterHeatPumpEQ) {
8947 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat(DesuperheaterNum) = DesupHtr.HeaterRate;
8948 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal = 0.0;
8949 1902 : for (auto &num : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeat)
8950 951 : state.dataHeatBal->HeatReclaimSimple_WAHPCoil(SourceID).WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8951 1120 : } else if (DesupHtr.ReclaimHeatingSource == ReclaimHeatObjectType::CoilCoolingDX) {
8952 1120 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat(
8953 1120 : DesuperheaterNum) = DesupHtr.HeaterRate;
8954 1120 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal =
8955 : 0.0;
8956 1120 : for (auto &num :
8957 3360 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum].reclaimHeat.WaterHeatingDesuperheaterReclaimedHeat)
8958 1120 : state.dataCoilCooingDX->coilCoolingDXs[DesupHtr.ReclaimHeatingSourceIndexNum]
8959 1120 : .reclaimHeat.WaterHeatingDesuperheaterReclaimedHeatTotal += num;
8960 : }
8961 : }
8962 : }
8963 :
8964 448686 : void WaterThermalTankData::CalcHeatPumpWaterHeater(EnergyPlusData &state, bool const FirstHVACIteration)
8965 : {
8966 :
8967 : // SUBROUTINE INFORMATION:
8968 : // AUTHOR Richard Raustad
8969 : // DATE WRITTEN March 2005
8970 : // MODIFIED B. Griffith, Jan 2012 for stratified tank
8971 : // B. Shen 12/2014, add air-source variable-speed heat pump water heating
8972 : // RE-ENGINEERED na
8973 :
8974 : // PURPOSE OF THIS SUBROUTINE:
8975 : // Simulates a heat pump water heater
8976 :
8977 : // METHODOLOGY EMPLOYED:
8978 : // Simulate the water heater tank, DX coil, and fan to meet the water heating requirements.
8979 :
8980 448686 : int constexpr MaxIte(500); // maximum number of iterations
8981 448686 : Real64 constexpr Acc(0.001); // Accuracy of result from RegulaFalsi
8982 :
8983 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8984 : Real64 MdotWater; // mass flow rate of condenser water, kg/s
8985 448686 : IntegratedHeatPump::IHPOperationMode IHPMode(IntegratedHeatPump::IHPOperationMode::Idle); // IHP working mode
8986 :
8987 : // References to objects used in this function
8988 448686 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
8989 :
8990 : // initialize local variables
8991 448686 : int AvailSchedule = ScheduleManager::GetCurrentScheduleValue(state, HeatPump.AvailSchedPtr);
8992 448686 : int HPAirInletNode = HeatPump.HeatPumpAirInletNode;
8993 448686 : int HPAirOutletNode = HeatPump.HeatPumpAirOutletNode;
8994 448686 : int OutdoorAirNode = HeatPump.OutsideAirNode;
8995 448686 : int ExhaustAirNode = HeatPump.ExhaustAirNode;
8996 448686 : int HPWaterInletNode = HeatPump.CondWaterInletNode;
8997 448686 : int HPWaterOutletNode = HeatPump.CondWaterOutletNode;
8998 448686 : int InletAirMixerNode = HeatPump.InletAirMixerNode;
8999 448686 : int OutletAirSplitterNode = HeatPump.OutletAirSplitterNode;
9000 448686 : int DXCoilAirInletNode = HeatPump.DXCoilAirInletNode;
9001 448686 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9002 448686 : HVAC::CompressorOp compressorOp = HVAC::CompressorOp::Off; // DX compressor operation; 1=on, 0=off
9003 448686 : HeatPump.OnCycParaFuelRate = 0.0;
9004 448686 : HeatPump.OnCycParaFuelEnergy = 0.0;
9005 448686 : HeatPump.OffCycParaFuelRate = 0.0;
9006 448686 : HeatPump.OffCycParaFuelEnergy = 0.0;
9007 448686 : state.dataLoopNodes->Node(HPWaterOutletNode) = state.dataLoopNodes->Node(HPWaterInletNode);
9008 448686 : int MaxSpeedNum = HeatPump.NumofSpeed; // speed number of variable speed HPWH coil
9009 :
9010 : // assign set point temperature (cut-out) and dead band temp diff (cut-in = cut-out minus dead band temp diff)
9011 448686 : Real64 HPSetPointTemp = HeatPump.SetPointTemp;
9012 448686 : Real64 DeadBandTempDiff = HeatPump.DeadBandTempDiff;
9013 448686 : Real64 RhoWater = Psychrometrics::RhoH2O(HPSetPointTemp); // initialize
9014 :
9015 : // store first iteration tank temperature and HP mode of operation
9016 : // this code can be called more than once with FirstHVACIteration = .TRUE., use FirstTimeThroughFlag to control save
9017 448686 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && HeatPump.FirstTimeThroughFlag) {
9018 63376 : this->SavedTankTemp = this->TankTemp;
9019 63376 : HeatPump.SaveMode = HeatPump.Mode;
9020 63376 : HeatPump.SaveWHMode = this->Mode;
9021 63376 : HeatPump.FirstTimeThroughFlag = false;
9022 : }
9023 :
9024 448686 : if (!FirstHVACIteration) HeatPump.FirstTimeThroughFlag = true;
9025 :
9026 : // check if HPWH is off for some reason and simulate HPWH air- and water-side mass flow rates of 0
9027 : // simulate only water heater tank if HP compressor is scheduled off
9028 : // simulate only water heater tank if HP compressor cut-out temperature is lower than the tank's cut-in temperature
9029 : // simulate only water heater tank if HP inlet air temperature is below minimum temperature for HP compressor operation
9030 : // if the tank maximum temperature limit is less than the HPWH set point temp, disable HPWH
9031 448686 : if (AvailSchedule == 0.0 || (HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp ||
9032 448686 : state.dataHVACGlobal->HPWHInletDBTemp < HeatPump.MinAirTempForHPOperation ||
9033 338494 : state.dataHVACGlobal->HPWHInletDBTemp > HeatPump.MaxAirTempForHPOperation || HPSetPointTemp >= this->TankTempLimit ||
9034 338494 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime && this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed &&
9035 897372 : this->SavedMode == TankOperatingMode::Heating) ||
9036 338494 : (!HeatPump.AllowHeatingElementAndHeatPumpToRunAtSameTime &&
9037 103940 : this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified && (this->SavedHeaterOn1 || this->SavedHeaterOn2))) {
9038 : // revert to float mode any time HPWH compressor is OFF
9039 112312 : HeatPump.Mode = TankOperatingMode::Floating;
9040 112312 : if (InletAirMixerNode > 0) {
9041 0 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
9042 : }
9043 : // pass node info and simulate crankcase heater
9044 112312 : if (MaxSpeedNum > 0) {
9045 58228 : int VSCoilNum = HeatPump.DXCoilNum;
9046 :
9047 58228 : if (HeatPump.bIsIHP) {
9048 4976 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(VSCoilNum).SCWHCoilIndex;
9049 : }
9050 : // set the SCWH mode
9051 58228 : Real64 SpeedRatio = 1.0; // speed ratio for interpolating between two speed levels
9052 58228 : int SpeedNum = 1;
9053 58228 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9054 58228 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9055 :
9056 58228 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9057 58228 : if (HeatPump.bIsIHP)
9058 4976 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9059 : "",
9060 : VSCoilNum,
9061 : HVAC::FanOp::Cycling,
9062 : HVAC::CompressorOp::On,
9063 4976 : state.dataWaterThermalTanks->hpPartLoadRatio,
9064 : SpeedNum,
9065 : SpeedRatio,
9066 : 0.0,
9067 : 0.0,
9068 : 1.0);
9069 : else
9070 53252 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9071 : HeatPump.DXCoilName,
9072 : VSCoilNum,
9073 : HVAC::FanOp::Cycling,
9074 : HVAC::CompressorOp::On,
9075 53252 : state.dataWaterThermalTanks->hpPartLoadRatio,
9076 : SpeedNum,
9077 : SpeedRatio,
9078 : 0.0,
9079 : 0.0,
9080 : 1.0);
9081 : } else {
9082 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9083 0 : if (HeatPump.bIsIHP)
9084 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9085 : "",
9086 : VSCoilNum,
9087 : HVAC::FanOp::Cycling,
9088 : HVAC::CompressorOp::On,
9089 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9090 : SpeedNum,
9091 : SpeedRatio,
9092 : 0.0,
9093 : 0.0,
9094 : 1.0);
9095 : else
9096 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9097 : HeatPump.DXCoilName,
9098 : VSCoilNum,
9099 : HVAC::FanOp::Cycling,
9100 : HVAC::CompressorOp::On,
9101 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9102 : SpeedNum,
9103 : SpeedRatio,
9104 : 0.0,
9105 : 0.0,
9106 : 1.0);
9107 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9108 : }
9109 :
9110 : // set the DWH mode
9111 58228 : if (HeatPump.bIsIHP) {
9112 4976 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
9113 :
9114 4976 : if (VSCoilNum > 0) // if DWH coil exists
9115 : {
9116 4976 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9117 4976 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9118 :
9119 4976 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9120 4976 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9121 : "",
9122 : VSCoilNum,
9123 : HVAC::FanOp::Cycling,
9124 : HVAC::CompressorOp::On,
9125 4976 : state.dataWaterThermalTanks->hpPartLoadRatio,
9126 : SpeedNum,
9127 : SpeedRatio,
9128 : 0.0,
9129 : 0.0,
9130 : 1.0);
9131 : } else {
9132 0 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9133 0 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9134 : "",
9135 : VSCoilNum,
9136 : HVAC::FanOp::Cycling,
9137 : HVAC::CompressorOp::On,
9138 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9139 : SpeedNum,
9140 : SpeedRatio,
9141 : 0.0,
9142 : 0.0,
9143 : 1.0);
9144 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9145 : }
9146 : }
9147 : }
9148 :
9149 : } else {
9150 54084 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9151 51964 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9152 :
9153 103928 : DXCoils::SimDXCoil(state,
9154 : HeatPump.DXCoilName,
9155 : compressorOp,
9156 : FirstHVACIteration,
9157 51964 : HeatPump.DXCoilNum,
9158 : HVAC::FanOp::Cycling,
9159 51964 : state.dataWaterThermalTanks->hpPartLoadRatio);
9160 : } else {
9161 4240 : DXCoils::SimDXCoil(state,
9162 : HeatPump.DXCoilName,
9163 : compressorOp,
9164 : FirstHVACIteration,
9165 2120 : HeatPump.DXCoilNum,
9166 : HVAC::FanOp::Cycling,
9167 2120 : state.dataWaterThermalTanks->hpPartLoadRatio);
9168 2120 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9169 : }
9170 : }
9171 :
9172 112312 : if (OutletAirSplitterNode > 0) {
9173 0 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
9174 : }
9175 :
9176 : // Simulate tank if HP compressor unavailable for water heating
9177 112312 : this->CalcWaterThermalTank(state);
9178 :
9179 : // If HPWH compressor is available and unit is off for another reason, off-cycle parasitics are calculated
9180 112312 : if (AvailSchedule != 0) {
9181 112312 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
9182 112312 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
9183 : }
9184 :
9185 : // Warn if HPWH compressor cut-in temperature is less than the water heater tank's set point temp
9186 112312 : if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
9187 13656 : if ((HPSetPointTemp - DeadBandTempDiff) <= this->SetPointTemp) {
9188 0 : Real64 HPMinTemp = HPSetPointTemp - DeadBandTempDiff;
9189 0 : const std::string HPMinTempChar = fmt::to_string(HPMinTemp);
9190 0 : ++HeatPump.HPSetPointError;
9191 : // add logic for warmup, DataGlobals::KickOffSimulation and doing sizing here
9192 0 : if (HeatPump.HPSetPointError == 1) {
9193 0 : ShowWarningError(state,
9194 0 : format("{} \"{}: Water heater tank set point temperature is greater than or equal to the cut-in temperature of "
9195 : "the heat pump water heater. Heat Pump will be disabled and simulation continues.",
9196 0 : HeatPump.Type,
9197 0 : HeatPump.Name));
9198 0 : ShowContinueErrorTimeStamp(state, format(" ...Heat Pump cut-in temperature={}", HPMinTempChar));
9199 : } else {
9200 0 : ShowRecurringWarningErrorAtEnd(state,
9201 0 : HeatPump.Type + " \"" + HeatPump.Name +
9202 : ": Water heater tank set point temperature is greater than or equal to the cut-in "
9203 : "temperature of the heat pump water heater. Heat Pump will be disabled error continues...",
9204 0 : HeatPump.HPSetPointErrIndex1,
9205 : HPMinTemp,
9206 : HPMinTemp);
9207 : }
9208 0 : }
9209 : }
9210 112312 : return;
9211 : }
9212 336374 : Real64 savedTankTemp = this->SavedTankTemp;
9213 336374 : HeatPump.Mode = HeatPump.SaveMode;
9214 :
9215 336374 : RhoWater = Psychrometrics::RhoH2O(savedTankTemp); // update water density using tank temp
9216 :
9217 : // set the heat pump air- and water-side mass flow rate
9218 336374 : MdotWater = HeatPump.OperatingWaterFlowRate * Psychrometrics::RhoH2O(savedTankTemp);
9219 :
9220 : // Select mode of operation (float mode or heat mode) from last iteration.
9221 : // Determine if heating will occur this iteration and get an estimate of the PLR
9222 336374 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9223 : // HPWH was heating last iteration and will continue to heat until the set point is reached
9224 110083 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9225 110083 : if (savedTankTemp > HPSetPointTemp) { // tank set point temp may have been reduced since last iteration and float mode may be needed
9226 202 : HeatPump.Mode = TankOperatingMode::Floating;
9227 202 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9228 : // check to see if HP needs to operate
9229 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9230 202 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9231 202 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9232 202 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9233 0 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9234 0 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9235 0 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9236 : }
9237 202 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9238 202 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9239 :
9240 : // Check tank temperature by setting source inlet mass flow rate to zero.
9241 202 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9242 :
9243 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9244 202 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9245 :
9246 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9247 202 : this->MaxCapacity = 0.0;
9248 202 : this->MinCapacity = 0.0;
9249 202 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9250 202 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9251 202 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9252 202 : this->CalcWaterThermalTank(state);
9253 202 : this->SourceEffectiveness = SourceEffectivenessBackup;
9254 202 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9255 :
9256 : // Reset the tank's internal heating element capacity.
9257 202 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9258 202 : this->MinCapacity = HeatPump.BackupElementCapacity;
9259 :
9260 : // Check to see if the tank drifts below set point if no heating happens.
9261 202 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9262 :
9263 : // HPWH is now in heating mode
9264 100 : HeatPump.Mode = TankOperatingMode::Heating;
9265 :
9266 : // Reset the water heater's mode (call above may have changed modes)
9267 100 : this->Mode = HeatPump.SaveWHMode;
9268 :
9269 100 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9270 : }
9271 : } else { // or use side nodes may meet set point without need for heat pump compressor operation
9272 : // check to see if HP needs to operate
9273 109881 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9274 32144 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9275 32144 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9276 77737 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9277 77737 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9278 77737 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9279 : }
9280 : // Check tank temperature by setting source inlet mass flow rate to zero.
9281 109881 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9282 109881 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9283 :
9284 109881 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9285 :
9286 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9287 109881 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9288 :
9289 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9290 109881 : this->MaxCapacity = 0.0;
9291 109881 : this->MinCapacity = 0.0;
9292 109881 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9293 109881 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9294 109881 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9295 109881 : this->CalcWaterThermalTank(state);
9296 109881 : this->SourceEffectiveness = SourceEffectivenessBackup;
9297 109881 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9298 :
9299 : // Reset the tank's internal heating element capacity.
9300 109881 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9301 109881 : this->MinCapacity = HeatPump.BackupElementCapacity;
9302 :
9303 : // Check to see if the tank meets set point if no heating happens.
9304 109881 : if (NewTankTemp > HPSetPointTemp) {
9305 :
9306 : // HPWH is now in floating mode
9307 0 : HeatPump.Mode = TankOperatingMode::Floating;
9308 :
9309 : } else {
9310 :
9311 : // HPWH remains in heating mode
9312 109881 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9313 : }
9314 :
9315 : // Reset the water heater's mode (call above may have changed modes)
9316 109881 : this->Mode = HeatPump.SaveWHMode;
9317 : }
9318 : } else {
9319 226291 : assert(HeatPump.Mode == TankOperatingMode::Floating);
9320 : // HPWH was floating last iteration and will continue to float until the cut-in temperature is reached
9321 :
9322 : // set the condenser inlet node temperature and full mass flow rate prior to calling the HPWH DX coil
9323 226291 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9324 104586 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = savedTankTemp;
9325 104586 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = savedTankTemp;
9326 121705 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9327 121705 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9328 121705 : state.dataLoopNodes->Node(HPWaterOutletNode).Temp = this->SourceInletTemp;
9329 : }
9330 226291 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = 0.0;
9331 226291 : state.dataLoopNodes->Node(HPWaterOutletNode).MassFlowRate = 0.0;
9332 :
9333 : // Check tank temperature by setting source inlet mass flow rate to zero.
9334 226291 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9335 :
9336 : // Set the full load outlet temperature on the water heater source inlet node (init has already been called).
9337 226291 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterOutletNode).Temp;
9338 :
9339 : // Disable the tank's internal heating element to find PLR of the HPWH using floating temperatures.
9340 226291 : this->MaxCapacity = 0.0;
9341 226291 : this->MinCapacity = 0.0;
9342 226291 : this->SourceMassFlowRate = 0.0; // disables heat pump for mixed tanks
9343 226291 : Real64 SourceEffectivenessBackup = this->SourceEffectiveness;
9344 226291 : this->SourceEffectiveness = 0.0; // disables heat pump for stratified tanks
9345 226291 : this->CalcWaterThermalTank(state);
9346 226291 : this->SourceEffectiveness = SourceEffectivenessBackup;
9347 226291 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9348 :
9349 : // Reset the tank's internal heating element capacity.
9350 226291 : this->MaxCapacity = HeatPump.BackupElementCapacity;
9351 226291 : this->MinCapacity = HeatPump.BackupElementCapacity;
9352 :
9353 : // Check to see if the tank drifts below set point if no heating happens.
9354 226291 : if (NewTankTemp <= (HPSetPointTemp - DeadBandTempDiff)) {
9355 :
9356 : // HPWH is now in heating mode
9357 45931 : HeatPump.Mode = TankOperatingMode::Heating;
9358 :
9359 : // Reset the water heater's mode (call above may have changed modes)
9360 45931 : this->Mode = HeatPump.SaveWHMode;
9361 :
9362 45931 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9363 : }
9364 : }
9365 :
9366 336374 : if (HeatPump.bIsIHP) // mark the water heating call, if existing
9367 : {
9368 5070 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9369 2534 : if (HeatPump.Mode == TankOperatingMode::Heating)
9370 1258 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = true;
9371 : else
9372 1276 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).IsWHCallAvail = false;
9373 : }
9374 : }
9375 :
9376 : // If the HPWH was in heating mode during the last DataGlobals::TimeStep or if it was determined that
9377 : // heating would be needed during this DataGlobals::TimeStep to maintain setpoint, do the heating calculation.
9378 336374 : int SpeedNum = 0;
9379 336374 : Real64 SpeedRatio = 0.0;
9380 336374 : if (HeatPump.Mode == TankOperatingMode::Heating) {
9381 :
9382 : // set up air flow on DX coil inlet node
9383 155912 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate =
9384 155912 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9385 :
9386 : // set the condenser inlet node mass flow rate prior to calling the DXCoils::CalcHPWHDXCoil
9387 155912 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9388 155912 : this->SourceMassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9389 :
9390 : // Do the coil and tank calculations at full PLR to see if it overshoots setpoint.
9391 155912 : bool bIterSpeed = false;
9392 155912 : if (MaxSpeedNum > 0) { // lowest speed of VS HPWH coil
9393 22227 : SpeedRatio = 1.0;
9394 22227 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9395 22227 : bIterSpeed = true; // prepare for iterating between speed levels
9396 22227 : SpeedNum = 1;
9397 22227 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9398 :
9399 22227 : if (HeatPump.bIsIHP) {
9400 2517 : bIterSpeed = false; // don't iterate speed unless match conditions below
9401 2517 : IHPMode = IntegratedHeatPump::GetCurWorkMode(state, HeatPump.DXCoilNum);
9402 :
9403 2517 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9404 : int VSCoilNum;
9405 1258 : if (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode) {
9406 48 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).DWHCoilIndex;
9407 48 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode =
9408 : IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg;
9409 : } else {
9410 1210 : VSCoilNum = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).SCWHCoilIndex;
9411 1210 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IntegratedHeatPump::IHPOperationMode::SCWHMatchWH;
9412 : }
9413 :
9414 1258 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9415 :
9416 1258 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9417 : "",
9418 : VSCoilNum,
9419 : HVAC::FanOp::Cycling,
9420 : HVAC::CompressorOp::On,
9421 1258 : state.dataWaterThermalTanks->hpPartLoadRatio,
9422 : SpeedNum,
9423 : SpeedRatio,
9424 : 0.0,
9425 : 0.0,
9426 : 1.0);
9427 :
9428 1258 : state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CurMode = IHPMode;
9429 1258 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9430 : } else {
9431 1259 : SpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9432 :
9433 1259 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9434 1259 : IntegratedHeatPump::SimIHP(state,
9435 : HeatPump.DXCoilName,
9436 1259 : HeatPump.DXCoilNum,
9437 : HVAC::FanOp::Cycling,
9438 : HVAC::CompressorOp::On,
9439 1259 : state.dataWaterThermalTanks->hpPartLoadRatio,
9440 : SpeedNum,
9441 : SpeedRatio,
9442 : 0.0,
9443 : 0.0,
9444 : true,
9445 : false,
9446 1259 : 1.0);
9447 :
9448 1259 : if ((IntegratedHeatPump::IHPOperationMode::SCWHMatchWH == IHPMode) ||
9449 : (IntegratedHeatPump::IHPOperationMode::DedicatedWaterHtg == IHPMode)) {
9450 112 : bIterSpeed = true;
9451 : } else {
9452 1147 : this->SourceMassFlowRate = state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TankSourceWaterMassFlowRate;
9453 1147 : MdotWater = this->SourceMassFlowRate;
9454 : }
9455 :
9456 1259 : if (IntegratedHeatPump::IHPOperationMode::SHDWHElecHeatOff == IHPMode) // turn off heater element
9457 : {
9458 0 : this->MaxCapacity = 0.0;
9459 0 : this->MinCapacity = 0.0;
9460 : }
9461 : }
9462 : } else {
9463 19710 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9464 : HeatPump.DXCoilName,
9465 19710 : HeatPump.DXCoilNum,
9466 : HVAC::FanOp::Cycling,
9467 : HVAC::CompressorOp::On,
9468 19710 : state.dataWaterThermalTanks->hpPartLoadRatio,
9469 : SpeedNum,
9470 : SpeedRatio,
9471 : 0.0,
9472 : 0.0,
9473 : 1.0);
9474 : }
9475 :
9476 22227 : this->CalcWaterThermalTank(state);
9477 : } else {
9478 133685 : this->ConvergeSingleSpeedHPWHCoilAndTank(state, state.dataWaterThermalTanks->hpPartLoadRatio);
9479 : }
9480 :
9481 155912 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
9482 155912 : Real64 LowSpeedTankTemp = NewTankTemp;
9483 155912 : Real64 HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9484 :
9485 155912 : if (NewTankTemp > HPSetPointTemp) {
9486 11666 : HeatPump.Mode = TankOperatingMode::Floating;
9487 11666 : TankOperatingMode tmpMode = HeatPump.SaveWHMode;
9488 56526 : auto f = [&state, this, HPSetPointTemp, tmpMode, MdotWater](Real64 const HPPartLoadRatio) {
9489 56526 : return this->PLRResidualHPWH(state, HPPartLoadRatio, HPSetPointTemp, tmpMode, MdotWater);
9490 11666 : };
9491 11666 : Real64 zeroResidual = 1.0;
9492 11666 : if (MaxSpeedNum > 0) {
9493 : // square the solving, and avoid warning
9494 : // due to very small capacity at lowest speed of VSHPWH coil
9495 1137 : if (bIterSpeed)
9496 1121 : zeroResidual = this->PLRResidualHPWH(state, 0.0, HPSetPointTemp, tmpMode, MdotWater);
9497 : else
9498 16 : zeroResidual = -1.0;
9499 : }
9500 :
9501 11666 : if (zeroResidual > 0.0) { // then iteration
9502 : int SolFla;
9503 11317 : General::SolveRoot(state, Acc, MaxIte, SolFla, state.dataWaterThermalTanks->hpPartLoadRatio, f, 0.0, 1.0);
9504 11317 : if (SolFla == -1) {
9505 0 : std::string IterNum;
9506 0 : IterNum = fmt::to_string(MaxIte);
9507 0 : if (!state.dataGlobal->WarmupFlag) {
9508 0 : ++HeatPump.IterLimitExceededNum2;
9509 0 : if (HeatPump.IterLimitExceededNum2 == 1) {
9510 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9511 0 : ShowContinueError(state,
9512 0 : format("Iteration limit exceeded calculating heat pump water heater compressor part-load ratio, "
9513 : "maximum iterations = {}. Part-load ratio returned = {:.3R}",
9514 : IterNum,
9515 0 : state.dataWaterThermalTanks->hpPartLoadRatio));
9516 0 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9517 : } else {
9518 0 : ShowRecurringWarningErrorAtEnd(
9519 : state,
9520 0 : HeatPump.Type + " \"" + HeatPump.Name +
9521 : "\": Iteration limit exceeded in float mode warning continues. Part-load ratio statistics follow.",
9522 0 : HeatPump.IterLimitErrIndex2,
9523 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9524 0 : state.dataWaterThermalTanks->hpPartLoadRatio);
9525 : }
9526 : }
9527 11317 : } else if (SolFla == -2) {
9528 304 : state.dataWaterThermalTanks->hpPartLoadRatio =
9529 304 : max(0.0, min(1.0, (HPSetPointTemp - savedTankTemp) / (NewTankTemp - savedTankTemp)));
9530 304 : if (!state.dataGlobal->WarmupFlag) {
9531 152 : ++HeatPump.RegulaFalsiFailedNum2;
9532 152 : if (HeatPump.RegulaFalsiFailedNum2 == 1) {
9533 1 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9534 2 : ShowContinueError(state,
9535 2 : format("Heat pump water heater compressor part-load ratio calculation failed: PLR limits of 0 to 1 "
9536 : "exceeded. Part-load ratio used = {:.3R}",
9537 1 : state.dataWaterThermalTanks->hpPartLoadRatio));
9538 1 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9539 1 : ShowContinueErrorTimeStamp(state, "This error occurred in float mode.");
9540 : } else {
9541 453 : ShowRecurringWarningErrorAtEnd(
9542 : state,
9543 302 : HeatPump.Type + " \"" + HeatPump.Name +
9544 : "\": Part-load ratio calculation failed in float mode warning continues. Part-load ratio statistics follow.",
9545 151 : HeatPump.RegulaFalsiFailedIndex2,
9546 151 : state.dataWaterThermalTanks->hpPartLoadRatio,
9547 151 : state.dataWaterThermalTanks->hpPartLoadRatio);
9548 : }
9549 : }
9550 : }
9551 : } else {
9552 349 : state.dataWaterThermalTanks->hpPartLoadRatio = 0.0;
9553 : }
9554 :
9555 : // Re-calculate the HPWH Coil to get the correct heat transfer rate.
9556 11666 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9557 11666 : if (MaxSpeedNum > 0) {
9558 1137 : SpeedRatio = 1.0;
9559 1137 : SpeedNum = 1;
9560 :
9561 1137 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9562 :
9563 1137 : if (HeatPump.bIsIHP) {
9564 32 : if (bIterSpeed) {
9565 16 : IntegratedHeatPump::SimIHP(state,
9566 : HeatPump.DXCoilName,
9567 16 : HeatPump.DXCoilNum,
9568 : HVAC::FanOp::Cycling,
9569 : HVAC::CompressorOp::On,
9570 16 : state.dataWaterThermalTanks->hpPartLoadRatio,
9571 : SpeedNum,
9572 : SpeedRatio,
9573 : 0.0,
9574 : 0.0,
9575 : true,
9576 : false,
9577 32 : 1.0);
9578 : }
9579 : } else {
9580 1105 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9581 : HeatPump.DXCoilName,
9582 1105 : HeatPump.DXCoilNum,
9583 : HVAC::FanOp::Cycling,
9584 : HVAC::CompressorOp::On,
9585 1105 : state.dataWaterThermalTanks->hpPartLoadRatio,
9586 : SpeedNum,
9587 : SpeedRatio,
9588 : 0.0,
9589 : 0.0,
9590 : 1.0);
9591 : }
9592 :
9593 : } else {
9594 10529 : DXCoils::CalcHPWHDXCoil(state, HeatPump.DXCoilNum, state.dataWaterThermalTanks->hpPartLoadRatio);
9595 : }
9596 144246 : } else if (bIterSpeed) {
9597 46759 : for (int loopIter = 1; loopIter <= 4; ++loopIter) {
9598 46759 : HeatPump.Mode = TankOperatingMode::Heating; // modHeatMode is important for system convergence
9599 46759 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9600 46759 : SpeedRatio = 1.0;
9601 46759 : int LowSpeedNum = 2;
9602 46759 : if (HeatPump.bIsIHP) {
9603 272 : LowSpeedNum = IntegratedHeatPump::GetLowSpeedNumIHP(state, HeatPump.DXCoilNum);
9604 272 : MaxSpeedNum = IntegratedHeatPump::GetMaxSpeedNumIHP(state, HeatPump.DXCoilNum);
9605 : }
9606 :
9607 380632 : for (int i = LowSpeedNum; i <= MaxSpeedNum; ++i) {
9608 346300 : SpeedNum = i;
9609 346300 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9610 346300 : if (HeatPump.bIsIHP) {
9611 2624 : IntegratedHeatPump::SimIHP(state,
9612 : HeatPump.DXCoilName,
9613 2624 : HeatPump.DXCoilNum,
9614 : HVAC::FanOp::Cycling,
9615 : HVAC::CompressorOp::On,
9616 2624 : state.dataWaterThermalTanks->hpPartLoadRatio,
9617 : SpeedNum,
9618 : SpeedRatio,
9619 : 0.0,
9620 : 0.0,
9621 : true,
9622 : false,
9623 5248 : 1.0);
9624 : } else {
9625 343676 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9626 : HeatPump.DXCoilName,
9627 343676 : HeatPump.DXCoilNum,
9628 : HVAC::FanOp::Cycling,
9629 : HVAC::CompressorOp::On,
9630 343676 : state.dataWaterThermalTanks->hpPartLoadRatio,
9631 : SpeedNum,
9632 : SpeedRatio,
9633 : 0.0,
9634 : 0.0,
9635 : 1.0);
9636 : }
9637 :
9638 : // HPWH condenser water temperature difference
9639 346300 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9640 :
9641 : // move the full load outlet temperature rate to the water heater structure variables
9642 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has
9643 : // been called)
9644 346300 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9645 : // this CALL does not update node temps, must use WaterThermalTank variables
9646 : // select tank type
9647 346300 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9648 339338 : this->CalcWaterThermalTankMixed(state);
9649 339338 : NewTankTemp = this->TankTemp;
9650 6962 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9651 6962 : this->CalcWaterThermalTankStratified(state);
9652 6962 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9653 : }
9654 :
9655 346300 : if (NewTankTemp > HPSetPointTemp) {
9656 12427 : SpeedNum = i;
9657 12427 : break;
9658 : } else {
9659 333873 : LowSpeedTankTemp = NewTankTemp;
9660 : }
9661 : }
9662 :
9663 46759 : if (NewTankTemp > HPSetPointTemp) {
9664 : int SolFla;
9665 12427 : std::string IterNum;
9666 77990 : auto f = [&state, this, SpeedNum, HPWaterInletNode, HPWaterOutletNode, RhoWater, HPSetPointTemp, &HeatPump, FirstHVACIteration](
9667 77990 : Real64 const SpeedRatio) {
9668 77990 : return this->PLRResidualIterSpeed(state,
9669 : SpeedRatio,
9670 : this->HeatPumpNum,
9671 : SpeedNum,
9672 : HPWaterInletNode,
9673 : HPWaterOutletNode,
9674 : RhoWater,
9675 : HPSetPointTemp,
9676 : HeatPump.SaveWHMode,
9677 77990 : FirstHVACIteration);
9678 12427 : };
9679 12427 : General::SolveRoot(state, Acc, MaxIte, SolFla, SpeedRatio, f, 1.0e-10, 1.0);
9680 :
9681 12427 : if (SolFla == -1) {
9682 0 : IterNum = fmt::to_string(MaxIte);
9683 0 : if (!state.dataGlobal->WarmupFlag) {
9684 0 : ++HeatPump.IterLimitExceededNum1;
9685 0 : if (HeatPump.IterLimitExceededNum1 == 1) {
9686 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9687 0 : ShowContinueError(state,
9688 0 : format("Iteration limit exceeded calculating heat pump water heater speed speed ratio ratio, "
9689 : "maximum iterations = {}. speed ratio returned = {:.3R}",
9690 : IterNum,
9691 : SpeedRatio));
9692 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9693 : } else {
9694 0 : ShowRecurringWarningErrorAtEnd(
9695 : state,
9696 0 : HeatPump.Type + " \"" + HeatPump.Name +
9697 : "\": Iteration limit exceeded in heating mode warning continues. speed ratio statistics follow.",
9698 0 : HeatPump.IterLimitErrIndex1,
9699 : SpeedRatio,
9700 : SpeedRatio);
9701 : }
9702 : }
9703 12427 : } else if (SolFla == -2) {
9704 0 : SpeedRatio = max(0.0, min(1.0, (HPSetPointTemp - LowSpeedTankTemp) / (NewTankTemp - LowSpeedTankTemp)));
9705 0 : if (!state.dataGlobal->WarmupFlag) {
9706 0 : ++HeatPump.RegulaFalsiFailedNum1;
9707 0 : if (HeatPump.RegulaFalsiFailedNum1 == 1) {
9708 0 : ShowWarningError(state, format("{} \"{}\"", HeatPump.Type, HeatPump.Name));
9709 0 : ShowContinueError(state,
9710 0 : format("Heat pump water heater speed ratio calculation failed: speed ratio limits of 0 to 1 "
9711 : "exceeded. speed ratio used = {:.3R}",
9712 : SpeedRatio));
9713 0 : ShowContinueError(state, "Please send this information to the EnergyPlus support group.");
9714 0 : ShowContinueErrorTimeStamp(state, "This error occurred in heating mode.");
9715 : } else {
9716 0 : ShowRecurringWarningErrorAtEnd(
9717 : state,
9718 0 : HeatPump.Type + " \"" + HeatPump.Name +
9719 : "\": Speed ratio calculation failed in heating mode warning continues. Speed ratio statistics follow.",
9720 0 : HeatPump.RegulaFalsiFailedIndex1,
9721 : SpeedRatio,
9722 : SpeedRatio);
9723 : }
9724 : }
9725 : }
9726 12427 : } else {
9727 34332 : SpeedNum = MaxSpeedNum;
9728 34332 : SpeedRatio = 1.0;
9729 : }
9730 :
9731 46759 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9732 46759 : this->SetVSHPWHFlowRates(state, HeatPump, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
9733 :
9734 46759 : if (HeatPump.bIsIHP) {
9735 272 : IntegratedHeatPump::SimIHP(state,
9736 : HeatPump.DXCoilName,
9737 272 : HeatPump.DXCoilNum,
9738 : HVAC::FanOp::Cycling,
9739 : HVAC::CompressorOp::On,
9740 272 : state.dataWaterThermalTanks->hpPartLoadRatio,
9741 : SpeedNum,
9742 : SpeedRatio,
9743 : 0.0,
9744 : 0.0,
9745 : true,
9746 : false,
9747 544 : 1.0);
9748 : } else {
9749 46487 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9750 : HeatPump.DXCoilName,
9751 46487 : HeatPump.DXCoilNum,
9752 : HVAC::FanOp::Cycling,
9753 : HVAC::CompressorOp::On,
9754 46487 : state.dataWaterThermalTanks->hpPartLoadRatio,
9755 : SpeedNum,
9756 : SpeedRatio,
9757 : 0.0,
9758 : 0.0,
9759 : 1.0);
9760 : }
9761 :
9762 : // HPWH condenser water temperature difference
9763 46759 : Real64 CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9764 :
9765 : // move the full load outlet temperature rate to the water heater structure variables
9766 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been
9767 : // called)
9768 46759 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
9769 : // this CALL does not update node temps, must use WaterThermalTank variables
9770 : // select tank type
9771 46759 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
9772 43278 : this->CalcWaterThermalTankMixed(state);
9773 43278 : NewTankTemp = this->TankTemp;
9774 3481 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
9775 3481 : this->CalcWaterThermalTankStratified(state);
9776 3481 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
9777 : }
9778 : // update inlet temp
9779 46759 : state.dataLoopNodes->Node(HPWaterInletNode).Temp = this->SourceOutletTemp;
9780 46759 : if (std::abs(state.dataLoopNodes->Node(HPWaterInletNode).Temp - HPWHCondInletNodeLast) < HVAC::SmallTempDiff) break;
9781 28058 : HPWHCondInletNodeLast = state.dataLoopNodes->Node(HPWaterInletNode).Temp;
9782 : }
9783 :
9784 : } else {
9785 : // Set the PLR to 1 if we're not going to reach setpoint during this DataGlobals::TimeStep.
9786 125545 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
9787 : }
9788 : }
9789 :
9790 336374 : if (HeatPump.bIsIHP) {
9791 5070 : if (state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).CheckWHCall) {
9792 2534 : IntegratedHeatPump::ClearCoils(state, HeatPump.DXCoilNum); // clear node info when checking the heating load
9793 : }
9794 : }
9795 :
9796 : // set air-side mass flow rate for final calculation
9797 336374 : if (InletAirMixerNode > 0) {
9798 20029 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRate =
9799 20029 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9800 40058 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio *
9801 20029 : (1.0 - state.dataWaterThermalTanks->mixerInletAirSchedule);
9802 20029 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9803 20029 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * state.dataWaterThermalTanks->mixerInletAirSchedule;
9804 : // IF HPWH is off, pass zone node conditions through HPWH air-side
9805 20029 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0)
9806 14806 : state.dataLoopNodes->Node(InletAirMixerNode) = state.dataLoopNodes->Node(HPAirInletNode);
9807 : } else {
9808 316345 : if (OutdoorAirNode == 0) {
9809 189127 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRate =
9810 189127 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9811 : } else {
9812 127218 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRate =
9813 127218 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio;
9814 : }
9815 : }
9816 336374 : if (state.dataWaterThermalTanks->hpPartLoadRatio == 0) this->SourceInletTemp = this->SourceOutletTemp;
9817 :
9818 : // set water-side mass flow rate for final calculation
9819 336374 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater * state.dataWaterThermalTanks->hpPartLoadRatio;
9820 :
9821 336374 : if (MaxSpeedNum > 0) {
9822 :
9823 : // it is important to use mdotAir to reset the notes, otherwise, could fail to converge
9824 50526 : if (InletAirMixerNode > 0) {
9825 7890 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9826 7890 : state.dataLoopNodes->Node(InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9827 : } else {
9828 42636 : if (OutdoorAirNode == 0) {
9829 15710 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9830 15710 : state.dataLoopNodes->Node(HPAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9831 : } else {
9832 26926 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9833 26926 : state.dataLoopNodes->Node(OutdoorAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
9834 : }
9835 : }
9836 :
9837 : // set the max mass flow rate for outdoor fans
9838 50526 : state.dataLoopNodes->Node(HeatPump.FanOutletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
9839 :
9840 50526 : if (HeatPump.bIsIHP) {
9841 : // pass node information using resulting PLR
9842 5070 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9843 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9844 5070 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9845 :
9846 5070 : IntegratedHeatPump::SimIHP(state,
9847 : HeatPump.DXCoilName,
9848 5070 : HeatPump.DXCoilNum,
9849 : HVAC::FanOp::Cycling,
9850 : compressorOp,
9851 5070 : state.dataWaterThermalTanks->hpPartLoadRatio,
9852 : SpeedNum,
9853 : SpeedRatio,
9854 : 0.0,
9855 : 0.0,
9856 : true,
9857 : false,
9858 5070 : 1.0);
9859 5070 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9860 :
9861 5070 : IntegratedHeatPump::SimIHP(state,
9862 : HeatPump.DXCoilName,
9863 5070 : HeatPump.DXCoilNum,
9864 : HVAC::FanOp::Cycling,
9865 : compressorOp,
9866 5070 : state.dataWaterThermalTanks->hpPartLoadRatio,
9867 : SpeedNum,
9868 : SpeedRatio,
9869 : 0.0,
9870 : 0.0,
9871 : true,
9872 : false,
9873 10140 : 1.0);
9874 : } else {
9875 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9876 0 : IntegratedHeatPump::SimIHP(state,
9877 : HeatPump.DXCoilName,
9878 0 : HeatPump.DXCoilNum,
9879 : HVAC::FanOp::Cycling,
9880 : compressorOp,
9881 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9882 : SpeedNum,
9883 : SpeedRatio,
9884 : 0.0,
9885 : 0.0,
9886 : true,
9887 : false,
9888 0 : 1.0);
9889 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9890 :
9891 0 : IntegratedHeatPump::SimIHP(state,
9892 : HeatPump.DXCoilName,
9893 0 : HeatPump.DXCoilNum,
9894 : HVAC::FanOp::Cycling,
9895 : compressorOp,
9896 0 : state.dataWaterThermalTanks->hpPartLoadRatio,
9897 : SpeedNum,
9898 : SpeedRatio,
9899 : 0.0,
9900 : 0.0,
9901 : true,
9902 : false,
9903 0 : 1.0);
9904 0 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9905 : }
9906 : } else {
9907 : // pass node information using resulting PLR
9908 45456 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9909 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9910 29676 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9911 :
9912 29676 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9913 : HeatPump.DXCoilName,
9914 29676 : HeatPump.DXCoilNum,
9915 : HVAC::FanOp::Cycling,
9916 : compressorOp,
9917 29676 : state.dataWaterThermalTanks->hpPartLoadRatio,
9918 : SpeedNum,
9919 : SpeedRatio,
9920 : 0.0,
9921 : 0.0,
9922 : 1.0);
9923 :
9924 29676 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9925 :
9926 29676 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9927 : HeatPump.DXCoilName,
9928 29676 : HeatPump.DXCoilNum,
9929 : HVAC::FanOp::Cycling,
9930 : compressorOp,
9931 29676 : state.dataWaterThermalTanks->hpPartLoadRatio,
9932 : SpeedNum,
9933 : SpeedRatio,
9934 : 0.0,
9935 : 0.0,
9936 : 1.0);
9937 : } else {
9938 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9939 15780 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9940 : HeatPump.DXCoilName,
9941 15780 : HeatPump.DXCoilNum,
9942 : HVAC::FanOp::Cycling,
9943 : compressorOp,
9944 15780 : state.dataWaterThermalTanks->hpPartLoadRatio,
9945 : SpeedNum,
9946 : SpeedRatio,
9947 : 0.0,
9948 : 0.0,
9949 : 1.0);
9950 :
9951 15780 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9952 :
9953 15780 : VariableSpeedCoils::SimVariableSpeedCoils(state,
9954 : HeatPump.DXCoilName,
9955 15780 : HeatPump.DXCoilNum,
9956 : HVAC::FanOp::Cycling,
9957 : compressorOp,
9958 15780 : state.dataWaterThermalTanks->hpPartLoadRatio,
9959 : SpeedNum,
9960 : SpeedRatio,
9961 : 0.0,
9962 : 0.0,
9963 : 1.0);
9964 :
9965 15780 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9966 : }
9967 : }
9968 : } else { // single speed
9969 :
9970 : // pass node information using resulting PLR
9971 285848 : if (HeatPump.fanPlace == HVAC::FanPlace::BlowThru) {
9972 : // simulate fan and DX coil twice to pass PLF (OnOffFanPartLoadFraction) to fan
9973 67738 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9974 :
9975 135476 : DXCoils::SimDXCoil(state,
9976 : HeatPump.DXCoilName,
9977 : compressorOp,
9978 : FirstHVACIteration,
9979 67738 : HeatPump.DXCoilNum,
9980 : HVAC::FanOp::Cycling,
9981 67738 : state.dataWaterThermalTanks->hpPartLoadRatio);
9982 :
9983 67738 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
9984 :
9985 135476 : DXCoils::SimDXCoil(state,
9986 : HeatPump.DXCoilName,
9987 : compressorOp,
9988 : FirstHVACIteration,
9989 67738 : HeatPump.DXCoilNum,
9990 : HVAC::FanOp::Cycling,
9991 67738 : state.dataWaterThermalTanks->hpPartLoadRatio);
9992 : } else {
9993 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
9994 436220 : DXCoils::SimDXCoil(state,
9995 : HeatPump.DXCoilName,
9996 : compressorOp,
9997 : FirstHVACIteration,
9998 218110 : HeatPump.DXCoilNum,
9999 : HVAC::FanOp::Cycling,
10000 218110 : state.dataWaterThermalTanks->hpPartLoadRatio);
10001 :
10002 218110 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
10003 :
10004 436220 : DXCoils::SimDXCoil(state,
10005 : HeatPump.DXCoilName,
10006 : compressorOp,
10007 : FirstHVACIteration,
10008 218110 : HeatPump.DXCoilNum,
10009 : HVAC::FanOp::Cycling,
10010 218110 : state.dataWaterThermalTanks->hpPartLoadRatio);
10011 :
10012 218110 : state.dataFans->fans(HeatPump.FanNum)->simulate(state, FirstHVACIteration, _, _);
10013 : }
10014 : }
10015 :
10016 : // Call the tank one more time with the final PLR
10017 336374 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10018 136932 : this->CalcWaterThermalTankMixed(state);
10019 199442 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10020 199442 : this->CalcWaterThermalTankStratified(state);
10021 : } else {
10022 0 : assert(0);
10023 : }
10024 :
10025 : // set HPWH outlet node equal to the outlet air splitter node conditions if outlet air splitter node exists
10026 336374 : if (OutletAirSplitterNode > 0) {
10027 20029 : state.dataLoopNodes->Node(HPAirOutletNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
10028 20029 : state.dataLoopNodes->Node(ExhaustAirNode) = state.dataLoopNodes->Node(OutletAirSplitterNode);
10029 : }
10030 :
10031 : // Check schedule to divert air-side cooling to outdoors.
10032 336374 : if (HeatPump.OutletAirSplitterSchPtr > 0) {
10033 20029 : Real64 OutletAirSplitterSch = ScheduleManager::GetCurrentScheduleValue(state, HeatPump.OutletAirSplitterSchPtr);
10034 20029 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate =
10035 20029 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * (1.0 - OutletAirSplitterSch);
10036 20029 : state.dataLoopNodes->Node(ExhaustAirNode).MassFlowRate =
10037 20029 : state.dataWaterThermalTanks->mdotAir * state.dataWaterThermalTanks->hpPartLoadRatio * OutletAirSplitterSch;
10038 : }
10039 :
10040 336374 : HeatPump.HeatingPLR = state.dataWaterThermalTanks->hpPartLoadRatio;
10041 336374 : HeatPump.OnCycParaFuelRate = HeatPump.OnCycParaLoad * state.dataWaterThermalTanks->hpPartLoadRatio;
10042 336374 : HeatPump.OnCycParaFuelEnergy = HeatPump.OnCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
10043 336374 : HeatPump.OffCycParaFuelRate = HeatPump.OffCycParaLoad * (1.0 - state.dataWaterThermalTanks->hpPartLoadRatio);
10044 336374 : HeatPump.OffCycParaFuelEnergy = HeatPump.OffCycParaFuelRate * state.dataHVACGlobal->TimeStepSysSec;
10045 336374 : if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10046 136932 : HeatPump.ControlTempAvg = this->TankTempAvg;
10047 136932 : HeatPump.ControlTempFinal = this->TankTemp;
10048 199442 : } else if (HeatPump.HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10049 199442 : HeatPump.ControlTempAvg = this->FindStratifiedTankSensedTemp(state, true);
10050 199442 : HeatPump.ControlTempFinal = this->FindStratifiedTankSensedTemp(state);
10051 : } else {
10052 0 : assert(0);
10053 : }
10054 :
10055 336374 : switch (HeatPump.InletAirConfiguration) {
10056 :
10057 : // no sensible capacity to zone for outdoor and scheduled HPWH
10058 146532 : case WTTAmbientTemp::OutsideAir:
10059 : case WTTAmbientTemp::Schedule: {
10060 146532 : HeatPump.HPWaterHeaterSensibleCapacity = 0.0;
10061 146532 : HeatPump.HPWaterHeaterLatentCapacity = 0.0;
10062 :
10063 : // calculate sensible capacity to zone for inlet air configuration equals Zone Only or Zone And Outdoor Air configurations
10064 146532 : break;
10065 : }
10066 189842 : default:
10067 :
10068 189842 : Real64 CpAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(HPAirInletNode).HumRat);
10069 :
10070 : // add parasitics to zone heat balance if parasitic heat load is to zone otherwise neglect parasitics
10071 189842 : if (HeatPump.ParasiticTempIndicator == WTTAmbientTemp::TempZone) {
10072 132070 : HeatPump.HPWaterHeaterSensibleCapacity =
10073 132070 : (state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
10074 132070 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp)) +
10075 132070 : HeatPump.OnCycParaFuelRate + HeatPump.OffCycParaFuelRate;
10076 : } else {
10077 57772 : HeatPump.HPWaterHeaterSensibleCapacity =
10078 57772 : state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate * CpAir *
10079 57772 : (state.dataLoopNodes->Node(HPAirOutletNode).Temp - state.dataLoopNodes->Node(HPAirInletNode).Temp);
10080 : }
10081 :
10082 189842 : HeatPump.HPWaterHeaterLatentCapacity = state.dataLoopNodes->Node(HPAirOutletNode).MassFlowRate *
10083 189842 : (state.dataLoopNodes->Node(HPAirOutletNode).HumRat - state.dataLoopNodes->Node(HPAirInletNode).HumRat);
10084 189842 : break;
10085 : }
10086 : }
10087 :
10088 993781 : void WaterThermalTankData::CalcWaterThermalTank(EnergyPlusData &state)
10089 : {
10090 993781 : switch (this->WaterThermalTankType) {
10091 387270 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
10092 387270 : this->CalcWaterThermalTankMixed(state);
10093 387270 : break;
10094 606511 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
10095 606511 : this->CalcWaterThermalTankStratified(state);
10096 606511 : break;
10097 0 : default:
10098 0 : assert(false);
10099 : }
10100 993781 : }
10101 :
10102 549933 : Real64 WaterThermalTankData::GetHPWHSensedTankTemp(EnergyPlusData &state)
10103 : {
10104 549933 : switch (this->WaterThermalTankType) {
10105 209584 : case DataPlant::PlantEquipmentType::WtrHeaterMixed:
10106 209584 : return this->TankTemp;
10107 : break;
10108 340349 : case DataPlant::PlantEquipmentType::WtrHeaterStratified:
10109 340349 : return this->FindStratifiedTankSensedTemp(state);
10110 : break;
10111 0 : default:
10112 0 : assert(false);
10113 : return 0.0; // silence compiler
10114 : }
10115 : }
10116 :
10117 133685 : void WaterThermalTankData::ConvergeSingleSpeedHPWHCoilAndTank(EnergyPlusData &state, Real64 const partLoadRatio)
10118 : {
10119 133685 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
10120 133685 : DXCoils::DXCoilData &Coil = state.dataDXCoils->DXCoil(HPWH.DXCoilNum);
10121 :
10122 133685 : Real64 PrevTankTemp = this->SourceOutletTemp;
10123 456268 : for (int i = 1; i <= 10; ++i) {
10124 :
10125 456268 : DXCoils::CalcHPWHDXCoil(state, HPWH.DXCoilNum, partLoadRatio);
10126 456268 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWH.CondWaterOutletNode).Temp;
10127 :
10128 456268 : this->CalcWaterThermalTank(state);
10129 456268 : state.dataLoopNodes->Node(Coil.WaterInNode).Temp = this->SourceOutletTemp;
10130 :
10131 456268 : if (std::abs(this->SourceOutletTemp - PrevTankTemp) < HVAC::SmallTempDiff) {
10132 133685 : break;
10133 : }
10134 :
10135 322583 : PrevTankTemp = this->SourceOutletTemp;
10136 : }
10137 133685 : }
10138 :
10139 562160 : void WaterThermalTankData::SetVSHPWHFlowRates(EnergyPlusData &state,
10140 : HeatPumpWaterHeaterData &HPWH, // heat pump coil
10141 : int const SpeedNum, // upper speed number
10142 : Real64 const SpeedRatio, // interpolation ration between upper and lower speed
10143 : Real64 const WaterDens, // tank water density
10144 : Real64 &MdotWater, // water flow rate
10145 : bool const FirstHVACIteration)
10146 : {
10147 : // FUNCTION INFORMATION:
10148 : // AUTHOR B.Shen, ORNL, 12/2014
10149 : // DATE WRITTEN May 2005
10150 : // MODIFIED
10151 : // RE-ENGINEERED
10152 :
10153 : // PURPOSE OF THIS FUNCTION:
10154 : // set water and air flow rates driven by the variable-speed HPWH coil
10155 : // calculate resultant HPWH coil output
10156 :
10157 562160 : int SpeedLow = SpeedNum - 1;
10158 562160 : if (SpeedLow < 1) SpeedLow = 1;
10159 :
10160 562160 : int HPWaterInletNode = HPWH.CondWaterInletNode;
10161 562160 : int DXCoilAirInletNode = HPWH.DXCoilAirInletNode;
10162 562160 : if (HPWH.bIsIHP) {
10163 19412 : HPWH.OperatingWaterFlowRate = IntegratedHeatPump::GetWaterVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio);
10164 19412 : state.dataWaterThermalTanks->mdotAir = IntegratedHeatPump::GetAirMassFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10165 19412 : HPWH.OperatingAirFlowRate = IntegratedHeatPump::GetAirVolFlowRateIHP(state, HPWH.DXCoilNum, SpeedNum, SpeedRatio, true);
10166 19412 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10167 19412 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10168 19412 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10169 : } else {
10170 542748 : HPWH.OperatingWaterFlowRate = HPWH.HPWHWaterVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHWaterVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10171 542748 : HPWH.OperatingAirFlowRate = HPWH.HPWHAirVolFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirVolFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10172 542748 : state.dataWaterThermalTanks->mdotAir =
10173 542748 : HPWH.HPWHAirMassFlowRate(SpeedNum) * SpeedRatio + HPWH.HPWHAirMassFlowRate(SpeedLow) * (1.0 - SpeedRatio);
10174 : }
10175 :
10176 562160 : MdotWater = HPWH.OperatingWaterFlowRate * WaterDens;
10177 562160 : this->SourceMassFlowRate = MdotWater;
10178 :
10179 562160 : state.dataLoopNodes->Node(DXCoilAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10180 562160 : state.dataLoopNodes->Node(HPWaterInletNode).MassFlowRate = MdotWater;
10181 562160 : this->SourceMassFlowRate = MdotWater;
10182 :
10183 562160 : if (HPWH.InletAirMixerNode > 0) {
10184 32423 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10185 32423 : state.dataLoopNodes->Node(HPWH.InletAirMixerNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10186 : } else {
10187 529737 : if (HPWH.OutsideAirNode == 0) {
10188 48807 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10189 48807 : state.dataLoopNodes->Node(HPWH.HeatPumpAirInletNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10190 : } else {
10191 480930 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10192 480930 : state.dataLoopNodes->Node(HPWH.OutsideAirNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10193 : }
10194 : }
10195 :
10196 : // put fan component first, regardless placement, to calculate fan power
10197 562160 : int FanInNode = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
10198 :
10199 562160 : state.dataLoopNodes->Node(FanInNode).MassFlowRate = state.dataWaterThermalTanks->mdotAir;
10200 562160 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10201 562160 : state.dataLoopNodes->Node(FanInNode).MassFlowRateMax = state.dataWaterThermalTanks->mdotAir;
10202 562160 : if (HPWH.fanType != HVAC::FanType::SystemModel) {
10203 84846 : state.dataFans->fans(HPWH.FanNum)->massFlowRateMaxAvail = state.dataWaterThermalTanks->mdotAir;
10204 : } // system fan will use the inlet node max avail.
10205 :
10206 562160 : state.dataFans->fans(HPWH.FanNum)->simulate(state, FirstHVACIteration, _, _);
10207 562160 : }
10208 :
10209 77990 : Real64 WaterThermalTankData::PLRResidualIterSpeed(EnergyPlusData &state,
10210 : Real64 const SpeedRatio, // speed ratio between two speed levels
10211 : int const HPNum,
10212 : int const SpeedNum,
10213 : int const HPWaterInletNode,
10214 : int const HPWaterOutletNode,
10215 : Real64 const RhoWater,
10216 : Real64 const desTankTemp,
10217 : TankOperatingMode const mode,
10218 : bool const FirstHVACIteration)
10219 : {
10220 : // FUNCTION INFORMATION:
10221 : // AUTHOR B.Shen, ORNL, 12/2014
10222 : // MODIFIED
10223 : // RE-ENGINEERED
10224 :
10225 : // PURPOSE OF THIS FUNCTION:
10226 : // Calculates residual function (desired tank temp - actual tank temp), when iterating speed ration between two speed levels
10227 : // HP water heater output depends on the speed ratio which is being varied to zero the residual.
10228 :
10229 : // METHODOLOGY EMPLOYED:
10230 : // Calls residuals to get tank temperature at the given speed ratio between a lower and an upper speed levels
10231 : // and calculates the residual as defined respectively for DataPlant::PlantEquipmentType::WtrHeaterMixed or
10232 : // DataPlant::PlantEquipmentType::WtrHeaterStratified
10233 :
10234 77990 : this->Mode = mode;
10235 77990 : state.dataWaterThermalTanks->hpPartLoadRatio = 1.0;
10236 77990 : Real64 MdotWater = 0.0;
10237 :
10238 77990 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
10239 :
10240 77990 : this->SetVSHPWHFlowRates(state, HPWH, SpeedNum, SpeedRatio, RhoWater, MdotWater, FirstHVACIteration);
10241 :
10242 77990 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
10243 96 : IntegratedHeatPump::SimIHP(state,
10244 96 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10245 96 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10246 : HVAC::FanOp::Cycling,
10247 : HVAC::CompressorOp::On,
10248 96 : state.dataWaterThermalTanks->hpPartLoadRatio,
10249 : SpeedNum,
10250 : SpeedRatio,
10251 : 0.0,
10252 : 0.0,
10253 : true,
10254 : false,
10255 192 : 1.0);
10256 : } else {
10257 77894 : VariableSpeedCoils::SimVariableSpeedCoils(state,
10258 77894 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
10259 77894 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
10260 : HVAC::FanOp::Cycling,
10261 : HVAC::CompressorOp::On,
10262 77894 : state.dataWaterThermalTanks->hpPartLoadRatio,
10263 : SpeedNum,
10264 : SpeedRatio,
10265 : 0.0,
10266 : 0.0,
10267 : 1.0);
10268 : }
10269 :
10270 : Real64 CondenserDeltaT;
10271 77990 : CondenserDeltaT = state.dataLoopNodes->Node(HPWaterOutletNode).Temp - state.dataLoopNodes->Node(HPWaterInletNode).Temp;
10272 :
10273 : // move the full load outlet temperature rate to the water heater structure variables
10274 : // (water heaters source inlet node temperature/mdot are set in Init, set it here after DXCoils::CalcHPWHDXCoil has been called)
10275 77990 : this->SourceInletTemp = state.dataLoopNodes->Node(HPWaterInletNode).Temp + CondenserDeltaT;
10276 :
10277 : // this CALL does not update node temps, must use WaterThermalTank variables
10278 : // select tank type
10279 77990 : Real64 NewTankTemp = 0.0;
10280 77990 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10281 26838 : this->CalcWaterThermalTankMixed(state);
10282 26838 : NewTankTemp = this->TankTemp;
10283 51152 : } else if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
10284 51152 : this->CalcWaterThermalTankStratified(state);
10285 51152 : NewTankTemp = this->FindStratifiedTankSensedTemp(state);
10286 : }
10287 :
10288 77990 : return desTankTemp - NewTankTemp;
10289 : }
10290 :
10291 57647 : Real64 WaterThermalTankData::PLRResidualHPWH(
10292 : EnergyPlusData &state, Real64 const HPPartLoadRatio, Real64 const desTankTemp, TankOperatingMode const mode, Real64 const mDotWater)
10293 : {
10294 : // FUNCTION INFORMATION:
10295 : // AUTHOR B.Griffith, Richard Raustad
10296 : // DATE WRITTEN Jan 2012
10297 : // MODIFIED
10298 : // RE-ENGINEERED Noel Merket, Oct 2015
10299 :
10300 : // PURPOSE OF THIS FUNCTION:
10301 : // Calculates residual function (desired tank temp - actual tank temp)
10302 : // HP water heater output depends on the part load ratio which is being varied to zero the residual.
10303 :
10304 : // METHODOLOGY EMPLOYED:
10305 : // Calls with CalcWaterThermalTankMixed or CalcWaterThermalTankStratified to get tank temperature at the given part load ratio (source water
10306 : // mass flow rate) and calculates the residual as defined above
10307 :
10308 57647 : HeatPumpWaterHeaterData &HeatPump = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
10309 57647 : bool const isVariableSpeed = (HeatPump.NumofSpeed > 0);
10310 57647 : this->Mode = mode;
10311 : // Apply the PLR
10312 57647 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
10313 : // For a mixed tank, the PLR is applied to the source mass flow rate.
10314 22169 : this->SourceMassFlowRate = mDotWater * HPPartLoadRatio;
10315 22169 : this->CalcWaterThermalTankMixed(state);
10316 : } else {
10317 35478 : assert(this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified);
10318 : // For a stratified tank, the PLR is applied to the Coil.TotalHeatingEnergyRate
10319 : // whether that's a VarSpeedCoil or DXCoils::DXCoil.
10320 : // Here we create a pointer to the TotalHeatingEnergyRate for the appropriate coil type.
10321 : Real64 *CoilTotalHeatingEnergyRatePtr;
10322 35478 : if (isVariableSpeed) {
10323 621 : if (HeatPump.bIsIHP)
10324 0 : CoilTotalHeatingEnergyRatePtr = &state.dataIntegratedHP->IntegratedHeatPumps(HeatPump.DXCoilNum).TotalWaterHeatingRate;
10325 : else
10326 621 : CoilTotalHeatingEnergyRatePtr = &state.dataVariableSpeedCoils->VarSpeedCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10327 : } else {
10328 34857 : CoilTotalHeatingEnergyRatePtr = &state.dataDXCoils->DXCoil(HeatPump.DXCoilNum).TotalHeatingEnergyRate;
10329 : }
10330 : // Copy the value of the total heating energy rate
10331 35478 : Real64 const CoilTotalHeatingEnergyRateBackup = *CoilTotalHeatingEnergyRatePtr;
10332 : // Apply the PLR
10333 35478 : *CoilTotalHeatingEnergyRatePtr *= HPPartLoadRatio;
10334 : // Tank Calculation
10335 35478 : this->CalcWaterThermalTankStratified(state);
10336 : // Restore the original value
10337 35478 : *CoilTotalHeatingEnergyRatePtr = CoilTotalHeatingEnergyRateBackup;
10338 : }
10339 57647 : Real64 NewTankTemp = this->GetHPWHSensedTankTemp(state);
10340 57647 : return desTankTemp - NewTankTemp;
10341 : }
10342 :
10343 1380552 : bool WaterThermalTankData::SourceHeatNeed(EnergyPlusData &state, Real64 const OutletTemp, Real64 const DeadBandTemp, Real64 const SetPointTemp_loc)
10344 : {
10345 : // FUNCTION INFORMATION:
10346 : // AUTHOR Yueyue Zhou
10347 : // DATE WRITTEN May 2019
10348 : // MODIFIED na
10349 : // RE-ENGINEERED na
10350 :
10351 : // PURPOSE OF THIS FUNCTION:
10352 : // Determine by tank type, tank temperature and control mode if source side flow is needed
10353 :
10354 : // return value initialization
10355 1380552 : bool NeedsHeatOrCool = false;
10356 :
10357 1380552 : if (!this->IsChilledWaterTank) {
10358 962056 : if (this->SourceSideControlMode == SourceSideControl::IndirectHeatPrimarySetpoint) {
10359 962056 : if (OutletTemp < DeadBandTemp) {
10360 143302 : NeedsHeatOrCool = true;
10361 818754 : } else if ((OutletTemp >= DeadBandTemp) && (OutletTemp < SetPointTemp_loc)) {
10362 : // inside the deadband, use saved mode from water heater calcs
10363 378433 : if (this->SavedMode == TankOperatingMode::Heating) {
10364 88317 : NeedsHeatOrCool = true;
10365 290116 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10366 290116 : NeedsHeatOrCool = false;
10367 : }
10368 :
10369 440321 : } else if (OutletTemp >= SetPointTemp_loc) {
10370 440321 : NeedsHeatOrCool = false;
10371 : }
10372 0 : } else if (this->SourceSideControlMode == SourceSideControl::IndirectHeatAltSetpoint) {
10373 : // get alternate setpoint
10374 0 : Real64 const AltSetpointTemp = ScheduleManager::GetCurrentScheduleValue(state, this->SourceSideAltSetpointSchedNum);
10375 0 : Real64 const AltDeadBandTemp = AltSetpointTemp - this->DeadBandDeltaTemp;
10376 0 : if (OutletTemp < AltDeadBandTemp) {
10377 0 : NeedsHeatOrCool = true;
10378 0 : } else if ((OutletTemp >= AltDeadBandTemp) && (OutletTemp < AltSetpointTemp)) {
10379 : // inside the deadband, use saved mode from water heater calcs
10380 0 : if (this->SavedMode == TankOperatingMode::Heating) {
10381 0 : NeedsHeatOrCool = true;
10382 0 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10383 0 : NeedsHeatOrCool = false;
10384 : }
10385 :
10386 0 : } else if (OutletTemp >= AltSetpointTemp) {
10387 0 : NeedsHeatOrCool = false;
10388 : }
10389 0 : } else if (this->SourceSideControlMode == SourceSideControl::StorageTank) {
10390 0 : if (OutletTemp < this->TankTempLimit) {
10391 0 : NeedsHeatOrCool = true;
10392 : } else {
10393 0 : NeedsHeatOrCool = false;
10394 : }
10395 : }
10396 : } else { // is a chilled water tank so flip logic
10397 418496 : if (OutletTemp > DeadBandTemp) {
10398 10920 : NeedsHeatOrCool = true;
10399 407576 : } else if ((OutletTemp <= DeadBandTemp) && (OutletTemp > SetPointTemp_loc)) {
10400 : // inside the deadband, use saved mode from water thermal tank calcs (modes only for mixed)
10401 301506 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankMixed) {
10402 275181 : if (this->SavedMode == TankOperatingMode::Cooling) {
10403 36160 : NeedsHeatOrCool = true;
10404 239021 : } else if (this->SavedMode == TankOperatingMode::Floating) {
10405 239021 : NeedsHeatOrCool = false;
10406 : }
10407 26325 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::ChilledWaterTankStratified) {
10408 26325 : NeedsHeatOrCool = true;
10409 : }
10410 :
10411 106070 : } else if (OutletTemp <= SetPointTemp_loc) {
10412 106070 : NeedsHeatOrCool = false;
10413 : }
10414 : }
10415 1380552 : return NeedsHeatOrCool;
10416 : }
10417 :
10418 7721520 : Real64 WaterThermalTankData::PlantMassFlowRatesFunc(EnergyPlusData &state,
10419 : int const InNodeNum,
10420 : bool const FirstHVACIteration,
10421 : WaterHeaterSide const WaterThermalTankSide,
10422 : const DataPlant::LoopSideLocation PlantLoopSide,
10423 : [[maybe_unused]] bool const PlumbedInSeries,
10424 : DataBranchAirLoopPlant::ControlType const BranchControlType,
10425 : Real64 const OutletTemp,
10426 : Real64 const DeadBandTemp,
10427 : Real64 const SetPointTemp_loc)
10428 : {
10429 :
10430 : // FUNCTION INFORMATION:
10431 : // AUTHOR Brent Griffith
10432 : // DATE WRITTEN October 2007
10433 : // MODIFIED na
10434 : // RE-ENGINEERED na
10435 :
10436 : // PURPOSE OF THIS FUNCTION:
10437 : // collect routines for setting flow rates for Water heaters
10438 : // with plant connections.
10439 :
10440 : // determine current mode. there are three possible
10441 : // 1. passing thru what was given to inlet node
10442 : // 2. potentially making a flow request
10443 : // 3. throttling flow in response to Plant's restrictions (MassFlowRateMaxAvail)
10444 : // init default mode changed to Unassigned
10445 7721520 : FlowMode CurrentMode = FlowMode::Invalid; // default
10446 :
10447 7721520 : if (PlantLoopSide == DataPlant::LoopSideLocation::Invalid) {
10448 478220 : CurrentMode = FlowMode::PassingFlowThru;
10449 7243300 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Supply) {
10450 : // If FlowLock is False (0), the tank sets the plant loop mdot
10451 : // If FlowLock is True (1), the new resolved plant loop mdot is used
10452 6683608 : if (this->UseCurrentFlowLock == DataPlant::FlowLock::Unlocked) {
10453 2781163 : CurrentMode = FlowMode::PassingFlowThru;
10454 2781163 : if ((this->UseSideLoadRequested > 0.0) && (WaterThermalTankSide == WaterHeaterSide::Use)) {
10455 2115551 : CurrentMode = FlowMode::MaybeRequestingFlow;
10456 : }
10457 : } else {
10458 3902445 : CurrentMode = FlowMode::PassingFlowThru;
10459 : }
10460 6683608 : if (WaterThermalTankSide == WaterHeaterSide::Source) {
10461 820860 : CurrentMode = FlowMode::MaybeRequestingFlow;
10462 : }
10463 559692 : } else if (PlantLoopSide == DataPlant::LoopSideLocation::Demand) {
10464 :
10465 : // 2. Might be Requesting Flow.
10466 559692 : if (FirstHVACIteration) {
10467 279604 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10468 0 : CurrentMode = FlowMode::PassingFlowThru;
10469 : } else {
10470 279604 : CurrentMode = FlowMode::MaybeRequestingFlow;
10471 : }
10472 : } else {
10473 280088 : if (BranchControlType == DataBranchAirLoopPlant::ControlType::Bypass) {
10474 0 : CurrentMode = FlowMode::PassingFlowThru;
10475 : } else {
10476 280088 : CurrentMode = FlowMode::ThrottlingFlow;
10477 : }
10478 : }
10479 : }
10480 :
10481 : // evaluate Availability schedule,
10482 7721520 : bool ScheduledAvail = true;
10483 7721520 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10484 5862748 : if (ScheduleManager::GetCurrentScheduleValue(state, this->UseSideAvailSchedNum) == 0.0) {
10485 0 : ScheduledAvail = false;
10486 : }
10487 1858772 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10488 1858772 : if (ScheduleManager::GetCurrentScheduleValue(state, this->SourceSideAvailSchedNum) == 0.0) {
10489 58864 : ScheduledAvail = false;
10490 : }
10491 : }
10492 :
10493 : // now act based on current mode
10494 7721520 : Real64 FlowResult = 0.0;
10495 7721520 : switch (CurrentMode) {
10496 :
10497 4225417 : case FlowMode::PassingFlowThru: {
10498 4225417 : if (!ScheduledAvail) {
10499 0 : FlowResult = 0.0;
10500 : } else {
10501 4225417 : FlowResult = state.dataLoopNodes->Node(InNodeNum).MassFlowRate;
10502 : }
10503 :
10504 4225417 : break;
10505 : }
10506 280088 : case FlowMode::ThrottlingFlow: {
10507 : // first determine what mass flow would be if it is to requested
10508 280088 : Real64 MassFlowRequest = 0.0;
10509 280088 : if (!ScheduledAvail) {
10510 29432 : MassFlowRequest = 0.0;
10511 : } else {
10512 250656 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10513 0 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10514 250656 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10515 250656 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10516 : } else {
10517 0 : assert(false);
10518 : }
10519 : }
10520 :
10521 : // next determine if tank temperature is such that source side flow might be requested
10522 280088 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10523 :
10524 280088 : if (MassFlowRequest > 0.0) {
10525 250656 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10526 0 : FlowResult = MassFlowRequest;
10527 250656 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10528 250656 : if (NeedsHeatOrCool) {
10529 54324 : FlowResult = MassFlowRequest;
10530 : } else {
10531 196332 : FlowResult = 0.0;
10532 : }
10533 : } else {
10534 0 : assert(false);
10535 : }
10536 : } else {
10537 29432 : FlowResult = 0.0;
10538 : }
10539 :
10540 : // now throttle against MassFlowRateMaxAvail, MassFlowRateMinAvail, MassFlowRateMax, and MassFlowRateMin
10541 : // see notes about reverse dd compliance (specifically 5ZoneWaterSystems file)
10542 280088 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMinAvail, FlowResult); // okay for compliance (reverse dd)
10543 280088 : FlowResult = max(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMin, FlowResult); // okay for compliance (reverse dd)
10544 280088 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMaxAvail, FlowResult);
10545 : //=> following might take out of reverse dd compliance
10546 280088 : FlowResult = min(state.dataLoopNodes->Node(InNodeNum).MassFlowRateMax, FlowResult);
10547 :
10548 280088 : break;
10549 : }
10550 3216015 : case FlowMode::MaybeRequestingFlow: {
10551 :
10552 : // first determine what mass flow would be if it is to requested
10553 3216015 : Real64 MassFlowRequest = 0.0;
10554 3216015 : if (!ScheduledAvail) {
10555 29432 : MassFlowRequest = 0.0;
10556 : } else {
10557 3186583 : if (WaterThermalTankSide == WaterHeaterSide::Use) {
10558 2115551 : if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested > 0.0)) {
10559 26542 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10560 2089009 : } else if ((this->IsChilledWaterTank) && (this->UseSideLoadRequested == 0.0)) {
10561 0 : MassFlowRequest = 0.0;
10562 : } else {
10563 2089009 : MassFlowRequest = this->PlantUseMassFlowRateMax;
10564 : }
10565 :
10566 1071032 : } else if (WaterThermalTankSide == WaterHeaterSide::Source) {
10567 1071032 : MassFlowRequest = this->PlantSourceMassFlowRateMax;
10568 : }
10569 : }
10570 :
10571 3216015 : if (WaterThermalTankSide == WaterHeaterSide::Source) { // temperature dependent controls for indirect heating/cooling
10572 1100464 : bool NeedsHeatOrCool = this->SourceHeatNeed(state, OutletTemp, DeadBandTemp, SetPointTemp_loc);
10573 1100464 : if (MassFlowRequest > 0.0) {
10574 1071011 : if (NeedsHeatOrCool) {
10575 230170 : FlowResult = MassFlowRequest;
10576 : } else {
10577 840841 : FlowResult = 0.0;
10578 : }
10579 : } else {
10580 29453 : FlowResult = 0.0;
10581 : }
10582 : } else { // end source side, begin use side
10583 2115551 : if (MassFlowRequest > 0.0) {
10584 2115551 : FlowResult = MassFlowRequest;
10585 : } else {
10586 0 : FlowResult = 0.0;
10587 : }
10588 : }
10589 3216015 : break;
10590 : }
10591 0 : default:
10592 0 : break;
10593 : }
10594 :
10595 7721520 : if (FlowResult < HVAC::VerySmallMassFlow) FlowResult = 0.0; // Catch underflow problems
10596 :
10597 7721520 : return FlowResult;
10598 : }
10599 :
10600 968 : void WaterThermalTankData::MinePlantStructForInfo(EnergyPlusData &state)
10601 : {
10602 :
10603 : // SUBROUTINE INFORMATION:
10604 : // AUTHOR Brent Griffith
10605 : // DATE WRITTEN October 2007
10606 : // MODIFIED na
10607 : // RE-ENGINEERED na
10608 :
10609 : // PURPOSE OF THIS SUBROUTINE:
10610 : // get information from plant loop data structure
10611 : // check what we can learn from plant structure against user inputs
10612 :
10613 968 : bool ErrorsFound = false;
10614 :
10615 968 : if (allocated(state.dataPlnt->PlantLoop) && this->UseSidePlantLoc.loopNum > 0) {
10616 :
10617 : // check plant structure for useful data.
10618 :
10619 895 : int PlantLoopNum = this->UseSidePlantLoc.loopNum;
10620 895 : DataPlant::LoopSideLocation LoopSideNum = this->UseSidePlantLoc.loopSideNum;
10621 :
10622 895 : if ((this->UseDesignVolFlowRateWasAutoSized) && (this->UseSidePlantSizNum == 0)) {
10623 0 : ShowSevereError(state,
10624 0 : format("Water heater = {} for autosizing Use side flow rate, did not find Sizing:Plant object {}",
10625 0 : this->Name,
10626 0 : state.dataPlnt->PlantLoop(PlantLoopNum).Name));
10627 0 : ErrorsFound = true;
10628 : }
10629 : // Is this wh Use side plumbed in series (default) or are there other branches in parallel?
10630 895 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.Exists) {
10631 895 : if (any_eq(state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.NodeNumOut,
10632 895 : this->UseInletNode)) { // this wh is on the splitter
10633 860 : if (state.dataPlnt->PlantLoop(PlantLoopNum).LoopSide(LoopSideNum).Splitter.TotalOutletNodes > 1) {
10634 784 : this->UseSideSeries = false;
10635 : }
10636 : }
10637 : }
10638 : }
10639 :
10640 968 : if (allocated(state.dataPlnt->PlantLoop) && this->SrcSidePlantLoc.loopNum > 0) {
10641 : // was user's input correct for plant loop name?
10642 231 : if ((this->SourceDesignVolFlowRateWasAutoSized) && (this->SourceSidePlantSizNum == 0) && (this->DesuperheaterNum == 0) &&
10643 0 : (this->HeatPumpNum == 0)) {
10644 0 : ShowSevereError(state,
10645 0 : format("Water heater = {}for autosizing Source side flow rate, did not find Sizing:Plant object {}",
10646 0 : this->Name,
10647 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).Name));
10648 0 : ErrorsFound = true;
10649 : }
10650 : // Is this wh Source side plumbed in series (default) or are there other branches in parallel?
10651 231 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.Exists) {
10652 231 : if (any_eq(state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.NodeNumOut,
10653 231 : this->SourceInletNode)) { // this wh is on the splitter
10654 231 : if (state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).LoopSide(this->SrcSidePlantLoc.loopSideNum).Splitter.TotalOutletNodes >
10655 : 1) {
10656 77 : this->SourceSideSeries = false;
10657 : }
10658 : }
10659 : }
10660 : }
10661 :
10662 968 : if (ErrorsFound) {
10663 0 : ShowFatalError(state, "Preceding water heater input errors cause program termination");
10664 : }
10665 968 : }
10666 :
10667 766 : void WaterThermalTankData::SizeSupplySidePlantConnections(EnergyPlusData &state, const int loopNum)
10668 : {
10669 :
10670 : // SUBROUTINE INFORMATION:
10671 : // AUTHOR Brent Griffith
10672 : // DATE WRITTEN October 2007
10673 : // MODIFIED na
10674 : // RE-ENGINEERED na
10675 :
10676 : // PURPOSE OF THIS SUBROUTINE:
10677 : // This subroutine is for sizing water heater plant connection flow rates
10678 : // on the supply that have not been specified in the input.
10679 :
10680 : // METHODOLOGY EMPLOYED:
10681 : // This routine is called later in the simulation than the sizing routine for the demand side
10682 : // because the simulation needs to be further along before the needed data are available.
10683 : // For water heaters sides on Supply LoopSide, obtains hot water flow rate from the plant sizing array
10684 : // (adapted approach from boiler sizing routines)
10685 :
10686 : static constexpr std::string_view RoutineName("SizeSupplySidePlantConnections");
10687 :
10688 766 : auto &PlantSizData = state.dataSize->PlantSizData;
10689 :
10690 766 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
10691 766 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
10692 :
10693 766 : if ((this->UseInletNode > 0) && (loopNum == this->UseSidePlantLoc.loopNum)) {
10694 661 : if (this->UseDesignVolFlowRateWasAutoSized) {
10695 631 : int PltSizNum = this->UseSidePlantSizNum;
10696 631 : if (PltSizNum > 0) { // we have a Plant Sizing Object
10697 631 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10698 631 : if (PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
10699 508 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10700 139 : this->UseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10701 : } else {
10702 369 : tmpUseDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10703 : }
10704 : } else {
10705 123 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10706 0 : this->UseDesignVolFlowRate = 0.0;
10707 : } else {
10708 123 : tmpUseDesignVolFlowRate = 0.0;
10709 : }
10710 : }
10711 631 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10712 123 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10713 : }
10714 631 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10715 8 : BaseSizer::reportSizerOutput(
10716 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
10717 : }
10718 631 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10719 139 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10720 : } else {
10721 492 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
10722 : }
10723 :
10724 631 : Real64 rho = FluidProperties::GetDensityGlycol(state,
10725 631 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
10726 : Constant::InitConvTemp,
10727 631 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
10728 : RoutineName);
10729 631 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10730 139 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10731 : } else {
10732 492 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
10733 : }
10734 : }
10735 : } else {
10736 : // do nothing
10737 : } // plant sizing object
10738 : } else {
10739 30 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
10740 : Real64 rho;
10741 30 : if (this->UseSidePlantLoc.loopNum > 0) {
10742 30 : rho = FluidProperties::GetDensityGlycol(state,
10743 30 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
10744 : Constant::InitConvTemp,
10745 30 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
10746 : RoutineName);
10747 : } else {
10748 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, this->waterIndex, RoutineName);
10749 : }
10750 :
10751 30 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
10752 :
10753 : } // autosizing needed.
10754 : } // connected to plant
10755 :
10756 766 : if ((this->SourceInletNode > 0) && (loopNum == this->SrcSidePlantLoc.loopNum)) {
10757 105 : if (this->SourceDesignVolFlowRateWasAutoSized) {
10758 55 : int PltSizNum = this->SourceSidePlantSizNum;
10759 55 : if (PltSizNum > 0) {
10760 55 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10761 25 : if (PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
10762 18 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10763 5 : this->SourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10764 : } else {
10765 13 : tmpSourceDesignVolFlowRate = PlantSizData(PltSizNum).DesVolFlowRate;
10766 : }
10767 : } else {
10768 7 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10769 0 : this->SourceDesignVolFlowRate = 0.0;
10770 : } else {
10771 7 : tmpSourceDesignVolFlowRate = 0.0;
10772 : }
10773 : }
10774 25 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10775 5 : BaseSizer::reportSizerOutput(
10776 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10777 : }
10778 25 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10779 0 : BaseSizer::reportSizerOutput(
10780 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
10781 : }
10782 25 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10783 5 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10784 : } else {
10785 20 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
10786 : }
10787 25 : Real64 rho = FluidProperties::GetDensityGlycol(state,
10788 25 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
10789 : Constant::InitConvTemp,
10790 25 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
10791 : RoutineName);
10792 25 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10793 5 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10794 : } else {
10795 20 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
10796 : }
10797 : } // plant loop allocation
10798 : } else {
10799 : // do nothing
10800 : } // plant sizing object
10801 : } else {
10802 50 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
10803 45 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
10804 : Real64 rho;
10805 45 : if (this->SrcSidePlantLoc.loopNum > 0) {
10806 45 : rho = FluidProperties::GetDensityGlycol(state,
10807 45 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
10808 : Constant::InitConvTemp,
10809 45 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
10810 : RoutineName);
10811 : } else {
10812 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, this->waterIndex, RoutineName);
10813 : }
10814 45 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
10815 : }
10816 : } // autosizing needed.
10817 : } // connected to plant
10818 766 : }
10819 :
10820 766 : void WaterThermalTankData::SizeTankForDemandSide(EnergyPlusData &state)
10821 : {
10822 :
10823 : // SUBROUTINE INFORMATION:
10824 : // AUTHOR Brent Griffith
10825 : // DATE WRITTEN February 2008
10826 : // MODIFIED na
10827 : // RE-ENGINEERED na
10828 :
10829 : // PURPOSE OF THIS SUBROUTINE:
10830 : // This subroutine is for sizing water heater tank volume and heater
10831 : // as best we can at this point in simulation. (prior to demand side
10832 : // sizing that needs volume).
10833 :
10834 : // METHODOLOGY EMPLOYED:
10835 : // depending on the sizing design mode...
10836 :
10837 : // REFERENCES:
10838 : // BA benchmark report for residential design mode
10839 :
10840 : // SUBROUTINE PARAMETER DEFINITIONS:
10841 : static constexpr std::string_view RoutineName("SizeTankForDemandSide");
10842 766 : Real64 constexpr GalTocubicMeters(0.0037854);
10843 766 : Real64 constexpr kBtuPerHrToWatts(293.1);
10844 :
10845 766 : Real64 Tstart = 14.44;
10846 766 : Real64 Tfinish = 57.22;
10847 :
10848 766 : Real64 tmpTankVolume = this->Volume;
10849 766 : Real64 tmpMaxCapacity = this->MaxCapacity;
10850 :
10851 766 : switch (this->Sizing.DesignMode) {
10852 :
10853 751 : case SizingMode::Invalid:
10854 : case SizingMode::PeakDraw: {
10855 :
10856 751 : break;
10857 : }
10858 15 : case SizingMode::ResidentialMin: {
10859 :
10860 : // assume can propagate rules for gas to other fuels.
10861 15 : bool FuelTypeIsLikeGas = false;
10862 15 : switch (this->FuelType) {
10863 0 : case Constant::eFuel::NaturalGas:
10864 : case Constant::eFuel::Diesel:
10865 : case Constant::eFuel::Gasoline:
10866 : case Constant::eFuel::Coal:
10867 : case Constant::eFuel::FuelOilNo1:
10868 : case Constant::eFuel::FuelOilNo2:
10869 : case Constant::eFuel::Propane:
10870 : case Constant::eFuel::OtherFuel1:
10871 : case Constant::eFuel::OtherFuel2:
10872 : case Constant::eFuel::DistrictHeatingWater:
10873 : case Constant::eFuel::DistrictHeatingSteam:
10874 0 : FuelTypeIsLikeGas = true;
10875 0 : break;
10876 15 : default: // FuelTypeIsLikeGas stays false
10877 15 : break;
10878 : }
10879 :
10880 15 : if (this->Sizing.NumberOfBedrooms == 1) {
10881 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10882 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
10883 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
10884 0 : } else if (FuelTypeIsLikeGas) {
10885 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
10886 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
10887 : }
10888 :
10889 15 : } else if (this->Sizing.NumberOfBedrooms == 2) {
10890 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10891 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10892 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10893 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
10894 0 : } else if (FuelTypeIsLikeGas) {
10895 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10896 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10897 : }
10898 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10899 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10900 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10901 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10902 0 : } else if (FuelTypeIsLikeGas) {
10903 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10904 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10905 : }
10906 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10907 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10908 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10909 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10910 0 : } else if (FuelTypeIsLikeGas) {
10911 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10912 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10913 : }
10914 : }
10915 15 : } else if (this->Sizing.NumberOfBedrooms == 3) {
10916 15 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10917 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10918 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10919 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
10920 0 : } else if (FuelTypeIsLikeGas) {
10921 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
10922 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10923 : }
10924 15 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10925 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10926 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10927 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10928 0 : } else if (FuelTypeIsLikeGas) {
10929 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10930 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10931 : }
10932 15 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10933 15 : if (this->FuelType == Constant::eFuel::Electricity) {
10934 15 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10935 15 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10936 0 : } else if (FuelTypeIsLikeGas) {
10937 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10938 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10939 : }
10940 : }
10941 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
10942 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
10943 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10944 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10945 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10946 0 : } else if (FuelTypeIsLikeGas) {
10947 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10948 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
10949 : }
10950 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
10951 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10952 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10953 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10954 0 : } else if (FuelTypeIsLikeGas) {
10955 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
10956 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10957 : }
10958 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
10959 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10960 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10961 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10962 0 : } else if (FuelTypeIsLikeGas) {
10963 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10964 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
10965 : }
10966 : }
10967 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
10968 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10969 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10970 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10971 0 : } else if (FuelTypeIsLikeGas) {
10972 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10973 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
10974 : }
10975 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
10976 0 : if (this->FuelType == Constant::eFuel::Electricity) {
10977 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
10978 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
10979 0 : } else if (FuelTypeIsLikeGas) {
10980 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
10981 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
10982 : }
10983 : }
10984 :
10985 15 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10986 0 : this->Volume = tmpTankVolume;
10987 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10988 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
10989 : }
10990 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
10991 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
10992 : }
10993 : }
10994 15 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
10995 3 : this->MaxCapacity = tmpMaxCapacity;
10996 3 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
10997 3 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
10998 : }
10999 3 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11000 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11001 : }
11002 : }
11003 15 : break;
11004 : }
11005 0 : case SizingMode::PerPerson: {
11006 : // how to get number of people?
11007 :
11008 : // MJW TODO: this won't compile now: Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
11009 0 : Real64 SumPeopleAllZones = 0.0;
11010 0 : for (auto &thisZone : state.dataHeatBal->Zone) {
11011 0 : SumPeopleAllZones += thisZone.TotOccupants;
11012 0 : }
11013 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
11014 :
11015 0 : if (this->MaxCapacityWasAutoSized) {
11016 : Real64 rho;
11017 : Real64 Cp;
11018 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11019 0 : rho = FluidProperties::GetDensityGlycol(state,
11020 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11021 0 : ((Tfinish + Tstart) / 2.0),
11022 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11023 : RoutineName);
11024 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11025 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11026 0 : ((Tfinish + Tstart) / 2.0),
11027 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11028 : RoutineName);
11029 : } else {
11030 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11031 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11032 : }
11033 :
11034 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) * (1.0 / Constant::SecInHour) * rho *
11035 : Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11036 : }
11037 :
11038 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11039 0 : this->Volume = tmpTankVolume;
11040 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11041 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11042 : }
11043 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11044 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11045 : }
11046 : }
11047 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11048 0 : this->MaxCapacity = tmpMaxCapacity;
11049 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11050 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11051 : }
11052 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11053 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11054 : }
11055 : }
11056 0 : break;
11057 : }
11058 0 : case SizingMode::PerFloorArea: {
11059 :
11060 : // MJW TODO: this won't compile now: Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
11061 0 : Real64 SumFloorAreaAllZones = 0.0;
11062 0 : for (auto &thisZone : state.dataHeatBal->Zone) {
11063 0 : SumFloorAreaAllZones += thisZone.FloorArea;
11064 0 : }
11065 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
11066 0 : if (this->MaxCapacityWasAutoSized) {
11067 : Real64 rho;
11068 : Real64 Cp;
11069 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11070 0 : rho = FluidProperties::GetDensityGlycol(state,
11071 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11072 0 : ((Tfinish + Tstart) / 2.0),
11073 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11074 : RoutineName);
11075 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11076 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11077 0 : ((Tfinish + Tstart) / 2.0),
11078 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11079 : RoutineName);
11080 : } else {
11081 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11082 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11083 : }
11084 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) * (1.0 / Constant::SecInHour) * rho *
11085 : Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11086 : }
11087 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11088 0 : this->Volume = tmpTankVolume;
11089 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11090 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11091 : }
11092 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11093 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11094 : }
11095 : }
11096 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11097 0 : this->MaxCapacity = tmpMaxCapacity;
11098 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11099 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11100 : }
11101 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11102 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11103 : }
11104 : }
11105 0 : break;
11106 : }
11107 0 : case SizingMode::PerUnit: {
11108 :
11109 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
11110 :
11111 0 : if (this->MaxCapacityWasAutoSized) {
11112 : Real64 rho;
11113 : Real64 Cp;
11114 0 : if (this->UseSidePlantLoc.loopNum > 0) {
11115 0 : rho = FluidProperties::GetDensityGlycol(state,
11116 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11117 0 : ((Tfinish + Tstart) / 2.0),
11118 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11119 : RoutineName);
11120 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11121 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11122 0 : ((Tfinish + Tstart) / 2.0),
11123 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11124 : RoutineName);
11125 : } else {
11126 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11127 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11128 : }
11129 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) * (1.0 / Constant::SecInHour) *
11130 : rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11131 : }
11132 :
11133 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11134 0 : this->Volume = tmpTankVolume;
11135 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11136 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11137 : }
11138 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11139 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11140 : }
11141 : }
11142 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11143 0 : this->MaxCapacity = tmpMaxCapacity;
11144 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11145 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11146 : }
11147 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11148 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11149 : }
11150 : }
11151 0 : break;
11152 : }
11153 0 : case SizingMode::PerSolarColArea: {
11154 0 : break;
11155 : }
11156 0 : default:
11157 0 : break;
11158 : }
11159 :
11160 766 : if (this->MaxCapacityWasAutoSized) this->setBackupElementCapacity(state);
11161 :
11162 : // if stratified, might set height.
11163 766 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
11164 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
11165 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
11166 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / Constant::Pi, 0.3333333333333333);
11167 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11168 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
11169 : }
11170 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11171 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
11172 : }
11173 : // check if Constant::AutoCalculate() Use outlet and source inlet are still set to autosize by earlier
11174 0 : if (this->UseOutletHeightWasAutoSized) {
11175 0 : this->UseOutletHeight = this->Height;
11176 : }
11177 0 : if (this->SourceInletHeightWasAutoSized) {
11178 0 : this->SourceInletHeight = this->Height;
11179 : }
11180 : }
11181 : }
11182 766 : }
11183 :
11184 766 : void WaterThermalTankData::SizeTankForSupplySide(EnergyPlusData &state)
11185 : {
11186 :
11187 : // SUBROUTINE INFORMATION:
11188 : // AUTHOR Brent Griffith
11189 : // DATE WRITTEN February 2008
11190 : // MODIFIED na
11191 : // RE-ENGINEERED na
11192 :
11193 : // PURPOSE OF THIS SUBROUTINE:
11194 : // This subroutine is for sizing water heater tank volume and heater
11195 : // at a later point in the simulation when more of the plant is ready.
11196 :
11197 : // METHODOLOGY EMPLOYED:
11198 : // depending on the sizing design mode...
11199 :
11200 : // REFERENCES:
11201 : // BA benchmark report for residential design mode
11202 :
11203 : static constexpr std::string_view RoutineName("SizeTankForSupplySide");
11204 :
11205 766 : Real64 tmpTankVolume = this->Volume;
11206 766 : Real64 tmpMaxCapacity = this->MaxCapacity;
11207 :
11208 766 : if (this->Sizing.DesignMode == SizingMode::PeakDraw) {
11209 20 : if (this->VolumeWasAutoSized)
11210 20 : tmpTankVolume = this->Sizing.TankDrawTime * this->UseDesignVolFlowRate * Constant::SecInHour; // hours | m3/s | (3600 s/1 hour)
11211 20 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11212 4 : this->Volume = tmpTankVolume;
11213 4 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11214 4 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11215 : }
11216 4 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11217 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11218 : }
11219 : }
11220 20 : if (this->MaxCapacityWasAutoSized) {
11221 0 : if (this->Sizing.RecoveryTime > 0.0) {
11222 0 : Real64 rho = 0.0;
11223 0 : Real64 Cp = 0.0;
11224 0 : constexpr Real64 Tstart = 14.44;
11225 0 : constexpr Real64 Tfinish = 57.22;
11226 :
11227 0 : if (this->SrcSidePlantLoc.loopNum > 0) {
11228 0 : rho = FluidProperties::GetDensityGlycol(state,
11229 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11230 : ((Tfinish + Tstart) / 2.0),
11231 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11232 : RoutineName);
11233 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
11234 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11235 : ((Tfinish + Tstart) / 2.0),
11236 0 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11237 : RoutineName);
11238 : } else {
11239 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11240 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11241 : }
11242 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11243 0 : (this->Sizing.RecoveryTime * Constant::SecInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11244 : } else {
11245 0 : ShowFatalError(
11246 0 : state, format("{}: Tank=\"{}\", requested sizing for max capacity but entered Recovery Time is zero.", RoutineName, this->Name));
11247 : }
11248 : }
11249 :
11250 20 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11251 0 : this->MaxCapacity = tmpMaxCapacity;
11252 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11253 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11254 : }
11255 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11256 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11257 : }
11258 : }
11259 746 : } else if (this->Sizing.DesignMode == SizingMode::PerSolarColArea) {
11260 :
11261 0 : this->Sizing.TotalSolarCollectorArea = 0.0;
11262 :
11263 0 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11264 0 : auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
11265 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
11266 : }
11267 :
11268 0 : for (int CollectorNum = 1; CollectorNum <= state.dataPhotovoltaicThermalCollector->NumPVT; ++CollectorNum) {
11269 0 : auto const &collector = state.dataPhotovoltaicThermalCollector->PVT(CollectorNum);
11270 0 : this->Sizing.TotalSolarCollectorArea += collector.AreaCol;
11271 : }
11272 :
11273 0 : if (this->VolumeWasAutoSized) {
11274 0 : if (this->Sizing.TotalSolarCollectorArea > 0) {
11275 0 : tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11276 : } else {
11277 0 : ShowFatalError(state,
11278 0 : format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
11279 : "area of Collectors is zero.",
11280 : RoutineName,
11281 0 : this->Name));
11282 : }
11283 : }
11284 0 : if (this->MaxCapacityWasAutoSized) {
11285 0 : tmpMaxCapacity = 0.0;
11286 : }
11287 0 : if (this->VolumeWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11288 0 : this->Volume = tmpTankVolume;
11289 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11290 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11291 : }
11292 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11293 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Volume [m3]", this->Volume);
11294 : }
11295 : }
11296 0 : if (this->MaxCapacityWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11297 0 : this->MaxCapacity = tmpMaxCapacity;
11298 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11299 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11300 : }
11301 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11302 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Maximum Heater Capacity [W]", this->MaxCapacity);
11303 : }
11304 : }
11305 : }
11306 :
11307 766 : if (this->MaxCapacityWasAutoSized) {
11308 15 : this->setBackupElementCapacity(state);
11309 : }
11310 :
11311 766 : if ((this->VolumeWasAutoSized) && (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) &&
11312 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) { // might set height
11313 0 : if ((this->HeightWasAutoSized) && (!this->VolumeWasAutoSized)) {
11314 0 : this->Height = std::pow((4.0 * this->Volume * pow_2(this->Sizing.HeightAspectRatio)) / Constant::Pi, 0.3333333333333333);
11315 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11316 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Height [m]", this->Height);
11317 : }
11318 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11319 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Initial Tank Height [m]", this->Height);
11320 : }
11321 : }
11322 : }
11323 766 : }
11324 :
11325 766 : void WaterThermalTankData::SizeDemandSidePlantConnections(EnergyPlusData &state)
11326 : {
11327 :
11328 : // SUBROUTINE INFORMATION:
11329 : // AUTHOR Brent Griffith
11330 : // DATE WRITTEN October 2007
11331 : // MODIFIED na
11332 : // RE-ENGINEERED na
11333 :
11334 : // PURPOSE OF THIS SUBROUTINE:
11335 : // This subroutine is for sizing water heater plant connection flow rates
11336 : // on the demand side that have not been specified in the input.
11337 :
11338 : // METHODOLOGY EMPLOYED:
11339 : // For water heater sides on the Demand side, hot water flow rates are modeled entirely from user input data
11340 : // because the plant loop is not yet set up nor is plant sizing info populated.
11341 : // sizing is done by calculating an initial
11342 : // recovery rate that if continued would reheat tank in user specified amount of time.
11343 : // initial and final tank temperatures are 14.44 and reheat to 57.22 (values from CalcStandardRatings routine)
11344 :
11345 : static constexpr std::string_view RoutineName("SizeDemandSidePlantConnections");
11346 :
11347 766 : auto &PlantSizData = state.dataSize->PlantSizData;
11348 :
11349 766 : Real64 tankRecoverhours = this->SizingRecoveryTime;
11350 766 : bool ErrorsFound = false;
11351 766 : Real64 tmpUseDesignVolFlowRate = this->UseDesignVolFlowRate;
11352 766 : Real64 tmpSourceDesignVolFlowRate = this->SourceDesignVolFlowRate;
11353 :
11354 : Real64 Tstart;
11355 : Real64 Tfinish;
11356 766 : if (!this->IsChilledWaterTank) {
11357 716 : Tstart = 14.44;
11358 716 : Tfinish = 57.22;
11359 : } else {
11360 50 : Tstart = 14.44;
11361 50 : Tfinish = 9.0;
11362 : }
11363 :
11364 : // determine tank volume to use for sizing.
11365 766 : Real64 TankVolume = this->Volume;
11366 766 : if (this->VolumeWasAutoSized) {
11367 20 : TankVolume = this->Sizing.NominalVolForSizingDemandSideFlow;
11368 : }
11369 :
11370 766 : if (this->UseInletNode > 0) {
11371 766 : if (this->UseDesignVolFlowRateWasAutoSized) {
11372 706 : int PltSizNum = this->UseSidePlantSizNum;
11373 706 : if (PltSizNum > 0) { // we have a Plant Sizing Object
11374 706 : if (this->UseSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11375 : // probably shouldn't come here as Use side is unlikley to be on demand side (?)
11376 : // but going to treat component with symetry so if connections are reversed it'll still work
11377 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11378 : // in user specified hours.
11379 : // using the plant inlet design temp for sizing.
11380 0 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11381 0 : Real64 eff = this->UseEffectiveness;
11382 0 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11383 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11384 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11385 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11386 : } else {
11387 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11388 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11389 : }
11390 0 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11391 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11392 0 : this->UseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11393 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11394 : } else {
11395 0 : tmpUseDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11396 0 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11397 : }
11398 : } else {
11399 0 : if (!this->IsChilledWaterTank) {
11400 : // plant sizing object design temperature is set too low throw warning.
11401 0 : ShowSevereError(state,
11402 : "Autosizing of Use side water heater design flow rate requires Sizing:Plant object to have an exit "
11403 : "temperature >= 58C");
11404 0 : ShowContinueError(state, format("Occurs for water heater object={}", this->Name));
11405 : } else {
11406 : // plant sizing object design temperature is set too hi throw warning.
11407 0 : ShowSevereError(state,
11408 : "Autosizing of Use side chilled water tank design flow rate requires Sizing:Plant object to have an "
11409 : "exit temperature <= 8C");
11410 0 : ShowContinueError(state, format("Occurs for chilled water storage tank object={}", this->Name));
11411 : }
11412 0 : ErrorsFound = true;
11413 : }
11414 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11415 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11416 : }
11417 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11418 0 : BaseSizer::reportSizerOutput(
11419 : state, this->Type, this->Name, "Initial Use Side Design Flow Rate [m3/s]", this->UseDesignVolFlowRate);
11420 : }
11421 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11422 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11423 : } else {
11424 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, tmpUseDesignVolFlowRate);
11425 : }
11426 0 : Real64 rho = FluidProperties::GetDensityGlycol(state,
11427 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11428 : Constant::InitConvTemp,
11429 0 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11430 : RoutineName);
11431 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11432 0 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11433 : } else {
11434 0 : this->PlantUseMassFlowRateMax = tmpUseDesignVolFlowRate * rho;
11435 : }
11436 : } // Demand side
11437 : } else {
11438 : // do nothing
11439 : } // plant sizing object
11440 :
11441 : } else {
11442 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11443 60 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->UseInletNode, this->UseDesignVolFlowRate);
11444 : Real64 rho;
11445 60 : if (this->UseSidePlantLoc.loopNum > 0) {
11446 60 : rho = FluidProperties::GetDensityGlycol(state,
11447 60 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidName,
11448 : Constant::InitConvTemp,
11449 60 : state.dataPlnt->PlantLoop(this->UseSidePlantLoc.loopNum).FluidIndex,
11450 : RoutineName);
11451 : } else {
11452 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, this->waterIndex, RoutineName);
11453 : }
11454 60 : this->PlantUseMassFlowRateMax = this->UseDesignVolFlowRate * rho;
11455 : } // autosizing needed.
11456 : } // connected to plant
11457 :
11458 766 : if (this->SourceInletNode > 0) {
11459 260 : if (this->SourceDesignVolFlowRateWasAutoSized) {
11460 125 : int PltSizNum = this->SourceSidePlantSizNum;
11461 125 : if (PltSizNum > 0) {
11462 110 : if (this->SrcSidePlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
11463 : // choose a flow rate that will allow the entire volume of the tank to go from 14.44 to 57.22 C
11464 : // in user specified hours.
11465 : // using the plant inlet design temp for sizing.
11466 60 : Real64 Tpdesign = PlantSizData(PltSizNum).ExitTemp;
11467 60 : Real64 eff = this->SourceEffectiveness;
11468 60 : if ((Tpdesign >= 58.0) && (!this->IsChilledWaterTank)) {
11469 :
11470 30 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11471 6 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11472 6 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11473 : } else {
11474 24 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11475 24 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11476 : }
11477 30 : } else if ((Tpdesign <= 8.0) && (this->IsChilledWaterTank)) {
11478 30 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11479 6 : this->SourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11480 6 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11481 : } else {
11482 24 : tmpSourceDesignVolFlowRate = -1.0 * (TankVolume / (tankRecoverhours * Constant::SecInHour * eff)) *
11483 24 : std::log((Tpdesign - Tfinish) / (Tpdesign - Tstart));
11484 : }
11485 : } else {
11486 0 : if (!this->IsChilledWaterTank) {
11487 : // plant sizing object design temperature is set too low throw warning.
11488 0 : ShowSevereError(state,
11489 : "Autosizing of Source side water heater design flow rate requires Sizing:Plant object to have an "
11490 : "exit temperature >= 58C");
11491 0 : ShowContinueError(state, format("Occurs for WaterHeater:Mixed object={}", this->Name));
11492 : } else {
11493 : // plant sizing object design temperature is set too hi throw warning.
11494 0 : ShowSevereError(state,
11495 : "Autosizing of Source side chilled water tank design flow rate requires Sizing:Plant object to have "
11496 : "an exit temperature <= 8C");
11497 0 : ShowContinueError(state, format("Occurs for chilled water storage tank object={}", this->Name));
11498 : }
11499 0 : ErrorsFound = true;
11500 : }
11501 60 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
11502 12 : BaseSizer::reportSizerOutput(
11503 : state, this->Type, this->Name, "Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11504 : }
11505 60 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
11506 0 : BaseSizer::reportSizerOutput(
11507 : state, this->Type, this->Name, "Initial Source Side Design Flow Rate [m3/s]", this->SourceDesignVolFlowRate);
11508 : }
11509 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11510 12 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11511 : } else {
11512 48 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, tmpSourceDesignVolFlowRate);
11513 : }
11514 60 : Real64 rho = FluidProperties::GetDensityGlycol(state,
11515 60 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11516 : Constant::InitConvTemp,
11517 60 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11518 : RoutineName);
11519 60 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
11520 12 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11521 : } else {
11522 48 : this->PlantSourceMassFlowRateMax = tmpSourceDesignVolFlowRate * rho;
11523 : }
11524 : } // demand side
11525 : } else {
11526 : // do nothing
11527 : } // plant sizing object
11528 :
11529 : } else {
11530 : // not autosized - report flow to RegisterPlantCompDesignFlow for supply side component sizing
11531 135 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->SourceInletNode, this->SourceDesignVolFlowRate);
11532 : Real64 rho;
11533 135 : if (this->SrcSidePlantLoc.loopNum > 0) {
11534 100 : rho = FluidProperties::GetDensityGlycol(state,
11535 100 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidName,
11536 : Constant::InitConvTemp,
11537 100 : state.dataPlnt->PlantLoop(this->SrcSidePlantLoc.loopNum).FluidIndex,
11538 : RoutineName);
11539 : } else {
11540 35 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, this->waterIndex, RoutineName);
11541 : }
11542 135 : this->PlantSourceMassFlowRateMax = this->SourceDesignVolFlowRate * rho;
11543 : } // autosizing needed.
11544 : } // connected to plant
11545 :
11546 766 : if (ErrorsFound) {
11547 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
11548 : }
11549 766 : }
11550 :
11551 49 : void WaterThermalTankData::SizeStandAloneWaterHeater(EnergyPlusData &state)
11552 : {
11553 :
11554 : // SUBROUTINE INFORMATION:
11555 : // AUTHOR B. Griffith
11556 : // DATE WRITTEN October 2013
11557 : // MODIFIED na
11558 : // RE-ENGINEERED na
11559 :
11560 : // PURPOSE OF THIS SUBROUTINE:
11561 : // allow autosizing of tank volume and heat capacity for stand alone tanks
11562 :
11563 : // METHODOLOGY EMPLOYED:
11564 : // same as for plant connected water heaters, only draws are scheduled.
11565 :
11566 : // SUBROUTINE PARAMETER DEFINITIONS:
11567 49 : Real64 constexpr GalTocubicMeters(0.0037854);
11568 49 : Real64 constexpr kBtuPerHrToWatts(293.1);
11569 : static constexpr std::string_view RoutineName("SizeStandAloneWaterHeater");
11570 :
11571 49 : Real64 Tstart = 14.44;
11572 49 : Real64 Tfinish = 57.22;
11573 49 : Real64 tmpTankVolume = this->Volume;
11574 49 : Real64 tmpMaxCapacity = this->MaxCapacity;
11575 :
11576 49 : if (this->VolumeWasAutoSized || this->MaxCapacityWasAutoSized) {
11577 :
11578 0 : switch (this->Sizing.DesignMode) {
11579 :
11580 0 : case SizingMode::PeakDraw: {
11581 : // get draw rate from maximum in schedule
11582 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, Constant::InitConvTemp, this->waterIndex, RoutineName);
11583 0 : Real64 DrawDesignVolFlowRate = ScheduleManager::GetScheduleMaxValue(state, this->FlowRateSchedule) * this->MassFlowRateMax / rho;
11584 :
11585 0 : if (this->VolumeWasAutoSized) {
11586 0 : tmpTankVolume = this->Sizing.TankDrawTime * DrawDesignVolFlowRate * Constant::SecInHour; // hours | m3/s | (3600 s/1 hour)
11587 0 : this->Volume = tmpTankVolume;
11588 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11589 : }
11590 0 : if (this->MaxCapacityWasAutoSized) {
11591 0 : if (this->Sizing.RecoveryTime > 0.0) {
11592 0 : rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11593 : Real64 Cp =
11594 0 : FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11595 :
11596 0 : tmpMaxCapacity = (this->Volume * rho * Cp * (Tfinish - Tstart)) /
11597 0 : (this->Sizing.RecoveryTime * Constant::SecInHour); // m3 | kg/m3 | J/Kg/K | K | seconds
11598 : } else {
11599 0 : ShowFatalError(
11600 : state,
11601 0 : format("{}: Tank=\"{}\", requested sizing for max capacity but entered Recovery Time is zero.", RoutineName, this->Name));
11602 : }
11603 0 : this->MaxCapacity = tmpMaxCapacity;
11604 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11605 : }
11606 :
11607 0 : break;
11608 : }
11609 0 : case SizingMode::ResidentialMin: {
11610 : // assume can propagate rules for gas to other fuels.
11611 0 : bool FuelTypeIsLikeGas = false;
11612 0 : switch (this->FuelType) {
11613 0 : case Constant::eFuel::NaturalGas:
11614 : case Constant::eFuel::Diesel:
11615 : case Constant::eFuel::Gasoline:
11616 : case Constant::eFuel::Coal:
11617 : case Constant::eFuel::FuelOilNo1:
11618 : case Constant::eFuel::FuelOilNo2:
11619 : case Constant::eFuel::Propane:
11620 : case Constant::eFuel::OtherFuel1:
11621 : case Constant::eFuel::OtherFuel2:
11622 : case Constant::eFuel::DistrictHeatingWater:
11623 : case Constant::eFuel::DistrictHeatingSteam:
11624 0 : FuelTypeIsLikeGas = true;
11625 0 : break;
11626 0 : default: // FuelTypeIsLikeGas stays false
11627 0 : break;
11628 : }
11629 :
11630 0 : if (this->Sizing.NumberOfBedrooms == 1) {
11631 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11632 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
11633 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 2.5 * 1000.0; // 2.5 kW
11634 0 : } else if (FuelTypeIsLikeGas) {
11635 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 20.0 * GalTocubicMeters;
11636 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 27.0 * kBtuPerHrToWatts; // 27kBtu/hr
11637 : }
11638 :
11639 0 : } else if (this->Sizing.NumberOfBedrooms == 2) {
11640 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11641 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11642 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11643 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 3.5 * 1000.0; // 3.5 kW
11644 0 : } else if (FuelTypeIsLikeGas) {
11645 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11646 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11647 : }
11648 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11649 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11650 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11651 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11652 0 : } else if (FuelTypeIsLikeGas) {
11653 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11654 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11655 : }
11656 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11657 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11658 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11659 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11660 0 : } else if (FuelTypeIsLikeGas) {
11661 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11662 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11663 : }
11664 : }
11665 0 : } else if (this->Sizing.NumberOfBedrooms == 3) {
11666 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11667 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11668 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11669 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 4.5 * 1000.0; // 4.5 kW
11670 0 : } else if (FuelTypeIsLikeGas) {
11671 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 30.0 * GalTocubicMeters;
11672 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11673 : }
11674 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11675 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11676 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11677 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11678 0 : } else if (FuelTypeIsLikeGas) {
11679 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11680 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11681 : }
11682 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11683 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11684 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11685 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11686 0 : } else if (FuelTypeIsLikeGas) {
11687 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11688 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11689 : }
11690 : }
11691 0 : } else if (this->Sizing.NumberOfBedrooms == 4) {
11692 0 : if (this->Sizing.NumberOfBathrooms <= 1.5) {
11693 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11694 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11695 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11696 0 : } else if (FuelTypeIsLikeGas) {
11697 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11698 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 36.0 * kBtuPerHrToWatts; // 36 kBtu/hr
11699 : }
11700 0 : } else if ((this->Sizing.NumberOfBathrooms > 1.5) && (this->Sizing.NumberOfBathrooms < 3.0)) {
11701 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11702 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11703 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11704 0 : } else if (FuelTypeIsLikeGas) {
11705 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 40.0 * GalTocubicMeters;
11706 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11707 : }
11708 0 : } else if (this->Sizing.NumberOfBathrooms >= 3.0) {
11709 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11710 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11711 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11712 0 : } else if (FuelTypeIsLikeGas) {
11713 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11714 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 38.0 * kBtuPerHrToWatts; // 38 kBtu/hr
11715 : }
11716 : }
11717 0 : } else if (this->Sizing.NumberOfBedrooms == 5) {
11718 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11719 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11720 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11721 0 : } else if (FuelTypeIsLikeGas) {
11722 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11723 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 47.0 * kBtuPerHrToWatts; // 47 kBtu/hr
11724 : }
11725 0 : } else if (this->Sizing.NumberOfBedrooms >= 6) {
11726 0 : if (this->FuelType == Constant::eFuel::Electricity) {
11727 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 66.0 * GalTocubicMeters;
11728 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 5.5 * 1000.0; // 5.5 kW
11729 0 : } else if (FuelTypeIsLikeGas) {
11730 0 : if (this->VolumeWasAutoSized) tmpTankVolume = 50.0 * GalTocubicMeters;
11731 0 : if (this->MaxCapacityWasAutoSized) tmpMaxCapacity = 50.0 * kBtuPerHrToWatts; // 50 kBtu/hr
11732 : }
11733 : }
11734 0 : if (this->VolumeWasAutoSized) {
11735 0 : this->Volume = tmpTankVolume;
11736 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11737 : }
11738 0 : if (this->MaxCapacityWasAutoSized) {
11739 0 : this->MaxCapacity = tmpMaxCapacity;
11740 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11741 : }
11742 :
11743 0 : break;
11744 : }
11745 0 : case SizingMode::PerPerson: {
11746 : // how to get number of people?
11747 :
11748 : // MJW TODO: this won't compile now: Real64 SumPeopleAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::TotOccupants);
11749 0 : Real64 SumPeopleAllZones = 0.0;
11750 0 : for (auto &thisZone : state.dataHeatBal->Zone) {
11751 0 : SumPeopleAllZones += thisZone.TotOccupants;
11752 0 : }
11753 0 : if (this->VolumeWasAutoSized) {
11754 0 : tmpTankVolume = this->Sizing.TankCapacityPerPerson * SumPeopleAllZones;
11755 : }
11756 0 : if (this->MaxCapacityWasAutoSized) {
11757 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11758 0 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11759 0 : tmpMaxCapacity = SumPeopleAllZones * this->Sizing.RecoveryCapacityPerPerson * (Tfinish - Tstart) * (1.0 / Constant::SecInHour) * rho *
11760 : Cp; // m3/hr/person | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11761 : }
11762 :
11763 0 : if (this->VolumeWasAutoSized) {
11764 0 : this->Volume = tmpTankVolume;
11765 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11766 : }
11767 0 : if (this->MaxCapacityWasAutoSized) {
11768 0 : this->MaxCapacity = tmpMaxCapacity;
11769 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11770 : }
11771 :
11772 0 : break;
11773 : }
11774 0 : case SizingMode::PerFloorArea: {
11775 :
11776 : // MJW TODO: this won't compile now: Real64 SumFloorAreaAllZones = sum(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::FloorArea);
11777 0 : Real64 SumFloorAreaAllZones = 0.0;
11778 0 : for (auto &thisZone : state.dataHeatBal->Zone) {
11779 0 : SumFloorAreaAllZones += thisZone.FloorArea;
11780 0 : }
11781 0 : if (this->VolumeWasAutoSized) {
11782 0 : tmpTankVolume = this->Sizing.TankCapacityPerArea * SumFloorAreaAllZones;
11783 : }
11784 :
11785 0 : if (this->MaxCapacityWasAutoSized) {
11786 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11787 0 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11788 0 : tmpMaxCapacity = SumFloorAreaAllZones * this->Sizing.RecoveryCapacityPerArea * (Tfinish - Tstart) * (1.0 / Constant::SecInHour) *
11789 : rho * Cp; // m2 | m3/hr/m2 | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11790 : }
11791 0 : if (this->VolumeWasAutoSized) {
11792 0 : this->Volume = tmpTankVolume;
11793 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11794 : }
11795 0 : if (this->MaxCapacityWasAutoSized) {
11796 0 : this->MaxCapacity = tmpMaxCapacity;
11797 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11798 : }
11799 0 : break;
11800 : }
11801 0 : case SizingMode::PerUnit: {
11802 :
11803 0 : if (this->VolumeWasAutoSized) tmpTankVolume = this->Sizing.TankCapacityPerUnit * this->Sizing.NumberOfUnits;
11804 :
11805 0 : if (this->MaxCapacityWasAutoSized) {
11806 0 : Real64 rho = FluidProperties::GetDensityGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11807 0 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, ((Tfinish + Tstart) / 2.0), this->waterIndex, RoutineName);
11808 0 : tmpMaxCapacity = this->Sizing.NumberOfUnits * this->Sizing.RecoveryCapacityPerUnit * (Tfinish - Tstart) *
11809 0 : (1.0 / Constant::SecInHour) * rho * Cp; // m3/hr/ea | delta T in K | 1 hr/ 3600 s | kg/m3 | J/Kg/k
11810 : }
11811 :
11812 0 : if (this->VolumeWasAutoSized) {
11813 0 : this->Volume = tmpTankVolume;
11814 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11815 : }
11816 0 : if (this->MaxCapacityWasAutoSized) {
11817 0 : this->MaxCapacity = tmpMaxCapacity;
11818 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11819 : }
11820 0 : break;
11821 : }
11822 0 : case SizingMode::PerSolarColArea: {
11823 :
11824 0 : this->Sizing.TotalSolarCollectorArea = 0.0;
11825 :
11826 0 : for (int CollectorNum = 1; CollectorNum <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum) {
11827 0 : auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
11828 0 : this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
11829 : }
11830 :
11831 0 : for (int CollectorNum = 1; CollectorNum <= state.dataPhotovoltaicThermalCollector->NumPVT; ++CollectorNum) {
11832 0 : auto const &collector = state.dataPhotovoltaicThermalCollector->PVT(CollectorNum);
11833 0 : this->Sizing.TotalSolarCollectorArea += collector.AreaCol;
11834 : }
11835 :
11836 0 : if (this->VolumeWasAutoSized) {
11837 0 : if (this->Sizing.TotalSolarCollectorArea > 0) {
11838 0 : tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
11839 : } else {
11840 0 : ShowFatalError(state,
11841 0 : format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
11842 : "area of Collectors is zero.",
11843 : RoutineName,
11844 0 : this->Name));
11845 : }
11846 : }
11847 0 : if (this->MaxCapacityWasAutoSized) {
11848 0 : tmpMaxCapacity = 0.0;
11849 : }
11850 :
11851 0 : if (this->VolumeWasAutoSized) {
11852 0 : this->Volume = tmpTankVolume;
11853 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Tank Volume [m3]", this->Volume);
11854 : }
11855 0 : if (this->MaxCapacityWasAutoSized) {
11856 0 : this->MaxCapacity = tmpMaxCapacity;
11857 0 : BaseSizer::reportSizerOutput(state, this->Type, this->Name, "Maximum Heater Capacity [W]", this->MaxCapacity);
11858 : }
11859 0 : break;
11860 : }
11861 0 : default:
11862 0 : if (this->MaxCapacityWasAutoSized) {
11863 0 : this->setBackupElementCapacity(state);
11864 : }
11865 0 : break;
11866 : }
11867 : }
11868 49 : }
11869 :
11870 6283865 : void WaterThermalTankData::UpdateWaterThermalTank(EnergyPlusData &state)
11871 : {
11872 :
11873 : // SUBROUTINE INFORMATION:
11874 : // AUTHOR Brandon Anderson
11875 : // DATE WRITTEN May 2000
11876 : // MODIFIED na
11877 : // Nov 2011, BAN; removed the use and source heat rate re-calculation for stratified tank
11878 : // for energy conservation verification.
11879 : // RE-ENGINEERED Feb 2004, PGE
11880 :
11881 : // PURPOSE OF THIS SUBROUTINE:
11882 : // Updates the node variables with local variables.
11883 :
11884 6283865 : if (this->UseInletNode > 0 && this->UseOutletNode > 0) {
11885 5863219 : state.dataLoopNodes->Node(UseOutletNode) = state.dataLoopNodes->Node(this->UseInletNode); // this could wipe out setpoints on outlet node
11886 :
11887 5863219 : state.dataLoopNodes->Node(this->UseOutletNode).Temp = this->UseOutletTemp;
11888 : }
11889 :
11890 6283865 : if (this->SourceInletNode > 0 && this->SourceOutletNode > 0) {
11891 1858870 : state.dataLoopNodes->Node(this->SourceOutletNode) = state.dataLoopNodes->Node(this->SourceInletNode);
11892 :
11893 1858870 : state.dataLoopNodes->Node(this->SourceOutletNode).Temp = this->SourceOutletTemp;
11894 : }
11895 6283865 : }
11896 :
11897 6283865 : void WaterThermalTankData::ReportWaterThermalTank(EnergyPlusData &state)
11898 : {
11899 :
11900 : // SUBROUTINE INFORMATION:
11901 : // AUTHOR Brandon Anderson
11902 : // DATE WRITTEN May 2000
11903 : // MODIFIED na
11904 : // RE-ENGINEERED Feb 2004, PGE
11905 :
11906 6283865 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
11907 :
11908 6283865 : this->UnmetEnergy = this->UnmetRate * SecInTimeStep;
11909 6283865 : this->LossEnergy = this->LossRate * SecInTimeStep;
11910 6283865 : this->FlueLossEnergy = this->FlueLossRate * SecInTimeStep;
11911 6283865 : this->UseEnergy = this->UseRate * SecInTimeStep;
11912 6283865 : this->TotalDemandEnergy = this->TotalDemandRate * SecInTimeStep;
11913 6283865 : this->SourceEnergy = this->SourceRate * SecInTimeStep;
11914 6283865 : this->HeaterEnergy = this->HeaterRate * SecInTimeStep;
11915 6283865 : this->HeaterEnergy1 = this->HeaterRate1 * SecInTimeStep;
11916 6283865 : this->HeaterEnergy2 = this->HeaterRate2 * SecInTimeStep;
11917 6283865 : this->FuelEnergy = this->FuelRate * SecInTimeStep;
11918 6283865 : this->VentEnergy = this->VentRate * SecInTimeStep;
11919 6283865 : this->OffCycParaFuelEnergy = this->OffCycParaFuelRate * SecInTimeStep;
11920 6283865 : this->OffCycParaEnergyToTank = this->OffCycParaRateToTank * SecInTimeStep;
11921 6283865 : this->OnCycParaFuelEnergy = this->OnCycParaFuelRate * SecInTimeStep;
11922 6283865 : this->OnCycParaEnergyToTank = this->OnCycParaRateToTank * SecInTimeStep;
11923 6283865 : this->NetHeatTransferEnergy = this->NetHeatTransferRate * SecInTimeStep;
11924 6283865 : this->VolumeConsumed = this->VolFlowRate * SecInTimeStep;
11925 6283865 : }
11926 :
11927 341 : void WaterThermalTankData::CalcStandardRatings(EnergyPlusData &state)
11928 : {
11929 :
11930 : // SUBROUTINE INFORMATION:
11931 : // AUTHOR Peter Graham Ellis
11932 : // DATE WRITTEN January 2005
11933 : // MODIFIED R. Raustad, July 2005 - added HPWH to ratings procedure
11934 : // RE-ENGINEERED na
11935 :
11936 : // PURPOSE OF THIS SUBROUTINE:
11937 : // Calculates the water heater standard ratings, such as Energy Factor and Recovery Efficiency. Results are written
11938 : // to the EIO file. Standard ratings are not calculated for storage-only tanks, i.e., MaxCapacity = 0, nor for Integrated Heat Pumps
11939 :
11940 : // METHODOLOGY EMPLOYED:
11941 : // Water heater inputs are set to the specified test conditions. For HPWHs, the heating capacity and COP are assumed
11942 : // to be the primary element in the water heater and are used during the rating procedure. CalcWaterThermalTankMixed
11943 : // is iteratively called in a self-contained, 24 hour simulation of the standard test procedure.
11944 :
11945 : // REFERENCES:
11946 : // Title 10, Code of Federal Regulations, Part 430- Energy Conservation Program for Consumer Products, Appendix E to
11947 : // Subpart B- Uniform Test Procedure for Measuring the Energy Consumption of Water Heaters, January 1, 2004.
11948 :
11949 341 : if (this->AlreadyRated) { // bail we already did this one
11950 150 : return;
11951 : }
11952 :
11953 : bool FirstTimeFlag; // used during HPWH rating procedure
11954 191 : bool bIsVSCoil = false;
11955 : Real64 RecoveryEfficiency;
11956 : Real64 EnergyFactor;
11957 191 : Real64 RatedDXCoilTotalCapacity = 0.0;
11958 191 : if (this->MaxCapacity > 0.0 || this->HeatPumpNum > 0) {
11959 : // Set test conditions
11960 186 : this->AmbientTemp = 19.7222; // 67.5 F
11961 186 : this->UseInletTemp = 14.4444; // 58 F
11962 186 : this->SetPointTemp = 57.2222; // 135 F
11963 186 : this->SetPointTemp2 = 57.2222; // 135 F
11964 186 : this->TankTemp = 57.2222; // Initialize tank temperature
11965 186 : if (this->Nodes > 0)
11966 144 : for (auto &e : this->Node)
11967 128 : e.Temp = 57.2222;
11968 :
11969 186 : Real64 TotalDrawMass = 0.243402 * Psychrometrics::RhoH2O(Constant::InitConvTemp); // 64.3 gal * rho
11970 186 : Real64 DrawMass = TotalDrawMass / 6.0; // 6 equal draws
11971 186 : Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
11972 186 : Real64 DrawMassFlowRate = DrawMass / SecInTimeStep;
11973 186 : Real64 FuelEnergy_loc = 0.0;
11974 186 : FirstTimeFlag = true;
11975 :
11976 186 : int TimeStepPerHour = int(1.0 / state.dataHVACGlobal->TimeStepSys);
11977 : // Simulate 24 hour test
11978 23274 : for (int Step = 1; Step <= TimeStepPerHour * 24; ++Step) {
11979 :
11980 23088 : if (Step == 1 || Step == (1 + TimeStepPerHour) || Step == (1 + TimeStepPerHour * 2) || Step == (1 + TimeStepPerHour * 3) ||
11981 22344 : Step == (1 + TimeStepPerHour * 4) || Step == (1 + TimeStepPerHour * 5)) { // Hour 1 | Hour 2 | Hour 3 | Hour 4 | Hour 5 | Hour 6
11982 :
11983 1116 : this->UseMassFlowRate = DrawMassFlowRate;
11984 : } else {
11985 21972 : this->UseMassFlowRate = 0.0;
11986 : }
11987 :
11988 23088 : this->SavedTankTemp = this->TankTemp;
11989 23088 : this->SavedMode = this->Mode;
11990 23088 : if (this->Nodes > 0) {
11991 33552 : for (auto &e : this->Node)
11992 30336 : e.SavedTemp = e.Temp;
11993 3216 : this->SavedHeaterOn1 = this->HeaterOn1;
11994 3216 : this->SavedHeaterOn2 = this->HeaterOn2;
11995 : }
11996 :
11997 23088 : if (this->HeatPumpNum == 0) {
11998 :
11999 19296 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
12000 18672 : this->CalcWaterThermalTankMixed(state);
12001 :
12002 624 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12003 624 : this->CalcWaterThermalTankStratified(state);
12004 : }
12005 :
12006 : } else {
12007 :
12008 3792 : int HPNum = this->HeatPumpNum; // Convenience variable
12009 3792 : Real64 AmbientHumRat = 0.00717; // Humidity ratio at 67.5 F / 50% RH
12010 :
12011 : // set the heat pump air- and water-side mass flow rate
12012 3792 : Real64 MdotWater = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingWaterFlowRate * Psychrometrics::RhoH2O(this->TankTemp);
12013 3792 : Real64 mdotAir = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OperatingAirMassFlowRate;
12014 :
12015 : // ?? why is HPWH condenser inlet node temp reset inside the for loop? shouldn't it chnage with the tank temp throughout these
12016 : // iterations?
12017 3792 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterPumped) {
12018 : // set the condenser inlet node mass flow rate and temperature
12019 2064 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).MassFlowRate = MdotWater;
12020 2064 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12021 : }
12022 :
12023 : // initialize temperatures for HPWH DX Coil heating capacity and COP curves
12024 3792 : state.dataHVACGlobal->HPWHInletDBTemp = this->AmbientTemp;
12025 7584 : state.dataHVACGlobal->HPWHInletWBTemp =
12026 3792 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataHVACGlobal->HPWHInletDBTemp, AmbientHumRat, state.dataEnvrn->OutBaroPress);
12027 :
12028 : // set up full air flow on DX coil inlet node
12029 3792 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode > 0) {
12030 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRate = mdotAir;
12031 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).MassFlowRateMaxAvail = mdotAir;
12032 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Temp = this->AmbientTemp;
12033 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).HumRat = AmbientHumRat;
12034 288 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).InletAirMixerNode).Enthalpy =
12035 288 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
12036 : } else {
12037 3504 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode == 0) {
12038 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRate = mdotAir;
12039 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).MassFlowRateMaxAvail =
12040 : mdotAir;
12041 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Temp = this->AmbientTemp;
12042 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).HumRat = AmbientHumRat;
12043 1056 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).HeatPumpAirInletNode).Enthalpy =
12044 1056 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
12045 : } else {
12046 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRate = mdotAir;
12047 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).MassFlowRateMaxAvail = mdotAir;
12048 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Temp = this->AmbientTemp;
12049 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).HumRat = AmbientHumRat;
12050 2448 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).OutsideAirNode).Enthalpy =
12051 2448 : Psychrometrics::PsyHFnTdbW(this->AmbientTemp, AmbientHumRat);
12052 : }
12053 : }
12054 :
12055 3792 : state.dataHVACGlobal->HPWHCrankcaseDBTemp = this->AmbientTemp;
12056 :
12057 3792 : if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilType,
12058 6960 : "Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed") ||
12059 3168 : (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP)) {
12060 768 : bIsVSCoil = true;
12061 768 : std::string VSCoilName = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName;
12062 768 : int VSCoilNum = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum;
12063 768 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).bIsIHP) {
12064 144 : VSCoilNum =
12065 144 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilIndex;
12066 : VSCoilName =
12067 144 : state.dataIntegratedHP->IntegratedHeatPumps(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).SCWHCoilName;
12068 : }
12069 :
12070 768 : Real64 RhoWater = Psychrometrics::RhoH2O(this->TankTemp);
12071 768 : auto &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HPNum);
12072 768 : this->SetVSHPWHFlowRates(
12073 : state,
12074 : HPWH,
12075 768 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12076 : 1.0,
12077 : RhoWater,
12078 : MdotWater,
12079 : true);
12080 : // simulate the HPWH coil/fan to find heating capacity
12081 768 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).fanPlace == HVAC::FanPlace::BlowThru) {
12082 : // simulate fan and DX coil twice
12083 576 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12084 :
12085 576 : VariableSpeedCoils::SimVariableSpeedCoils(
12086 : state,
12087 : VSCoilName,
12088 : VSCoilNum,
12089 : HVAC::FanOp::Cycling,
12090 : HVAC::CompressorOp::On,
12091 : 1.0,
12092 576 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12093 : 1.0,
12094 : 0.0,
12095 : 0.0,
12096 : 1.0);
12097 :
12098 576 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12099 :
12100 576 : VariableSpeedCoils::SimVariableSpeedCoils(
12101 : state,
12102 : VSCoilName,
12103 : VSCoilNum,
12104 : HVAC::FanOp::Cycling,
12105 : HVAC::CompressorOp::On,
12106 : 1.0,
12107 576 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12108 : 1.0,
12109 : 0.0,
12110 : 0.0,
12111 : 1.0);
12112 : } else {
12113 : // simulate DX coil and fan twice to pass fan power (FanElecPower) to DX coil
12114 192 : VariableSpeedCoils::SimVariableSpeedCoils(
12115 : state,
12116 : VSCoilName,
12117 : VSCoilNum,
12118 : HVAC::FanOp::Cycling,
12119 : HVAC::CompressorOp::On,
12120 : 1.0,
12121 192 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12122 : 1.0,
12123 : 0.0,
12124 : 0.0,
12125 : 1.0);
12126 :
12127 192 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12128 :
12129 192 : VariableSpeedCoils::SimVariableSpeedCoils(
12130 : state,
12131 : VSCoilName,
12132 : VSCoilNum,
12133 : HVAC::FanOp::Cycling,
12134 : HVAC::CompressorOp::On,
12135 : 1.0,
12136 192 : state.dataVariableSpeedCoils->VarSpeedCoil(state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum).NormSpedLevel,
12137 : 1.0,
12138 : 0.0,
12139 : 0.0,
12140 : 1.0);
12141 :
12142 192 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12143 : }
12144 :
12145 768 : this->MaxCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
12146 768 : this->MinCapacity = state.dataVariableSpeedCoils->VSHPWHHeatingCapacity;
12147 768 : this->Efficiency = state.dataVariableSpeedCoils->VSHPWHHeatingCOP;
12148 768 : } else {
12149 3024 : bIsVSCoil = false;
12150 : // simulate the HPWH coil/fan to find heating capacity
12151 3024 : if (state.dataWaterThermalTanks->HPWaterHeater(HPNum).fanPlace == HVAC::FanPlace::BlowThru) {
12152 720 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
12153 : // reset water inlet temp. If already sized, no harm.
12154 7 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12155 :
12156 21 : DXCoils::SimDXCoil(state,
12157 7 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12158 : HVAC::CompressorOp::On,
12159 : true,
12160 7 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12161 : HVAC::FanOp::Cycling,
12162 14 : 1.0);
12163 7 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12164 : }
12165 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
12166 : // PLR=1 here.
12167 720 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12168 :
12169 2160 : DXCoils::SimDXCoil(state,
12170 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12171 : HVAC::CompressorOp::On,
12172 : true,
12173 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12174 : HVAC::FanOp::Cycling,
12175 1440 : 1.0);
12176 :
12177 720 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12178 :
12179 2160 : DXCoils::SimDXCoil(state,
12180 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12181 : HVAC::CompressorOp::On,
12182 : true,
12183 720 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12184 : HVAC::FanOp::Cycling,
12185 1440 : 1.0);
12186 : } else {
12187 2304 : if (FirstTimeFlag) { // first time DXCoils::DXCoil is called, it's sized at the RatedCondenserWaterInlet temp, size and
12188 : // reset water inlet temp. If already sized, no harm.
12189 27 : DXCoils::SimDXCoil(state,
12190 9 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12191 : HVAC::CompressorOp::On,
12192 : true,
12193 9 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12194 : HVAC::FanOp::Cycling,
12195 18 : 1.0);
12196 9 : state.dataLoopNodes->Node(state.dataWaterThermalTanks->HPWaterHeater(HPNum).CondWaterInletNode).Temp = this->TankTemp;
12197 : }
12198 : // ?? should only need to call twice if PLR<1 since this might affect OnOffFanPartLoadFraction which impacts fan energy.
12199 : // PLR=1 here.
12200 6912 : DXCoils::SimDXCoil(state,
12201 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12202 : HVAC::CompressorOp::On,
12203 : true,
12204 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12205 : HVAC::FanOp::Cycling,
12206 4608 : 1.0);
12207 :
12208 2304 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12209 :
12210 6912 : DXCoils::SimDXCoil(state,
12211 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilName,
12212 : HVAC::CompressorOp::On,
12213 : true,
12214 2304 : state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilNum,
12215 : HVAC::FanOp::Cycling,
12216 4608 : 1.0);
12217 :
12218 2304 : state.dataFans->fans(state.dataWaterThermalTanks->HPWaterHeater(HPNum).FanNum)->simulate(state, true, _, _);
12219 : }
12220 :
12221 3024 : this->MaxCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12222 3024 : this->MinCapacity = state.dataDXCoils->HPWHHeatingCapacity;
12223 3024 : this->Efficiency = state.dataDXCoils->HPWHHeatingCOP;
12224 : }
12225 :
12226 3792 : if (FirstTimeFlag) {
12227 23 : RatedDXCoilTotalCapacity = state.dataHVACGlobal->DXCoilTotalCapacity;
12228 23 : FirstTimeFlag = false;
12229 : }
12230 :
12231 : // Switch the HPWH info with the tank info and call CalcWaterThermalTankMixed to get Standard Rating
12232 : // (backup element is assumed to be disabled during the rating procedure)
12233 3792 : this->SourceMassFlowRate = 0.0;
12234 3792 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OnCycParaLoad;
12235 3792 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).OffCycParaLoad;
12236 3792 : this->OffCycParaFracToTank = 0.0;
12237 3792 : this->OnCycParaFracToTank = 0.0;
12238 3792 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).DXCoilPLFFPLR;
12239 :
12240 3792 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterMixed) {
12241 1200 : if (this->Efficiency > 0.0) this->CalcWaterThermalTankMixed(state);
12242 :
12243 2592 : } else if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12244 2592 : if (this->Efficiency > 0.0) this->CalcWaterThermalTankStratified(state);
12245 : }
12246 :
12247 : // reset the water heater data to original values
12248 3792 : this->MaxCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12249 3792 : this->MinCapacity = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementCapacity;
12250 3792 : this->Efficiency = state.dataWaterThermalTanks->HPWaterHeater(HPNum).BackupElementEfficiency;
12251 3792 : this->OnCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaLoad;
12252 3792 : this->OffCycParaLoad = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaLoad;
12253 3792 : this->OnCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOnCycParaFracToTank;
12254 3792 : this->OffCycParaFracToTank = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHOffCycParaFracToTank;
12255 3792 : this->PLFCurve = state.dataWaterThermalTanks->HPWaterHeater(HPNum).WHPLFCurve;
12256 : }
12257 :
12258 23088 : FuelEnergy_loc += (this->FuelRate + this->OffCycParaFuelRate + this->OnCycParaFuelRate) * SecInTimeStep;
12259 :
12260 : } // Step
12261 :
12262 186 : if (this->FirstRecoveryDone && this->FirstRecoveryFuel > 0.0) {
12263 : // Calculate Recovery Efficiency based on energy used to recover from the first draw
12264 : // FirstRecoveryFuel is recorded inside the CalcWaterThermalTank subroutine
12265 168 : RecoveryEfficiency = DrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / this->FirstRecoveryFuel;
12266 :
12267 : // Calculate Energy Factor based on total energy (including parasitics) used over entire test
12268 168 : EnergyFactor = TotalDrawMass * Psychrometrics::CPHW(57.2222) * (57.2222 - 14.4444) / FuelEnergy_loc;
12269 :
12270 : } else {
12271 18 : RecoveryEfficiency = 0.0;
12272 18 : EnergyFactor = 0.0;
12273 : // If this a regular tank, or an HPWH that's not an Integrated one
12274 18 : if ((this->HeatPumpNum == 0) || !state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).bIsIHP) {
12275 34 : ShowWarningError(
12276 : state,
12277 34 : format("Water heater = {}: Recovery Efficiency and Energy Factor could not be calculated during the test for standard ratings",
12278 17 : this->Name));
12279 17 : ShowContinueError(state, "Setpoint was never recovered and/or heater never turned on");
12280 : }
12281 : }
12282 :
12283 186 : } else {
12284 :
12285 : // Storage-only tank
12286 5 : RecoveryEfficiency = 0.0;
12287 5 : EnergyFactor = 0.0;
12288 :
12289 : } // WaterThermalTank(WaterThermalTankNum)%MaxCapacity > 0.0
12290 :
12291 : // create predefined report
12292 : // Store values for the input verification and summary report
12293 191 : std::string equipName;
12294 191 : if (this->HeatPumpNum == 0) {
12295 168 : equipName = this->Name;
12296 168 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHType, equipName, this->Type);
12297 168 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12298 168 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, this->MaxCapacity);
12299 168 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12300 168 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12301 168 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12302 : } else {
12303 23 : equipName = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name;
12304 46 : OutputReportPredefined::PreDefTableEntry(
12305 46 : state, state.dataOutRptPredefined->pdchSWHType, equipName, state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type);
12306 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHVol, equipName, this->Volume);
12307 23 : if (bIsVSCoil) {
12308 21 : OutputReportPredefined::PreDefTableEntry(
12309 14 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataVariableSpeedCoils->VSHPWHHeatingCapacity);
12310 : } else {
12311 48 : OutputReportPredefined::PreDefTableEntry(
12312 32 : state, state.dataOutRptPredefined->pdchSWHHeatIn, equipName, state.dataDXCoils->HPWHHeatingCapacity);
12313 : }
12314 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHThEff, equipName, this->Efficiency);
12315 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHRecEff, equipName, RecoveryEfficiency);
12316 23 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchSWHEnFac, equipName, EnergyFactor);
12317 : }
12318 :
12319 : // Write test results
12320 191 : if (this->HeatPumpNum == 0) {
12321 : Real64 MaxCapacity_loc;
12322 168 : if (this->WaterThermalTankType == DataPlant::PlantEquipmentType::WtrHeaterStratified) {
12323 5 : if (this->StratifiedControlMode == PriorityControlMode::MasterSlave) {
12324 5 : MaxCapacity_loc = max(this->MaxCapacity, this->MaxCapacity2);
12325 : } else { // PrioritySimultaneous
12326 0 : MaxCapacity_loc = this->MaxCapacity + this->MaxCapacity2;
12327 : }
12328 : } else { // WaterHeaterMixed
12329 163 : MaxCapacity_loc = this->MaxCapacity;
12330 : }
12331 :
12332 : static constexpr std::string_view Format_720("Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T}\n");
12333 168 : print(state.files.eio, Format_720, this->Type, this->Name, this->Volume, MaxCapacity_loc, RecoveryEfficiency, EnergyFactor);
12334 : } else {
12335 : static constexpr std::string_view Format_721("Heat Pump Water Heater Information,{},{},{:.4T},{:.1T},{:.3T},{:.4T},{:.0T}\n");
12336 23 : print(state.files.eio,
12337 : Format_721,
12338 23 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Type,
12339 23 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).Name,
12340 23 : this->Volume,
12341 23 : state.dataDXCoils->HPWHHeatingCapacity,
12342 : RecoveryEfficiency,
12343 : EnergyFactor,
12344 : RatedDXCoilTotalCapacity);
12345 : }
12346 :
12347 191 : this->AlreadyRated = true;
12348 191 : }
12349 :
12350 10 : void WaterThermalTankData::ReportCWTankInits(EnergyPlusData &state)
12351 : {
12352 :
12353 : // SUBROUTINE INFORMATION:
12354 : // AUTHOR B. Griffith
12355 : // DATE WRITTEN March 2009
12356 : // MODIFIED na
12357 : // RE-ENGINEERED na
12358 :
12359 : // PURPOSE OF THIS SUBROUTINE:
12360 : // send chilled water tank info to EIO
12361 :
12362 10 : if (this->myOneTimeInitFlag) {
12363 0 : this->setupOutputVars(state);
12364 0 : this->myOneTimeInitFlag = false;
12365 : }
12366 :
12367 10 : if (this->AlreadyReported) { // bail we already did this one
12368 4 : return;
12369 : }
12370 :
12371 : static constexpr std::string_view Format_728("Chilled Water Tank Information,{},{},{:.4T},{:.4T},{:.4T}\n");
12372 6 : print(state.files.eio, Format_728, this->Type, this->Name, this->Volume, this->UseDesignVolFlowRate, this->SourceDesignVolFlowRate);
12373 :
12374 6 : this->AlreadyReported = true;
12375 : }
12376 :
12377 800828 : Real64 WaterThermalTankData::FindStratifiedTankSensedTemp(EnergyPlusData &state, bool UseAverage)
12378 : {
12379 :
12380 : // FUNCTION INFORMATION:
12381 : // AUTHOR B. Griffith
12382 : // DATE WRITTEN March 2012
12383 : // MODIFIED na
12384 : // RE-ENGINEERED Noel Merket, April 2015
12385 :
12386 : // PURPOSE OF THIS FUNCTION:
12387 : // find tank temperature depending on how sensed
12388 :
12389 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
12390 800828 : HeatPumpWaterHeaterData const &HPWH = state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum);
12391 : Real64 ControlSensor1Temp;
12392 : Real64 ControlSensor2Temp;
12393 :
12394 800828 : if (UseAverage) {
12395 199442 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).TempAvg;
12396 199442 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).TempAvg;
12397 : } else {
12398 601386 : ControlSensor1Temp = this->Node(HPWH.ControlSensor1Node).Temp;
12399 601386 : ControlSensor2Temp = this->Node(HPWH.ControlSensor2Node).Temp;
12400 : }
12401 :
12402 800828 : Real64 SensedTemp = ControlSensor1Temp * HPWH.ControlSensor1Weight + ControlSensor2Temp * HPWH.ControlSensor2Weight;
12403 :
12404 800828 : return SensedTemp;
12405 : }
12406 :
12407 6523006 : Real64 WaterThermalTankData::getDeadBandTemp()
12408 : {
12409 6523006 : if (this->IsChilledWaterTank) {
12410 397780 : return (this->SetPointTemp + this->DeadBandDeltaTemp);
12411 : } else {
12412 6125226 : return (this->SetPointTemp - this->DeadBandDeltaTemp);
12413 : }
12414 : }
12415 5835179 : void WaterThermalTankData::oneTimeInit(EnergyPlusData &state)
12416 : {
12417 5835179 : if (this->myOneTimeInitFlag) {
12418 174 : this->setupOutputVars(state);
12419 174 : this->myOneTimeInitFlag = false;
12420 : }
12421 5835179 : }
12422 :
12423 30 : void WaterThermalTankData::setBackupElementCapacity(EnergyPlusData &state)
12424 : {
12425 : // Fix for #9001: The BackupElementCapacity was not being reset from the autosize value (-99999) which resulted in
12426 : // negative electric consumption. Using a test for any negative numbers here instead of just -99999 for safety.
12427 : // Only reset the backup element capacity if a problem has been occured.
12428 30 : if (this->HeatPumpNum > 0) {
12429 20 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).HPWHType == DataPlant::PlantEquipmentType::HeatPumpWtrHeaterWrapped) return;
12430 0 : if (state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity < 0.0) {
12431 0 : state.dataWaterThermalTanks->HPWaterHeater(this->HeatPumpNum).BackupElementCapacity = this->MaxCapacity;
12432 : }
12433 10 : } else if (this->DesuperheaterNum > 0) {
12434 0 : if (state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity < 0.0) {
12435 0 : state.dataWaterThermalTanks->WaterHeaterDesuperheater(this->DesuperheaterNum).BackupElementCapacity = this->MaxCapacity;
12436 : }
12437 : }
12438 : }
12439 :
12440 16 : bool GetHeatPumpWaterHeaterNodeNumber(EnergyPlusData &state, int const NodeNumber)
12441 : {
12442 : // PURPOSE OF THIS FUNCTION:
12443 : // Check if a node is used by a heat pump water heater
12444 : // and can be excluded from an airflow network.
12445 :
12446 : // Return value
12447 : bool HeatPumpWaterHeaterNodeException;
12448 :
12449 : int HeatPumpWaterHeaterIndex;
12450 :
12451 16 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
12452 0 : GetWaterThermalTankInput(state);
12453 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
12454 : }
12455 :
12456 16 : HeatPumpWaterHeaterNodeException = false;
12457 :
12458 26 : for (HeatPumpWaterHeaterIndex = 1; HeatPumpWaterHeaterIndex <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HeatPumpWaterHeaterIndex) {
12459 :
12460 : // Get heat pump water heater data
12461 16 : HeatPumpWaterHeaterData &HPWH = state.dataWaterThermalTanks->HPWaterHeater(HeatPumpWaterHeaterIndex);
12462 :
12463 : // "Zone and outdoor air" configuration is expected break the conservation of mass
12464 16 : if (HPWH.InletAirConfiguration != WTTAmbientTemp::ZoneAndOA) {
12465 :
12466 : // Air outlet node
12467 16 : if (NodeNumber == HPWH.HeatPumpAirOutletNode) {
12468 2 : HeatPumpWaterHeaterNodeException = true;
12469 2 : break;
12470 : }
12471 :
12472 : // Air inlet node
12473 14 : if (NodeNumber == HPWH.HeatPumpAirInletNode) {
12474 2 : HeatPumpWaterHeaterNodeException = true;
12475 2 : break;
12476 : }
12477 :
12478 : // Get fan inlet node index
12479 12 : int FanInletNodeIndex = state.dataFans->fans(HPWH.FanNum)->inletNodeNum;
12480 :
12481 : // Fan inlet node
12482 12 : if (NodeNumber == FanInletNodeIndex) {
12483 2 : HeatPumpWaterHeaterNodeException = true;
12484 2 : break;
12485 : }
12486 :
12487 : // Fan outlet node
12488 10 : if (NodeNumber == HPWH.FanOutletNode) {
12489 0 : HeatPumpWaterHeaterNodeException = true;
12490 0 : break;
12491 : }
12492 :
12493 : // Outside air node
12494 10 : if (NodeNumber == HPWH.OutsideAirNode) {
12495 0 : HeatPumpWaterHeaterNodeException = true;
12496 0 : break;
12497 : }
12498 :
12499 : // Exhaust air node
12500 10 : if (NodeNumber == HPWH.ExhaustAirNode) {
12501 0 : HeatPumpWaterHeaterNodeException = true;
12502 0 : break;
12503 : }
12504 : }
12505 : }
12506 :
12507 16 : return HeatPumpWaterHeaterNodeException;
12508 : }
12509 :
12510 0 : int getHeatPumpWaterHeaterIndex(EnergyPlusData &state, std::string_view CompName)
12511 : {
12512 0 : if (state.dataWaterThermalTanks->getWaterThermalTankInputFlag) {
12513 0 : GetWaterThermalTankInput(state);
12514 0 : state.dataWaterThermalTanks->getWaterThermalTankInputFlag = false;
12515 : }
12516 :
12517 0 : for (int HPNum = 1; HPNum <= state.dataWaterThermalTanks->numHeatPumpWaterHeater; ++HPNum) {
12518 0 : if (Util::SameString(state.dataWaterThermalTanks->HPWaterHeater(HPNum).Name, CompName)) {
12519 0 : return HPNum;
12520 : }
12521 : }
12522 :
12523 0 : return 0;
12524 : }
12525 :
12526 : } // namespace EnergyPlus::WaterThermalTanks
|