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 123951 : 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 123951 : this->InitPondGroundHeatExchanger(state, FirstHVACIteration);
125 123951 : this->CalcPondGroundHeatExchanger(state);
126 123951 : this->UpdatePondGroundHeatExchanger(state);
127 123951 : }
128 :
129 3 : PlantComponent *PondGroundHeatExchangerData::factory(EnergyPlusData &state, std::string const &objectName)
130 : {
131 3 : if (state.dataPondGHE->GetInputFlag) {
132 3 : GetPondGroundHeatExchanger(state);
133 3 : state.dataPondGHE->GetInputFlag = false;
134 : }
135 3 : for (auto &ghx : state.dataPondGHE->PondGHE) {
136 3 : if (ghx.Name == objectName) {
137 3 : 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 15 : void PondGroundHeatExchangerData::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
147 : {
148 15 : this->InitPondGroundHeatExchanger(state, true);
149 15 : }
150 :
151 15 : void PondGroundHeatExchangerData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
152 : [[maybe_unused]] const PlantLocation &calledFromLocation,
153 : Real64 &MaxLoad,
154 : Real64 &MinLoad,
155 : Real64 &OptLoad)
156 : {
157 15 : MaxLoad = this->DesignCapacity;
158 15 : MinLoad = 0.0;
159 15 : OptLoad = this->DesignCapacity;
160 15 : }
161 :
162 3 : 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 3 : 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 3 : state.dataIPShortCut->cCurrentModuleObject = "GroundHeatExchanger:Pond";
185 6 : state.dataPondGHE->NumOfPondGHEs =
186 3 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
187 : // allocate data structures
188 3 : if (allocated(state.dataPondGHE->PondGHE)) {
189 0 : state.dataPondGHE->PondGHE.deallocate();
190 : }
191 :
192 3 : state.dataPondGHE->PondGHE.allocate(state.dataPondGHE->NumOfPondGHEs);
193 :
194 : // Obtain all of the user data related to the ponds...
195 6 : for (Item = 1; Item <= state.dataPondGHE->NumOfPondGHEs; ++Item) {
196 :
197 : // get the input data
198 9 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
199 3 : state.dataIPShortCut->cCurrentModuleObject,
200 : Item,
201 3 : state.dataIPShortCut->cAlphaArgs,
202 : NumAlphas,
203 3 : state.dataIPShortCut->rNumericArgs,
204 : NumNumbers,
205 : IOStatus,
206 : _,
207 : _,
208 3 : state.dataIPShortCut->cAlphaFieldNames,
209 3 : state.dataIPShortCut->cNumericFieldNames);
210 :
211 3 : if ((state.dataPondGHE->PondGHE(Item).water = Fluid::GetWater(state)) == nullptr) {
212 0 : ShowSevereError(state, "Fluid Properties for WATER not found");
213 0 : ErrorsFound = true;
214 : }
215 :
216 : // General user input data
217 3 : state.dataPondGHE->PondGHE(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
218 :
219 : // get inlet node data
220 3 : state.dataPondGHE->PondGHE(Item).InletNode = state.dataIPShortCut->cAlphaArgs(2);
221 3 : state.dataPondGHE->PondGHE(Item).InletNodeNum =
222 6 : NodeInputManager::GetOnlySingleNode(state,
223 3 : state.dataIPShortCut->cAlphaArgs(2),
224 : ErrorsFound,
225 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerPond,
226 3 : state.dataIPShortCut->cAlphaArgs(1),
227 : DataLoopNode::NodeFluidType::Water,
228 : DataLoopNode::ConnectionType::Inlet,
229 : NodeInputManager::CompFluidStream::Primary,
230 : DataLoopNode::ObjectIsNotParent);
231 3 : if (state.dataPondGHE->PondGHE(Item).InletNodeNum == 0) {
232 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
233 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
234 0 : ErrorsFound = true;
235 : }
236 :
237 : // get outlet node data
238 3 : state.dataPondGHE->PondGHE(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(3);
239 3 : state.dataPondGHE->PondGHE(Item).OutletNodeNum =
240 6 : NodeInputManager::GetOnlySingleNode(state,
241 3 : state.dataIPShortCut->cAlphaArgs(3),
242 : ErrorsFound,
243 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerPond,
244 3 : state.dataIPShortCut->cAlphaArgs(1),
245 : DataLoopNode::NodeFluidType::Water,
246 : DataLoopNode::ConnectionType::Outlet,
247 : NodeInputManager::CompFluidStream::Primary,
248 : DataLoopNode::ObjectIsNotParent);
249 3 : if (state.dataPondGHE->PondGHE(Item).OutletNodeNum == 0) {
250 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
251 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
252 0 : ErrorsFound = true;
253 : }
254 :
255 6 : BranchNodeConnections::TestCompSet(state,
256 3 : state.dataIPShortCut->cCurrentModuleObject,
257 3 : state.dataIPShortCut->cAlphaArgs(1),
258 3 : state.dataIPShortCut->cAlphaArgs(2),
259 3 : state.dataIPShortCut->cAlphaArgs(3),
260 : "Condenser Water Nodes");
261 :
262 : // pond geometry data
263 3 : state.dataPondGHE->PondGHE(Item).Depth = state.dataIPShortCut->rNumericArgs(1);
264 3 : state.dataPondGHE->PondGHE(Item).Area = state.dataIPShortCut->rNumericArgs(2);
265 3 : if (state.dataIPShortCut->rNumericArgs(1) <= 0.0) {
266 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(1), state.dataIPShortCut->rNumericArgs(1)));
267 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
268 0 : ShowContinueError(state, "Value must be greater than 0.0");
269 0 : ErrorsFound = true;
270 : }
271 3 : if (state.dataIPShortCut->rNumericArgs(2) <= 0.0) {
272 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
273 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
274 0 : ShowContinueError(state, "Value must be greater than 0.0");
275 0 : ErrorsFound = true;
276 : }
277 :
278 : // tube data
279 3 : state.dataPondGHE->PondGHE(Item).TubeInDiameter = state.dataIPShortCut->rNumericArgs(3);
280 3 : state.dataPondGHE->PondGHE(Item).TubeOutDiameter = state.dataIPShortCut->rNumericArgs(4);
281 :
282 3 : if (state.dataIPShortCut->rNumericArgs(3) <= 0.0) {
283 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(3), state.dataIPShortCut->rNumericArgs(3)));
284 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
285 0 : ShowContinueError(state, "Value must be greater than 0.0");
286 0 : ErrorsFound = true;
287 : }
288 3 : if (state.dataIPShortCut->rNumericArgs(4) <= 0.0) {
289 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(4), state.dataIPShortCut->rNumericArgs(4)));
290 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
291 0 : ShowContinueError(state, "Value must be greater than 0.0");
292 0 : ErrorsFound = true;
293 : }
294 3 : if (state.dataIPShortCut->rNumericArgs(3) > state.dataIPShortCut->rNumericArgs(4)) { // error
295 0 : ShowSevereError(state, format("For {}: {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
296 0 : ShowContinueError(state,
297 0 : format("{} [{:.2R}] > {} [{:.2R}]",
298 0 : state.dataIPShortCut->cNumericFieldNames(3),
299 0 : state.dataIPShortCut->rNumericArgs(3),
300 0 : state.dataIPShortCut->cNumericFieldNames(4),
301 0 : state.dataIPShortCut->rNumericArgs(4)));
302 0 : ErrorsFound = true;
303 : }
304 :
305 : // thermal conductivity data
306 3 : state.dataPondGHE->PondGHE(Item).TubeConductivity = state.dataIPShortCut->rNumericArgs(5);
307 3 : state.dataPondGHE->PondGHE(Item).GrndConductivity = state.dataIPShortCut->rNumericArgs(6);
308 :
309 3 : if (state.dataIPShortCut->rNumericArgs(5) <= 0.0) {
310 0 : ShowSevereError(state, format("Invalid {}={:.4R}", state.dataIPShortCut->cNumericFieldNames(5), state.dataIPShortCut->rNumericArgs(5)));
311 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
312 0 : ShowContinueError(state, "Value must be greater than 0.0");
313 0 : ErrorsFound = true;
314 : }
315 3 : if (state.dataIPShortCut->rNumericArgs(6) <= 0.0) {
316 0 : ShowSevereError(state, format("Invalid {}={:.4R}", state.dataIPShortCut->cNumericFieldNames(6), state.dataIPShortCut->rNumericArgs(6)));
317 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
318 0 : ShowContinueError(state, "Value must be greater than 0.0");
319 0 : ErrorsFound = true;
320 : }
321 :
322 : // circuits
323 3 : state.dataPondGHE->PondGHE(Item).NumCircuits = state.dataIPShortCut->rNumericArgs(7);
324 :
325 3 : if (state.dataIPShortCut->rNumericArgs(7) <= 0) {
326 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(7), state.dataIPShortCut->rNumericArgs(7)));
327 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
328 0 : ShowContinueError(state, "Value must be greater than 0.0");
329 0 : ErrorsFound = true;
330 : }
331 3 : state.dataPondGHE->PondGHE(Item).CircuitLength = state.dataIPShortCut->rNumericArgs(8);
332 3 : if (state.dataIPShortCut->rNumericArgs(8) <= 0) {
333 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(8), state.dataIPShortCut->rNumericArgs(8)));
334 0 : ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
335 0 : ShowContinueError(state, "Value must be greater than 0.0");
336 0 : ErrorsFound = true;
337 : }
338 :
339 : } // end of input loop
340 :
341 : // final error check
342 3 : if (ErrorsFound) {
343 0 : ShowFatalError(state, format("Errors found in processing input for {}", state.dataIPShortCut->cCurrentModuleObject));
344 : }
345 :
346 3 : if (!state.dataEnvrn->GroundTempInputs[(int)DataEnvironment::GroundTempType::Deep]) {
347 0 : ShowWarningError(state, "GetPondGroundHeatExchanger: No \"Site:GroundTemperature:Deep\" were input.");
348 0 : ShowContinueError(state,
349 0 : format("Defaults, constant throughout the year of ({:.1R}) will be used.",
350 0 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]));
351 : }
352 3 : }
353 :
354 3 : void PondGroundHeatExchangerData::setupOutputVars(EnergyPlusData &state)
355 : {
356 6 : SetupOutputVariable(state,
357 : "Pond Heat Exchanger Heat Transfer Rate",
358 : Constant::Units::W,
359 3 : this->HeatTransferRate,
360 : OutputProcessor::TimeStepType::System,
361 : OutputProcessor::StoreType::Average,
362 3 : this->Name);
363 6 : SetupOutputVariable(state,
364 : "Pond Heat Exchanger Heat Transfer Energy",
365 : Constant::Units::J,
366 3 : this->Energy,
367 : OutputProcessor::TimeStepType::System,
368 : OutputProcessor::StoreType::Sum,
369 3 : this->Name);
370 6 : SetupOutputVariable(state,
371 : "Pond Heat Exchanger Mass Flow Rate",
372 : Constant::Units::kg_s,
373 3 : this->MassFlowRate,
374 : OutputProcessor::TimeStepType::System,
375 : OutputProcessor::StoreType::Average,
376 3 : this->Name);
377 6 : SetupOutputVariable(state,
378 : "Pond Heat Exchanger Inlet Temperature",
379 : Constant::Units::C,
380 3 : this->InletTemp,
381 : OutputProcessor::TimeStepType::System,
382 : OutputProcessor::StoreType::Average,
383 3 : this->Name);
384 6 : SetupOutputVariable(state,
385 : "Pond Heat Exchanger Outlet Temperature",
386 : Constant::Units::C,
387 3 : this->OutletTemp,
388 : OutputProcessor::TimeStepType::System,
389 : OutputProcessor::StoreType::Average,
390 3 : this->Name);
391 6 : SetupOutputVariable(state,
392 : "Pond Heat Exchanger Bulk Temperature",
393 : Constant::Units::C,
394 3 : this->PondTemp,
395 : OutputProcessor::TimeStepType::System,
396 : OutputProcessor::StoreType::Average,
397 3 : this->Name);
398 3 : }
399 :
400 123966 : void PondGroundHeatExchangerData::InitPondGroundHeatExchanger(EnergyPlusData &state,
401 : bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
402 : )
403 : {
404 :
405 : // SUBROUTINE INFORMATION:
406 : // AUTHOR Simon Rees
407 : // DATE WRITTEN August 2002
408 : // MODIFIED na
409 : // RE-ENGINEERED na
410 :
411 : // PURPOSE OF THIS SUBROUTINE:
412 : // This subroutine Resets the elements of the data structure as necessary
413 : // at the first HVAC iteration of each time step.
414 :
415 : // METHODOLOGY EMPLOYED:
416 : // One of the things done here is to update the record of the past pond
417 : // temperature. This is needed in order to solve the diff. eqn. to find
418 : // the temperature at the end of the next time step.
419 : // Also set module variables to data structure for this pond. Set flow rate
420 : // from node data and hypothetical design flow.
421 :
422 : // repeated warm up days tend to drive the initial pond temperature toward the drybulb temperature
423 : // For each environment start the pond midway between drybulb and ground temp.
424 :
425 123966 : this->oneTimeInit(state);
426 :
427 123966 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && this->firstTimeThrough) {
428 : // update past temperature
429 6088 : this->PastBulkTemperature = this->BulkTemperature;
430 6088 : this->firstTimeThrough = false;
431 117878 : } else if (!FirstHVACIteration) {
432 61968 : this->firstTimeThrough = true;
433 : }
434 :
435 123966 : this->InletTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
436 123966 : this->PondTemp = this->BulkTemperature;
437 :
438 : // Hypothetical design flow rate
439 123966 : Real64 DesignFlow = PlantUtilities::RegulateCondenserCompFlowReqOp(state, this->plantLoc, this->DesignMassFlowRate);
440 :
441 123966 : PlantUtilities::SetComponentFlowRate(state, DesignFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
442 :
443 : // get the current flow rate - module variable
444 123966 : this->MassFlowRate = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
445 123966 : }
446 :
447 123951 : void PondGroundHeatExchangerData::CalcPondGroundHeatExchanger(EnergyPlusData &state)
448 : {
449 :
450 : // AUTHOR Simon Rees
451 : // DATE WRITTEN August 2002
452 : // MODIFIED na
453 : // RE-ENGINEERED na
454 :
455 : // PURPOSE OF THIS SUBROUTINE:
456 : // This subroutine does all of the stuff that is necessary to simulate
457 : // a pond ground heat exchanger. Calls are made to appropriate subroutines
458 : // either in this module or outside of it.
459 :
460 : // METHODOLOGY EMPLOYED:
461 : // The differential equation defined by the heat balance is solved using
462 : // a fourth order Runge-Kutta numerical integration method. The differential
463 : // equation is:
464 : // Mdot*Cp*dT/dt = Sum of fluxes.
465 :
466 : // REFERENCES:
467 : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
468 : // M.S. Thesis, Oklahoma State University, December 1999.
469 : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith. 2000. A Model For
470 : // Simulating The Performance Of A Shallow Pond As A Supplemental Heat
471 : // Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
472 : // ASHRAE Transactions. 106(2):107-121.
473 :
474 : static constexpr std::string_view RoutineName("CalcPondGroundHeatExchanger");
475 :
476 123951 : Real64 PondMass = this->Depth * this->Area * this->water->getDensity(state, max(this->PondTemp, 0.0), RoutineName);
477 123951 : Real64 SpecificHeat = this->water->getSpecificHeat(state, max(this->PondTemp, 0.0), RoutineName);
478 :
479 123951 : Real64 Flux = this->CalcTotalFLux(state, this->PondTemp);
480 : Real64 PondTempStar =
481 123951 : this->PastBulkTemperature + 0.5 * Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * Flux / (SpecificHeat * PondMass);
482 :
483 123951 : Real64 FluxStar = this->CalcTotalFLux(state, PondTempStar);
484 : Real64 PondTempStarStar =
485 123951 : this->PastBulkTemperature + 0.5 * Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * FluxStar / (SpecificHeat * PondMass);
486 :
487 123951 : Real64 FluxStarStar = this->CalcTotalFLux(state, PondTempStarStar);
488 : Real64 PondTempStarStarStar =
489 123951 : this->PastBulkTemperature + Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * FluxStarStar / (SpecificHeat * PondMass);
490 :
491 123951 : this->PondTemp = this->PastBulkTemperature + Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys *
492 123951 : (Flux + 2.0 * FluxStar + 2.0 * FluxStarStar + this->CalcTotalFLux(state, PondTempStarStarStar)) /
493 123951 : (6.0 * SpecificHeat * PondMass);
494 123951 : }
495 :
496 495804 : Real64 PondGroundHeatExchangerData::CalcTotalFLux(EnergyPlusData &state, Real64 const PondBulkTemp // pond temp for this flux calculation
497 : )
498 : {
499 : // AUTHOR Simon Rees
500 : // DATE WRITTEN August 2002
501 : // MODIFIED na
502 : // RE-ENGINEERED na
503 :
504 : // PURPOSE OF THIS FUNCTION:
505 : // This calculates the summation of the heat fluxes on the pond for a
506 : // given pond temperature. The following heat fluxes are calculated:
507 : // convection,
508 : // long-wave radiation,
509 : // solar gain,
510 : // evaporation,
511 : // ground conduction,
512 : // along with heat exchange with the fluid
513 :
514 : // METHODOLOGY EMPLOYED:
515 : // Convection is calculated with the ASHRAE simple convection coefficients.
516 : // Evaporation is calculated assuming a fixed Lewis number - not as in
517 : // Chaisson model. Heat transfer with the fluid is calculated using a heat
518 : // exchanger Effectiveness-NTU method, where the pond is seen as a static
519 : // fluid - this is also different from Chaisson's original model (assumed
520 : // pond at average of inlet and outlet temps).
521 :
522 : // REFERENCES:
523 : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
524 : // M.S. Thesis, Oklahoma State University, December 1999.
525 : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith. 2000. A Model For
526 : // Simulating The Performance Of A Shallow Pond As A Supplemental Heat
527 : // Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
528 : // ASHRAE Transactions. 106(2):107-121.
529 : // Hull, J.R., K.V. Liu, W.T. Sha, J. Kamal, and C.E. Nielsen, 1984.
530 : // Dependence of Ground Heat Losses Upon Solar Pond Size and Perimeter
531 : // Insulation Calculated and Experimental Results. Solar Energy,33(1):25-33.
532 :
533 : Real64 CalcTotalFLux; // function return variable
534 :
535 495804 : Real64 constexpr PrandtlAir(0.71); // Prandtl number for air - assumed constant
536 495804 : Real64 constexpr SchmidtAir(0.6); // Schmidt number for air - assumed constant
537 495804 : Real64 constexpr PondHeight(0.0); // for now
538 :
539 : static constexpr std::string_view RoutineName("PondGroundHeatExchanger:CalcTotalFlux");
540 :
541 : // make a surface heat balance and solve for temperature
542 495804 : Real64 ThermalAbs = 0.9; // thermal absorptivity
543 :
544 : // set appropriate external temp
545 : // use height dependency -- if there was a height for this unit, it could be inserted.
546 : // parameter PondHeight=0.0 is used.
547 495804 : Real64 OutDryBulb = DataEnvironment::OutDryBulbTempAt(state, PondHeight);
548 495804 : Real64 OutWetBulb = DataEnvironment::OutWetBulbTempAt(state, PondHeight);
549 :
550 : Real64 ExternalTemp; // external environmental temp - drybulb or wetbulb
551 495804 : if (state.dataEnvrn->IsSnow || state.dataEnvrn->IsRain) {
552 0 : ExternalTemp = OutWetBulb;
553 : } else { // normal dry conditions
554 495804 : ExternalTemp = OutDryBulb;
555 : }
556 :
557 : // absolute temperatures
558 495804 : Real64 SurfTempAbs = PondBulkTemp + Constant::Kelvin; // absolute value of surface temp
559 495804 : Real64 SkyTempAbs = state.dataEnvrn->SkyTemp + Constant::Kelvin; // absolute value of sky temp
560 :
561 : // ASHRAE simple convection coefficient model for external surfaces.
562 495804 : Real64 ConvCoef = Convect::CalcASHRAESimpExtConvCoeff(Material::SurfaceRoughness::VeryRough, DataEnvironment::WindSpeedAt(state, PondHeight));
563 :
564 : // convective flux
565 495804 : Real64 FluxConvect = ConvCoef * (PondBulkTemp - ExternalTemp);
566 :
567 : // long-wave radiation between pond and sky.
568 495804 : Real64 FluxLongwave = StefBoltzmann * ThermalAbs * (pow_4(SurfTempAbs) - pow_4(SkyTempAbs));
569 :
570 : // total absorbed solar using function - no ground solar
571 495804 : Real64 FluxSolAbsorbed = CalcSolarFlux(state);
572 :
573 : // specific heat from fluid prop routines
574 495804 : Real64 SpecHeat = this->plantLoc.loop->glycol->getSpecificHeat(state, max(this->InletTemp, 0.0), RoutineName);
575 : // heat transfer with fluid - heat exchanger analogy.
576 :
577 : // convective flux
578 495804 : Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, PondBulkTemp, this->MassFlowRate);
579 495804 : Real64 Qfluid = this->MassFlowRate * SpecHeat * effectiveness * (this->InletTemp - PondBulkTemp);
580 :
581 : // evaporation flux
582 : // get air properties
583 495804 : Real64 HumRatioAir = Psychrometrics::PsyWFnTdbTwbPb(state, OutDryBulb, OutWetBulb, state.dataEnvrn->OutBaroPress);
584 :
585 : // humidity ratio at pond surface/film temperature
586 495804 : Real64 HumRatioFilm = Psychrometrics::PsyWFnTdbTwbPb(state, PondBulkTemp, PondBulkTemp, state.dataEnvrn->OutBaroPress);
587 495804 : Real64 SpecHeatAir = Psychrometrics::PsyCpAirFnW(HumRatioAir);
588 495804 : Real64 LatentHeatAir = Psychrometrics::PsyHfgAirFnWTdb(HumRatioAir, OutDryBulb);
589 :
590 : // evaporative heat flux
591 495804 : Real64 FluxEvap = pow_2(PrandtlAir / SchmidtAir) / 3.0 * ConvCoef / SpecHeatAir * (HumRatioFilm - HumRatioAir) * LatentHeatAir;
592 :
593 : // ground heat transfer flux
594 495804 : Real64 Perimeter = 4.0 * std::sqrt(this->Area); // pond perimeter -- square assumption
595 :
596 : // ground heat transfer coefficient
597 495804 : Real64 UvalueGround = 0.999 * (this->GrndConductivity / this->Depth) + 1.37 * (this->GrndConductivity * Perimeter / this->Area);
598 :
599 : // ground heat transfer flux
600 495804 : Real64 FluxGround = UvalueGround * (PondBulkTemp - state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
601 :
602 495804 : CalcTotalFLux = Qfluid + this->Area * (FluxSolAbsorbed - FluxConvect - FluxLongwave - FluxEvap - FluxGround);
603 :
604 495804 : return CalcTotalFLux;
605 : }
606 :
607 495804 : Real64 PondGroundHeatExchangerData::CalcSolarFlux(EnergyPlusData &state) const
608 : {
609 :
610 : // FUNCTION INFORMATION:
611 : // AUTHOR Simon Rees
612 : // DATE WRITTEN August 2002
613 : // MODIFIED na
614 : // RE-ENGINEERED na
615 :
616 : // PURPOSE OF THIS SUBROUTINE:
617 : // This is used to calculate the net solar flux absorbed by the pond.
618 :
619 : // METHODOLOGY EMPLOYED:
620 : // This is calculated from basic optical formula using the extinction
621 : // coefficient of the pond as the main parameter. This can be in a
622 : // wide range: 0.13 - 7.5 in the literature depending on algae, suspended
623 : // solids etc. ??
624 :
625 : // REFERENCES:
626 : // Duffie, J.A. and W.A. Beckman, 1991. Solar Engineering of Thermal
627 : // Processes, 2 nd Edition. John Wiley and Sons.
628 : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
629 : // M.S. Thesis, Oklahoma State University, December 1999.
630 : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith. 2000. A Model For
631 : // Simulating The Performance Of A Shallow Pond As A Supplemental Heat
632 : // Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
633 : // ASHRAE Transactions. 106(2):107-121.
634 :
635 : Real64 CalcSolarFlux; // Function return variable
636 :
637 495804 : Real64 constexpr WaterRefIndex(1.33); // refractive index of water
638 495804 : Real64 constexpr AirRefIndex(1.0003); // refractive index of air
639 495804 : Real64 constexpr PondExtCoef(0.3); // extinction coefficient of water
640 :
641 : // check for sun up.
642 495804 : if (!state.dataEnvrn->SunIsUp) {
643 249836 : CalcSolarFlux = 0.0;
644 249836 : return CalcSolarFlux;
645 : }
646 :
647 : // get the incidence and reflection angles
648 245968 : Real64 IncidAngle = std::acos(state.dataEnvrn->SOLCOS(3));
649 245968 : Real64 RefractAngle = std::asin(std::sin(IncidAngle) * AirRefIndex / WaterRefIndex);
650 :
651 : // absorbed component: Tau_a
652 245968 : Real64 Absorbtance = std::exp(-PondExtCoef * this->Depth / std::cos(RefractAngle));
653 :
654 : // parallel and perpendicular components
655 245968 : Real64 ParallelRad = pow_2(std::tan(RefractAngle - IncidAngle)) / pow_2(std::tan(RefractAngle + IncidAngle));
656 245968 : Real64 PerpendRad = pow_2(std::sin(RefractAngle - IncidAngle)) / pow_2(std::sin(RefractAngle + IncidAngle));
657 :
658 : // transmittance: Tau
659 245968 : Real64 Transmitance = 0.5 * Absorbtance * ((1.0 - ParallelRad) / (1.0 + ParallelRad) + (1.0 - PerpendRad) / (1.0 + PerpendRad));
660 :
661 : // reflectance: Tau_a - Tau
662 245968 : Real64 Reflectance = Absorbtance - Transmitance;
663 :
664 : // apply reflectance to beam and diffuse solar to find flux
665 245968 : CalcSolarFlux = (1.0 - Reflectance) * (state.dataEnvrn->SOLCOS(3) * state.dataEnvrn->BeamSolarRad + state.dataEnvrn->DifSolarRad);
666 :
667 245968 : return CalcSolarFlux;
668 : }
669 :
670 619755 : Real64 PondGroundHeatExchangerData::CalcEffectiveness(EnergyPlusData &state,
671 : Real64 const InsideTemperature, // Temperature of fluid in pipe circuit, in C
672 : Real64 const PondTemperature, // Temperature of pond water (i.e. outside the pipe), in C
673 : Real64 const massFlowRate // Mass flow rate, in kg/s
674 : )
675 : {
676 :
677 : // FUNCTION INFORMATION:
678 : // AUTHOR Simon Rees
679 : // DATE WRITTEN August 2002
680 : // MODIFIED na
681 : // RE-ENGINEERED na
682 :
683 : // PURPOSE OF THIS SUBROUTINE:
684 : // This subroutine calculates the "heat exchanger" effectiveness.
685 : // This routine is adapted from that in the low temp radiant pond model.
686 :
687 : // METHODOLOGY EMPLOYED:
688 : // The heat transfer coefficient is calculated at the pipe and
689 : // consists of inside and outside convection coefficients and conduction
690 : // through the pipe. The other assumptions are that the tube inside
691 : // surface temperature is equal to the "source location temperature"
692 : // and that it is a CONSTANT throughout the pond. External convection is
693 : // natural mode using Churchill and Chu correlation. Inside convection
694 : // calculated using the Dittus-Boelter equation.
695 :
696 : // REFERENCES:
697 : // Incropera, F.P. and D.P. DeWitt, 1996. Introduction to Heat Transfer,
698 : // 3 rd Edition. John Wiley & Sons.
699 : // Churchill, S.W. and H.H.S. Chu. 1975. Correlating Equations for
700 : // Laminar and Turbulent Free Convection from a Horizontal Cylinder.
701 : // International Journal of Heat and Mass Transfer, 18: 1049-1053.
702 : // See also RadiantSystemLowTemp module.
703 :
704 : Real64 CalcEffectiveness; // Function return variable
705 :
706 619755 : Real64 constexpr MaxLaminarRe(2300.0); // Maximum Reynolds number for laminar flow
707 619755 : Real64 constexpr GravConst(9.81); // gravitational constant - should be fixed!
708 : static constexpr std::string_view CalledFrom("PondGroundHeatExchanger:CalcEffectiveness");
709 :
710 : // evaluate properties at pipe fluid temperature for given pipe fluid
711 :
712 619755 : Real64 SpecificHeat = this->plantLoc.loop->glycol->getSpecificHeat(state, InsideTemperature, CalledFrom);
713 619755 : Real64 Conductivity = this->plantLoc.loop->glycol->getConductivity(state, InsideTemperature, CalledFrom);
714 619755 : Real64 Viscosity = this->plantLoc.loop->glycol->getViscosity(state, InsideTemperature, CalledFrom);
715 :
716 : // Calculate the Reynold's number from RE=(4*Mdot)/(Pi*Mu*Diameter)
717 619755 : Real64 ReynoldsNum = 4.0 * massFlowRate / (Constant::Pi * Viscosity * this->TubeInDiameter * this->NumCircuits);
718 :
719 619755 : Real64 PrantlNum = Viscosity * SpecificHeat / Conductivity;
720 :
721 : Real64 NusseltNum; // Nusselt number (dimensionless)
722 :
723 : // Calculate the Nusselt number based on what flow regime one is in. h = (k)(Nu)/D
724 619755 : if (ReynoldsNum >= MaxLaminarRe) { // Turbulent flow --> use Dittus-Boelter equation
725 500120 : NusseltNum = 0.023 * std::pow(ReynoldsNum, 0.8) * std::pow(PrantlNum, 0.3);
726 : } else { // Laminar flow --> use constant surface temperature relation
727 119635 : NusseltNum = 3.66;
728 : }
729 :
730 : // inside convection resistance, from Nu
731 619755 : Real64 ConvCoefIn = Conductivity * NusseltNum / this->TubeInDiameter;
732 :
733 : // now find properties of pond water - always assume pond fluid is water
734 619755 : Real64 WaterSpecHeat = this->water->getSpecificHeat(state, max(PondTemperature, 0.0), CalledFrom);
735 619755 : Real64 WaterConductivity = this->water->getConductivity(state, max(PondTemperature, 0.0), CalledFrom);
736 619755 : Real64 WaterViscosity = this->water->getViscosity(state, max(PondTemperature, 0.0), CalledFrom);
737 619755 : Real64 WaterDensity = this->water->getDensity(state, max(PondTemperature, 0.0), CalledFrom);
738 :
739 : // derived properties for natural convection coefficient
740 : // expansion coef (Beta) = -1/Rho. dRho/dT
741 : // The following code includes some slight modifications from Simon's original code.
742 : // It guarantees that the delta T is 10C and also avoids the problems associated with
743 : // water hitting a maximum density at around 4C. (RKS)
744 619755 : Real64 ExpansionCoef = -(this->water->getDensity(state, max(PondTemperature, 10.0) + 5.0, CalledFrom) -
745 619755 : this->water->getDensity(state, max(PondTemperature, 10.0) - 5.0, CalledFrom)) /
746 619755 : (10.0 * WaterDensity);
747 :
748 619755 : Real64 ThermDiff = WaterConductivity / (WaterDensity * WaterSpecHeat);
749 619755 : PrantlNum = WaterViscosity * WaterSpecHeat / WaterConductivity;
750 :
751 619755 : Real64 RayleighNum = WaterDensity * GravConst * ExpansionCoef * std::abs(InsideTemperature - PondTemperature) * pow_3(TubeOutDiameter) /
752 619755 : (WaterViscosity * ThermDiff);
753 :
754 : // Calculate the Nusselt number for natural convection at outside of pipe
755 619755 : 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))));
756 :
757 : // outside convection resistance, from Nu
758 619755 : Real64 ConvCoefOut = WaterConductivity * NusseltNum / this->TubeOutDiameter;
759 :
760 : // conduction resistance of pipe
761 619755 : Real64 PipeResistance = this->TubeInDiameter / this->TubeConductivity * std::log(this->TubeOutDiameter / this->TubeInDiameter);
762 :
763 : // total pipe thermal resistance - conduction and convection
764 619755 : Real64 TotalResistance = PipeResistance + 1.0 / ConvCoefIn + this->TubeInDiameter / (this->TubeOutDiameter * ConvCoefOut);
765 :
766 : // Calculate the NTU parameter
767 : // NTU = UA/[(Mdot*Cp)min] = A/[Rtot*(Mdot*Cp)min]
768 : // where: Rtot = Ri,convection + Rconduction + Ro,conveciton
769 : // A = Pi*D*TubeLength
770 :
771 : Real64 NTU; // Number of transfer units, non-dimensional
772 :
773 619755 : if (massFlowRate == 0.0) {
774 55955 : CalcEffectiveness = 1.0;
775 : } else {
776 563800 : NTU = Constant::Pi * TubeInDiameter * this->CircuitLength * this->NumCircuits / (TotalResistance * massFlowRate * SpecificHeat);
777 : // Calculate effectiveness - formula for static fluid
778 563800 : CalcEffectiveness = (1.0 - std::exp(-NTU));
779 : }
780 :
781 : // Check for frozen pond
782 619755 : if (PondTemperature < 0.0) {
783 0 : ++this->ConsecutiveFrozen;
784 0 : if (this->FrozenErrIndex == 0) {
785 0 : ShowWarningMessage(state,
786 0 : format("GroundHeatExchanger:Pond=\"{}\", is frozen; Pond model not valid. Calculated Pond Temperature=[{:.2R}] C",
787 0 : this->Name,
788 : PondTemperature));
789 0 : ShowContinueErrorTimeStamp(state, "");
790 : }
791 0 : ShowRecurringWarningErrorAtEnd(state,
792 0 : "GroundHeatExchanger:Pond=\"" + this->Name + "\", is frozen",
793 0 : this->FrozenErrIndex,
794 : PondTemperature,
795 : PondTemperature,
796 : _,
797 : "[C]",
798 : "[C]");
799 0 : if (this->ConsecutiveFrozen >= state.dataGlobal->TimeStepsInHour * 30) {
800 0 : ShowFatalError(state,
801 0 : format("GroundHeatExchanger:Pond=\"{}\" has been frozen for 30 consecutive hours. Program terminates.", this->Name));
802 : }
803 : } else {
804 619755 : this->ConsecutiveFrozen = 0;
805 : }
806 :
807 619755 : return CalcEffectiveness;
808 : }
809 :
810 123951 : void PondGroundHeatExchangerData::UpdatePondGroundHeatExchanger(EnergyPlusData &state)
811 : {
812 :
813 : // SUBROUTINE INFORMATION:
814 : // AUTHOR Simon Rees
815 : // DATE WRITTEN August 2002
816 : // MODIFIED na
817 : // RE-ENGINEERED na
818 :
819 : // PURPOSE OF THIS SUBROUTINE:
820 : // This subroutine does any updating that needs to be done for pond
821 : // ground heat exchangers. This routine must also set the outlet water
822 : // conditions.
823 :
824 : static constexpr std::string_view RoutineName("PondGroundHeatExchanger:Update");
825 :
826 : // Calculate the water side outlet conditions and set the
827 : // appropriate conditions on the correct HVAC node.
828 123951 : Real64 CpFluid = this->plantLoc.loop->glycol->getSpecificHeat(state, this->InletTemp, RoutineName);
829 :
830 123951 : PlantUtilities::SafeCopyPlantNode(state, InletNodeNum, OutletNodeNum);
831 :
832 : // update outlet temp
833 123951 : if ((CpFluid > 0.0) && (this->MassFlowRate > 0.0)) {
834 112760 : this->OutletTemp = this->InletTemp - this->HeatTransferRate / (this->MassFlowRate * CpFluid);
835 : } else {
836 11191 : this->OutletTemp = this->InletTemp;
837 : }
838 :
839 : // update node
840 123951 : state.dataLoopNodes->Node(this->OutletNodeNum).Temp = this->OutletTemp;
841 123951 : state.dataLoopNodes->Node(this->OutletNodeNum).MassFlowRate = this->MassFlowRate;
842 :
843 : // update heat transfer rate
844 : // compute pond heat transfer
845 123951 : Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, this->PondTemp, this->MassFlowRate);
846 123951 : this->HeatTransferRate = this->MassFlowRate * CpFluid * effectiveness * (this->InletTemp - this->PondTemp);
847 123951 : this->Energy = this->HeatTransferRate * state.dataHVACGlobal->TimeStepSysSec;
848 :
849 : // keep track of the bulk temperature
850 123951 : this->BulkTemperature = this->PondTemp;
851 123951 : }
852 123966 : void PondGroundHeatExchangerData::oneTimeInit(EnergyPlusData &state)
853 : {
854 123966 : Real64 constexpr DesignVelocity(0.5); // Hypothetical design max pipe velocity [m/s]
855 123966 : Real64 constexpr PondHeight(0.0); // for now
856 :
857 123972 : static std::string const RoutineName("InitPondGroundHeatExchanger");
858 :
859 123966 : if (this->setupOutputVarsFlag) {
860 3 : this->setupOutputVars(state);
861 3 : this->setupOutputVarsFlag = false;
862 : }
863 :
864 123966 : if (this->OneTimeFlag || state.dataGlobal->WarmupFlag) {
865 : // initialize pond temps to mean of drybulb and ground temps.
866 106490 : this->BulkTemperature = this->PastBulkTemperature =
867 106490 : 0.5 * (DataEnvironment::OutDryBulbTempAt(state, PondHeight) + state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
868 106490 : this->OneTimeFlag = false;
869 : }
870 :
871 : // Init more variables
872 123966 : if (this->MyFlag) {
873 : // Locate the hx on the plant loops for later usage
874 3 : bool errFlag = false;
875 6 : PlantUtilities::ScanPlantLoopsForObject(
876 3 : state, this->Name, DataPlant::PlantEquipmentType::GrndHtExchgPond, this->plantLoc, errFlag, _, _, _, _, _);
877 3 : if (errFlag) {
878 0 : ShowFatalError(state, "InitPondGroundHeatExchanger: Program terminated due to previous condition(s).");
879 : }
880 3 : Real64 rho = this->plantLoc.loop->glycol->getDensity(state, 0.0, RoutineName);
881 3 : Real64 Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, 0.0, RoutineName);
882 3 : this->DesignMassFlowRate = Constant::Pi / 4.0 * pow_2(this->TubeInDiameter) * DesignVelocity * rho * this->NumCircuits;
883 3 : this->DesignCapacity = this->DesignMassFlowRate * Cp * 10.0; // assume 10C delta T?
884 3 : PlantUtilities::InitComponentNodes(state, 0.0, this->DesignMassFlowRate, this->InletNodeNum, this->OutletNodeNum);
885 3 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->InletNodeNum, this->DesignMassFlowRate / rho);
886 :
887 3 : this->MyFlag = false;
888 : }
889 123966 : }
890 :
891 : } // namespace EnergyPlus::PondGroundHeatExchanger
|