Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/BranchNodeConnections.hh>
57 : #include <EnergyPlus/ConvectionCoefficients.hh>
58 : #include <EnergyPlus/Data/EnergyPlusData.hh>
59 : #include <EnergyPlus/DataEnvironment.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/DataHeatBalance.hh>
62 : #include <EnergyPlus/DataIPShortCuts.hh>
63 : #include <EnergyPlus/DataLoopNode.hh>
64 : #include <EnergyPlus/DataPrecisionGlobals.hh>
65 : #include <EnergyPlus/FluidProperties.hh>
66 : #include <EnergyPlus/General.hh>
67 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
68 : #include <EnergyPlus/NodeInputManager.hh>
69 : #include <EnergyPlus/OutputProcessor.hh>
70 : #include <EnergyPlus/Plant/DataPlant.hh>
71 : #include <EnergyPlus/PlantUtilities.hh>
72 : #include <EnergyPlus/PondGroundHeatExchanger.hh>
73 : #include <EnergyPlus/Psychrometrics.hh>
74 : #include <EnergyPlus/UtilityRoutines.hh>
75 :
76 : namespace EnergyPlus::PondGroundHeatExchanger {
77 :
78 : // Module containing the routines dealing with pond ground heat exchangers
79 :
80 : // MODULE INFORMATION:
81 : // AUTHOR Simon Rees
82 : // DATE WRITTEN September 2002
83 : // MODIFIED Brent Griffith Sept 2010, plant upgrades
84 : // RE-ENGINEERED na
85 :
86 : // PURPOSE OF THIS MODULE:
87 : // This model represents a shallow pond with submerged hydronic tubes through
88 : // which the heat transfer fluid is circulated. The model represents a 'shallow'
89 : // pond in that no attempt is made to model any stratification effects that may
90 : // be present in deeper ponds. This type of heat rejector is intended to be
91 : // connected in a condenser loop, with or without other forms of heat rejector.
92 : // The pond model is a 'lumped parameter' model where the pond is represented
93 : // by a single node with thermal mass. The pond surface temperature is the same
94 : // as the temperature at this node, i.e. the surface temperature is the same as
95 : // the bulk temperature. A first order differential equation is solved in the
96 : // model to calculated the pond temperature at each time step. This type of heat
97 : // rejector is modelled as several circuits connected in parallel.
98 :
99 : // METHODOLOGY EMPLOYED:
100 : // A heat balance is calculated at a single node that represents the pond.
101 : // heat transfer takes palce by surface convection, long-wave radiation to the
102 : // sky, absoption of solar energy, ground heat transfer and heat exchange with
103 : // the fluid. A heat exchanger analogy is used to calculate the heat transfer
104 : // between the heat transfer fluid and the pond. The differential equation
105 : // defined by the heat balance is solved using a fourth order Runge-Kutta
106 : // numerical integration method.
107 :
108 : // REFERENCES:
109 : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
110 : // M.S. Thesis, Oklahoma State University, December 1999.
111 : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith. 2000. A Model For
112 : // Simulating The Performance Of A Shallow Pond As A Supplemental Heat Rejecter
113 : // With Closed-Loop Ground-Source Heat Pump Systems.
114 : // ASHRAE Transactions. 106(2):107-121.
115 :
116 : Real64 constexpr StefBoltzmann(5.6697e-08); // Stefan-Boltzmann constant
117 :
118 0 : void PondGroundHeatExchangerData::simulate(EnergyPlusData &state,
119 : [[maybe_unused]] const PlantLocation &calledFromLocation,
120 : bool const FirstHVACIteration,
121 : [[maybe_unused]] Real64 &CurLoad,
122 : [[maybe_unused]] bool const RunFlag)
123 : {
124 0 : this->InitPondGroundHeatExchanger(state, FirstHVACIteration);
125 0 : this->CalcPondGroundHeatExchanger(state);
126 0 : this->UpdatePondGroundHeatExchanger(state);
127 0 : }
128 :
129 0 : PlantComponent *PondGroundHeatExchangerData::factory(EnergyPlusData &state, std::string const &objectName)
130 : {
131 0 : if (state.dataPondGHE->GetInputFlag) {
132 0 : GetPondGroundHeatExchanger(state);
133 0 : state.dataPondGHE->GetInputFlag = false;
134 : }
135 0 : for (auto &ghx : state.dataPondGHE->PondGHE) {
136 0 : if (ghx.Name == objectName) {
137 0 : return &ghx;
138 : }
139 : }
140 : // If we didn't find it, fatal
141 0 : ShowFatalError(state, format("Pond Heat Exchanger Factory: Error getting inputs for GHX named: {}", objectName));
142 : // Shut up the compiler
143 0 : return nullptr;
144 : }
145 :
146 0 : void PondGroundHeatExchangerData::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
147 : {
148 0 : this->InitPondGroundHeatExchanger(state, true);
149 0 : }
150 :
151 0 : void PondGroundHeatExchangerData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
152 : [[maybe_unused]] const PlantLocation &calledFromLocation,
153 : Real64 &MaxLoad,
154 : Real64 &MinLoad,
155 : Real64 &OptLoad)
156 : {
157 0 : MaxLoad = this->DesignCapacity;
158 0 : MinLoad = 0.0;
159 0 : OptLoad = this->DesignCapacity;
160 0 : }
161 :
162 0 : void GetPondGroundHeatExchanger(EnergyPlusData &state)
163 : {
164 :
165 : // SUBROUTINE INFORMATION:
166 : // AUTHOR Simon Rees
167 : // DATE WRITTEN August 2002
168 : // MODIFIED na
169 : // RE-ENGINEERED na
170 :
171 : // PURPOSE OF THIS SUBROUTINE:
172 : // This subroutine reads the input for hydronic Pond Ground Heat Exchangers
173 : // from the user input file. This will contain all of the information
174 : // needed to define and simulate the pond.
175 :
176 0 : bool ErrorsFound(false); // Set to true if errors in input,
177 :
178 : int IOStatus; // Used in GetObjectItem
179 : int Item; // Item to be "gotten"
180 : int NumAlphas; // Number of Alphas for each GetObjectItem call
181 : int NumNumbers; // Number of Numbers for each GetObjectItem call
182 :
183 : // Initializations and allocations
184 0 : state.dataIPShortCut->cCurrentModuleObject = "GroundHeatExchanger:Pond";
185 0 : state.dataPondGHE->NumOfPondGHEs =
186 0 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
187 : // allocate data structures
188 0 : if (allocated(state.dataPondGHE->PondGHE)) state.dataPondGHE->PondGHE.deallocate();
189 :
190 0 : state.dataPondGHE->PondGHE.allocate(state.dataPondGHE->NumOfPondGHEs);
191 :
192 : // Obtain all of the user data related to the ponds...
193 0 : for (Item = 1; Item <= state.dataPondGHE->NumOfPondGHEs; ++Item) {
194 :
195 : // get the input data
196 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
197 0 : state.dataIPShortCut->cCurrentModuleObject,
198 : Item,
199 0 : state.dataIPShortCut->cAlphaArgs,
200 : NumAlphas,
201 0 : state.dataIPShortCut->rNumericArgs,
202 : NumNumbers,
203 : IOStatus,
204 : _,
205 : _,
206 0 : state.dataIPShortCut->cAlphaFieldNames,
207 0 : state.dataIPShortCut->cNumericFieldNames);
208 :
209 0 : if ((state.dataPondGHE->PondGHE(Item).water = Fluid::GetWater(state)) == nullptr) {
210 0 : ShowSevereError(state, "Fluid Properties for WATER not found");
211 0 : ErrorsFound = true;
212 : }
213 :
214 : // General user input data
215 0 : state.dataPondGHE->PondGHE(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
216 :
217 : // get inlet node data
218 0 : state.dataPondGHE->PondGHE(Item).InletNode = state.dataIPShortCut->cAlphaArgs(2);
219 0 : state.dataPondGHE->PondGHE(Item).InletNodeNum =
220 0 : NodeInputManager::GetOnlySingleNode(state,
221 0 : state.dataIPShortCut->cAlphaArgs(2),
222 : ErrorsFound,
223 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerPond,
224 0 : state.dataIPShortCut->cAlphaArgs(1),
225 : DataLoopNode::NodeFluidType::Water,
226 : DataLoopNode::ConnectionType::Inlet,
227 : NodeInputManager::CompFluidStream::Primary,
228 : DataLoopNode::ObjectIsNotParent);
229 0 : if (state.dataPondGHE->PondGHE(Item).InletNodeNum == 0) {
230 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
231 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
232 0 : ErrorsFound = true;
233 : }
234 :
235 : // get outlet node data
236 0 : state.dataPondGHE->PondGHE(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(3);
237 0 : state.dataPondGHE->PondGHE(Item).OutletNodeNum =
238 0 : NodeInputManager::GetOnlySingleNode(state,
239 0 : state.dataIPShortCut->cAlphaArgs(3),
240 : ErrorsFound,
241 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerPond,
242 0 : state.dataIPShortCut->cAlphaArgs(1),
243 : DataLoopNode::NodeFluidType::Water,
244 : DataLoopNode::ConnectionType::Outlet,
245 : NodeInputManager::CompFluidStream::Primary,
246 : DataLoopNode::ObjectIsNotParent);
247 0 : if (state.dataPondGHE->PondGHE(Item).OutletNodeNum == 0) {
248 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
249 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
250 0 : ErrorsFound = true;
251 : }
252 :
253 0 : BranchNodeConnections::TestCompSet(state,
254 0 : state.dataIPShortCut->cCurrentModuleObject,
255 0 : state.dataIPShortCut->cAlphaArgs(1),
256 0 : state.dataIPShortCut->cAlphaArgs(2),
257 0 : state.dataIPShortCut->cAlphaArgs(3),
258 : "Condenser Water Nodes");
259 :
260 : // pond geometry data
261 0 : state.dataPondGHE->PondGHE(Item).Depth = state.dataIPShortCut->rNumericArgs(1);
262 0 : state.dataPondGHE->PondGHE(Item).Area = state.dataIPShortCut->rNumericArgs(2);
263 0 : if (state.dataIPShortCut->rNumericArgs(1) <= 0.0) {
264 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(1), state.dataIPShortCut->rNumericArgs(1)));
265 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
266 0 : ShowContinueError(state, "Value must be greater than 0.0");
267 0 : ErrorsFound = true;
268 : }
269 0 : if (state.dataIPShortCut->rNumericArgs(2) <= 0.0) {
270 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
271 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
272 0 : ShowContinueError(state, "Value must be greater than 0.0");
273 0 : ErrorsFound = true;
274 : }
275 :
276 : // tube data
277 0 : state.dataPondGHE->PondGHE(Item).TubeInDiameter = state.dataIPShortCut->rNumericArgs(3);
278 0 : state.dataPondGHE->PondGHE(Item).TubeOutDiameter = state.dataIPShortCut->rNumericArgs(4);
279 :
280 0 : if (state.dataIPShortCut->rNumericArgs(3) <= 0.0) {
281 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(3), state.dataIPShortCut->rNumericArgs(3)));
282 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
283 0 : ShowContinueError(state, "Value must be greater than 0.0");
284 0 : ErrorsFound = true;
285 : }
286 0 : if (state.dataIPShortCut->rNumericArgs(4) <= 0.0) {
287 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(4), state.dataIPShortCut->rNumericArgs(4)));
288 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
289 0 : ShowContinueError(state, "Value must be greater than 0.0");
290 0 : ErrorsFound = true;
291 : }
292 0 : if (state.dataIPShortCut->rNumericArgs(3) > state.dataIPShortCut->rNumericArgs(4)) { // error
293 0 : ShowSevereError(state, format("For {}: {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
294 0 : ShowContinueError(state,
295 0 : format("{} [{:.2R}] > {} [{:.2R}]",
296 0 : state.dataIPShortCut->cNumericFieldNames(3),
297 0 : state.dataIPShortCut->rNumericArgs(3),
298 0 : state.dataIPShortCut->cNumericFieldNames(4),
299 0 : state.dataIPShortCut->rNumericArgs(4)));
300 0 : ErrorsFound = true;
301 : }
302 :
303 : // thermal conductivity data
304 0 : state.dataPondGHE->PondGHE(Item).TubeConductivity = state.dataIPShortCut->rNumericArgs(5);
305 0 : state.dataPondGHE->PondGHE(Item).GrndConductivity = state.dataIPShortCut->rNumericArgs(6);
306 :
307 0 : if (state.dataIPShortCut->rNumericArgs(5) <= 0.0) {
308 0 : ShowSevereError(state, format("Invalid {}={:.4R}", state.dataIPShortCut->cNumericFieldNames(5), state.dataIPShortCut->rNumericArgs(5)));
309 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
310 0 : ShowContinueError(state, "Value must be greater than 0.0");
311 0 : ErrorsFound = true;
312 : }
313 0 : if (state.dataIPShortCut->rNumericArgs(6) <= 0.0) {
314 0 : ShowSevereError(state, format("Invalid {}={:.4R}", state.dataIPShortCut->cNumericFieldNames(6), state.dataIPShortCut->rNumericArgs(6)));
315 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
316 0 : ShowContinueError(state, "Value must be greater than 0.0");
317 0 : ErrorsFound = true;
318 : }
319 :
320 : // circuits
321 0 : state.dataPondGHE->PondGHE(Item).NumCircuits = state.dataIPShortCut->rNumericArgs(7);
322 :
323 0 : if (state.dataIPShortCut->rNumericArgs(7) <= 0) {
324 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(7), state.dataIPShortCut->rNumericArgs(7)));
325 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
326 0 : ShowContinueError(state, "Value must be greater than 0.0");
327 0 : ErrorsFound = true;
328 : }
329 0 : state.dataPondGHE->PondGHE(Item).CircuitLength = state.dataIPShortCut->rNumericArgs(8);
330 0 : if (state.dataIPShortCut->rNumericArgs(8) <= 0) {
331 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(8), state.dataIPShortCut->rNumericArgs(8)));
332 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
333 0 : ShowContinueError(state, "Value must be greater than 0.0");
334 0 : ErrorsFound = true;
335 : }
336 :
337 : } // end of input loop
338 :
339 : // final error check
340 0 : if (ErrorsFound) {
341 0 : ShowFatalError(state, format("Errors found in processing input for {}", state.dataIPShortCut->cCurrentModuleObject));
342 : }
343 :
344 0 : if (!state.dataEnvrn->GroundTempInputs[(int)DataEnvironment::GroundTempType::Deep]) {
345 0 : ShowWarningError(state, "GetPondGroundHeatExchanger: No \"Site:GroundTemperature:Deep\" were input.");
346 0 : ShowContinueError(state,
347 0 : format("Defaults, constant throughout the year of ({:.1R}) will be used.",
348 0 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]));
349 : }
350 0 : }
351 :
352 0 : void PondGroundHeatExchangerData::setupOutputVars(EnergyPlusData &state)
353 : {
354 0 : SetupOutputVariable(state,
355 : "Pond Heat Exchanger Heat Transfer Rate",
356 : Constant::Units::W,
357 0 : this->HeatTransferRate,
358 : OutputProcessor::TimeStepType::System,
359 : OutputProcessor::StoreType::Average,
360 0 : this->Name);
361 0 : SetupOutputVariable(state,
362 : "Pond Heat Exchanger Heat Transfer Energy",
363 : Constant::Units::J,
364 0 : this->Energy,
365 : OutputProcessor::TimeStepType::System,
366 : OutputProcessor::StoreType::Sum,
367 0 : this->Name);
368 0 : SetupOutputVariable(state,
369 : "Pond Heat Exchanger Mass Flow Rate",
370 : Constant::Units::kg_s,
371 0 : this->MassFlowRate,
372 : OutputProcessor::TimeStepType::System,
373 : OutputProcessor::StoreType::Average,
374 0 : this->Name);
375 0 : SetupOutputVariable(state,
376 : "Pond Heat Exchanger Inlet Temperature",
377 : Constant::Units::C,
378 0 : this->InletTemp,
379 : OutputProcessor::TimeStepType::System,
380 : OutputProcessor::StoreType::Average,
381 0 : this->Name);
382 0 : SetupOutputVariable(state,
383 : "Pond Heat Exchanger Outlet Temperature",
384 : Constant::Units::C,
385 0 : this->OutletTemp,
386 : OutputProcessor::TimeStepType::System,
387 : OutputProcessor::StoreType::Average,
388 0 : this->Name);
389 0 : SetupOutputVariable(state,
390 : "Pond Heat Exchanger Bulk Temperature",
391 : Constant::Units::C,
392 0 : this->PondTemp,
393 : OutputProcessor::TimeStepType::System,
394 : OutputProcessor::StoreType::Average,
395 0 : this->Name);
396 0 : }
397 :
398 0 : void PondGroundHeatExchangerData::InitPondGroundHeatExchanger(EnergyPlusData &state,
399 : bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
400 : )
401 : {
402 :
403 : // SUBROUTINE INFORMATION:
404 : // AUTHOR Simon Rees
405 : // DATE WRITTEN August 2002
406 : // MODIFIED na
407 : // RE-ENGINEERED na
408 :
409 : // PURPOSE OF THIS SUBROUTINE:
410 : // This subroutine Resets the elements of the data structure as necessary
411 : // at the first HVAC iteration of each time step.
412 :
413 : // METHODOLOGY EMPLOYED:
414 : // One of the things done here is to update the record of the past pond
415 : // temperature. This is needed in order to solve the diff. eqn. to find
416 : // the temperature at the end of the next time step.
417 : // Also set module variables to data structure for this pond. Set flow rate
418 : // from node data and hypothetical design flow.
419 :
420 : // repeated warm up days tend to drive the initial pond temperature toward the drybulb temperature
421 : // For each environment start the pond midway between drybulb and ground temp.
422 :
423 0 : this->oneTimeInit(state);
424 :
425 0 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && this->firstTimeThrough) {
426 : // update past temperature
427 0 : this->PastBulkTemperature = this->BulkTemperature;
428 0 : this->firstTimeThrough = false;
429 0 : } else if (!FirstHVACIteration) {
430 0 : this->firstTimeThrough = true;
431 : }
432 :
433 0 : this->InletTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
434 0 : this->PondTemp = this->BulkTemperature;
435 :
436 : // Hypothetical design flow rate
437 0 : Real64 DesignFlow = PlantUtilities::RegulateCondenserCompFlowReqOp(state, this->plantLoc, this->DesignMassFlowRate);
438 :
439 0 : PlantUtilities::SetComponentFlowRate(state, DesignFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
440 :
441 : // get the current flow rate - module variable
442 0 : this->MassFlowRate = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
443 0 : }
444 :
445 0 : void PondGroundHeatExchangerData::CalcPondGroundHeatExchanger(EnergyPlusData &state)
446 : {
447 :
448 : // AUTHOR Simon Rees
449 : // DATE WRITTEN August 2002
450 : // MODIFIED na
451 : // RE-ENGINEERED na
452 :
453 : // PURPOSE OF THIS SUBROUTINE:
454 : // This subroutine does all of the stuff that is necessary to simulate
455 : // a pond ground heat exchanger. Calls are made to appropriate subroutines
456 : // either in this module or outside of it.
457 :
458 : // METHODOLOGY EMPLOYED:
459 : // The differential equation defined by the heat balance is solved using
460 : // a fourth order Runge-Kutta numerical integration method. The differential
461 : // equation is:
462 : // Mdot*Cp*dT/dt = Sum of fluxes.
463 :
464 : // REFERENCES:
465 : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
466 : // M.S. Thesis, Oklahoma State University, December 1999.
467 : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith. 2000. A Model For
468 : // Simulating The Performance Of A Shallow Pond As A Supplemental Heat
469 : // Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
470 : // ASHRAE Transactions. 106(2):107-121.
471 :
472 : static constexpr std::string_view RoutineName("CalcPondGroundHeatExchanger");
473 :
474 0 : Real64 PondMass = this->Depth * this->Area * this->water->getDensity(state, max(this->PondTemp, 0.0), RoutineName);
475 0 : Real64 SpecificHeat = this->water->getSpecificHeat(state, max(this->PondTemp, 0.0), RoutineName);
476 :
477 0 : Real64 Flux = this->CalcTotalFLux(state, this->PondTemp);
478 : Real64 PondTempStar =
479 0 : this->PastBulkTemperature + 0.5 * Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * Flux / (SpecificHeat * PondMass);
480 :
481 0 : Real64 FluxStar = this->CalcTotalFLux(state, PondTempStar);
482 : Real64 PondTempStarStar =
483 0 : this->PastBulkTemperature + 0.5 * Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * FluxStar / (SpecificHeat * PondMass);
484 :
485 0 : Real64 FluxStarStar = this->CalcTotalFLux(state, PondTempStarStar);
486 : Real64 PondTempStarStarStar =
487 0 : this->PastBulkTemperature + Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * FluxStarStar / (SpecificHeat * PondMass);
488 :
489 0 : this->PondTemp = this->PastBulkTemperature + Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys *
490 0 : (Flux + 2.0 * FluxStar + 2.0 * FluxStarStar + this->CalcTotalFLux(state, PondTempStarStarStar)) /
491 0 : (6.0 * SpecificHeat * PondMass);
492 0 : }
493 :
494 0 : Real64 PondGroundHeatExchangerData::CalcTotalFLux(EnergyPlusData &state, Real64 const PondBulkTemp // pond temp for this flux calculation
495 : )
496 : {
497 : // AUTHOR Simon Rees
498 : // DATE WRITTEN August 2002
499 : // MODIFIED na
500 : // RE-ENGINEERED na
501 :
502 : // PURPOSE OF THIS FUNCTION:
503 : // This calculates the summation of the heat fluxes on the pond for a
504 : // given pond temperature. The following heat fluxes are calculated:
505 : // convection,
506 : // long-wave radiation,
507 : // solar gain,
508 : // evaporation,
509 : // ground conduction,
510 : // along with heat exchange with the fluid
511 :
512 : // METHODOLOGY EMPLOYED:
513 : // Convection is calculated with the ASHRAE simple convection coefficients.
514 : // Evaporation is calculated assuming a fixed Lewis number - not as in
515 : // Chaisson model. Heat transfer with the fluid is calculated using a heat
516 : // exchanger Effectiveness-NTU method, where the pond is seen as a static
517 : // fluid - this is also different from Chaisson's original model (assumed
518 : // pond at average of inlet and outlet temps).
519 :
520 : // REFERENCES:
521 : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
522 : // M.S. Thesis, Oklahoma State University, December 1999.
523 : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith. 2000. A Model For
524 : // Simulating The Performance Of A Shallow Pond As A Supplemental Heat
525 : // Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
526 : // ASHRAE Transactions. 106(2):107-121.
527 : // Hull, J.R., K.V. Liu, W.T. Sha, J. Kamal, and C.E. Nielsen, 1984.
528 : // Dependence of Ground Heat Losses Upon Solar Pond Size and Perimeter
529 : // Insulation Calculated and Experimental Results. Solar Energy,33(1):25-33.
530 :
531 : Real64 CalcTotalFLux; // function return variable
532 :
533 0 : Real64 constexpr PrandtlAir(0.71); // Prandtl number for air - assumed constant
534 0 : Real64 constexpr SchmidtAir(0.6); // Schmidt number for air - assumed constant
535 0 : Real64 constexpr PondHeight(0.0); // for now
536 :
537 : static constexpr std::string_view RoutineName("PondGroundHeatExchanger:CalcTotalFlux");
538 :
539 : // make a surface heat balance and solve for temperature
540 0 : Real64 ThermalAbs = 0.9; // thermal absorptivity
541 :
542 : // set appropriate external temp
543 : // use height dependency -- if there was a height for this unit, it could be inserted.
544 : // parameter PondHeight=0.0 is used.
545 0 : Real64 OutDryBulb = DataEnvironment::OutDryBulbTempAt(state, PondHeight);
546 0 : Real64 OutWetBulb = DataEnvironment::OutWetBulbTempAt(state, PondHeight);
547 :
548 : Real64 ExternalTemp; // external environmental temp - drybulb or wetbulb
549 0 : if (state.dataEnvrn->IsSnow || state.dataEnvrn->IsRain) {
550 0 : ExternalTemp = OutWetBulb;
551 : } else { // normal dry conditions
552 0 : ExternalTemp = OutDryBulb;
553 : }
554 :
555 : // absolute temperatures
556 0 : Real64 SurfTempAbs = PondBulkTemp + Constant::Kelvin; // absolute value of surface temp
557 0 : Real64 SkyTempAbs = state.dataEnvrn->SkyTemp + Constant::Kelvin; // absolute value of sky temp
558 :
559 : // ASHRAE simple convection coefficient model for external surfaces.
560 0 : Real64 ConvCoef = Convect::CalcASHRAESimpExtConvCoeff(Material::SurfaceRoughness::VeryRough, DataEnvironment::WindSpeedAt(state, PondHeight));
561 :
562 : // convective flux
563 0 : Real64 FluxConvect = ConvCoef * (PondBulkTemp - ExternalTemp);
564 :
565 : // long-wave radiation between pond and sky.
566 0 : Real64 FluxLongwave = StefBoltzmann * ThermalAbs * (pow_4(SurfTempAbs) - pow_4(SkyTempAbs));
567 :
568 : // total absorbed solar using function - no ground solar
569 0 : Real64 FluxSolAbsorbed = CalcSolarFlux(state);
570 :
571 : // specific heat from fluid prop routines
572 0 : Real64 SpecHeat = this->plantLoc.loop->glycol->getSpecificHeat(state, max(this->InletTemp, 0.0), RoutineName);
573 : // heat transfer with fluid - heat exchanger analogy.
574 :
575 : // convective flux
576 0 : Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, PondBulkTemp, this->MassFlowRate);
577 0 : Real64 Qfluid = this->MassFlowRate * SpecHeat * effectiveness * (this->InletTemp - PondBulkTemp);
578 :
579 : // evaporation flux
580 : // get air properties
581 0 : Real64 HumRatioAir = Psychrometrics::PsyWFnTdbTwbPb(state, OutDryBulb, OutWetBulb, state.dataEnvrn->OutBaroPress);
582 :
583 : // humidity ratio at pond surface/film temperature
584 0 : Real64 HumRatioFilm = Psychrometrics::PsyWFnTdbTwbPb(state, PondBulkTemp, PondBulkTemp, state.dataEnvrn->OutBaroPress);
585 0 : Real64 SpecHeatAir = Psychrometrics::PsyCpAirFnW(HumRatioAir);
586 0 : Real64 LatentHeatAir = Psychrometrics::PsyHfgAirFnWTdb(HumRatioAir, OutDryBulb);
587 :
588 : // evaporative heat flux
589 0 : Real64 FluxEvap = pow_2(PrandtlAir / SchmidtAir) / 3.0 * ConvCoef / SpecHeatAir * (HumRatioFilm - HumRatioAir) * LatentHeatAir;
590 :
591 : // ground heat transfer flux
592 0 : Real64 Perimeter = 4.0 * std::sqrt(this->Area); // pond perimeter -- square assumption
593 :
594 : // ground heat transfer coefficient
595 0 : Real64 UvalueGround = 0.999 * (this->GrndConductivity / this->Depth) + 1.37 * (this->GrndConductivity * Perimeter / this->Area);
596 :
597 : // ground heat transfer flux
598 0 : Real64 FluxGround = UvalueGround * (PondBulkTemp - state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
599 :
600 0 : CalcTotalFLux = Qfluid + this->Area * (FluxSolAbsorbed - FluxConvect - FluxLongwave - FluxEvap - FluxGround);
601 :
602 0 : return CalcTotalFLux;
603 : }
604 :
605 0 : Real64 PondGroundHeatExchangerData::CalcSolarFlux(EnergyPlusData &state) const
606 : {
607 :
608 : // FUNCTION INFORMATION:
609 : // AUTHOR Simon Rees
610 : // DATE WRITTEN August 2002
611 : // MODIFIED na
612 : // RE-ENGINEERED na
613 :
614 : // PURPOSE OF THIS SUBROUTINE:
615 : // This is used to calculate the net solar flux absorbed by the pond.
616 :
617 : // METHODOLOGY EMPLOYED:
618 : // This is calculated from basic optical formula using the extinction
619 : // coefficient of the pond as the main parameter. This can be in a
620 : // wide range: 0.13 - 7.5 in the literature depending on algae, suspended
621 : // solids etc. ??
622 :
623 : // REFERENCES:
624 : // Duffie, J.A. and W.A. Beckman, 1991. Solar Engineering of Thermal
625 : // Processes, 2 nd Edition. John Wiley and Sons.
626 : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
627 : // M.S. Thesis, Oklahoma State University, December 1999.
628 : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith. 2000. A Model For
629 : // Simulating The Performance Of A Shallow Pond As A Supplemental Heat
630 : // Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
631 : // ASHRAE Transactions. 106(2):107-121.
632 :
633 : Real64 CalcSolarFlux; // Function return variable
634 :
635 0 : Real64 constexpr WaterRefIndex(1.33); // refractive index of water
636 0 : Real64 constexpr AirRefIndex(1.0003); // refractive index of air
637 0 : Real64 constexpr PondExtCoef(0.3); // extinction coefficient of water
638 :
639 : // check for sun up.
640 0 : if (!state.dataEnvrn->SunIsUp) {
641 0 : CalcSolarFlux = 0.0;
642 0 : return CalcSolarFlux;
643 : }
644 :
645 : // get the incidence and reflection angles
646 0 : Real64 IncidAngle = std::acos(state.dataEnvrn->SOLCOS(3));
647 0 : Real64 RefractAngle = std::asin(std::sin(IncidAngle) * AirRefIndex / WaterRefIndex);
648 :
649 : // absorbed component: Tau_a
650 0 : Real64 Absorbtance = std::exp(-PondExtCoef * this->Depth / std::cos(RefractAngle));
651 :
652 : // parallel and perpendicular components
653 0 : Real64 ParallelRad = pow_2(std::tan(RefractAngle - IncidAngle)) / pow_2(std::tan(RefractAngle + IncidAngle));
654 0 : Real64 PerpendRad = pow_2(std::sin(RefractAngle - IncidAngle)) / pow_2(std::sin(RefractAngle + IncidAngle));
655 :
656 : // transmittance: Tau
657 0 : Real64 Transmitance = 0.5 * Absorbtance * ((1.0 - ParallelRad) / (1.0 + ParallelRad) + (1.0 - PerpendRad) / (1.0 + PerpendRad));
658 :
659 : // reflectance: Tau_a - Tau
660 0 : Real64 Reflectance = Absorbtance - Transmitance;
661 :
662 : // apply reflectance to beam and diffuse solar to find flux
663 0 : CalcSolarFlux = (1.0 - Reflectance) * (state.dataEnvrn->SOLCOS(3) * state.dataEnvrn->BeamSolarRad + state.dataEnvrn->DifSolarRad);
664 :
665 0 : return CalcSolarFlux;
666 : }
667 :
668 0 : Real64 PondGroundHeatExchangerData::CalcEffectiveness(EnergyPlusData &state,
669 : Real64 const InsideTemperature, // Temperature of fluid in pipe circuit, in C
670 : Real64 const PondTemperature, // Temperature of pond water (i.e. outside the pipe), in C
671 : Real64 const massFlowRate // Mass flow rate, in kg/s
672 : )
673 : {
674 :
675 : // FUNCTION INFORMATION:
676 : // AUTHOR Simon Rees
677 : // DATE WRITTEN August 2002
678 : // MODIFIED na
679 : // RE-ENGINEERED na
680 :
681 : // PURPOSE OF THIS SUBROUTINE:
682 : // This subroutine calculates the "heat exchanger" effectiveness.
683 : // This routine is adapted from that in the low temp radiant pond model.
684 :
685 : // METHODOLOGY EMPLOYED:
686 : // The heat transfer coefficient is calculated at the pipe and
687 : // consists of inside and outside convection coefficients and conduction
688 : // through the pipe. The other assumptions are that the tube inside
689 : // surface temperature is equal to the "source location temperature"
690 : // and that it is a CONSTANT throughout the pond. External convection is
691 : // natural mode using Churchill and Chu correlation. Inside convection
692 : // calculated using the Dittus-Boelter equation.
693 :
694 : // REFERENCES:
695 : // Incropera, F.P. and D.P. DeWitt, 1996. Introduction to Heat Transfer,
696 : // 3 rd Edition. John Wiley & Sons.
697 : // Churchill, S.W. and H.H.S. Chu. 1975. Correlating Equations for
698 : // Laminar and Turbulent Free Convection from a Horizontal Cylinder.
699 : // International Journal of Heat and Mass Transfer, 18: 1049-1053.
700 : // See also RadiantSystemLowTemp module.
701 :
702 : Real64 CalcEffectiveness; // Function return variable
703 :
704 0 : Real64 constexpr MaxLaminarRe(2300.0); // Maximum Reynolds number for laminar flow
705 0 : Real64 constexpr GravConst(9.81); // gravitational constant - should be fixed!
706 : static constexpr std::string_view CalledFrom("PondGroundHeatExchanger:CalcEffectiveness");
707 :
708 : // evaluate properties at pipe fluid temperature for given pipe fluid
709 :
710 0 : Real64 SpecificHeat = this->plantLoc.loop->glycol->getSpecificHeat(state, InsideTemperature, CalledFrom);
711 0 : Real64 Conductivity = this->plantLoc.loop->glycol->getConductivity(state, InsideTemperature, CalledFrom);
712 0 : Real64 Viscosity = this->plantLoc.loop->glycol->getViscosity(state, InsideTemperature, CalledFrom);
713 :
714 : // Calculate the Reynold's number from RE=(4*Mdot)/(Pi*Mu*Diameter)
715 0 : Real64 ReynoldsNum = 4.0 * massFlowRate / (Constant::Pi * Viscosity * this->TubeInDiameter * this->NumCircuits);
716 :
717 0 : Real64 PrantlNum = Viscosity * SpecificHeat / Conductivity;
718 :
719 : Real64 NusseltNum; // Nusselt number (dimensionless)
720 :
721 : // Calculate the Nusselt number based on what flow regime one is in. h = (k)(Nu)/D
722 0 : if (ReynoldsNum >= MaxLaminarRe) { // Turbulent flow --> use Dittus-Boelter equation
723 0 : NusseltNum = 0.023 * std::pow(ReynoldsNum, 0.8) * std::pow(PrantlNum, 0.3);
724 : } else { // Laminar flow --> use constant surface temperature relation
725 0 : NusseltNum = 3.66;
726 : }
727 :
728 : // inside convection resistance, from Nu
729 0 : Real64 ConvCoefIn = Conductivity * NusseltNum / this->TubeInDiameter;
730 :
731 : // now find properties of pond water - always assume pond fluid is water
732 0 : Real64 WaterSpecHeat = this->water->getSpecificHeat(state, max(PondTemperature, 0.0), CalledFrom);
733 0 : Real64 WaterConductivity = this->water->getConductivity(state, max(PondTemperature, 0.0), CalledFrom);
734 0 : Real64 WaterViscosity = this->water->getViscosity(state, max(PondTemperature, 0.0), CalledFrom);
735 0 : Real64 WaterDensity = this->water->getDensity(state, max(PondTemperature, 0.0), CalledFrom);
736 :
737 : // derived properties for natural convection coefficient
738 : // expansion coef (Beta) = -1/Rho. dRho/dT
739 : // The following code includes some slight modifications from Simon's original code.
740 : // It guarantees that the delta T is 10C and also avoids the problems associated with
741 : // water hitting a maximum density at around 4C. (RKS)
742 0 : Real64 ExpansionCoef = -(this->water->getDensity(state, max(PondTemperature, 10.0) + 5.0, CalledFrom) -
743 0 : this->water->getDensity(state, max(PondTemperature, 10.0) - 5.0, CalledFrom)) /
744 0 : (10.0 * WaterDensity);
745 :
746 0 : Real64 ThermDiff = WaterConductivity / (WaterDensity * WaterSpecHeat);
747 0 : PrantlNum = WaterViscosity * WaterSpecHeat / WaterConductivity;
748 :
749 0 : Real64 RayleighNum = WaterDensity * GravConst * ExpansionCoef * std::abs(InsideTemperature - PondTemperature) * pow_3(TubeOutDiameter) /
750 0 : (WaterViscosity * ThermDiff);
751 :
752 : // Calculate the Nusselt number for natural convection at outside of pipe
753 0 : NusseltNum = pow_2(0.6 + (0.387 * std::pow(RayleighNum, 1.0 / 6.0) / (std::pow(1.0 + 0.559 / std::pow(PrantlNum, 9.0 / 16.0), 8.0 / 27.0))));
754 :
755 : // outside convection resistance, from Nu
756 0 : Real64 ConvCoefOut = WaterConductivity * NusseltNum / this->TubeOutDiameter;
757 :
758 : // conduction resistance of pipe
759 0 : Real64 PipeResistance = this->TubeInDiameter / this->TubeConductivity * std::log(this->TubeOutDiameter / this->TubeInDiameter);
760 :
761 : // total pipe thermal resistance - conduction and convection
762 0 : Real64 TotalResistance = PipeResistance + 1.0 / ConvCoefIn + this->TubeInDiameter / (this->TubeOutDiameter * ConvCoefOut);
763 :
764 : // Calculate the NTU parameter
765 : // NTU = UA/[(Mdot*Cp)min] = A/[Rtot*(Mdot*Cp)min]
766 : // where: Rtot = Ri,convection + Rconduction + Ro,conveciton
767 : // A = Pi*D*TubeLength
768 :
769 : Real64 NTU; // Number of transfer units, non-dimensional
770 :
771 0 : if (massFlowRate == 0.0) {
772 0 : CalcEffectiveness = 1.0;
773 : } else {
774 0 : NTU = Constant::Pi * TubeInDiameter * this->CircuitLength * this->NumCircuits / (TotalResistance * massFlowRate * SpecificHeat);
775 : // Calculate effectiveness - formula for static fluid
776 0 : CalcEffectiveness = (1.0 - std::exp(-NTU));
777 : }
778 :
779 : // Check for frozen pond
780 0 : if (PondTemperature < 0.0) {
781 0 : ++this->ConsecutiveFrozen;
782 0 : if (this->FrozenErrIndex == 0) {
783 0 : ShowWarningMessage(state,
784 0 : format("GroundHeatExchanger:Pond=\"{}\", is frozen; Pond model not valid. Calculated Pond Temperature=[{:.2R}] C",
785 0 : this->Name,
786 : PondTemperature));
787 0 : ShowContinueErrorTimeStamp(state, "");
788 : }
789 0 : ShowRecurringWarningErrorAtEnd(state,
790 0 : "GroundHeatExchanger:Pond=\"" + this->Name + "\", is frozen",
791 0 : this->FrozenErrIndex,
792 : PondTemperature,
793 : PondTemperature,
794 : _,
795 : "[C]",
796 : "[C]");
797 0 : if (this->ConsecutiveFrozen >= state.dataGlobal->TimeStepsInHour * 30) {
798 0 : ShowFatalError(state,
799 0 : format("GroundHeatExchanger:Pond=\"{}\" has been frozen for 30 consecutive hours. Program terminates.", this->Name));
800 : }
801 : } else {
802 0 : this->ConsecutiveFrozen = 0;
803 : }
804 :
805 0 : return CalcEffectiveness;
806 : }
807 :
808 0 : void PondGroundHeatExchangerData::UpdatePondGroundHeatExchanger(EnergyPlusData &state)
809 : {
810 :
811 : // SUBROUTINE INFORMATION:
812 : // AUTHOR Simon Rees
813 : // DATE WRITTEN August 2002
814 : // MODIFIED na
815 : // RE-ENGINEERED na
816 :
817 : // PURPOSE OF THIS SUBROUTINE:
818 : // This subroutine does any updating that needs to be done for pond
819 : // ground heat exchangers. This routine must also set the outlet water
820 : // conditions.
821 :
822 : static constexpr std::string_view RoutineName("PondGroundHeatExchanger:Update");
823 :
824 : // Calculate the water side outlet conditions and set the
825 : // appropriate conditions on the correct HVAC node.
826 0 : Real64 CpFluid = this->plantLoc.loop->glycol->getSpecificHeat(state, this->InletTemp, RoutineName);
827 :
828 0 : PlantUtilities::SafeCopyPlantNode(state, InletNodeNum, OutletNodeNum);
829 :
830 : // update outlet temp
831 0 : if ((CpFluid > 0.0) && (this->MassFlowRate > 0.0)) {
832 0 : this->OutletTemp = this->InletTemp - this->HeatTransferRate / (this->MassFlowRate * CpFluid);
833 : } else {
834 0 : this->OutletTemp = this->InletTemp;
835 : }
836 :
837 : // update node
838 0 : state.dataLoopNodes->Node(this->OutletNodeNum).Temp = this->OutletTemp;
839 0 : state.dataLoopNodes->Node(this->OutletNodeNum).MassFlowRate = this->MassFlowRate;
840 :
841 : // update heat transfer rate
842 : // compute pond heat transfer
843 0 : Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, this->PondTemp, this->MassFlowRate);
844 0 : this->HeatTransferRate = this->MassFlowRate * CpFluid * effectiveness * (this->InletTemp - this->PondTemp);
845 0 : this->Energy = this->HeatTransferRate * state.dataHVACGlobal->TimeStepSysSec;
846 :
847 : // keep track of the bulk temperature
848 0 : this->BulkTemperature = this->PondTemp;
849 0 : }
850 0 : void PondGroundHeatExchangerData::oneTimeInit(EnergyPlusData &state)
851 : {
852 0 : Real64 constexpr DesignVelocity(0.5); // Hypothetical design max pipe velocity [m/s]
853 0 : Real64 constexpr PondHeight(0.0); // for now
854 :
855 0 : static std::string const RoutineName("InitPondGroundHeatExchanger");
856 :
857 0 : if (this->setupOutputVarsFlag) {
858 0 : this->setupOutputVars(state);
859 0 : this->setupOutputVarsFlag = false;
860 : }
861 :
862 0 : if (this->OneTimeFlag || state.dataGlobal->WarmupFlag) {
863 : // initialize pond temps to mean of drybulb and ground temps.
864 0 : this->BulkTemperature = this->PastBulkTemperature =
865 0 : 0.5 * (DataEnvironment::OutDryBulbTempAt(state, PondHeight) + state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
866 0 : this->OneTimeFlag = false;
867 : }
868 :
869 : // Init more variables
870 0 : if (this->MyFlag) {
871 : // Locate the hx on the plant loops for later usage
872 0 : bool errFlag = false;
873 0 : PlantUtilities::ScanPlantLoopsForObject(
874 0 : state, this->Name, DataPlant::PlantEquipmentType::GrndHtExchgPond, this->plantLoc, errFlag, _, _, _, _, _);
875 0 : if (errFlag) {
876 0 : ShowFatalError(state, "InitPondGroundHeatExchanger: Program terminated due to previous condition(s).");
877 : }
878 0 : Real64 rho = this->plantLoc.loop->glycol->getDensity(state, 0.0, RoutineName);
879 0 : Real64 Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, 0.0, RoutineName);
880 0 : this->DesignMassFlowRate = Constant::Pi / 4.0 * pow_2(this->TubeInDiameter) * DesignVelocity * rho * this->NumCircuits;
881 0 : this->DesignCapacity = this->DesignMassFlowRate * Cp * 10.0; // assume 10C delta T?
882 0 : PlantUtilities::InitComponentNodes(state, 0.0, this->DesignMassFlowRate, this->InletNodeNum, this->OutletNodeNum);
883 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->InletNodeNum, this->DesignMassFlowRate / rho);
884 :
885 0 : this->MyFlag = false;
886 : }
887 0 : }
888 :
889 : } // namespace EnergyPlus::PondGroundHeatExchanger
|