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